Ruby: a constant assignment whose RHS is ``Struct.new(...)``, ``Class.new(Super)`` or ``Data.define(...)`` defines a class named after the constant (#1640). Synthesize the class node, attach block-defined methods via ``method`` (by recursing the block with the new node as parent), and em
(node, source: bytes, file_nid: str, stem: str, str_path: str,
nodes: list, edges: list, seen_ids: set, function_bodies: list,
parent_class_nid: str | None, add_node, add_edge, walk,
callable_def_nids: set)
| 3380 | |
| 3381 | |
| 3382 | def _ruby_extra_walk(node, source: bytes, file_nid: str, stem: str, str_path: str, |
| 3383 | nodes: list, edges: list, seen_ids: set, function_bodies: list, |
| 3384 | parent_class_nid: str | None, add_node, add_edge, walk, |
| 3385 | callable_def_nids: set) -> bool: |
| 3386 | """Ruby: a constant assignment whose RHS is ``Struct.new(...)``, |
| 3387 | ``Class.new(Super)`` or ``Data.define(...)`` defines a class named after the |
| 3388 | constant (#1640). Synthesize the class node, attach block-defined methods via |
| 3389 | ``method`` (by recursing the block with the new node as parent), and emit an |
| 3390 | ``inherits`` edge for ``Class.new(Super)``. Returns True if handled. |
| 3391 | """ |
| 3392 | if node.type != "assignment": |
| 3393 | return False |
| 3394 | left = node.child_by_field_name("left") |
| 3395 | right = node.child_by_field_name("right") |
| 3396 | if left is None or right is None or left.type != "constant" or right.type != "call": |
| 3397 | return False |
| 3398 | recv = right.child_by_field_name("receiver") |
| 3399 | meth = right.child_by_field_name("method") |
| 3400 | if recv is None or meth is None or recv.type != "constant": |
| 3401 | return False |
| 3402 | if (_read_text(recv, source), _read_text(meth, source)) not in _RUBY_CLASS_FACTORIES: |
| 3403 | return False |
| 3404 | |
| 3405 | const_name = _read_text(left, source) |
| 3406 | if not const_name: |
| 3407 | return False |
| 3408 | line = node.start_point[0] + 1 |
| 3409 | class_nid = _make_id(stem, const_name) |
| 3410 | add_node(class_nid, const_name, line) |
| 3411 | callable_def_nids.add(class_nid) # a class is callable (its constructor) |
| 3412 | # Mirror the generic class branch: containment always hangs off the file node. |
| 3413 | add_edge(file_nid, class_nid, "contains", line) |
| 3414 | |
| 3415 | # `Class.new(Super)` — the first positional constant argument is the superclass. |
| 3416 | if _read_text(recv, source) == "Class": |
| 3417 | args = next((c for c in right.children if c.type == "argument_list"), None) |
| 3418 | if args is not None: |
| 3419 | for arg in args.children: |
| 3420 | if arg.type in ("constant", "scope_resolution"): |
| 3421 | base = _ruby_const_last_name(arg, source) |
| 3422 | if base: |
| 3423 | base_nid = _make_id(stem, base) |
| 3424 | if base_nid not in seen_ids: |
| 3425 | base_nid = _make_id(base) |
| 3426 | if base_nid not in seen_ids: |
| 3427 | nodes.append({ |
| 3428 | "id": base_nid, "label": base, |
| 3429 | "file_type": "code", "source_file": "", |
| 3430 | "source_location": "", |
| 3431 | }) |
| 3432 | seen_ids.add(base_nid) |
| 3433 | add_edge(class_nid, base_nid, "inherits", line) |
| 3434 | break |
| 3435 | |
| 3436 | # Recurse the do/brace block so block-defined methods attach to the class. |
| 3437 | # The block wraps its statements in a `body_statement` (like a class body); |
| 3438 | # descend into it so the method handler sees parent_class_nid — otherwise the |
| 3439 | # default recurse resets the parent to None and the method hangs off the file |
no test coverage detected