Coverage for src / renaissance / syntax_tree / ast_refactor_actions.py: 91%
47 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-09-09 14:04 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-09-09 14:04 +0000
1from collections.abc import Sequence
2from functools import cache
3from typing import TYPE_CHECKING
5from ..integrations.types import BogusType, Type
6from .ast_finder import ASTFinder, matches_kind
7from .ast_node import ASTNode
8from .ast_processor import ASTProcessor
9from .match_finder import MatchFinder, PatternMatch
11if TYPE_CHECKING:
12 from renaissance.integrations.clang.c_pattern_factory import CPPPatternFactory
15class ASTRefactorActions:
16 def __init__(self, processor: ASTProcessor, pattern_factory: CPPPatternFactory) -> None:
17 self.processor = processor
18 self.pattern_factory = pattern_factory
19 self.replaced: set[int] = set()
21 def replace_expr(self, name: str, replacement: str, kind: type[Type]):
22 def test(n: ASTNode):
23 if (kind and matches_kind(n, kind)) and n.name == name:
24 yield n
26 [self.processor.replace(found.text.replace(found.name, replacement, 1), found) for found in self.processor.find_all(test)]
28 def replace_name(
29 self,
30 name: str,
31 replacement: str,
32 kind: type[Type] = None,
33 skip_kind: type[Type] = BogusType,
34 ):
35 def matches_name(n1: ASTNode) -> bool:
36 return (
37 (not kind or ASTFinder.matches_kind(n1, kind))
38 and (not skip_kind or not ASTFinder.matches_kind(n1, skip_kind))
39 and n1
40 and n1.name == name
41 )
43 found_nodes = self.processor.find_all(matches_name)
44 [self.replaced.add(found.offset) for found in found_nodes if found.offset not in self.replaced]
45 for n in found_nodes:
46 self.processor.replace(n.text.replace(n.name, replacement, 1), n)
48 def replace_text(
49 self,
50 text: str,
51 replacement: str,
52 kind: type[Type] = None,
53 skip_kind: type[Type] = BogusType,
54 ):
55 def matches_text(n: ASTNode) -> bool:
56 return (
57 (not kind or ASTFinder.matches_kind(n, kind))
58 and (not skip_kind or not ASTFinder.matches_kind(n, skip_kind))
59 and n is not None
60 and n.text == text
61 )
63 found_nodes = self.processor.find_all(matches_text)
64 [self.replaced.add(found.offset) for found in found_nodes if found.offset not in self.replaced]
66 [self.processor.replace(n.text.replace(n.name, replacement, 1), n) for n in found_nodes]
68 def replace_declaration(self, declaration: str, replacement: str):
69 for match in self.find_declaration(declaration):
70 self.processor.replace(replacement, match)
72 def _replace_patterns(
73 self,
74 node: ASTNode,
75 replacement: str,
76 patterns: Sequence[Sequence[ASTNode]],
77 matches: Sequence[PatternMatch],
78 ):
79 if not patterns:
80 self.processor.replace(replacement, matches)
81 return
82 [
83 self._replace_patterns(m.nodes[0], replacement, patterns[1:], list(matches) + [m])
84 for m in MatchFinder.find_all([node], patterns[0])
85 ]
87 @cache
88 def find_declaration(self, decl_pattern: str):
89 pattern = self.pattern_factory.create_declaration(decl_pattern)
90 return self.processor.find_match(pattern)
92 @cache
93 def collect(self, pattern: str, pattern_kind: str):
94 root = self.pattern_factory.create(pattern, pattern_kind)
96 return self.processor.find_match(root)