|
87 | 87 | Do,
|
88 | 88 | Fn,
|
89 | 89 | FnArity,
|
| 90 | + FunctionContext, |
90 | 91 | HostCall,
|
91 | 92 | HostField,
|
92 | 93 | If,
|
@@ -309,7 +310,7 @@ def __init__(
|
309 | 310 | ) -> None:
|
310 | 311 | self._allow_unresolved_symbols = allow_unresolved_symbols
|
311 | 312 | self._filename = Maybe(filename).or_else_get(DEFAULT_COMPILER_FILE_PATH)
|
312 |
| - self._func_ctx: Deque[bool] = collections.deque([]) |
| 313 | + self._func_ctx: Deque[FunctionContext] = collections.deque([]) |
313 | 314 | self._is_quoted: Deque[bool] = collections.deque([])
|
314 | 315 | self._macro_ns: Deque[Optional[runtime.Namespace]] = collections.deque([])
|
315 | 316 | self._opts = (
|
@@ -406,34 +407,33 @@ def should_macroexpand(self) -> bool:
|
406 | 407 | return self._should_macroexpand
|
407 | 408 |
|
408 | 409 | @property
|
409 |
| - def is_async_ctx(self) -> bool: |
410 |
| - """If True, the current node appears inside of an async function definition. |
| 410 | + def func_ctx(self) -> Optional[FunctionContext]: |
| 411 | + """Return the current function or method context of the current node, if one. |
| 412 | + Return None otherwise. |
| 413 | +
|
411 | 414 | It is possible that the current function is defined inside other functions,
|
412 | 415 | so this does not imply anything about the nesting level of the current node."""
|
413 | 416 | try:
|
414 |
| - return self._func_ctx[-1] is True |
| 417 | + return self._func_ctx[-1] |
415 | 418 | except IndexError:
|
416 |
| - return False |
| 419 | + return None |
417 | 420 |
|
418 | 421 | @property
|
419 |
| - def in_func_ctx(self) -> bool: |
420 |
| - """If True, the current node appears inside of a function definition. |
| 422 | + def is_async_ctx(self) -> bool: |
| 423 | + """Return True if the current node appears inside of an async function |
| 424 | + definition. Return False otherwise. |
| 425 | +
|
421 | 426 | It is possible that the current function is defined inside other functions,
|
422 | 427 | so this does not imply anything about the nesting level of the current node."""
|
423 |
| - try: |
424 |
| - self._func_ctx[-1] |
425 |
| - except IndexError: |
426 |
| - return False |
427 |
| - else: |
428 |
| - return True |
| 428 | + return self.func_ctx == FunctionContext.ASYNC_FUNCTION |
429 | 429 |
|
430 | 430 | @contextlib.contextmanager
|
431 |
| - def new_func_ctx(self, is_async: bool = False): |
432 |
| - """Context manager which can be used to set a function context for child |
433 |
| - nodes to examine. A new function context is pushed onto the stack each time |
434 |
| - the Analyzer finds a new function definition, so there may be many nested |
435 |
| - function contexts.""" |
436 |
| - self._func_ctx.append(is_async) |
| 431 | + def new_func_ctx(self, context_type: FunctionContext): |
| 432 | + """Context manager which can be used to set a function or method context for |
| 433 | + child nodes to examine. A new function context is pushed onto the stack each |
| 434 | + time the Analyzer finds a new function or method definition, so there may be |
| 435 | + many nested function contexts.""" |
| 436 | + self._func_ctx.append(context_type) |
437 | 437 | yield
|
438 | 438 | self._func_ctx.pop()
|
439 | 439 |
|
@@ -578,8 +578,14 @@ def syntax_position(self) -> NodeSyntacticPosition:
|
578 | 578 | parent node."""
|
579 | 579 | return self._syntax_pos[-1]
|
580 | 580 |
|
581 |
| - def get_node_env(self, pos: Optional[NodeSyntacticPosition] = None): |
582 |
| - return NodeEnv(ns=self.current_ns, file=self.filename, pos=pos) |
| 581 | + def get_node_env(self, pos: Optional[NodeSyntacticPosition] = None) -> NodeEnv: |
| 582 | + """Return the current Node environment. |
| 583 | +
|
| 584 | + If a synax position is given, it will be included in the environment. |
| 585 | + Otherwise, the position will be set to None.""" |
| 586 | + return NodeEnv( |
| 587 | + ns=self.current_ns, file=self.filename, pos=pos, func_ctx=self.func_ctx |
| 588 | + ) |
583 | 589 |
|
584 | 590 |
|
585 | 591 | MetaGetter = Callable[[Union[IMeta, Var]], bool]
|
@@ -810,10 +816,11 @@ def _def_ast( # pylint: disable=too-many-branches,too-many-locals
|
810 | 816 | f"def names must be symbols, not {type(name)}", form=name
|
811 | 817 | )
|
812 | 818 |
|
| 819 | + children: vec.Vector[kw.Keyword] |
813 | 820 | if nelems == 2:
|
814 | 821 | init = None
|
815 | 822 | doc = None
|
816 |
| - children: vec.Vector[kw.Keyword] = vec.Vector.empty() |
| 823 | + children = vec.Vector.empty() |
817 | 824 | elif nelems == 3:
|
818 | 825 | with ctx.expr_pos():
|
819 | 826 | init = _analyze_form(ctx, runtime.nth(form, 2))
|
@@ -895,7 +902,6 @@ def _def_ast( # pylint: disable=too-many-branches,too-many-locals
|
895 | 902 | var=var,
|
896 | 903 | init=init,
|
897 | 904 | doc=doc,
|
898 |
| - in_func_ctx=ctx.in_func_ctx, |
899 | 905 | children=children,
|
900 | 906 | env=def_node_env,
|
901 | 907 | )
|
@@ -1037,7 +1043,7 @@ def __deftype_classmethod(
|
1037 | 1043 | has_vargs, fixed_arity, param_nodes = __deftype_method_param_bindings(
|
1038 | 1044 | ctx, params
|
1039 | 1045 | )
|
1040 |
| - with ctx.expr_pos(): |
| 1046 | + with ctx.new_func_ctx(FunctionContext.CLASSMETHOD), ctx.expr_pos(): |
1041 | 1047 | stmts, ret = _body_ast(ctx, runtime.nthrest(form, 2))
|
1042 | 1048 | method = DefTypeClassMethodArity(
|
1043 | 1049 | form=form,
|
@@ -1097,7 +1103,7 @@ def __deftype_method(
|
1097 | 1103 |
|
1098 | 1104 | loop_id = genname(method_name)
|
1099 | 1105 | with ctx.new_recur_point(loop_id, param_nodes):
|
1100 |
| - with ctx.expr_pos(): |
| 1106 | + with ctx.new_func_ctx(FunctionContext.METHOD), ctx.expr_pos(): |
1101 | 1107 | stmts, ret = _body_ast(ctx, runtime.nthrest(form, 2))
|
1102 | 1108 | method = DefTypeMethodArity(
|
1103 | 1109 | form=form,
|
@@ -1160,7 +1166,7 @@ def __deftype_property(
|
1160 | 1166 |
|
1161 | 1167 | assert not has_vargs, "deftype* properties may not have arguments"
|
1162 | 1168 |
|
1163 |
| - with ctx.expr_pos(): |
| 1169 | + with ctx.new_func_ctx(FunctionContext.PROPERTY), ctx.expr_pos(): |
1164 | 1170 | stmts, ret = _body_ast(ctx, runtime.nthrest(form, 2))
|
1165 | 1171 | prop = DefTypeProperty(
|
1166 | 1172 | form=form,
|
@@ -1192,7 +1198,7 @@ def __deftype_staticmethod(
|
1192 | 1198 | """Emit a node for a :staticmethod member of a deftype* form."""
|
1193 | 1199 | with ctx.hide_parent_symbol_table(), ctx.new_symbol_table(method_name):
|
1194 | 1200 | has_vargs, fixed_arity, param_nodes = __deftype_method_param_bindings(ctx, args)
|
1195 |
| - with ctx.expr_pos(): |
| 1201 | + with ctx.new_func_ctx(FunctionContext.STATICMETHOD), ctx.expr_pos(): |
1196 | 1202 | stmts, ret = _body_ast(ctx, runtime.nthrest(form, 2))
|
1197 | 1203 | method = DefTypeStaticMethodArity(
|
1198 | 1204 | form=form,
|
@@ -1652,7 +1658,10 @@ def _do_ast(ctx: AnalyzerContext, form: ISeq) -> Do:
|
1652 | 1658 |
|
1653 | 1659 |
|
1654 | 1660 | def __fn_method_ast( # pylint: disable=too-many-branches,too-many-locals
|
1655 |
| - ctx: AnalyzerContext, form: ISeq, fnname: Optional[sym.Symbol] = None |
| 1661 | + ctx: AnalyzerContext, |
| 1662 | + form: ISeq, |
| 1663 | + fnname: Optional[sym.Symbol] = None, |
| 1664 | + is_async: bool = False, |
1656 | 1665 | ) -> FnArity:
|
1657 | 1666 | with ctx.new_symbol_table("fn-method"):
|
1658 | 1667 | params = form.first
|
@@ -1711,7 +1720,9 @@ def __fn_method_ast( # pylint: disable=too-many-branches,too-many-locals
|
1711 | 1720 |
|
1712 | 1721 | fn_loop_id = genname("fn_arity" if fnname is None else fnname.name)
|
1713 | 1722 | with ctx.new_recur_point(fn_loop_id, param_nodes):
|
1714 |
| - with ctx.expr_pos(): |
| 1723 | + with ctx.new_func_ctx( |
| 1724 | + FunctionContext.ASYNC_FUNCTION if is_async else FunctionContext.FUNCTION |
| 1725 | + ), ctx.expr_pos(): |
1715 | 1726 | stmts, ret = _body_ast(ctx, form.rest)
|
1716 | 1727 | method = FnArity(
|
1717 | 1728 | form=form,
|
@@ -1770,11 +1781,11 @@ def _fn_ast( # pylint: disable=too-many-branches
|
1770 | 1781 | form=form,
|
1771 | 1782 | )
|
1772 | 1783 |
|
| 1784 | + name_node: Optional[Binding] |
1773 | 1785 | if isinstance(name, sym.Symbol):
|
1774 |
| - name_node: Optional[Binding] = Binding( |
| 1786 | + name_node = Binding( |
1775 | 1787 | form=name, name=name.name, local=LocalType.FN, env=ctx.get_node_env()
|
1776 | 1788 | )
|
1777 |
| - assert name_node is not None |
1778 | 1789 | is_async = _is_async(name) or isinstance(form, IMeta) and _is_async(form)
|
1779 | 1790 | kwarg_support = (
|
1780 | 1791 | __fn_kwargs_support(name)
|
@@ -1802,23 +1813,24 @@ def _fn_ast( # pylint: disable=too-many-branches
|
1802 | 1813 | form=form,
|
1803 | 1814 | )
|
1804 | 1815 |
|
1805 |
| - with ctx.new_func_ctx(is_async=is_async): |
1806 |
| - if isinstance(arity_or_args, llist.List): |
1807 |
| - arities = vec.vector( |
1808 |
| - map( |
1809 |
| - partial(__fn_method_ast, ctx, fnname=name), |
1810 |
| - runtime.nthrest(form, idx), |
1811 |
| - ) |
1812 |
| - ) |
1813 |
| - elif isinstance(arity_or_args, vec.Vector): |
1814 |
| - arities = vec.v( |
1815 |
| - __fn_method_ast(ctx, runtime.nthrest(form, idx), fnname=name) |
| 1816 | + if isinstance(arity_or_args, llist.List): |
| 1817 | + arities = vec.vector( |
| 1818 | + map( |
| 1819 | + partial(__fn_method_ast, ctx, fnname=name, is_async=is_async), |
| 1820 | + runtime.nthrest(form, idx), |
1816 | 1821 | )
|
1817 |
| - else: |
1818 |
| - raise AnalyzerException( |
1819 |
| - "fn form must match: (fn* name? [arg*] body*) or (fn* name? method*)", |
1820 |
| - form=form, |
| 1822 | + ) |
| 1823 | + elif isinstance(arity_or_args, vec.Vector): |
| 1824 | + arities = vec.v( |
| 1825 | + __fn_method_ast( |
| 1826 | + ctx, runtime.nthrest(form, idx), fnname=name, is_async=is_async |
1821 | 1827 | )
|
| 1828 | + ) |
| 1829 | + else: |
| 1830 | + raise AnalyzerException( |
| 1831 | + "fn form must match: (fn* name? [arg*] body*) or (fn* name? method*)", |
| 1832 | + form=form, |
| 1833 | + ) |
1822 | 1834 |
|
1823 | 1835 | nmethods = count(arities)
|
1824 | 1836 | assert nmethods > 0, "fn must have at least one arity"
|
|
0 commit comments