diff --git a/features/steps/conftest.py b/features/steps/conftest.py index 3ee3c889..dd11dd27 100644 --- a/features/steps/conftest.py +++ b/features/steps/conftest.py @@ -44,7 +44,7 @@ } -def pytest_collection_modifyitems(items): +def pytest_collection_modifyitems(items) -> None: """AI: Convert bare Examples-block tags into properly configured xfail markers.""" for item in items: for tag, xfail_kwargs in _XFAIL_TAGS.items(): diff --git a/features/steps/test_refactor.py b/features/steps/test_refactor.py index 9f073f09..9a4c13fa 100644 --- a/features/steps/test_refactor.py +++ b/features/steps/test_refactor.py @@ -19,7 +19,7 @@ def __getattr__(self, name): """AI: Return the dict item named `name` as an attribute.""" return self[name] - def __setattr__(self, name, value): + def __setattr__(self, name, value) -> None: """AI: Set the dict item named `name` as an attribute.""" self[name] = value @@ -36,24 +36,24 @@ def context(): encoding="utf-8", features_base_dir=str(FEATURES_BASE_DIR), ) -def test_refactor_python_file(): +def test_refactor_python_file() -> None: """AI: Scenario test for the 'python code' refactor-python-file.feature scenario.""" @given("'python' programming language") -def init_language_factory(context): +def init_language_factory(context) -> None: """AI: Initialize the AST factory for the Python programming language.""" context["factory"] = ASTFactory(PythonRstNode, "") @given(parsers.parse("'{file}' file written in that programming language")) -def step_given_file_in_language(context, file): +def step_given_file_in_language(context, file) -> None: """AI: Parse the given file into an AST using the scenario's factory.""" context["atu"] = context["factory"].create(FEATURES_BASE_DIR / Path(file)) @given(parsers.parse("node '{old}' exits within that AST")) -def step_given_node_exists(context, old): +def step_given_node_exists(context, old) -> None: """AI: Assert that a node matching the given pattern exists in the AST.""" pattern_factory = PythonPatternFactory(context["factory"]) find = pattern_factory.create_statements(old) @@ -62,13 +62,13 @@ def step_given_node_exists(context, old): @given("a sequence of descendant nodes of that node") -def step_given_descendant_nodes(context): +def step_given_descendant_nodes(context) -> None: """AI: Assert that the matched node has descendant children.""" assert context["result"][0].nodes[0].children @when(parsers.parse("that node is replaced by '{replacement}'")) -def step_when_node_replaced(context, replacement): +def step_when_node_replaced(context, replacement) -> None: """AI: Queue a replacement of the matched node with the given text.""" context["replacement"] = replacement context["rewriter"] = ASTRewriter(context["atu"]) @@ -76,18 +76,18 @@ def step_when_node_replaced(context, replacement): @when("rewrites replace is performed on that sequence of descendant nodes") -def step_when_rewrites_applied(context): +def step_when_rewrites_applied(context) -> None: """AI: Apply the queued rewrite to the AST.""" context["rewriter"].apply() @then("in the modified source file that node is replaced by the given text") -def step_then_replaced_in_source(context): +def step_then_replaced_in_source(context) -> None: """AI: Assert the replacement text appears in the rewritten source.""" assert context["replacement"] in context["rewriter"].apply_to_string() @then("all rewrites on that sequence of descendant nodes are not performed or hidden") -def step_then_rewrites_not_performed_or_hidden(context): +def step_then_rewrites_not_performed_or_hidden(context) -> None: """AI: Assert the rewriter reports pending, unapplied changes.""" assert context["rewriter"].has_changed() diff --git a/features/steps/test_rewrite_semantics.py b/features/steps/test_rewrite_semantics.py index 2dfbfbfb..820a90de 100644 --- a/features/steps/test_rewrite_semantics.py +++ b/features/steps/test_rewrite_semantics.py @@ -41,7 +41,7 @@ # Scenario 0 — Scenario Outline: three representative examples. # Universal property test: test/syntax_tree/test_rewrite_semantics_properties.py @scenario(_FEATURE, "Replacements of the same node produce an error") -def test_replacements_of_same_node_produce_error(): +def test_replacements_of_same_node_produce_error() -> None: """AI: Scenario test for 'Replacements of the same node produce an error'.""" @@ -50,37 +50,37 @@ def test_replacements_of_same_node_produce_error(): strict=True, ) @scenario(_FEATURE, "Dominated change is not applied") -def test_dominated_change_not_applied(): +def test_dominated_change_not_applied() -> None: """AI: Scenario test for 'Dominated change is not applied'.""" @scenario(_FEATURE, "Overlapping replacements produce an error") -def test_overlapping_replacements_produce_error(): +def test_overlapping_replacements_produce_error() -> None: """AI: Scenario test for 'Overlapping replacements produce an error'.""" @scenario(_FEATURE, "Prepend of ancestor precedes prepend of descendant regardless of collection order") -def test_prepend_ordering(): +def test_prepend_ordering() -> None: """AI: Scenario test for 'Prepend of ancestor precedes prepend of descendant regardless of collection order'.""" @scenario(_FEATURE, "Append of descendant precedes append of ancestor regardless of collection order") -def test_append_ordering(): +def test_append_ordering() -> None: """AI: Scenario test for 'Append of descendant precedes append of ancestor regardless of collection order'.""" @scenario(_FEATURE, "Operation on first sibling precedes operation on second sibling \u2014 first sibling collected first") -def test_sibling_sib1_first(): +def test_sibling_sib1_first() -> None: """AI: Scenario test for 'Operation on first sibling precedes operation on second sibling' (first sibling collected first).""" @scenario(_FEATURE, "Operation on first sibling precedes operation on second sibling \u2014 second sibling collected first") -def test_sibling_sib2_first(): +def test_sibling_sib2_first() -> None: """AI: Scenario test for 'Operation on first sibling precedes operation on second sibling' (second sibling collected first).""" @scenario(_FEATURE, "Replacements of the same sibling range produce an error") -def test_replacements_of_same_sibling_range_produce_error(): +def test_replacements_of_same_sibling_range_produce_error() -> None: """AI: Scenario test for 'Replacements of the same sibling range produce an error'.""" @@ -89,7 +89,7 @@ def test_replacements_of_same_sibling_range_produce_error(): strict=True, ) @scenario(_FEATURE, "Sibling range dominates a proper subrange regardless of collection order") -def test_sibling_range_dominates_proper_subrange(): +def test_sibling_range_dominates_proper_subrange() -> None: """AI: Scenario test for 'Sibling range dominates a proper subrange regardless of collection order'.""" @@ -98,12 +98,12 @@ def test_sibling_range_dominates_proper_subrange(): strict=True, ) @scenario(_FEATURE, "Sibling range dominates a single contained sibling regardless of collection order") -def test_sibling_range_dominates_single_sibling(): +def test_sibling_range_dominates_single_sibling() -> None: """AI: Scenario test for 'Sibling range dominates a single contained sibling regardless of collection order'.""" @scenario(_FEATURE, "Prepends of same node are applied in order of collection.") -def test_prepends_of_same_node_in_order(): +def test_prepends_of_same_node_in_order() -> None: """AI: Scenario test for 'Prepends of same node are applied in order of collection.'.""" @@ -112,7 +112,7 @@ def test_prepends_of_same_node_in_order(): strict=True, ) @scenario(_FEATURE, "Appends of same node are applied in reversed order of collection.") -def test_appends_of_same_node_in_reversed_order(): +def test_appends_of_same_node_in_reversed_order() -> None: """AI: Scenario test for 'Appends of same node are applied in reversed order of collection.'.""" @@ -121,22 +121,22 @@ def test_appends_of_same_node_in_reversed_order(): strict=True, ) @scenario(_FEATURE, "Surrounds of same node: before texts in collection order, after texts in reversed collection order") -def test_surrounds_of_same_node(): +def test_surrounds_of_same_node() -> None: """AI: Scenario test for 'Surrounds of same node' before/after text ordering.""" @scenario(_FEATURE, "Surround of ancestor precedes surround of descendant at shared start location regardless of collection order") -def test_surround_ancestor_precedes_surround_descendant_start(): +def test_surround_ancestor_precedes_surround_descendant_start() -> None: """AI: Scenario test for 'Surround of ancestor precedes surround of descendant at shared start location'.""" @scenario(_FEATURE, "Surround of descendant precedes surround of ancestor at shared end location regardless of collection order") -def test_surround_descendant_precedes_surround_ancestor_end(): +def test_surround_descendant_precedes_surround_ancestor_end() -> None: """AI: Scenario test for 'Surround of descendant precedes surround of ancestor at shared end location'.""" @scenario(_FEATURE, "Prepend is outside surround of the same node \u2014 prepend collected first") -def test_prepend_outside_surround_prepend_first(): +def test_prepend_outside_surround_prepend_first() -> None: """AI: Scenario test for 'Prepend is outside surround of the same node' (prepend collected first).""" @@ -146,7 +146,7 @@ def test_prepend_outside_surround_prepend_first(): strict=True, ) @scenario(_FEATURE, "Prepend is outside surround of the same node \u2014 surround collected first") -def test_prepend_outside_surround_surround_first(): +def test_prepend_outside_surround_surround_first() -> None: """AI: Scenario test for 'Prepend is outside surround of the same node' (surround collected first).""" @@ -156,17 +156,17 @@ def test_prepend_outside_surround_surround_first(): strict=True, ) @scenario(_FEATURE, "Append is outside surround of the same node \u2014 append collected first") -def test_append_outside_surround_append_first(): +def test_append_outside_surround_append_first() -> None: """AI: Scenario test for 'Append is outside surround of the same node' (append collected first).""" @scenario(_FEATURE, "Append is outside surround of the same node \u2014 surround collected first") -def test_append_outside_surround_surround_first(): +def test_append_outside_surround_surround_first() -> None: """AI: Scenario test for 'Append is outside surround of the same node' (surround collected first).""" @scenario(_FEATURE, "Prepend appears before replacement of the same node \u2014 prepend collected first") -def test_prepend_before_replacement_prepend_first(): +def test_prepend_before_replacement_prepend_first() -> None: """AI: Scenario test for 'Prepend appears before replacement of the same node' (prepend collected first).""" @@ -176,17 +176,17 @@ def test_prepend_before_replacement_prepend_first(): strict=True, ) @scenario(_FEATURE, "Prepend appears before replacement of the same node \u2014 replace collected first") -def test_prepend_before_replacement_replace_first(): +def test_prepend_before_replacement_replace_first() -> None: """AI: Scenario test for 'Prepend appears before replacement of the same node' (replace collected first).""" @scenario(_FEATURE, "Replacement appears before append of the same node \u2014 replace collected first") -def test_replacement_before_append_replace_first(): +def test_replacement_before_append_replace_first() -> None: """AI: Scenario test for 'Replacement appears before append of the same node' (replace collected first).""" @scenario(_FEATURE, "Replacement appears before append of the same node \u2014 append collected first") -def test_replacement_before_append_append_first(): +def test_replacement_before_append_append_first() -> None: """AI: Scenario test for 'Replacement appears before append of the same node' (append collected first).""" @@ -196,12 +196,12 @@ def test_replacement_before_append_append_first(): strict=True, ) @scenario(_FEATURE, "Surround wraps replacement of the same node \u2014 replace collected first") -def test_surround_wraps_replacement_replace_first(): +def test_surround_wraps_replacement_replace_first() -> None: """AI: Scenario test for 'Surround wraps replacement of the same node' (replace collected first).""" @scenario(_FEATURE, "Surround wraps replacement of the same node \u2014 surround collected first") -def test_surround_wraps_replacement_surround_first(): +def test_surround_wraps_replacement_surround_first() -> None: """AI: Scenario test for 'Surround wraps replacement of the same node' (surround collected first).""" @@ -211,17 +211,17 @@ def test_surround_wraps_replacement_surround_first(): strict=True, ) @scenario(_FEATURE, "Prepend of descendant is inside surround of ancestor at shared start location \u2014 prepend collected first") -def test_prepend_descendant_inside_surround_ancestor_start_prepend_first(): +def test_prepend_descendant_inside_surround_ancestor_start_prepend_first() -> None: """AI: Scenario test for 'Prepend of descendant is inside surround of ancestor at shared start location' (prepend collected first).""" @scenario(_FEATURE, "Prepend of descendant is inside surround of ancestor at shared start location \u2014 surround collected first") -def test_prepend_descendant_inside_surround_ancestor_start_surround_first(): +def test_prepend_descendant_inside_surround_ancestor_start_surround_first() -> None: """AI: Scenario test for 'Prepend of descendant is inside surround of ancestor at shared start location' (surround collected first).""" @scenario(_FEATURE, "Append of descendant is inside surround of ancestor at shared end location \u2014 append collected first") -def test_append_descendant_inside_surround_ancestor_end_append_first(): +def test_append_descendant_inside_surround_ancestor_end_append_first() -> None: """AI: Scenario test for 'Append of descendant is inside surround of ancestor at shared end location' (append collected first).""" @@ -231,12 +231,12 @@ def test_append_descendant_inside_surround_ancestor_end_append_first(): strict=True, ) @scenario(_FEATURE, "Append of descendant is inside surround of ancestor at shared end location \u2014 surround collected first") -def test_append_descendant_inside_surround_ancestor_end_surround_first(): +def test_append_descendant_inside_surround_ancestor_end_surround_first() -> None: """AI: Scenario test for 'Append of descendant is inside surround of ancestor at shared end location' (surround collected first).""" @scenario(_FEATURE, "Surround of descendant is inside prepend of ancestor at shared start location \u2014 prepend collected first") -def test_surround_descendant_inside_prepend_ancestor_start_prepend_first(): +def test_surround_descendant_inside_prepend_ancestor_start_prepend_first() -> None: """AI: Scenario test for 'Surround of descendant is inside prepend of ancestor at shared start location' (prepend collected first).""" @@ -246,7 +246,7 @@ def test_surround_descendant_inside_prepend_ancestor_start_prepend_first(): strict=True, ) @scenario(_FEATURE, "Surround of descendant is inside prepend of ancestor at shared start location \u2014 surround collected first") -def test_surround_descendant_inside_prepend_ancestor_start_surround_first(): +def test_surround_descendant_inside_prepend_ancestor_start_surround_first() -> None: """AI: Scenario test for 'Surround of descendant is inside prepend of ancestor at shared start location' (surround collected first).""" @@ -256,12 +256,12 @@ def test_surround_descendant_inside_prepend_ancestor_start_surround_first(): strict=True, ) @scenario(_FEATURE, "Surround of descendant is inside append of ancestor at shared end location \u2014 append collected first") -def test_surround_descendant_inside_append_ancestor_end_append_first(): +def test_surround_descendant_inside_append_ancestor_end_append_first() -> None: """AI: Scenario test for 'Surround of descendant is inside append of ancestor at shared end location' (append collected first).""" @scenario(_FEATURE, "Surround of descendant is inside append of ancestor at shared end location \u2014 surround collected first") -def test_surround_descendant_inside_append_ancestor_end_surround_first(): +def test_surround_descendant_inside_append_ancestor_end_surround_first() -> None: """AI: Scenario test for 'Surround of descendant is inside append of ancestor at shared end location' (surround collected first).""" diff --git a/features/steps/test_steps.py b/features/steps/test_steps.py index 6e4917d4..45d5a055 100644 --- a/features/steps/test_steps.py +++ b/features/steps/test_steps.py @@ -15,7 +15,7 @@ class Ast: """AI: Hold the parsed AST state shared across BDD refactor steps.""" - def __init__(self): + def __init__(self) -> None: """AI: Hold the parsed AST state shared across BDD refactor steps.""" self.file = "" self.atu = None @@ -29,7 +29,7 @@ def context(): @given(parsers.parse("'{file}' file")) -def step_given_file(context, file): +def step_given_file(context, file) -> None: """AI: Parse the named file into an AST and record its signature.""" context.file = str(FEATURES_DIR / file) context.factory = PythonFactory(PythonRstNode) @@ -39,7 +39,7 @@ def step_given_file(context, file): @given(parsers.parse("it contains '{statement}'")) @then(parsers.parse("it should contain '{statement}'")) -def step_given_contains(context, statement): +def step_given_contains(context, statement) -> None: """AI: Assert the AST signature contains the given statement.""" statement = statement.replace("\\n", "\n") assert_that(context.signature, contains_string(statement), f"Expected '{statement}' in source") @@ -47,7 +47,7 @@ def step_given_contains(context, statement): @given("an AST extracted from that source file without errors") @then("AST extracted from that conversion should without errors") -def step_given_ast_no_errors(context): +def step_given_ast_no_errors(context) -> None: """AI: Assert diagnostics can be checked without raising an exception.""" assert_that( calling(context.atu.translation_unit.check_diagnostics), @@ -56,6 +56,6 @@ def step_given_ast_no_errors(context): @then(parsers.parse("it should not contain '{statement}'")) -def step_then_not_contain(context, statement): +def step_then_not_contain(context, statement) -> None: """AI: Assert the AST signature does not contain the given statement.""" assert_that(context.signature, not_(contains_string(statement))) diff --git a/features/steps/test_taut_refactor.py b/features/steps/test_taut_refactor.py index 2a20aad8..25ae241a 100644 --- a/features/steps/test_taut_refactor.py +++ b/features/steps/test_taut_refactor.py @@ -14,12 +14,12 @@ encoding="utf-8", features_base_dir=str(FEATURES_BASE_DIR), ) -def test_taut_test(): +def test_taut_test() -> None: """AI: Scenario test for the 'migrate taut to unittest without syntax errors' scenario.""" @when("I convert taut to unittest") -def step_when_convert(context): +def step_when_convert(context) -> None: """AI: Convert the scenario's taut test file to a Python unittest and record its output.""" converter = TautToPythonUnittest(context.file) converter.in_memory = True diff --git a/features/steps/unit_to_pytest_steps.py b/features/steps/unit_to_pytest_steps.py index 6eb57727..c7cc3050 100644 --- a/features/steps/unit_to_pytest_steps.py +++ b/features/steps/unit_to_pytest_steps.py @@ -7,12 +7,12 @@ @scenario("convert-unit-to-pytest.feature", "convert unittest to pytest", "utf-8", "..") -def test_convert_unit_to_pytest(): +def test_convert_unit_to_pytest() -> None: """AI: Scenario test for the 'convert unittest to pytest' scenario.""" @when("I convert it to pytest") -def step_when_convert(context): +def step_when_convert(context) -> None: """AI: Convert the scenario's unittest file to pytest and re-parse it.""" converter = UnitToPytest(context.file) converter.run() diff --git a/features/targets/demo.py b/features/targets/demo.py index 7b7558b5..d739ad24 100644 --- a/features/targets/demo.py +++ b/features/targets/demo.py @@ -17,7 +17,7 @@ def some_old_fun(): long_expression = component_one + component_two + component_three + component_four + component_five + component_six -def xyzzy(a1, a2, long_parameter_1, a3, a4, long_parameter_2): +def xyzzy(a1, a2, long_parameter_1, a3, a4, long_parameter_2) -> None: """AI: No-op function with many parameters, used to exercise long-signature formatting.""" diff --git a/features/targets/go/extractor.py b/features/targets/go/extractor.py index e013b695..090c7493 100644 --- a/features/targets/go/extractor.py +++ b/features/targets/go/extractor.py @@ -12,7 +12,7 @@ class GoExtractor: nodes: dict = {} edges: dict = {} - def process_file(self, file: Path): + def process_file(self, file: Path) -> None: """AI: Parse a Go source file and merge its nodes and reference edges into the codebase graph.""" root = GoAstNode.load(file) tu = root.translation_unit diff --git a/features/targets/pyunit_test_example.py b/features/targets/pyunit_test_example.py index fa414b2d..f5f62b5e 100644 --- a/features/targets/pyunit_test_example.py +++ b/features/targets/pyunit_test_example.py @@ -20,7 +20,7 @@ class FindMatchTest(unittest.TestCase): # def setUpClass(cls): # cls.code_text: str = "int my_function();" - def setUp(self): + def setUp(self) -> None: """AI: Initialize test fixture strings used by the C AST pattern-matching tests.""" self.b = 55 print(f"{self.b=}") @@ -33,7 +33,7 @@ def setUp(self): if self.extra_declarations_inner_text: print(f"{self.extra_declarations_inner_text[0]}") - def tearDown(self): + def tearDown(self) -> None: """AI: Clear the fixture strings set up in setUp.""" self.outer_text: str = None self.inner_text: str = None @@ -42,7 +42,7 @@ def tearDown(self): # def tearDownClass(cls): # cls.code_text: str = None - def test_is_match(self): + def test_is_match(self) -> None: """AI: Assert basic equality/identity semantics used to sanity-check the test fixtures.""" self.assertIn(self.a, [self.a], "An expression matches itself") @@ -58,7 +58,7 @@ def test_is_match(self): ) # TODO: self.a and a string are both expression, so msg is incorrect @parameterized.expand(Factories.factories) - def test_case(self, _: str, factory: ASTFactory): + def test_case(self, _: str, factory: ASTFactory) -> None: """AI: Assert an inner expression pattern doesn't spuriously match an unrelated outer statement pattern.""" pattern_factory = CPatternFactory(factory) code_pattern = factory.create_from_text(self.code_text, "text.c") @@ -101,7 +101,7 @@ class TestBasicNoNamespace(TestCase): ) @unittest.skip("stmt and expr are the same") # unused param - def test_snippet(self, _: str, factory: ASTFactory, snippet: str, extra_declarations: list[str]): + def test_snippet(self, _: str, factory: ASTFactory, snippet: str, extra_declarations: list[str]) -> None: """AI: Assert a literal or placeholder snippet matches exactly one node among the code's children.""" pattern_factory = CPatternFactory(factory) code_pattern = factory.create_from_text(self.code_text, "text.c") # file extension consistent with C Pattern Factory @@ -112,13 +112,13 @@ def test_snippet(self, _: str, factory: ASTFactory, snippet: str, extra_declarat self.assertEqual(1, count, "count = " + str(count)) -def test_it_can_be_created(): +def test_it_can_be_created() -> None: """AI: Assert a PythonRstNode wrapping an ast.Pass node can be constructed.""" it = PythonRstNode(ast.Pass()) assert it -def test_it_has_elements(): +def test_it_has_elements() -> None: """AI: Assert a PythonRstNode's item access matches its children list.""" it = PythonRstNode(ast.parse("def fun(): pass")) assert it[0] == it.children[0] diff --git a/lint-budget.json b/lint-budget.json index 93f8334c..c0b4ea79 100644 --- a/lint-budget.json +++ b/lint-budget.json @@ -21,11 +21,11 @@ "reportPrivateUsage": 73, "reportReturnType": 7, "reportSelfClsParameterName": 7, - "reportUnknownArgumentType": 1139, - "reportUnknownLambdaType": 39, - "reportUnknownMemberType": 2018, - "reportUnknownParameterType": 703, - "reportUnknownVariableType": 889, + "reportUnknownArgumentType": 1077, + "reportUnknownLambdaType": 37, + "reportUnknownMemberType": 2017, + "reportUnknownParameterType": 700, + "reportUnknownVariableType": 879, "reportUnnecessaryComparison": 7, "reportUnnecessaryContains": 1, "reportUnnecessaryIsInstance": 9, @@ -36,10 +36,10 @@ "ruff": { "ANN001": 615, "ANN002": 3, - "ANN201": 757, - "ANN202": 62, - "ANN204": 54, - "ANN205": 32, + "ANN201": 71, + "ANN202": 34, + "ANN204": 10, + "ANN205": 27, "ANN401": 25, "ARG001": 8, "ARG002": 17, diff --git a/src/rejuvenation/batch_process_examples.py b/src/rejuvenation/batch_process_examples.py index be83c205..4bfe4820 100644 --- a/src/rejuvenation/batch_process_examples.py +++ b/src/rejuvenation/batch_process_examples.py @@ -68,7 +68,7 @@ def simple_codebase_provider() -> Iterable[tuple[ASTFactory, ASTNode]]: yield factory, atu2 -def print_results(title, batch_processor): +def print_results(title, batch_processor) -> None: """AI: Print the given title followed by each in-memory file's path and rewritten content.""" print(title + ":") for file, code in batch_processor.in_memory_files.items(): @@ -76,7 +76,7 @@ def print_results(title, batch_processor): print(TextUtils.shift_right(code, 8) + "\n") -def batch_remove_unused_variable_once_example(): +def batch_remove_unused_variable_once_example() -> None: """Demonstrate a batch processing example using different AST node implementations. It iterates over a list of AST node implementations (`ClangASTNode` and `ClangJsonASTNode`), @@ -93,7 +93,7 @@ def batch_remove_unused_variable_once_example(): print_results("example batch remove unused variable once", batch_processor) -def batch_repeat_example(): +def batch_repeat_example() -> None: """Demonstrates the use of a batch processor to perform multiple refactoring operations on a codebase. This example creates an in-memory batch processor and applies two refactoring operations: @@ -114,7 +114,7 @@ def batch_repeat_example(): batch_processor = BatchASTProcessor(in_memory=True) # remove a function to create more unused variables - def remove_function(ast_processor: ASTProcessor): + def remove_function(ast_processor: ASTProcessor) -> None: [ast_processor.insert_before("// ", node, False, False) for node in ast_processor.find_semantic_kind(SemanticKind.CALL)] # batch_processor.repeat(simple_codebase_provider, [remove_function]) @@ -137,7 +137,7 @@ class CallInfo: class AnalysisRecipe: """AI: Recipe that collects function-call analysis results across the processed AST.""" - def __init__(self): + def __init__(self) -> None: """AI: Initialize an empty recipe for collecting function-call analysis results.""" self._calls = [] @@ -155,25 +155,25 @@ def store_function_call(self, ast_processor: ASTProcessor) -> Callable[[], None] return None @after_step("store_function_call") - def just_show_the_method(self): + def just_show_the_method(self) -> None: """AI: Print a marker showing this hook ran after store_function_call.""" print("called after store_function_call") @final_action() - def final_action(self): + def final_action(self) -> None: """AI: Print all collected function calls after the recipe finishes.""" print("Calls:") for call in self._calls: print(" " + call.callee + " -- calls --> " + call.calls) @staticmethod - def _add_function_call(call: ASTNode, calls: list[CallInfo]): + def _add_function_call(call: ASTNode, calls: list[CallInfo]) -> None: callee = call.get_ancestor("(?i)Function_?Decl") if callee: calls.append(CallInfo(callee.name, call.children[0].name)) -def batch_recipe_example(): +def batch_recipe_example() -> None: """AI: Run the analysis recipe over the example codebase and print discovered calls.""" print("example batch analysis using recipe:\n") recipe_ast_processor = RecipeASTProcessor(AnalysisRecipe(), simple_codebase_provider, r".*", in_memory=True) diff --git a/src/rejuvenation/cli.py b/src/rejuvenation/cli.py index 425353e4..04f88dd1 100644 --- a/src/rejuvenation/cli.py +++ b/src/rejuvenation/cli.py @@ -13,7 +13,7 @@ # See https://github.com/TNO/Renaissance.Py/issues/196 -def refactor(): +def refactor() -> None: """AI: Run the `refactor`/`extract`/`inspect` CLI subcommands based on `sys.argv`.""" if sys.argv[1] == "refactor": refactoring = sys.argv[2] diff --git a/src/rejuvenation/recipe_example.py b/src/rejuvenation/recipe_example.py index ff61e78d..de04788a 100644 --- a/src/rejuvenation/recipe_example.py +++ b/src/rejuvenation/recipe_example.py @@ -223,12 +223,12 @@ def simple_codebase_provider() -> Iterable[tuple[ASTFactory, ASTNode]]: class MyRefactor: """AI: Example recipe demonstrating text/name/declaration replacement via ASTRefactorActions.""" - def __init__(self): + def __init__(self) -> None: """AI: Initialize an empty recipe for collecting refactoring actions.""" self._calls = [] @recipe_step(order=0) - def recipe(self, ast_processor: ASTProcessor): + def recipe(self, ast_processor: ASTProcessor) -> None: """AI: Apply the example text, name, and declaration replacements to the processed AST.""" pattern = CPPPatternFactory(ast_processor.factory) actions = ASTRefactorActions(ast_processor, pattern) @@ -290,7 +290,7 @@ def recipe(self, ast_processor: ASTProcessor): TextUtils.to_clipboard(ast_processor.apply_to_string()) -def batch_recipe_example(): +def batch_recipe_example() -> None: """AI: Run `MyRefactor` over the example codebase and copy the rewritten source to the clipboard.""" print("example batch analysis using recipe:\n") recipe_ast_processor = RecipeASTProcessor(MyRefactor(), simple_codebase_provider, r".*", in_memory=True) diff --git a/src/rejuvenation/refactor_examples_different_styles.py b/src/rejuvenation/refactor_examples_different_styles.py index 2816ac00..e6537463 100644 --- a/src/rejuvenation/refactor_examples_different_styles.py +++ b/src/rejuvenation/refactor_examples_different_styles.py @@ -160,7 +160,7 @@ def match(node): return result, expected_result_old_fancy_new -def main(args): +def main(args) -> None: """AI: Run all four 'old'-to-'fancy_new' refactoring style examples against the parsed args.""" # the first argument is the code to be parsed code = args[1] if len(args) > 1 else "" diff --git a/src/rejuvenation/walk_compilation_database.py b/src/rejuvenation/walk_compilation_database.py index a2cf4722..83babfe5 100644 --- a/src/rejuvenation/walk_compilation_database.py +++ b/src/rejuvenation/walk_compilation_database.py @@ -11,7 +11,7 @@ from renaissance.syntax_tree.semantic_kind import SemanticKind -def main(args): +def main(args) -> None: """AI: Walk the compilation database given in `args` for each Clang integration and show its AST.""" # the first argument is the code to be parsed database = args[0] if len(args) > 0 else "" diff --git a/src/renaissance/integrations/clang/c_pattern_factory.py b/src/renaissance/integrations/clang/c_pattern_factory.py index 0b849598..4f4b33ea 100644 --- a/src/renaissance/integrations/clang/c_pattern_factory.py +++ b/src/renaissance/integrations/clang/c_pattern_factory.py @@ -67,7 +67,7 @@ def __init__( factory: ASTFactory, ref_node: ASTNode | None = None, language: str = "c", - ): + ) -> None: """AI: Prepare a pattern factory for creating C-family AST patterns from text.""" self.factory = factory self.header, self.language = derive_header_text(language, ref_node) @@ -270,7 +270,7 @@ def _to_typedef(keywords: Sequence[str], prefix: str = "typedef int ", postfix: class CPPPatternFactory(CPatternFactory): """AI: Factory for building C++-specific AST patterns, extending CPatternFactory with constructor-call support.""" - def __init__(self, factory: ASTFactory, ref_node: ASTNode | None = None): + def __init__(self, factory: ASTFactory, ref_node: ASTNode | None = None) -> None: """AI: Prepare a pattern factory for creating C++-specific AST patterns from text.""" super().__init__(factory, ref_node, "cpp") diff --git a/src/renaissance/integrations/clang/clang_adapter.py b/src/renaissance/integrations/clang/clang_adapter.py index 3dbed28d..13a7921d 100644 --- a/src/renaissance/integrations/clang/clang_adapter.py +++ b/src/renaissance/integrations/clang/clang_adapter.py @@ -9,7 +9,7 @@ class ClangAdapter: """AI: Adapter that parses C/C++ source with libclang into the internal LST representation.""" - def __init__(self, clang_path: str | None = None, args: list | None = None): + def __init__(self, clang_path: str | None = None, args: list | None = None) -> None: """AI: Configure the libclang native library location and default parse arguments.""" if clang_path and cindex.Config.library_path is None: cindex.Config.set_library_path(clang_path) diff --git a/src/renaissance/integrations/clang/clang_ast_node.py b/src/renaissance/integrations/clang/clang_ast_node.py index ea4b39fd..05882052 100644 --- a/src/renaissance/integrations/clang/clang_ast_node.py +++ b/src/renaissance/integrations/clang/clang_ast_node.py @@ -44,7 +44,7 @@ class ClangTranslationUnit: cache = [] - def __init__(self, clang_atu: ClangCindexTranslationUnit, file_name: str): + def __init__(self, clang_atu: ClangCindexTranslationUnit, file_name: str) -> None: """AI: Wrap a parsed clang translation unit with lazily-built reference and macro-expansion caches.""" self.clang_atu = clang_atu self.file_name = file_name @@ -110,7 +110,7 @@ def __init__( start_offset: int | None = None, length: int | None = None, insert_kind: str | None = None, - ): + ) -> None: """AI: Wrap a clang cursor as an AST node within the given translation unit.""" super().__init__(self if parent is None else parent.root) self.node = node @@ -433,7 +433,7 @@ def references(self) -> Sequence[ASTReference[Cursor, ClangTranslationUnit]]: for ref in self.translation_unit._references.get(self.node.hash, EMPTY_LIST) ) - def _add_tokens(self, result: dict[str, str], *token_kind): + def _add_tokens(self, result: dict[str, str], *token_kind) -> None: for token in self.node.get_tokens(): # find all attr of token that are of type str or int kind = str(token.kind).split(".")[-1] @@ -486,7 +486,7 @@ def remove_wrapper(cursor): return cursor @staticmethod - def _is_reference(node): + def _is_reference(node) -> bool | None: # refactor this try: print(type(node)) diff --git a/src/renaissance/integrations/clang/clang_json_ast_node.py b/src/renaissance/integrations/clang/clang_json_ast_node.py index 07691fcc..0e72aa34 100644 --- a/src/renaissance/integrations/clang/clang_json_ast_node.py +++ b/src/renaissance/integrations/clang/clang_json_ast_node.py @@ -55,7 +55,7 @@ def __init__(self, node_id: str, ref_kind: str, properties: dict[str, Any]) -> N class ClangJsonTranslationUnit: """AI: Wrap a parsed clang JSON AST dump with lazily-built reference caches.""" - def __init__(self, json_root: dict[str, Any], file_name: str): + def __init__(self, json_root: dict[str, Any], file_name: str) -> None: """AI: Wrap a parsed clang JSON AST dump with lazily-built reference caches.""" self.json_root = json_root self.filename = file_name @@ -492,7 +492,7 @@ def _get_property[T](target: dict[str, Any], path: Sequence[str], default: T) -> return default @property - def is_implicit(self): + def is_implicit(self) -> None: """AI: Return whether this node is implicitly generated (part of the translation unit).""" self.is_part_of_translation_unit() diff --git a/src/renaissance/integrations/python/ast/cst_node.py b/src/renaissance/integrations/python/ast/cst_node.py index b15dde3c..84dc1352 100644 --- a/src/renaissance/integrations/python/ast/cst_node.py +++ b/src/renaissance/integrations/python/ast/cst_node.py @@ -16,7 +16,7 @@ class PythonCstTranslationUnit: """AI: Parse Python source into a libcst tree with position lookups for AST-node wrapping.""" - def __init__(self, content, file_name: str): + def __init__(self, content, file_name: str) -> None: """AI: Parse Python source into a libcst tree with position lookups for AST-node wrapping.""" self.content = content self.lines = content.splitlines() @@ -47,7 +47,7 @@ def signature_of(self, node: CSTNode) -> str: class PythonCstNode: """AI: ASTNode implementation backed by libcst's concrete syntax tree.""" - def __init__(self, node: CSTNode, translation_unit: PythonCstTranslationUnit, parent=None): + def __init__(self, node: CSTNode, translation_unit: PythonCstTranslationUnit, parent=None) -> None: """AI: Wrap a libcst node as an AST node within the given translation unit.""" self.parent = parent if parent and parent.root: @@ -71,11 +71,11 @@ def __init__(self, node: CSTNode, translation_unit: PythonCstTranslationUnit, pa # for rewriter self.text = self.signature - def __str__(self): + def __str__(self) -> str: """AI: Return the string representation of the wrapped CST node.""" return str(self.node) - def __repr__(self): + def __repr__(self) -> str: """AI: Return the repr of the wrapped CST node.""" return repr(self.node) diff --git a/src/renaissance/integrations/python/ast/extractor.py b/src/renaissance/integrations/python/ast/extractor.py index 20b762b3..31da0f32 100644 --- a/src/renaissance/integrations/python/ast/extractor.py +++ b/src/renaissance/integrations/python/ast/extractor.py @@ -14,7 +14,7 @@ class PythonExtractor: graph = nx.DiGraph() codebase: dict = {} - def process(self, file: Path): + def process(self, file: Path) -> None: """AI: Parse file and merge its module/import/function/class nodes into the codebase graph.""" root = PythonRstNode.load(file) module_name = root.filename.replace("/", ".").replace(".py", "") @@ -46,7 +46,7 @@ def process(self, file: Path): # self.edges |=tu._references # self.edges |= tu._referenced_by - def save_graph(self, filename: str): + def save_graph(self, filename: str) -> None: """AI: Write the extracted codebase graph to a GraphML file named filename.""" nx.write_graphml(self.graph, filename) print(f"Graph saved to: {filename}") diff --git a/src/renaissance/integrations/python/ast/factory.py b/src/renaissance/integrations/python/ast/factory.py index 7674cc5f..a9ca0c3f 100644 --- a/src/renaissance/integrations/python/ast/factory.py +++ b/src/renaissance/integrations/python/ast/factory.py @@ -28,7 +28,7 @@ class PythonPattern(NodeProtocol): """AI: Wrap a Python AST/RST node as a matchable pattern, detecting match-all/match-one placeholders.""" - def __init__(self, node): + def __init__(self, node) -> None: """AI: Wrap a Python AST/RST node as a matchable pattern, detecting match-all/match-one placeholders.""" self.node: PythonRstNode = node if type(node) is str: @@ -51,7 +51,7 @@ def __eq__(self, other: NodeProtocol) -> bool: """AI: Return whether `other` matches this pattern node.""" return is_match(other, self) - def __repr__(self): + def __repr__(self) -> str: """AI: Return a dollar-escaped repr of the wrapped node.""" return use_dollar(str(self.node)) @@ -126,7 +126,7 @@ def load_from_lst(text: str, _file_name: str) -> LSTNode: class PythonPatternFactory: """AI: Factory for building Python AST patterns from text.""" - def __init__(self, factory: PythonFactory): + def __init__(self, factory: PythonFactory) -> None: """AI: Prepare a pattern factory for creating Python AST patterns from text.""" self.factory = factory diff --git a/src/renaissance/integrations/python/ast/rst_node.py b/src/renaissance/integrations/python/ast/rst_node.py index 49811043..bdee5159 100644 --- a/src/renaissance/integrations/python/ast/rst_node.py +++ b/src/renaissance/integrations/python/ast/rst_node.py @@ -39,7 +39,7 @@ class ImplicitNode(ast.Name): "body": list, } - def __init__(self, name, children=None): + def __init__(self, name, children=None) -> None: """AI: Represent a synthetic AST node inserted where the real source has none.""" super().__init__(name, children or []) self.lineno = 0 @@ -51,7 +51,7 @@ def __init__(self, name, children=None): class PythonRSTReference: """AI: Represent a reference from one Python RST AST node to another by id.""" - def __repr__(self): + def __repr__(self) -> str: """AI: Return a string identifying the referenced node id and reference kind.""" return f"{self.node_id}:{self.ref_kind}" @@ -67,7 +67,7 @@ class PythonRstTranslationUnit: cache = {} - def __init__(self, content, file_name: str): + def __init__(self, content, file_name: str) -> None: """AI: Parse Python source into a stdlib ast tree with lazily-built reference caches.""" self.content = content.encode(sys.getfilesystemencoding()) self.atu = ast.parse(content, file_name) @@ -99,7 +99,7 @@ def lazy_create_refers(self, node: PythonRstNode) -> None: self.create_references(n) self.references_initialized = True - def add(self, node): + def add(self, node) -> None: """AI: Register node in the node-name lookup table, keyed by its parser-kind-specific name.""" match node.parser_kind: case "Name": @@ -213,7 +213,7 @@ def get_references(self, node_id): class PythonRstNode: """AI: ASTNode implementation backed by the stdlib ast module.""" - def __init__(self, node: ast.AST, translation_unit: PythonRstTranslationUnit = None, parent=None): + def __init__(self, node: ast.AST, translation_unit: PythonRstTranslationUnit = None, parent=None) -> None: """AI: Wrap a stdlib ast node as an AST node within the given translation unit.""" self.root = parent.root if parent and parent.root else self self.node = node @@ -285,11 +285,11 @@ def __eq__(self, other): and match_children(self.children, other.children, IRRELEVANT_NODES) ) - def __contains__(self, item): + def __contains__(self, item) -> bool: """AI: Return whether item(s) are found among this node's children.""" if not isinstance(item, list): item = [item] - return find_in_list(self.children, item) + return bool(find_in_list(self.children, item)) def __getitem__(self, key): """Allow indexing/slicing into node to access children. @@ -298,7 +298,7 @@ def __getitem__(self, key): """ return self.children[key] - def __repr__(self): + def __repr__(self) -> str: """AI: Return the formatted node representation.""" return format_node(self) @@ -318,7 +318,7 @@ def process(self, function: Callable[[Self], None]) -> None: for child in self.children: child.process(function) - def derive_position(self, node: ast.AST, translation_unit: PythonRstTranslationUnit, parent): + def derive_position(self, node: ast.AST, translation_unit: PythonRstTranslationUnit, parent) -> None: """AI: Compute and set this node's offset and length in the source text from its ast position attributes.""" if node._attributes: if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)) and node.decorator_list: @@ -495,7 +495,7 @@ def references(self) -> list[PythonRSTReference]: self.translation_unit.lazy_create_refers(self) return self.translation_unit.get_references(self.name) - def add_node(self): + def add_node(self) -> None: """AI: Register this node in its translation unit's node-name lookup table.""" self.translation_unit.add(self) diff --git a/src/renaissance/integrations/tree_sitter/adapter.py b/src/renaissance/integrations/tree_sitter/adapter.py index 68e3cb10..84048856 100644 --- a/src/renaissance/integrations/tree_sitter/adapter.py +++ b/src/renaissance/integrations/tree_sitter/adapter.py @@ -8,7 +8,7 @@ class TreeSitterAdapter: """AI: Adapter that parses source code with tree-sitter into the internal LST representation.""" - def __init__(self, grammar_module): + def __init__(self, grammar_module) -> None: """AI: Configure a tree-sitter parser for the given language grammar.""" language = Language(grammar_module.language()) self.language = language diff --git a/src/renaissance/integrations/tree_sitter/extractor.py b/src/renaissance/integrations/tree_sitter/extractor.py index d1dc498d..c5e3dfdf 100644 --- a/src/renaissance/integrations/tree_sitter/extractor.py +++ b/src/renaissance/integrations/tree_sitter/extractor.py @@ -21,7 +21,7 @@ def _has_semantic_kind(node, kind: SemanticKind) -> bool: class Extractor: """AI: Find occurrences of a set of patterns in tree-sitter-parsed code.""" - def __init__(self, factory: TreeSitterPatternFactory, patterns: list[str]): + def __init__(self, factory: TreeSitterPatternFactory, patterns: list[str]) -> None: """AI: Configure an extractor that finds occurrences of the given patterns in code.""" self.pattern_factory = factory self.patterns = patterns @@ -39,14 +39,14 @@ def run(self, raw: str) -> list[PatternMatch]: class BaseCodeGraphExtractor: """AI: Base class that extracts a code graph from a language's tree-sitter-parsed files.""" - def __init__(self, language: str, lib_path: str): + def __init__(self, language: str, lib_path: str) -> None: """AI: Configure a code-graph extractor for the given language and grammar library.""" self.language = language self.lib_path = lib_path self.adapter = TreeSitterAdapter(lib_path) self.graph = nx.DiGraph() - def extract(self, files): + def extract(self, files) -> None: """AI: Parse each file and build the code graph by processing its LST.""" for f in files: try: @@ -60,7 +60,7 @@ def extract(self, files): def _process_file(self, file_path: str, lst): raise NotImplementedError - def save_graph(self, filename: str): + def save_graph(self, filename: str) -> None: """AI: Write the extracted code graph to a GraphML file named filename.""" path = Path(GRAPHML_DIR) / filename nx.write_graphml(self.graph, path) @@ -70,7 +70,7 @@ def save_graph(self, filename: str): class PythonCodeGraphExtractor(BaseCodeGraphExtractor): """AI: Extract a code graph (functions, calls) from Python source files.""" - def _process_file(self, file_path, lst): + def _process_file(self, file_path, lst) -> None: folder = str(Path(file_path).parent) self.graph.add_node(file_path, type="file", folder=folder) self.graph.add_node(folder, type="folder") @@ -91,7 +91,7 @@ def _process_file(self, file_path, lst): class JavaCodeGraphExtractor(BaseCodeGraphExtractor): """AI: Extract a code graph (methods, calls) from Java source files.""" - def _process_file(self, file_path, lst): + def _process_file(self, file_path, lst) -> None: folder = str(Path(file_path).parent) self.graph.add_node(file_path, type="file", folder=folder) self.graph.add_node(folder, type="folder") @@ -112,7 +112,7 @@ def _process_file(self, file_path, lst): class CppCodeGraphExtractor(BaseCodeGraphExtractor): """AI: Extract a code graph (functions, calls) from C++ source files.""" - def _process_file(self, file_path, lst): + def _process_file(self, file_path, lst) -> None: folder = str(Path(file_path).parent) self.graph.add_node(file_path, type="file", folder=folder) self.graph.add_node(folder, type="folder") diff --git a/src/renaissance/integrations/tree_sitter/factory.py b/src/renaissance/integrations/tree_sitter/factory.py index cb36d443..7063aabe 100644 --- a/src/renaissance/integrations/tree_sitter/factory.py +++ b/src/renaissance/integrations/tree_sitter/factory.py @@ -10,7 +10,7 @@ class TreeSitterPatternFactory: """AI: Factory for building tree-sitter AST patterns from text.""" - def __init__(self, adapter: TreeSitterAdapter, language: str = "python"): + def __init__(self, adapter: TreeSitterAdapter, language: str = "python") -> None: """AI: Prepare a pattern factory for creating tree-sitter AST patterns from text.""" self.adapter = adapter self.language = language diff --git a/src/renaissance/integrations/tree_sitter/kinds.py b/src/renaissance/integrations/tree_sitter/kinds.py index 71db9f79..5300f3d4 100644 --- a/src/renaissance/integrations/tree_sitter/kinds.py +++ b/src/renaissance/integrations/tree_sitter/kinds.py @@ -67,5 +67,5 @@ "binary_operator": SemanticKind.BINARY_OPERATION, "unary_operator": SemanticKind.UNARY_OPERATION, "with": SemanticKind.LOOP, - "type_alias_statement": SemanticKind.STATEMENT + "type_alias_statement": SemanticKind.STATEMENT, } diff --git a/src/renaissance/integrations/tree_sitter/lst.py b/src/renaissance/integrations/tree_sitter/lst.py index 0dcd6d88..55b41444 100644 --- a/src/renaissance/integrations/tree_sitter/lst.py +++ b/src/renaissance/integrations/tree_sitter/lst.py @@ -23,7 +23,7 @@ def __init__( children: list[Self] | None = None, parent: Self | None = None, root: Self | None = None, - ): + ) -> None: """AI: Wrap a tree-sitter node as a language-syntax-tree node with derived semantic kind.""" self.root = root or self self.parent = parent @@ -78,7 +78,7 @@ def match_children(self, children): """AI: Return whether this node's children match the given children at each corresponding index.""" return all(i < len(self.children) and self.children[i] == child for i, child in enumerate(children)) - def add_child(self, child): # LSTNode): + def add_child(self, child) -> None: # LSTNode): """AI: Append child to this node's children and set its parent to this node.""" self.children.append(child) child.parent = self @@ -108,7 +108,7 @@ def node(self): """AI: Return this node itself.""" return self - def __repr__(self): + def __repr__(self) -> str: """AI: Return the formatted node representation.""" return format_node(self) # raw_lines = self.signature.splitlines() @@ -129,6 +129,6 @@ def is_part_of_translation_unit(self): class LST: """AI: Hold the root node of a language-syntax tree.""" - def __init__(self, root: LSTNode): + def __init__(self, root: LSTNode) -> None: """AI: Hold the root node of a language-syntax tree.""" self.root = root diff --git a/src/renaissance/integrations/tree_sitter/visualizer.py b/src/renaissance/integrations/tree_sitter/visualizer.py index 4f0c6ad5..a6481008 100644 --- a/src/renaissance/integrations/tree_sitter/visualizer.py +++ b/src/renaissance/integrations/tree_sitter/visualizer.py @@ -7,7 +7,7 @@ class LstVisualizer: """AI: Render a tree-sitter LST as a Mermaid graph for visualization.""" - def __init__(self): + def __init__(self) -> None: """AI: Prepare a visualizer that renders an LST as a Mermaid graph.""" self.lines = ["graph TD"] self.counter = 0 @@ -19,7 +19,7 @@ def _get_node_id(self, node): self.node_ids[node] = f"n{self.counter}" return self.node_ids[node] - def _render_node(self, node): + def _render_node(self, node) -> None: node_id = self._get_node_id(node) label = f"""\ {node_id}: {node.semantic_kind} ({node.parser_kind}) {{ diff --git a/src/renaissance/recipes/cleanup_refactoring.py b/src/renaissance/recipes/cleanup_refactoring.py index 312f008d..87a3223c 100644 --- a/src/renaissance/recipes/cleanup_refactoring.py +++ b/src/renaissance/recipes/cleanup_refactoring.py @@ -10,7 +10,7 @@ class CleanupRefactoring: """AI: Static-only namespace exposing AST cleanup recipe steps (e.g. removing unused variables).""" - def __init__(self): + def __init__(self) -> None: """AI: Prevent instantiation; this class only exposes static cleanup recipe steps.""" raise Exception("This class should not be instantiated") diff --git a/src/renaissance/recipes/python_refactoring.py b/src/renaissance/recipes/python_refactoring.py index 6eeeeac2..587d90af 100644 --- a/src/renaissance/recipes/python_refactoring.py +++ b/src/renaissance/recipes/python_refactoring.py @@ -19,7 +19,7 @@ class PythonRefactoring(ASTProcessor): """AI: Base processor for Python-specific source refactoring recipes.""" - def __init__(self, file): + def __init__(self, file) -> None: """AI: Prepare a Python-specific refactoring processor for the given source file.""" factory = PythonFactory(PythonRstNode) atu = factory.create(file) @@ -28,7 +28,7 @@ def __init__(self, file): self.black_list_pattern = ".git" self.white_list_pattern = "" - def replace_stmt(self, find, repl): + def replace_stmt(self, find, repl) -> None: """AI: Replace all statements matching the find pattern with the repl template, expanding captures.""" pattern = self.pattern_factory.create_statements(find) for match in match_pattern(self.root.children, pattern): @@ -41,7 +41,7 @@ def replace_stmt(self, find, repl): self.replace(replacement, match.nodes, False, False) @staticmethod - def process(class_name, file): + def process(class_name, file) -> None: """Return a subclass by name using importlib, like Java's Class.forName().""" snake = snake_case(class_name) module = importlib.import_module(f"renaissance.recipes.{snake}") @@ -77,7 +77,7 @@ def extract_call_arguments(self, node: PythonRstNode) -> tuple[list[str], dict[s positional_args = [arg_node.signature for arg_node in (args_implicit.children if args_implicit else [])] keyword_args: dict[str, str] = {} - for kw_node in (keywords_implicit.children if keywords_implicit else []): + for kw_node in keywords_implicit.children if keywords_implicit else []: kw_name = kw_node.node.arg if kw_name: value_node = kw_node.children[0] if kw_node.children else kw_node @@ -109,5 +109,5 @@ def body(self) -> Sequence[PythonRstNode]: """AI: Return the root node's body statements.""" return cast("PythonRstNode", cast("object", self.root)).body - def run(self): + def run(self) -> None: """AI: Run this refactoring recipe. Subclasses override this to perform the refactoring.""" diff --git a/src/renaissance/recipes/simplify_renaissance.py b/src/renaissance/recipes/simplify_renaissance.py index 47d38cad..d9edbf9a 100644 --- a/src/renaissance/recipes/simplify_renaissance.py +++ b/src/renaissance/recipes/simplify_renaissance.py @@ -9,14 +9,14 @@ class SimplifyRenaissance(PythonRefactoring): """AI: Recipe that simplifies Renaissance's own unit-to-pytest conversion files.""" - def __init__(self, file): + def __init__(self, file) -> None: """AI: Prepare a refactoring processor scoped to Renaissance's own unit-to-pytest conversion files.""" super().__init__(file) self.white_list_pattern = "unit_to_pytest" self.black_list_pattern = "SimplifyRenaissance" @override - def run(self): + def run(self) -> None: if self.black_list_pattern in self.filename or self.white_list_pattern not in self.filename: print(f"skipping: {Path(self.filename).resolve()}") return diff --git a/src/renaissance/recipes/taut_to_python_unittest.py b/src/renaissance/recipes/taut_to_python_unittest.py index b9cfe045..683c2680 100644 --- a/src/renaissance/recipes/taut_to_python_unittest.py +++ b/src/renaissance/recipes/taut_to_python_unittest.py @@ -15,14 +15,14 @@ class TautToPythonUnittest(PythonRefactoring): """AI: Recipe that converts taut-style test files to Python's unittest style.""" - def __init__(self, file): + def __init__(self, file) -> None: """AI: Prepare a refactoring processor that converts taut-style tests to Python unittest style.""" super().__init__(file) self.white_list_reg = r"_test|_unittest|_tests" self.black_list_reg = r"_migrated|_after|_original" self.comp = "ABCD" - def run(self): + def run(self) -> None: """AI: Apply the full sequence of taut-to-unittest migration transforms and write the migrated file.""" if re.search(self.black_list_reg, self.filename): print(f"skipping: {Path(self.filename).resolve()}") @@ -82,7 +82,7 @@ def get_migrated_path(self, file_path): path = Path(file_path) return path.with_stem(f"{path.stem}_migrated") - def replace_taut(self): + def replace_taut(self) -> None: """Replace TAUT.TestCase by unittest.TestCase.""" [ self.replace("unittest.TestCase", node, False, False) @@ -95,11 +95,11 @@ def replace_taut(self): if node.name == "TestCase" ] - def remove_decorator(self): + def remove_decorator(self) -> None: """AI: Remove the @TAUT.log_stub decorator.""" [self.remove(node, False, False) for node in self.find_semantic_kind(SemanticKind.ATTRIBUTE) if node.name == "TAUT.log_stub"] - def add_self(self): + def add_self(self) -> None: """AI: Prefix known TAUT helper attribute names with "self." so they resolve as instance members.""" matching = [ "emrwxread", @@ -134,7 +134,7 @@ def add_self(self): if node.name in matching2 and node.get_ancestor("FunctionDef").name not in parent_func ] - def convert_assert(self): + def convert_assert(self) -> None: """AI: Replace TAUT-style assert_false/assert_true/assert_equal calls with unittest equivalents.""" [ self.replace("self.assertFalse", node, False, False) @@ -152,11 +152,11 @@ def convert_assert(self): if node.name == "self.assert_equal" ] - def remove_stubserver(self): + def remove_stubserver(self) -> None: """AI: Remove references to TAUT.StubServer.""" [self.remove(node, False, False) for node in self.find_semantic_kind(SemanticKind.ATTRIBUTE) if node.name == "TAUT.StubServer"] - def replace_mock(self): + def replace_mock(self) -> None: """AI: Replace mock.patch decorators with patch.""" [ self.replace("patch", node, False, False) @@ -164,7 +164,7 @@ def replace_mock(self): if node.name == "mock.patch" and node.parent.parent.name == "decorator_list" ] - def replace_log_compxtl(self, comp): + def replace_log_compxtl(self, comp) -> None: """AI: Replace direct and assigned calls to {comp}xtl with a fake_{comp}xtl double, including TestDoubles blocks.""" func_call = self.pattern_factory.create_statements(f"{comp}xtl.$a($$bb)") for call in match_pattern(self.root.children, func_call): @@ -184,13 +184,13 @@ def replace_log_compxtl(self, comp): self.replace(repl, match.nodes, False, False) self.commit() - def remove_taut_import(self): + def remove_taut_import(self) -> None: """AI: Remove the "import TAUT" statement.""" taut_import = self.pattern_factory.create_statements("import TAUT\n") for match in match_pattern(self.root.children, taut_import): self.remove(match.nodes, False, False) - def replace_taut_import(self): + def replace_taut_import(self) -> None: """Replace mock by unittest.mock and using patch.""" mock = self.pattern_factory.create_statements("import mock\n") for match in match_pattern(self.root.children, mock): @@ -208,7 +208,7 @@ def replace_taut_import(self): repl = "try:\n from unittest.mock import patch\nexcept ImportError:\n from mock import patch\n" self.replace(repl, match.nodes, False, False) - def convert_tds(self): + def convert_tds(self) -> None: """AI: Convert self.tds.append(TestDoubles(...)) calls into add_patcher calls or direct stub assignments.""" tds = self.pattern_factory.create_statements("self.tds.append(TestDoubles($a, $b=$c))") for match in match_pattern(self.root.children, tds): @@ -220,7 +220,7 @@ def convert_tds(self): repl = f"self.{match['$a']} = ImprovedStub({match['$b']})" self.replace(repl, match.nodes, False, False) - def convert_setup_common(self): + def convert_setup_common(self) -> None: """AI: Convert self.tds = [...] blocks into ImprovedStub setup plus patch.object patcher lists.""" insert_code = """ImprovedStub.ret_vals = {} ImprovedStub.ret_vals_ex = {} @@ -249,7 +249,7 @@ def convert_setup_common(self): repl = insert_code + init_stubs + repl + p_start self.replace(repl, match.nodes, False, False) - def convert_teardown_common(self): + def convert_teardown_common(self) -> None: """AI: Replace tearDownCommon's body with logic that stops all patchers.""" teardown_common = self.pattern_factory.create_statements("def tearDownCommon(self):\n $$aa") repl = """def tearDownCommon(self): @@ -262,7 +262,7 @@ def convert_teardown_common(self): for match in match_pattern(self.root.children, teardown_common): self.replace(repl, match.nodes, False, False) - def convert_add_patcher(self): + def convert_add_patcher(self) -> None: """AI: Insert an add_patcher helper method into tearDownCommon if one doesn't already exist.""" pattern = self.pattern_factory.create_statements("def tearDownCommon(self):\n $$aa") for match in match_pattern(self.root.children, pattern): @@ -282,7 +282,7 @@ def find_import_interface(self, name: str): interface = node_list[0].name if node_list else name return interface.split(".")[0] - def convert_setup(self): + def convert_setup(self) -> None: """AI: Convert TAUT.TestDoubles-based setUp logic into patch/patch.object based setup with self.patches.""" # remove doubles init pattern1 = self.pattern_factory.create_statements("doubles = []") @@ -342,7 +342,7 @@ def convert_setup(self): if node.name == "context_stub" ] - def convert_teardown(self): + def convert_teardown(self) -> None: """AI: Replace tearDown's body with logic that stops all patches.""" matched_pattern = self.pattern_factory.create_statements("def tearDown(self):\n $$aa") repl_pattern = """def tearDown(self): @@ -351,7 +351,7 @@ def convert_teardown(self): for match in match_pattern(self.root.children, matched_pattern): self.replace(repl_pattern, match.nodes, False, False) - def refactor_teardown(self): + def refactor_teardown(self) -> None: """AI: Replace the doubles-exit teardown loop with patch.stopall() and reset context method attributes.""" self.comp = "abcd" pattern1 = self.pattern_factory.create_statements("for double in self.doubles:\n double.exit()") @@ -369,7 +369,7 @@ def refactor_teardown(self): for match in match_pattern(self.root.children, pattern2): self.insert_before(insert_code, match.nodes, False, False) - def convert_test_doubles(self, doubles: str): + def convert_test_doubles(self, doubles: str) -> None: """AI: Convert matches of the given doubles-append pattern into self.patches.append(patch(...)) calls.""" mappings: dict[str, str] = { "emrmxcontext": "EMRMxCONTEXT", @@ -385,7 +385,7 @@ def convert_test_doubles(self, doubles: str): repl_pattern = repl_pattern.replace("context_stub", "self.context_stub") self.replace(repl_pattern, match.nodes, False, False) - def insert_patch_import(self): + def insert_patch_import(self) -> None: """AI: Insert a try/except import of unittest.mock.patch (falling back to mock.patch) if missing.""" insert = "\ntry:\n from unittest.mock import patch\nexcept ImportError:\n from mock import patch" insert_pattern = self.pattern_factory.create_statements(insert) @@ -394,32 +394,32 @@ def insert_patch_import(self): for match in match_pattern(self.root.children, pattern): self.insert_after(insert, match.nodes, False, False) - def replace_taut_skip(self): + def replace_taut_skip(self) -> None: """Replace @TAUT.skip_test by @unittest.skip.""" [self.replace("@unittest.skip", node) for node in self.find_semantic_kind(SemanticKind.ATTRIBUTE) if node.name == "TAUT.skip_test"] - def convert_import_verify(self): + def convert_import_verify(self) -> None: """AI: Replace self.import_and_verify_module('$a') calls with a real import plus an assertIsNotNone check.""" import_verify = self.pattern_factory.create_statements("self.import_and_verify_module('$a')") for match in match_pattern(self.root.children, import_verify): repl = f"import {match.expansions['$a'][0]}\nself.assertIsNotNone({match.expansions['$a'][0]})" self.replace(repl, match.nodes, False, False) - def with_testdoubles(self): + def with_testdoubles(self) -> None: """AI: Replace "with TAUT.TestDoubles(...)" blocks with equivalent "with patch.object(...)" blocks.""" pattern1 = self.pattern_factory.create_statements("with TAUT.TestDoubles(module=$a, $b=$c):\n $$ee") for match in match_pattern(self.root.children, pattern1): repl_pattern = f"with patch.object({match['$a']}, '{match['$b']}', new={match['$c']}):\n {match['$$ee']}" self.replace(repl_pattern, match.nodes, False, False) - def shared_setup(self): + def shared_setup(self) -> None: """AI: Rename a sharedSetUp method to setUp.""" setup_function = self.pattern_factory.create_statements("def sharedSetUp(self):\n $$stmts") for match in match_pattern(self.root.children, setup_function): repl = match.signature.replace("def sharedSetUp", " def setUp") self.replace(textwrap.dedent(repl), match.nodes, False, False) - def insert_class(self): + def insert_class(self) -> None: """AI: Insert the Asserter helper class if it doesn't already exist in the file.""" class_pattern = self.pattern_factory.create_statements("class Asserter(unittest.TestCase):\n $$aa") if len(match_pattern(self.root.children, class_pattern)) == 0: @@ -428,21 +428,21 @@ def insert_class(self): for match in match_pattern(self.root.children, insert_pattern): self.insert_after(insert_code, match.nodes, False, False) - def insert_asserter(self): + def insert_asserter(self) -> None: """AI: Insert the assert_double_equal helper function's replacement code.""" insert_pattern = self.pattern_factory.create_statements("def assert_double_equal($$arg, $$other=$$value):\n $$bb") insert_code = tst_insert.insert_code for match in match_pattern(self.root.children, insert_pattern): self.insert_after(insert_code, match.nodes, False, False) - def remove_assert_func(self): + def remove_assert_func(self) -> None: """AI: Remove the original assert_double_equal function definition.""" pattern = self.pattern_factory.create_statements("def assert_double_equal($$arg, $$other=$$value):\n $$bb") for match in match_pattern(self.root.children, pattern): self.remove(match.nodes, False, False) self.commit() - def replace_unittest_with_asserter(self): + def replace_unittest_with_asserter(self) -> None: """AI: Make classes that use assert_raises/assert_double_equal extend Asserter instead of TAUT.TestCase.""" pattern = self.pattern_factory.create_statements("class $a(TAUT.TestCase):\n $$bb") for match in match_pattern(self.root.children, pattern): @@ -451,7 +451,7 @@ def replace_unittest_with_asserter(self): self.replace(repl, match.nodes, False, False) self.commit() - def assert_func(self): + def assert_func(self) -> None: """AI: Prefix assert_raises and assert_double_equal usages with "self.".""" matching = [ "assert_raises", @@ -463,7 +463,7 @@ def assert_func(self): if node.name in matching ] - def move_indent(self, indent): + def move_indent(self, indent) -> None: """AI: Replace a doubles-append call inside a function with an indented patch.object with-block.""" pattern1 = self.pattern_factory.create_statements("""def $a($$b): self.doubles.append(TAUT.TestDoubles($mod, $e, $f)) @@ -478,7 +478,7 @@ def move_indent(self, indent): replace_pattern = replace_pattern.replace(double_pattern, "") self.replace(replace_pattern, match.nodes, False, False) - def convert_testdoubles_fun(self): + def convert_testdoubles_fun(self) -> None: """AI: Use this for taut migration, where the function pattern is found in a class.""" # case1 two TestDoubles are defined pattern1 = self.pattern_factory.create_statements("""def $a($$b): @@ -552,7 +552,7 @@ def convert_testdoubles_fun(self): self.replace(replace_pattern, match.nodes, False, False) self.commit() - def refactor_testdoubles_fun(self): + def refactor_testdoubles_fun(self) -> None: """Use this for unittest, where the function pattern is not found in a class.""" # case1 two TestDoubles are defined pattern1 = self.pattern_factory.create_statements("""def $a($$b): @@ -616,7 +616,7 @@ def refactor_testdoubles_fun(self): replace_pattern = replace_pattern.replace(textwrap.indent(double_pattern, " "), "") self.replace(replace_pattern, match.nodes, False, False) - def refactor_testdoubles_class(self): + def refactor_testdoubles_class(self) -> None: """AI: Convert a class-level TAUT.TestDoubles-based setUp/tearDown into patch.object based setup/teardown.""" pattern = self.pattern_factory.create_statements("""class $a(TAUT.TestCase): @@ -664,7 +664,7 @@ def tearDown(self): p.stop()""" self.replace(replace_pattern, match.nodes, False, False) - def insert_doc_func(self): + def insert_doc_func(self) -> None: """AI: Insert a change-log comment before the existing copyright comment block.""" pattern = self.pattern_factory.create_statements("""# -----------------------------------------------------------------------------# # # @@ -696,7 +696,7 @@ def insert_doc(content: str, date): return modified_content -def get_change_comment(date=None): +def get_change_comment(date=None) -> str: """Generate a formatted change comment with today's date. Args: diff --git a/src/renaissance/recipes/unit_to_pytest.py b/src/renaissance/recipes/unit_to_pytest.py index 0e25be05..cb548d82 100644 --- a/src/renaissance/recipes/unit_to_pytest.py +++ b/src/renaissance/recipes/unit_to_pytest.py @@ -16,19 +16,19 @@ class UnitToPytest(PythonRefactoring): """AI: Recipe that converts unittest-style test files to pytest style.""" - def __init__(self, file): + def __init__(self, file) -> None: """Hide internal administration in the parent class so that this class you only deals with specific refactors.""" super().__init__(file) self.black_list_pattern = "utils_for_test" self.white_list_pattern = "test" - def run(self): + def run(self) -> None: """Entry point for converting unittest to pytest.""" self.refactor() self.post_processing() - def refactor(self): + def refactor(self) -> None: """AI: Apply file-, class-, and function-level unittest-to-pytest conversions.""" # 1: file level changes self.convert_test_class() @@ -65,7 +65,7 @@ def refactor(self): self.replace_stmt("self.assertIsInstance($act, $exp)", "assert_that($act, is_($exp))") self.replace_stmt("with self.assertRaises($exc): $call()", "assert_that(calling($call), raises($exc))") - def post_processing(self): + def post_processing(self) -> None: """AI: Repeatedly simplify assert_that(...) expressions until no further changes occur.""" # 4: improve to more concise asserts while self.has_changed(): @@ -89,7 +89,7 @@ def post_processing(self): self.remove_duplicate_import("import pytest\nfrom hamcrest import *") self.commit() - def convert_test_class(self): + def convert_test_class(self) -> None: """AI: Rewrite TestCase-derived class headers to drop the unittest base class.""" test_main: Sequence[NodeProtocol] = self.pattern_factory.create_statements( "class $klass($test_class):\n $$test_cases\n", @@ -110,7 +110,7 @@ def convert_test_class(self): # repl = f'class {match.expansions["$klass"][0]}:\n{raw(match.expansions["$$test_cases"])}' self.replace(repl, match.nodes, False, False) - def convert_test_setup(self): + def convert_test_setup(self) -> None: """AI: Convert a setUp method into a pytest autouse fixture named setup.""" setup_function = self.pattern_factory.create_statements("def setUp(self): $$stmts") for match in match_pattern(self.body, setup_function): @@ -118,7 +118,7 @@ def convert_test_setup(self): repl = f"@pytest.fixture(autouse=True)\n{match.signature}".replace(" setUp(self)", " setup(self)") self.replace(repl, match.nodes, False, False) - def convert_assert(self, pattern, replacement): + def convert_assert(self, pattern, replacement) -> None: """AI: Replace calls matching pattern with replacement, swapping expected/actual arguments as needed.""" pat = self.pattern_factory.create_statements(pattern) for match in match_pattern(self.root.children, pat): @@ -136,7 +136,7 @@ def is_swapped(self, match: PatternMatch) -> bool: """AI: Return whether $exp and $act appear swapped in the match (i.e. $exp is a literal).""" return match.expansions["$exp"][0].semantic_kind is SemanticKind.LITERAL - def convert_parameterized_test(self): + def convert_parameterized_test(self) -> None: """AI: Convert @parameterized.expand-decorated test functions into @pytest.mark.parametrize.""" unittest = self.pattern_factory.create_statements( textwrap.dedent(""" @@ -163,7 +163,7 @@ def $fun($$args, *$$varg): self.replace(repl, fun, False, False) - def remove_print(self): + def remove_print(self) -> None: """AI: Remove print(...) statements, or their containing block if it's the only statement.""" print_msg = self.pattern_factory.create_statements("print($$msg)") # type: ignore[assignment] for match in match_pattern(self.root.children, print_msg): @@ -172,7 +172,7 @@ def remove_print(self): else: self.remove(match.nodes, False, False) - def convert_plain_assert_same_length(self): + def convert_plain_assert_same_length(self) -> None: """AI: Replace a manual length-check assert with an assert_that(...) has_length assertion.""" pattern: Sequence[NodeProtocol] = self.pattern_factory.create_statements( '$act: int = len($real)\nassert $exp == $act, "$act = " + str($act)', @@ -184,14 +184,14 @@ def convert_plain_assert_same_length(self): repl = repl.replace("$exp", exp).replace("$real", real) self.replace(repl, match.nodes, False, False) - def convert_skip_test(self): + def convert_skip_test(self) -> None: """AI: Replace unittest.skip attribute references with pytest.mark.skip.""" nodes = find_semantic_kind(self.root, SemanticKind.ATTRIBUTE) for node in nodes: if node.signature == "unittest.skip": self.replace("pytest.mark.skip", node, False, False) - def swap_expected_and_actual(self): + def swap_expected_and_actual(self) -> None: """AI: Swap the $exp and $act arguments of assert_that(...) calls when they appear reversed.""" pattern: Sequence[NodeProtocol] = self.pattern_factory.create_statements("assert_that($exp, is_($act))") # type: ignore[assignment] for match in match_pattern(self.root.children, pattern): @@ -202,7 +202,7 @@ def swap_expected_and_actual(self): repl = repl.replace("$exp", exp).replace("$act", act) self.replace(repl, match.nodes, False, False) - def restructure_module(self): + def restructure_module(self) -> None: """AI: Move module-level functions into a test class, creating one if none exists.""" funs = [stmt for stmt in self.body if stmt.semantic_kind is SemanticKind.FUNCTION] test_classes = [stmt for stmt in self.body if stmt.semantic_kind is SemanticKind.CLASS and stmt.name.startswith("Test")] @@ -241,7 +241,7 @@ def convert_file_to_test_class(self): name = "".join(word.capitalize() for word in parts) return name if name.startswith("Test") else f"Test{name}" - def remove_duplicate_import(self, import_str): + def remove_duplicate_import(self, import_str) -> None: """AI: Remove duplicate occurrences of the given import statement, keeping the first and last.""" import_stmt: Sequence[NodeProtocol] = self.pattern_factory.create_statements(import_str) # type: ignore[assignment] # type: ignore[assignment] diff --git a/src/renaissance/syntax_tree/ast_node.py b/src/renaissance/syntax_tree/ast_node.py index 701341fb..85fdf345 100644 --- a/src/renaissance/syntax_tree/ast_node.py +++ b/src/renaissance/syntax_tree/ast_node.py @@ -74,7 +74,7 @@ def __init__(self, root: Self) -> None: self.node: NodeT | None = None self.indent = "" - def __repr__(self): + def __repr__(self) -> str: """AI: Return the formatted node representation.""" return format_node(self) diff --git a/src/renaissance/syntax_tree/ast_refactor_actions.py b/src/renaissance/syntax_tree/ast_refactor_actions.py index 9bfcff9b..36ec5ad9 100644 --- a/src/renaissance/syntax_tree/ast_refactor_actions.py +++ b/src/renaissance/syntax_tree/ast_refactor_actions.py @@ -33,7 +33,7 @@ def __init__(self, processor: ASTProcessor, pattern_factory: CPPPatternFactory) self.pattern_factory = pattern_factory self.replaced: set[int] = set() - def replace_expr(self, name: str, replacement: str, kind: SemanticKind | Callable[[ASTNode], bool]): + def replace_expr(self, name: str, replacement: str, kind: SemanticKind | Callable[[ASTNode], bool]) -> None: """AI: Replace occurrences of the named expression matching kind with replacement.""" kind_predicate = _kind_predicate(kind) @@ -49,7 +49,7 @@ def replace_name( replacement: str, kind: SemanticKind | Callable[[ASTNode], bool] | None = None, skip_kind: SemanticKind | Callable[[ASTNode], bool] | None = None, - ): + ) -> None: """AI: Replace occurrences of the named node (matching kind, excluding skip_kind) with replacement.""" kind_predicate = _kind_predicate(kind) skip_kind_predicate = _kind_predicate(skip_kind) @@ -73,7 +73,7 @@ def replace_text( replacement: str, kind: SemanticKind | Callable[[ASTNode], bool] | None = None, skip_kind: SemanticKind | Callable[[ASTNode], bool] | None = None, - ): + ) -> None: """AI: Replace nodes whose text equals text (matching kind, excluding skip_kind) with replacement.""" kind_predicate = _kind_predicate(kind) skip_kind_predicate = _kind_predicate(skip_kind) @@ -91,7 +91,7 @@ def matches_text(n: ASTNode) -> bool: [self.processor.replace(n.text.replace(n.name, replacement, 1), n) for n in found_nodes] - def replace_declaration(self, declaration: str, replacement: str): + def replace_declaration(self, declaration: str, replacement: str) -> None: """AI: Replace every match of declaration with replacement.""" for match in self.find_declaration(declaration): self.processor.replace(replacement, match) @@ -102,7 +102,7 @@ def _replace_patterns( replacement: str, patterns: Sequence[Sequence[ASTNode]], matches: Sequence[PatternMatch], - ): + ) -> None: if not patterns: self.processor.replace(replacement, matches) return diff --git a/src/renaissance/syntax_tree/ast_rewriter.py b/src/renaissance/syntax_tree/ast_rewriter.py index 47e7a603..bc889c82 100644 --- a/src/renaissance/syntax_tree/ast_rewriter.py +++ b/src/renaissance/syntax_tree/ast_rewriter.py @@ -60,7 +60,7 @@ def replace( target: Rewritable | Sequence[Rewritable] | PatternMatch | Sequence[PatternMatch], include_whitespace: bool = True, include_comments: bool = True, - ): + ) -> None: """AI: Queue a rewrite replacing target's source text with new_content.""" self.__rewrites.add( _RewriteActionType.REPLACE, @@ -75,7 +75,7 @@ def remove( target: Rewritable | Sequence[Rewritable] | PatternMatch | Sequence[PatternMatch], include_whitespace: bool = True, include_comments: bool = True, - ): + ) -> None: """AI: Queue a rewrite removing target's source text.""" self.__rewrites.add(_RewriteActionType.REMOVE, target, "", include_whitespace, include_comments) @@ -85,7 +85,7 @@ def insert_before( target: Rewritable | Sequence[Rewritable] | PatternMatch | Sequence[PatternMatch], include_whitespace: bool = True, include_comments: bool = True, - ): + ) -> None: """AI: Queue a rewrite inserting new_content immediately before target's source text.""" self.__rewrites.add( _RewriteActionType.INSERT_BEFORE, @@ -101,7 +101,7 @@ def insert_after( target: Rewritable | Sequence[Rewritable] | PatternMatch | Sequence[PatternMatch], include_whitespace: bool = True, include_comments: bool = True, - ): + ) -> None: """AI: Queue a rewrite inserting new_content immediately after target's source text.""" self.__rewrites.add( _RewriteActionType.INSERT_AFTER, @@ -192,11 +192,11 @@ def add( replacement: str, include_whitespace: bool, include_comments: bool, - ): + ) -> None: rewrite = _RewriteAction(action, target, replacement, include_whitespace, include_comments) self.add_rewrite(rewrite) - def add_rewrite(self, rewrite: _RewriteAction): + def add_rewrite(self, rewrite: _RewriteAction) -> None: self.rewrites.append(rewrite) def apply(self) -> bytes: @@ -304,7 +304,7 @@ def __is_ancestor_in_nodes(self, node: Rewritable) -> bool: # 2 # | rew | # |node| - def no_conflict(node1, rew): + def no_conflict(node1, rew) -> bool: return not (node1.end_offset < rew.offset or node1.offset > rew.end_offset) result = any(no_conflict(node, rew) for rew in rewrite_nodes) @@ -318,7 +318,7 @@ def __replace( nodes: Sequence[Rewritable], include_whitespace: bool, include_comments: bool, - ): + ) -> None: """Replace the content of the given node(s) with new content. Args: @@ -354,7 +354,7 @@ def __remove( nodes: Sequence[Rewritable], include_whitespace: bool = False, include_comments: bool = False, - ): + ) -> None: """Remove a list of AST nodes from the content, optionally including surrounding whitespace and comments. Args: @@ -404,7 +404,7 @@ def __insert( nodes: Sequence[Rewritable], include_whitespace: bool, include_comments: bool, - ): + ) -> None: if not nodes: return content = self.content diff --git a/src/renaissance/syntax_tree/batch_ast_processor.py b/src/renaissance/syntax_tree/batch_ast_processor.py index d842d40b..7142d055 100644 --- a/src/renaissance/syntax_tree/batch_ast_processor.py +++ b/src/renaissance/syntax_tree/batch_ast_processor.py @@ -18,7 +18,7 @@ class BatchASTProcessor: """AI: Process multiple ASTs in parallel by applying a shared action to each.""" - def __init__(self, in_memory: bool = False, max_processes: int = 4): + def __init__(self, in_memory: bool = False, max_processes: int = 4) -> None: """Initialize the BatchASTProcessor. Args: diff --git a/src/renaissance/syntax_tree/match_finder.py b/src/renaissance/syntax_tree/match_finder.py index 54e4189c..38c447d1 100644 --- a/src/renaissance/syntax_tree/match_finder.py +++ b/src/renaissance/syntax_tree/match_finder.py @@ -50,7 +50,7 @@ def __init__( greedy: str | None, expansion_start: int, end_index: int = INCOMPLETE_MATCH, - ): + ) -> None: """AI: Track one candidate pattern-match state (bound expansions, greedy position) during matching.""" self.exp: dict[str, Sequence[NodeProtocol]] = exp self.index: int = index @@ -58,12 +58,12 @@ def __init__( self.end_index: int = end_index self.expansion_start: int = expansion_start - def reset_greedy(self): + def reset_greedy(self) -> None: """AI: Clear the current greedy-expansion tracking state.""" self.greedy = None self.expansion_start = -1 - def close_greedy(self, key, nodes, start, end): + def close_greedy(self, key, nodes, start, end) -> None: """Store a completed greedy expansion and reset greedy state.""" value = nodes[start:end] self.exp[key] = value @@ -77,13 +77,13 @@ def fork(self) -> Variant: class PatternMatch: """AI: Represent a successful match of a pattern against a sequence of AST nodes.""" - def __init__(self, nodes, expansions, patterns): + def __init__(self, nodes, expansions, patterns) -> None: """AI: Represent a successful match of a pattern against a sequence of AST nodes.""" self.nodes = nodes self.expansions = expansions self.patterns = patterns - def __str__(self): + def __str__(self) -> str: """AI: Return the newline-joined signatures of the matched nodes.""" return "\n".join(node.signature for node in self.nodes) @@ -148,7 +148,7 @@ def variant_in_match_stmt(src: NodeProtocol, cmp: NodeProtocol, expansions) -> l return [] -def _advance_match_all(variant: Variant, cmp: Sequence, src: Sequence, i: int, new_variants: list): +def _advance_match_all(variant: Variant, cmp: Sequence, src: Sequence, i: int, new_variants: list) -> None: """Advance variant.index past consecutive MATCH_ALL pattern nodes, forking new_variants as needed.""" while pattern_kind(cmp[variant.index]) is PatternKind.MATCH_ALL: current_name = cmp[variant.index].name @@ -170,7 +170,7 @@ def _advance_match_all(variant: Variant, cmp: Sequence, src: Sequence, i: int, n break -def _apply_child_match(variant: Variant, child_variants: list, cmp: Sequence, src: Sequence, i: int, new_variants: list): +def _apply_child_match(variant: Variant, child_variants: list, cmp: Sequence, src: Sequence, i: int, new_variants: list) -> None: """Apply a successful child match, forking if there are multiple child variants.""" greedy_open = variant.greedy is not None and variant.expansion_start != -1 and variant.greedy not in variant.exp if greedy_open: @@ -189,7 +189,7 @@ def _apply_child_match(variant: Variant, child_variants: list, cmp: Sequence, sr variant.end_index = len(src) - 1 -def _advance_greedy(variant: Variant, cmp: Sequence, src: Sequence, i: int): +def _advance_greedy(variant: Variant, cmp: Sequence, src: Sequence, i: int) -> None: """Accumulate or verify greedy expansion for the current source node.""" exp_for_key = variant.exp.get(cmp[variant.index].name) exp_index = i - variant.expansion_start @@ -270,7 +270,7 @@ def find_variants(src: Sequence, cmp: Sequence, expansion=None, start: int = 0, return valid_variants -def find_in_list(src: Sequence, cmp: Sequence, exp=None, start: int = 0): +def find_in_list(src: Sequence, cmp: Sequence, exp=None, start: int = 0) -> int: """AI: Return the end index of the first full match of cmp within src starting at start, or -2 if none.""" if exp is None: exp = {} diff --git a/src/renaissance/syntax_tree/recipe_ast_processor.py b/src/renaissance/syntax_tree/recipe_ast_processor.py index f311aec9..485b17c3 100644 --- a/src/renaissance/syntax_tree/recipe_ast_processor.py +++ b/src/renaissance/syntax_tree/recipe_ast_processor.py @@ -40,7 +40,7 @@ def final_action() -> TFunc: def final_action_decorator(func: TFunc) -> TFunc: @functools.wraps(func) - def final_action_wrapper(recipe: TFunc): + def final_action_wrapper(recipe: TFunc) -> None: func(recipe) return final_action_wrapper @@ -75,7 +75,7 @@ def after_step(step: str) -> TFunc: def after_step_decorator(func: TFunc) -> TFunc: @functools.wraps(func) - def after_step_wrapper(preceding_methods: Sequence[str], recipe: TFunc): + def after_step_wrapper(preceding_methods: Sequence[str], recipe: TFunc) -> None: if step in preceding_methods: func(recipe) @@ -94,21 +94,21 @@ def __init__( file_filter: str, in_memory: bool = False, max_processes: int = 4, - ): + ) -> None: """AI: Prepare a processor that runs a recipe's steps across a batch of files, optionally in parallel.""" self.__recipe: TRecipe = recipe self.__batch_processor = BatchASTProcessor(in_memory=in_memory, max_processes=max_processes) self.__iterableProvider = iterable_provider self.__file_filter = file_filter - def run(self): + def run(self) -> None: """AI: Run each of the recipe's decorated steps across the batch of files and return the collected results.""" actions: list[TFunc] = [] results: list[Any] = [] for idx, recipe_step_method in enumerate(get_methods_with_decorator(type(self.__recipe), recipe_step)): results.append(None) - def recipe_action(ast_processor: ASTProcessor): + def recipe_action(ast_processor: ASTProcessor) -> None: result = recipe_step_method(step, self.__recipe, ast_processor) if result: results[idx] = result @@ -118,7 +118,7 @@ def recipe_action(ast_processor: ASTProcessor): after_step_actions: list[TFunc] = [] for after_step_method in get_methods_with_decorator(self.__recipe.__class__, after_step): - def after_step_action(): + def after_step_action() -> None: after_step_method(results, self.__recipe) after_step_actions.append(after_step_action) diff --git a/test/c_cpp/test_ast_factory.py b/test/c_cpp/test_ast_factory.py index f760640b..28a12cba 100644 --- a/test/c_cpp/test_ast_factory.py +++ b/test/c_cpp/test_ast_factory.py @@ -12,7 +12,7 @@ class TestASTFactory: """AI: Tests creating C/C++ AST nodes via ASTFactory.""" @pytest.mark.parametrize("_, factory", Factories.factories) - def test_create(self, _, factory): + def test_create(self, _, factory) -> None: """AI: Verify factory.create_from_text parses C source and produces a renderable AST node.""" ast = factory.create_from_text("/*comment1 */ int main() { return 0; } /* comment at end */", "test.c") text = ASTShower.get_node(ast) diff --git a/test/c_cpp/test_ast_finder.py b/test/c_cpp/test_ast_finder.py index 69fceee6..f564cecc 100644 --- a/test/c_cpp/test_ast_finder.py +++ b/test/c_cpp/test_ast_finder.py @@ -26,14 +26,14 @@ class TestKindFinder(TestFinder): """AI: Tests ASTFinder.find single-node lookups by semantic/parser kind.""" @pytest.mark.parametrize("_, factory", Factories.factories) - def test_find_bogus(self, _, factory): + def test_find_bogus(self, _, factory) -> None: """AI: Verify find_nodes finds no matches for a bogus parser kind.""" model = self.load_model(factory) total = len(find_nodes(model, lambda node: node.semantic_kind is SemanticKind.NODE and node.parser_kind == "BogusType")) assert_that(total, is_(0)) @pytest.mark.parametrize("_, factory", Factories.factories) - def test_find_expr(self, _, factory): + def test_find_expr(self, _, factory) -> None: """AI: Verify find_nodes finds expression nodes in the parsed model.""" model = self.load_model(factory) ASTShower.show_node(model) @@ -44,7 +44,7 @@ class TestAllFinder(TestFinder): """AI: Tests ASTFinder.find_all lookups against a predicate.""" @pytest.mark.parametrize("_, factory", Factories.factories) - def test_find_all_bogus(self, _, factory): + def test_find_all_bogus(self, _, factory) -> None: """AI: Verify ASTFinder.find_all finds no matches for a bogus parser kind.""" model = self.load_model(factory) @@ -55,7 +55,7 @@ def is_bogus(node: ASTNode): assert_that(ASTFinder.find_all(model, is_bogus), has_length(0)) @pytest.mark.parametrize("_, factory", Factories.factories) - def test_find_all_expr(self, _, factory): + def test_find_all_expr(self, _, factory) -> None: """AI: Verify ASTFinder.find_all finds binary operator expression nodes in the parsed model.""" model = self.load_model(factory) diff --git a/test/c_cpp/test_ast_references.py b/test/c_cpp/test_ast_references.py index e81e299f..c6b3cd56 100644 --- a/test/c_cpp/test_ast_references.py +++ b/test/c_cpp/test_ast_references.py @@ -30,7 +30,7 @@ class TestASTReference: ], ), ) - def test_definition_declaration_references(self, _, factory, code, args): + def test_definition_declaration_references(self, _, factory, code, args) -> None: """AI: Verify a call/construct expression references its function definition/declaration and vice versa.""" ast = factory.create_from_text(code, "test.cpp") with tempfile.TemporaryDirectory() as temp_dir: @@ -58,7 +58,7 @@ def test_definition_declaration_references(self, _, factory, code, args): assert_that(declarations, has_length(greater_than(0))) @pytest.mark.parametrize("_, factory", Factories.factories) - def test_call_reference(self, _, factory): + def test_call_reference(self, _, factory) -> None: """AI: Verify a function call reference resolves to its declared function and back via referenced_by.""" ast = factory.create_from_text("void f(){} void f1(){ f();}", "test.c") call = first(find_nodes(ast, is_clang_declaration_reference)) @@ -86,7 +86,7 @@ def test_call_reference(self, _, factory): ], ), ) - def test_var_reference(self, _, factory, code, args): + def test_var_reference(self, _, factory, code, args) -> None: """AI: Verify a variable-use reference resolves to its declaration/parameter and back via referenced_by.""" ast = factory.create_from_text(code, "test.c") using = first(find_nodes(ast, is_clang_declaration_reference)) @@ -112,7 +112,7 @@ def test_var_reference(self, _, factory, code, args): ], ), ) - def test_type_reference(self, _, factory, code, language): + def test_type_reference(self, _, factory, code, language) -> None: """AI: Verify a type-use reference resolves to its class/typedef declaration and back via referenced_by.""" ast = factory.create_from_text(code, "test." + language) # in clang python, there is a TYPE_REF below the VAR_DECL node whereas @@ -148,7 +148,7 @@ def test_type_reference(self, _, factory, code, language): ], ), ) - def test_base_class_reference(self, _, factory, code, language): + def test_base_class_reference(self, _, factory, code, language) -> None: """AI: Verify a derived class references its base class declaration.""" ast = factory.create_from_text(code, "test." + language) diff --git a/test/c_cpp/test_astshower.py b/test/c_cpp/test_astshower.py index 0746f076..7d366fe0 100644 --- a/test/c_cpp/test_astshower.py +++ b/test/c_cpp/test_astshower.py @@ -13,7 +13,7 @@ class TestCcppShower: """AI: Tests rendering C/C++ AST nodes via ASTShower.""" @pytest.fixture(autouse=True) - def setup(self): + def setup(self) -> None: """AI: Prepare a shared AST factory, parsed model, and pattern factory for shower tests.""" self.factory = ASTFactory(ClangASTNode, []) self.atu = self.factory.create_from_text( @@ -27,7 +27,7 @@ def setup(self): ) self.pattern_factory = CPatternFactory(self.factory, self.atu) - def test_show_call_using_repr(self): + def test_show_call_using_repr(self) -> None: """AI: Verify the string repr of a matched call node renders its signature and location.""" pattern = self.pattern_factory.create(""" int $xx; @@ -42,14 +42,14 @@ def test_show_call_using_repr(self): matches_regexp("(CALL_EXPR, $pa, test.c[\\d+:\\d+]): |$pa($xx);|\n"), ) - def test_show_main(self): + def test_show_main(self) -> None: """AI: Verify ASTShower renders the translation unit and its top-level declarations.""" text = ASTShower.get_node(self.atu, display_parser_kind=True) assert_that(text, starts_with("(TRANSLATION_UNIT,")) assert_that(text, contains_string("(FUNCTION_DECL, ba,")) assert_that(text, contains_string("(VAR_DECL, na,")) - def test_show_body(self): + def test_show_body(self) -> None: """AI: Verify ASTShower renders each top-level declaration's body correctly.""" assert_that( ASTShower.get_node(self.atu.children[0], display_parser_kind=True), @@ -68,7 +68,7 @@ def test_show_body(self): matches_regexp("(VAR_DECL, na, test.c[\\d+:\\d+]): |int na = 55;|\n"), ) - def test_show_ast(self): + def test_show_ast(self) -> None: """AI: Verify ASTShower renders the full AST including nested declarations and expressions.""" text = ASTShower.get_node(self.atu, display_parser_kind=True) assert_that(text, contains_string("(TRANSLATION_UNIT,")) @@ -81,7 +81,7 @@ def test_show_ast(self): assert_that(text, contains_string("(INTEGER_LITERAL,")) assert_that(text, not_(contains_string("FunctionDef"))) - def test_show_if_else(self): + def test_show_if_else(self) -> None: """AI: Verify ASTShower renders an if/else statement and its branches correctly.""" factory = ASTFactory(ClangASTNode, []) atu = factory.create_from_text( diff --git a/test/c_cpp/test_c_match_finder.py b/test/c_cpp/test_c_match_finder.py index dddafa68..f7be3465 100644 --- a/test/c_cpp/test_c_match_finder.py +++ b/test/c_cpp/test_c_match_finder.py @@ -43,7 +43,7 @@ class TestCMatchFinder: } """ - def test_simple_pattern(self): + def test_simple_pattern(self) -> None: """AI: Verify a single-statement pattern matches its occurrence in parsed source.""" factory = ASTFactory(ClangASTNode, []) patterns = CPatternFactory(factory).create_statements("b--;") @@ -65,7 +65,7 @@ def do_test(factory: ASTFactory, cpp_code, patterns: list[ASTNode], recursive: b return matches @staticmethod - def assert_matches(expected_dicts_per_match, actual_matches): + def assert_matches(expected_dicts_per_match, actual_matches) -> None: """AI: Assert that actual_matches' expansion text matches expected_dicts_per_match element-wise.""" assert_that(actual_matches, has_length(len(expected_dicts_per_match))) for actual, expected_dict in zip(actual_matches, expected_dicts_per_match, strict=True): @@ -77,7 +77,7 @@ def assert_matches(expected_dicts_per_match, actual_matches): class TestExpressions(TestCMatchFinder): """AI: Tests matching C/C++ expression patterns.""" - def test_match_expr(self): + def test_match_expr(self) -> None: """AI: Verify an expression pattern with a placeholder matches multiple occurrences in parsed source.""" factory = ASTFactory(ClangJsonASTNode, []) expr_node = CPatternFactory(factory).create_expression("a == $x") @@ -121,7 +121,7 @@ def test( expression, expected_full_matches: list[str], expected_dicts_per_match: list[dict[str, list[str]]], - ): + ) -> None: """AI: Verify an expression pattern matches the expected occurrences and placeholder bindings.""" expr_node = CPatternFactory(factory).create_expression(expression) found_matches = self.do_test(factory, TestStatements.SIMPLE_CPP, [expr_node], recursive=True) @@ -203,7 +203,7 @@ def test( factory, statements, expected_dicts_per_match: list[dict[str, list[str]]], - ): + ) -> None: """AI: Verify a statement pattern with expansions matches the expected placeholder bindings.""" patterns = CPatternFactory(factory).create_statements(statements) @@ -258,7 +258,7 @@ def test( statements, extra_declarations, expected_dicts_per_match: list[dict[str, list[str]]], - ): + ) -> None: """AI: Verify a function-call statement pattern with variadic placeholders matches the expected bindings.""" code = """ int one(int a); @@ -308,7 +308,7 @@ def test_args( statements, extra_declarations, expected_dicts_per_match: list[dict[str, list[str]]], - ): + ) -> None: """AI: Verify a statement pattern repeating the same placeholder across two call sites matches consistently.""" code = """ int fc(int a, int b, int c, int d, int e); @@ -353,7 +353,7 @@ def test_statements( statements, extra_declarations, expected_dicts_per_match: list[dict[str, list[str]]], - ): + ) -> None: """AI: Verify an if/else statement pattern matches the true and false branches with consistent placeholders.""" code = """ @@ -435,7 +435,7 @@ class TestUseAtuToCreatePattern(TestCMatchFinder): ], ), ) - def test(self, name, factory, statements, pattern_type, expected, names): + def test(self, name, factory, statements, pattern_type, expected, names) -> None: """AI: Verify a pattern built from the parsed translation unit's own nodes matches occurrences of that pattern.""" code = """ #define FOO "foo" @@ -484,7 +484,7 @@ def test(self, name, factory, statements, pattern_type, expected, names): @pytest.mark.parametrize("_, factory", Factories.factories) @pytest.mark.skip("stmt and expr are the same") - def test_is_match_expression_differs_from_stmt(self, _: str, factory: ASTFactory): + def test_is_match_expression_differs_from_stmt(self, _: str, factory: ASTFactory) -> None: """AI: Verify an expression pattern does not match an equivalent statement pattern.""" pattern_factory = CPatternFactory(factory) expression_pattern = pattern_factory.create_expression("x=3", ["int x;"]) @@ -507,7 +507,7 @@ def test_is_match_expression_differs_from_stmt(self, _: str, factory: ASTFactory class TestIndividualCases: """AI: Ad-hoc regression tests for specific C/C++ pattern-matching cases.""" - def test_multi_single(self): + def test_multi_single(self) -> None: """AI: Verify a variadic-placeholder statement pattern matches exactly one variant across multiple call sites.""" factory = ASTFactory(ClangASTNode) atu = factory.create_from_text( diff --git a/test/c_cpp/test_c_pattern_factory.py b/test/c_cpp/test_c_pattern_factory.py index f2f63753..1f817328 100644 --- a/test/c_cpp/test_c_pattern_factory.py +++ b/test/c_cpp/test_c_pattern_factory.py @@ -15,7 +15,7 @@ class TestCPatternFactory: """AI: Tests deriving header text from a C/C++ translation unit.""" - def test_derive_header(self): + def test_derive_header(self) -> None: """AI: Verify derive_header_text extracts includes, defines, and typedefs from a translation unit.""" code = """ #include @@ -153,7 +153,7 @@ class TestExpression: ], ), ) - def test(self, _, factory, expression, expected): + def test(self, _, factory, expression, expected) -> None: """AI: Verify create_expression builds a pattern node matching the expected rendered signature.""" pattern_factory = CPatternFactory(factory) node = pattern_factory.create_expression(expression) @@ -189,7 +189,7 @@ def test( parameters, expected_vars, expected_refs, - ): + ) -> None: """AI: Verify create_declarations builds the expected number of declaration and reference nodes.""" pattern_factory = CPatternFactory(factory) created_declarations = list(pattern_factory.create_declarations(declaration_text, parameters=parameters, types=types)) @@ -230,7 +230,7 @@ def test( extra_declarations, expected_stmts, expected_refs, - ): + ) -> None: """AI: Verify create_statements builds the expected number of statement and reference nodes.""" pattern_factory = CPatternFactory(factory) created_statements = list(pattern_factory.create_statements(statement_text, extra_declarations=extra_declarations)) @@ -263,7 +263,7 @@ class TestUseAtuToCreatePatterns: ), ), ) - def test(self, _, factory, statement_text, expected_stmts, expected_refs): + def test(self, _, factory, statement_text, expected_stmts, expected_refs) -> None: """AI: Verify a complex pattern built from a translation unit's typedef/struct/define/statement matches the source.""" code = """ int print(const char*,const char*,const char*,const char*); diff --git a/test/c_cpp/test_clang_ast_node.py b/test/c_cpp/test_clang_ast_node.py index d8845b0b..679b3ec3 100644 --- a/test/c_cpp/test_clang_ast_node.py +++ b/test/c_cpp/test_clang_ast_node.py @@ -10,46 +10,46 @@ class TestClangAstNode: """AI: Tests for the Clang-backed ASTNode implementation.""" - def test_is_same_node(self): + def test_is_same_node(self) -> None: """AI: Verify two occurrences of an identical pattern statement resolve to the same node.""" factory = ASTFactory(ClangASTNode, []) src = CPatternFactory(factory).create_statements("a == 3;a == 3;") CPatternFactory(factory).create_statement("a == 3;") assert_that(src[0], is_(src[1])) - def test_find_all_in_clang_list_with_expansion(self): + def test_find_all_in_clang_list_with_expansion(self) -> None: """AI: Verify a declaration reference node's name property is populated within a Clang AST list.""" factory = ASTFactory(ClangASTNode, []) src = CPatternFactory(factory).create_statement("a == 3;") assert_that("a", is_(src.children[0].children[0].properties["name"])) - def test_marco_also_include_define(self): + def test_marco_also_include_define(self) -> None: """AI: Verify a #define macro is parsed as a single child node.""" src = ClangASTNode.load_from_text('#define x "xxx"', "test.c") assert_that(src.children, has_length(1)) - def test_marco_also_include_define_signature(self): + def test_marco_also_include_define_signature(self) -> None: """AI: Verify a #define macro's signature text matches the original source.""" src = ClangASTNode.load_from_text('#define x "xxx"', "test.c") assert_that('#define x "xxx"', is_(src.children[-1].signature)) - def test_var_decl_includesemi_column(self): + def test_var_decl_includesemi_column(self) -> None: """AI: Verify a variable declaration's signature includes its trailing semicolon.""" src = ClangASTNode.load_from_text("int x= 0;", "test.c") assert_that(src.children[-1].signature, is_("int x= 0;")) - def test_var_decl_in_ancestor(self): + def test_var_decl_in_ancestor(self) -> None: """AI: Verify get_ancestor finds the enclosing VAR_DECL ancestor node.""" src = ClangASTNode.load_from_text("int x= 0;", "test.c") assert_that(src.children[-1].children[-1].get_ancestor("VAR_DECL")) - def test_var_decl_in_ancestor_of(self): + def test_var_decl_in_ancestor_of(self) -> None: """AI: Verify is_ancestor_of confirms the translation unit is an ancestor of a nested declaration node.""" src = ClangASTNode.load_from_text("int x= 0;", "test.c") assert_that(src.is_ancestor_of(src.children[-1].children[-1])) @pytest.mark.skip("last semicolon is cut off from decl") - def test_var_decl_include_semi_column_and_keep_space(self): + def test_var_decl_include_semi_column_and_keep_space(self) -> None: """AI: Verify a variable declaration's signature preserves surrounding whitespace and trailing semicolon.""" src = ClangASTNode.load_from_text(" int x = 0 ;", "test.c") assert_that(src.children[-1].signature, is_(" int x = 0 ;")) @@ -62,7 +62,7 @@ def test_var_decl_include_semi_column_and_keep_space(self): "so slicing the (differently-sized) cached byte array with clang's offsets returns " "corrupted text.", ) - def test_signature_after_multibyte_char_when_filesystem_encoding_is_not_utf8(self, mocker): + def test_signature_after_multibyte_char_when_filesystem_encoding_is_not_utf8(self, mocker) -> None: """AI: Verify a node's signature offsets remain correct after a multi-byte character when the filesystem encoding isn't UTF-8.""" # 'é' encodes as 1 byte in latin-1 but 2 bytes in UTF-8. libclang parses/reports # offsets against a UTF-8 encoding of the source regardless of the platform's @@ -72,18 +72,18 @@ def test_signature_after_multibyte_char_when_filesystem_encoding_is_not_utf8(sel src = ClangASTNode.load_from_text(code, "test.c") assert_that(src.children[-1].signature, is_("int x = 0;")) - def test_struct_include_semicolon(self): + def test_struct_include_semicolon(self) -> None: """AI: Verify a struct forward-declaration's signature includes its trailing semicolon.""" src = ClangASTNode.load_from_text("struct s;", "test.c") assert_that(src.children[-1].signature, is_("struct s;")) @pytest.mark.skip("last semicolon is cut off from struct") - def test_struct_include_semicolon_and_space(self): + def test_struct_include_semicolon_and_space(self) -> None: """AI: Verify a struct definition's signature includes trailing whitespace and semicolon.""" src = ClangASTNode.load_from_text("struct s{int x; int y;} ;", "test.c") assert_that("struct s{int x; int y;} ;", is_(src.children[-1].signature)) - def test_mix_of_macro_and_decl(self): + def test_mix_of_macro_and_decl(self) -> None: """AI: Verify a mix of macro defines and declarations parse correctly together.""" src = ClangASTNode.load_from_text( """ diff --git a/test/c_cpp/test_clang_json_ast_node.py b/test/c_cpp/test_clang_json_ast_node.py index 131ef7a6..0c939d21 100644 --- a/test/c_cpp/test_clang_json_ast_node.py +++ b/test/c_cpp/test_clang_json_ast_node.py @@ -15,17 +15,17 @@ class TestClangJsonAstNode: """AI: Tests for the Clang JSON-backed ASTNode implementation.""" - def test_load_from_text_empty_dir(self): + def test_load_from_text_empty_dir(self) -> None: """AI: Verify load_from_text with an empty work directory returns a ClangJsonASTNode instance.""" node = ClangJsonASTNode.load_from_text("int main(){return 0;}", "hello.c", [], Path()) assert_that(isinstance(node, ClangJsonASTNode)) - def test_load_from_text(self): + def test_load_from_text(self) -> None: """AI: Verify load_from_text returns a ClangJsonASTNode instance for simple source text.""" node = ClangJsonASTNode.load_from_text("int main(){return 0;}", "hello.c", [], Path()) assert_that(isinstance(node, ClangJsonASTNode)) - def test_name_in_props(self): + def test_name_in_props(self) -> None: """AI: Verify a declaration reference node's name is exposed in its properties dict.""" factory = ASTFactory(ClangJsonASTNode, []) src = CPatternFactory(factory).create_statement("a == 3;") @@ -38,7 +38,7 @@ def test_name_in_props(self): "which defines both.", strict=True, ) - def test_is_hashable(self): + def test_is_hashable(self) -> None: """AI: Verify hashing a ClangJsonASTNode fails since it defines __eq__ without __hash__.""" node = ClangJsonASTNode.load_from_text("int main(){return 0;}", "hello.c", [], Path()) hash(node) diff --git a/test/c_cpp/test_clang_json_match_finder.py b/test/c_cpp/test_clang_json_match_finder.py index f98b7907..abeb2ee9 100644 --- a/test/c_cpp/test_clang_json_match_finder.py +++ b/test/c_cpp/test_clang_json_match_finder.py @@ -15,7 +15,7 @@ class TestClangJsonMatchFinder: """AI: Tests matching patterns against Clang JSON AST nodes.""" @pytest.mark.skip - def test_is_match_using_macro_from_atu(self): + def test_is_match_using_macro_from_atu(self) -> None: """AI: Verify a pattern derived from a translation unit's own macro matches the equivalent source usage.""" code = """ #define BAR "bar" diff --git a/test/c_cpp/test_clang_match_finder.py b/test/c_cpp/test_clang_match_finder.py index 3400042c..1574df2f 100644 --- a/test/c_cpp/test_clang_match_finder.py +++ b/test/c_cpp/test_clang_match_finder.py @@ -11,7 +11,7 @@ class ClangMatchFinderTest: """AI: Tests matching patterns against Clang AST nodes.""" - def test_is_match(self): + def test_is_match(self) -> None: """AI: Verify a pattern derived from a translation unit's macro matches the corresponding statement in source.""" code = """ #define BAR "bar" @@ -34,7 +34,7 @@ def test_is_match(self): result = MatchFinder.match_pattern(func_body, [statements]) assert_that(result, has_length(1)) - def test_typedef_in_pattern(self): + def test_typedef_in_pattern(self) -> None: """AI: Verify a pattern built with a typedef declaration parses without error.""" factory = ASTFactory(ClangASTNode, []) pattern_factory = CPatternFactory(factory) diff --git a/test/c_cpp/test_clang_predicates.py b/test/c_cpp/test_clang_predicates.py index e4b35bc7..6f71a438 100644 --- a/test/c_cpp/test_clang_predicates.py +++ b/test/c_cpp/test_clang_predicates.py @@ -14,13 +14,13 @@ class Node: """AI: Build a minimal stand-in node for exercising clang predicate functions.""" - def __init__(self, parser_kind: str, semantic_kind: SemanticKind = SemanticKind.NODE): + def __init__(self, parser_kind: str, semantic_kind: SemanticKind = SemanticKind.NODE) -> None: """AI: Store the parser-reported kind string and semantic kind for this stand-in node.""" self.parser_kind = parser_kind self.semantic_kind = semantic_kind -def test_clang_parser_predicates_cover_native_and_json_spellings(): +def test_clang_parser_predicates_cover_native_and_json_spellings() -> None: """AI: Assert clang predicates recognize both native cursor-kind and clang_json spellings.""" assert is_clang_type_reference(Node("TypeRef")) assert is_clang_type_reference(Node("TYPE_REF")) @@ -35,7 +35,7 @@ def test_clang_parser_predicates_cover_native_and_json_spellings(): assert is_clang_constructor(Node("CXX_CONSTRUCTOR")) -def test_clang_macro_and_semantic_predicates_are_explicit(): +def test_clang_macro_and_semantic_predicates_are_explicit() -> None: """AI: Assert macro-definition predicates and semantic-kind defaults behave as expected.""" assert is_clang_macro_definition(Node("MacroDefinition")) assert is_clang_macro_definition(Node("MACRO_DEFINITION")) diff --git a/test/c_cpp/test_node_protocol_metadata.py b/test/c_cpp/test_node_protocol_metadata.py index 890f2fd8..92369c6d 100644 --- a/test/c_cpp/test_node_protocol_metadata.py +++ b/test/c_cpp/test_node_protocol_metadata.py @@ -8,7 +8,7 @@ from renaissance.syntax_tree.semantic_kind import SemanticKind -def test_clang_json_nodes_expose_protocol_metadata(): +def test_clang_json_nodes_expose_protocol_metadata() -> None: """AI: Assert a Clang JSON AST node exposes the expected parser_kind and semantic_kind.""" node = ClangJsonASTNode.load_from_text("int f() { return 1; }", "test.c", [], Path()) @@ -16,7 +16,7 @@ def test_clang_json_nodes_expose_protocol_metadata(): assert node.semantic_kind is SemanticKind.TRANSLATION_UNIT -def test_clang_common_kinds_map_to_shared_semantic_kinds(): +def test_clang_common_kinds_map_to_shared_semantic_kinds() -> None: """AI: Assert common clang parser kinds map to their expected shared semantic kinds.""" assert CLANG_KIND_MAP["FunctionDecl"] is SemanticKind.FUNCTION assert CLANG_KIND_MAP["CallExpr"] is SemanticKind.CALL @@ -24,7 +24,7 @@ def test_clang_common_kinds_map_to_shared_semantic_kinds(): assert CLANG_KIND_MAP["CXXRecordDecl"] is SemanticKind.CLASS -def test_clang_specific_unknown_kinds_keep_parser_identity(): +def test_clang_specific_unknown_kinds_keep_parser_identity() -> None: """AI: Assert an unmapped clang parser kind falls back to the generic NODE semantic kind.""" parser_kind = "FriendDecl" @@ -32,7 +32,7 @@ def test_clang_specific_unknown_kinds_keep_parser_identity(): assert CLANG_KIND_MAP.get(parser_kind, SemanticKind.NODE) is SemanticKind.NODE -def test_clang_parser_kind_predicate_preserves_specific_concepts(): +def test_clang_parser_kind_predicate_preserves_specific_concepts() -> None: """AI: Assert is_clang_kind matches only the exact requested parser kind.""" node = type("Node", (), {"parser_kind": "CXXConstructorDecl"})() diff --git a/test/common/test_rewriter.py b/test/common/test_rewriter.py index 59ff67fa..adee0246 100644 --- a/test/common/test_rewriter.py +++ b/test/common/test_rewriter.py @@ -21,14 +21,14 @@ class TestRewriter: (b"abcdefghij", 5, -1, b"AT_END", b"abcdeAT_END"), ], ) - def test_replace(self, initial_bytes, start, end, new_content, expected_bytes): + def test_replace(self, initial_bytes, start, end, new_content, expected_bytes) -> None: """AI: Assert Rewriter.replace produces the expected byte sequence after a single replacement.""" rewriter = Rewriter(initial_bytes) rewriter.replace(start, end, new_content) result = rewriter.apply() assert_that(expected_bytes, is_(result)) - def test_multiple_replaces(self): + def test_multiple_replaces(self) -> None: """AI: Assert Rewriter correctly applies several overlapping and non-overlapping replacements together.""" initial_bytes = b"abcdefghij" rewriter = Rewriter(initial_bytes) diff --git a/test/conftest.py b/test/conftest.py index 9729f59d..8c8d8605 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -252,7 +252,7 @@ } -def pytest_addoption(parser): +def pytest_addoption(parser) -> None: """AI: Register the --skip-slow-hypothesis pytest CLI option.""" parser.addoption( "--skip-slow-hypothesis", @@ -262,12 +262,12 @@ def pytest_addoption(parser): ) -def pytest_configure(config): +def pytest_configure(config) -> None: """AI: Register the custom hypothesisslow marker with pytest.""" config.addinivalue_line("markers", "hypothesisslow: mark test as a slow hypothesis test") -def pytest_collection_modifyitems(config, items): +def pytest_collection_modifyitems(config, items) -> None: """AI: Skip slow hypothesis tests unless requested, and skip known pre-existing failing tests.""" if config.getoption("--skip-slow-hypothesis"): skip = pytest.mark.skip(reason="Do not pass --skip-slow-hypothesis to run slow hypothesis test") diff --git a/test/examples/test_descendant_search.py b/test/examples/test_descendant_search.py index e11408de..a763c615 100644 --- a/test/examples/test_descendant_search.py +++ b/test/examples/test_descendant_search.py @@ -38,7 +38,7 @@ class TestFindDescendantMatch: inner_text: str = "my_function()" extra_declarations_inner_text: list[str] = ["int my_function();"] - def test_descendant_search_with_clang(self): + def test_descendant_search_with_clang(self) -> None: """AI: Verify find_descendant_match locates nested call expressions inside outer if-statements using Clang.""" factory = ASTFactory(ClangASTNode) pattern_factory = CPatternFactory(factory) @@ -49,7 +49,7 @@ def test_descendant_search_with_clang(self): assert_that(results, has_length(3), f"length of results = {len(results)}") - def test_descendant_search_with_json(self): + def test_descendant_search_with_json(self) -> None: """AI: Verify find_descendant_match locates nested call expressions inside outer if-statements using Clang JSON.""" factory = ASTFactory(ClangJsonASTNode) pattern_factory = CPatternFactory(factory) @@ -88,7 +88,7 @@ class TestBasic: ), ), ) - def test_snippet(self, _: str, factory: ASTFactory, snippet: str, extra_declarations: list[str]): + def test_snippet(self, _: str, factory: ASTFactory, snippet: str, extra_declarations: list[str]) -> None: """AI: Verify a literal or placeholder call expression pattern matches its single occurrence in source.""" pattern_factory = CPatternFactory(factory) code_pattern = factory.create_from_text(self.code_text, "text.c") # file extension consistent with C Pattern Factory @@ -97,7 +97,7 @@ def test_snippet(self, _: str, factory: ASTFactory, snippet: str, extra_declarat assert_that(results, has_length(1), f"length of results = {len(results)}") @pytest.mark.parametrize("_, factory", Factories.factories) - def test_is_match_assignment_expression(self, _: str, factory: ASTFactory): + def test_is_match_assignment_expression(self, _: str, factory: ASTFactory) -> None: """AI: Verify identical assignment expressions match themselves and each other.""" pattern_factory = CPatternFactory(factory) expression1_pattern: NodeProtocol = pattern_factory.create_expression("x=3", ["int x;"]) @@ -115,7 +115,7 @@ def test_is_match_assignment_expression(self, _: str, factory: ASTFactory): ) @pytest.mark.parametrize("_, factory", Factories.factories) - def test_is_match_call_expression(self, _: str, factory: ASTFactory): + def test_is_match_call_expression(self, _: str, factory: ASTFactory) -> None: """AI: Verify identical call expressions match themselves and each other.""" pattern_factory = CPatternFactory(factory) expression1_pattern = pattern_factory.create_expression("f()", ["int f();"]) @@ -133,7 +133,7 @@ def test_is_match_call_expression(self, _: str, factory: ASTFactory): ) @pytest.mark.parametrize("_, factory", Factories.factories) - def test_is_match_statement(self, _: str, factory: ASTFactory): + def test_is_match_statement(self, _: str, factory: ASTFactory) -> None: """AI: Verify identical call statements match themselves and each other despite whitespace differences.""" pattern_factory = CPatternFactory(factory) statement1_pattern = pattern_factory.create_statement("f();", extra_declarations=["int f();"]) diff --git a/test/examples/test_examples.py b/test/examples/test_examples.py index bb26fc4e..d87443d0 100644 --- a/test/examples/test_examples.py +++ b/test/examples/test_examples.py @@ -44,7 +44,7 @@ class TestRefactorWithNestedCompositions: """AI: Tests the nested-compositions refactor example produces the expected rewritten code.""" - def test_refactor_with_nested_compositions(self): + def test_refactor_with_nested_compositions(self) -> None: """AI: Verify the nested-compositions refactor example produces the expected rewritten C source.""" result = refactor_with_nested_compositions(["", ""]) assert_that(result, is_not(None)) @@ -91,7 +91,7 @@ class TestReplaceIfWithTernaryOperator: """AI: Tests the replace-if-with-ternary refactor example produces the expected rewritten code.""" # didn't check expected result - def test_refactor_with_nested_compositions(self): + def test_refactor_with_nested_compositions(self) -> None: """AI: Verify the replace-if-with-ternary refactor example produces the expected rewritten C source.""" result = replace_if_with_ternary() @@ -112,13 +112,13 @@ class TestRemoveUnusedVariable: """AI: Tests the remove-unused-variable refactor examples produce the expected rewritten code.""" @pytest.mark.parametrize("_, node_type", Factories.node_types) - def test_remove_unused_variable_using_refactor_method(self, _: str, node_type: type[ASTNode]): + def test_remove_unused_variable_using_refactor_method(self, _: str, node_type: type[ASTNode]) -> None: """AI: Verify remove_unused_variable_using_refactor_method produces the expected rewritten result.""" result, expected = remove_unused_variable_using_refactor_method(node_type) assert_that(result, is_(expected)) @pytest.mark.parametrize("_, node_type", Factories.node_types) - def test_remove_unused_variable_low_level(self, _: str, node_type: type[ASTNode]): + def test_remove_unused_variable_low_level(self, _: str, node_type: type[ASTNode]) -> None: """AI: Verify remove_unused_variable_low_level produces the expected rewritten result.""" result, expected_result = remove_unused_variable_low_level(node_type) assert_that(result, is_(expected_result)) @@ -144,7 +144,7 @@ def test( factory: ASTFactory, _node_type: type[ASTNode], method: Callable[[ASTFactory, CPatternFactory], tuple[str, str]], - ): + ) -> None: """AI: Verify the given AST-finder-style example method produces the expected refactor result.""" pattern_factory = CPatternFactory(factory) result, expected = method(factory, pattern_factory) @@ -156,7 +156,7 @@ def test( "comment is inserted twice before every declaration instead of once before each 'old' one.", strict=True, ) - def test_example_add_comment_and_commit(self): + def test_example_add_comment_and_commit(self) -> None: """Verify example_add_comment_and_commit adds the obsolete-comment once before each 'old' declaration using Clang.""" factory = ASTFactory(ClangASTNode) pattern_factory = CPatternFactory(factory) @@ -164,7 +164,7 @@ def test_example_add_comment_and_commit(self): assert_that(result, is_(expected)) - def test_example_add_comment_and_commit_json(self): + def test_example_add_comment_and_commit_json(self) -> None: """AI: Verify example_add_comment_and_commit inserts the expected obsolete-comment text using Clang JSON.""" factory = ASTFactory(ClangJsonASTNode) pattern_factory = CPatternFactory(factory) @@ -176,7 +176,7 @@ def test_example_add_comment_and_commit_json(self): reason="The declaration patterns never bind $old to the 'old' type, so no declaration is replaced.", strict=True, ) - def test_example_replace_old_by_fancy_new(self): + def test_example_replace_old_by_fancy_new(self) -> None: """Verify example_replace_old_by_fancy_new replaces every 'old' declaration type with 'fancy_new' using Clang.""" factory = ASTFactory(ClangASTNode) pattern_factory = CPatternFactory(factory) @@ -184,23 +184,23 @@ def test_example_replace_old_by_fancy_new(self): assert_that(result, is_(expected)) - def test_make_sure_that_batch_remove_proc_still_run(self): + def test_make_sure_that_batch_remove_proc_still_run(self) -> None: """AI: Verify batch_remove_unused_variable_once_example runs without raising an exception.""" assert_that(calling(batch_remove_unused_variable_once_example), not_(raises(Exception))) - def test_make_sure_that_batch_repeat_proc_still_run(self): + def test_make_sure_that_batch_repeat_proc_still_run(self) -> None: """AI: Verify batch_repeat_example runs without raising an exception.""" assert_that(calling(batch_repeat_example), not_(raises(Exception))) - def test_make_sure_that_batch_recipe_proc_still_run(self): + def test_make_sure_that_batch_recipe_proc_still_run(self) -> None: """AI: Verify batch_recipe_example runs without raising an exception.""" assert_that(calling(batch_recipe_example), not_(raises(Exception))) - def test_make_sure_that_recipe_still_run(self): + def test_make_sure_that_recipe_still_run(self) -> None: """AI: Verify the recipe example raises the expected 'stddef.h not found' exception.""" assert_that(calling(receipe_example), raises(Exception, pattern="'stddef.h' file not found")) - def test_make_sure_different_style_still_run(self): + def test_make_sure_different_style_still_run(self) -> None: """AI: Verify all example refactor/finder functions run without raising an exception.""" factory = ASTFactory(ClangASTNode) pattern_factory = CPatternFactory(factory) @@ -223,7 +223,7 @@ def test_make_sure_different_style_still_run(self): ) assert_that(calling(lambda: main([])), not_(raises(Exception))) - def test_make_sure_that_nested_compositions_still_run(self): + def test_make_sure_that_nested_compositions_still_run(self) -> None: """AI: Verify refactor_with_nested_compositions runs without raising an exception.""" assert_that(calling(lambda: refactor_with_nested_compositions([])), not_(raises(Exception))) @@ -234,7 +234,7 @@ def test_make_sure_that_nested_compositions_still_run(self): strict=True, ) @pytest.mark.parametrize("node_type", [ClangASTNode, ClangJsonASTNode]) - def test_make_sure_unused_var_still_run(self, node_type): + def test_make_sure_unused_var_still_run(self, node_type) -> None: """AI: Verify remove_unused_variable_low_level and remove_unused_variable_using_refactor_method run without raising.""" assert_that( calling(lambda: remove_unused_variable_low_level(node_type)), @@ -245,7 +245,7 @@ def test_make_sure_unused_var_still_run(self, node_type): not_(raises(Exception)), ) - def test_make_sure_replace_if_with_ternary_still_run(self): + def test_make_sure_replace_if_with_ternary_still_run(self) -> None: """AI: Verify replace_if_with_ternary produces the expected rewritten C source.""" result = replace_if_with_ternary() diff --git a/test/examples/test_python_examples.py b/test/examples/test_python_examples.py index fdb09a86..0deda4b8 100644 --- a/test/examples/test_python_examples.py +++ b/test/examples/test_python_examples.py @@ -19,17 +19,17 @@ class TestPythonExamples: # result = python_ast_smoke_test() # assert_that(result, is_(result)) - def test_python_cst_still_works(self): + def test_python_cst_still_works(self) -> None: """AI: Verify the Python CST rewriting example script still runs and produces output.""" result = python_cst_smoke_test() assert_that(result, is_(result)) - def test_python_lst_still_works(self): + def test_python_lst_still_works(self) -> None: """AI: Verify the Python LST rewriting example script still runs and produces output.""" result = python_lst_smoke_test() assert_that(result, is_(result)) - def test_python_rst_still_works(self): + def test_python_rst_still_works(self) -> None: """AI: Verify the Python RST rewriting example script still runs and produces output.""" result = python_rst_smoke_test() assert_that(result, is_(result)) diff --git a/test/extractors/test_code_graph_extractors.py b/test/extractors/test_code_graph_extractors.py index 537f9437..555f67dc 100644 --- a/test/extractors/test_code_graph_extractors.py +++ b/test/extractors/test_code_graph_extractors.py @@ -32,7 +32,7 @@ def make_lst_node(kind, signature, name=None): class TestBaseCodeGraphExtractor: """AI: Tests the shared BaseCodeGraphExtractor behavior (abstract hook, file iteration, graph saving).""" - def test_is_abstract(self): + def test_is_abstract(self) -> None: """AI: Verify _process_file raises NotImplementedError on the base extractor.""" with patch("renaissance.integrations.tree_sitter.adapter.TreeSitterAdapter"): extractor = BaseCodeGraphExtractor.__new__(BaseCodeGraphExtractor) @@ -40,7 +40,7 @@ def test_is_abstract(self): with pytest.raises(NotImplementedError): extractor._process_file("file.py", MagicMock()) - def test_extract_calls_process_file_for_each_file(self, mocker, tmp_path): + def test_extract_calls_process_file_for_each_file(self, mocker, tmp_path) -> None: """AI: Verify extract() calls _process_file once per input file.""" f1 = tmp_path / "a.py" f1.write_text("x = 1") @@ -59,7 +59,7 @@ def test_extract_calls_process_file_for_each_file(self, mocker, tmp_path): assert_that(spy.call_count, is_(2)) - def test_extract_skips_file_on_error(self, tmp_path): + def test_extract_skips_file_on_error(self, tmp_path) -> None: """AI: Verify extract() skips a file whose parsing raises an error without propagating it.""" with patch("renaissance.integrations.tree_sitter.adapter.TreeSitterAdapter") as mock_adapter_cls: mock_adapter = mock_adapter_cls.return_value @@ -69,7 +69,7 @@ def test_extract_skips_file_on_error(self, tmp_path): # Should not raise extractor.extract([str(tmp_path / "nonexistent.py")]) - def test_save_graph_writes_file(self, tmp_path, mocker): + def test_save_graph_writes_file(self, tmp_path, mocker) -> None: """AI: Verify save_graph writes the graph to a graphml file.""" with patch("renaissance.integrations.tree_sitter.adapter.TreeSitterAdapter"): extractor = PythonCodeGraphExtractor("python", tree_sitter_python) @@ -80,7 +80,7 @@ def test_save_graph_writes_file(self, tmp_path, mocker): mock_write.assert_called_once() - def test_constructor_creates_directed_graph(self): + def test_constructor_creates_directed_graph(self) -> None: """AI: Verify the extractor constructor initializes a directed graph.""" with patch("renaissance.integrations.tree_sitter.adapter.TreeSitterAdapter"): extractor = PythonCodeGraphExtractor("python", tree_sitter_python) @@ -107,7 +107,7 @@ def _make_extractor(): with patch("renaissance.integrations.tree_sitter.adapter.TreeSitterAdapter"): return PythonCodeGraphExtractor("python", tree_sitter_python) - def test_adds_file_and_folder_nodes(self): + def test_adds_file_and_folder_nodes(self) -> None: """AI: Verify processing a Python file adds file and folder nodes to the graph.""" extractor = self._make_extractor() lst = self.make_lst([]) @@ -117,7 +117,7 @@ def test_adds_file_and_folder_nodes(self): assert_that(extractor.graph.nodes, has_item("/project/src/foo.py")) assert_that(extractor.graph.nodes, has_item("/project/src")) - def test_adds_contains_edge_from_folder_to_file(self): + def test_adds_contains_edge_from_folder_to_file(self) -> None: """AI: Verify processing a Python file adds a contains edge from its folder to the file.""" extractor = self._make_extractor() lst = self.make_lst([]) @@ -127,7 +127,7 @@ def test_adds_contains_edge_from_folder_to_file(self): assert_that(extractor.graph.has_edge("/project/src", "/project/src/foo.py"), is_(True)) assert_that(extractor.graph.edges["/project/src", "/project/src/foo.py"]["type"], is_("contains")) - def test_adds_function_node_for_function_definition(self): + def test_adds_function_node_for_function_definition(self) -> None: """AI: Verify a Python function definition adds a function node to the graph.""" extractor = self._make_extractor() func_node = make_lst_node(SemanticKind.FUNCTION, "def my_func(x):") @@ -138,7 +138,7 @@ def test_adds_function_node_for_function_definition(self): assert_that(extractor.graph.nodes, has_item("my_func")) assert_that(extractor.graph.nodes["my_func"]["type"], is_("function")) - def test_adds_defines_edge_for_function(self): + def test_adds_defines_edge_for_function(self) -> None: """AI: Verify a Python function definition adds a defines edge from the file to the function.""" extractor = self._make_extractor() func_node = make_lst_node(SemanticKind.FUNCTION, "def my_func(x):") @@ -149,7 +149,7 @@ def test_adds_defines_edge_for_function(self): assert_that(extractor.graph.has_edge("/src/foo.py", "my_func"), is_(True)) assert_that(extractor.graph.edges["/src/foo.py", "my_func"]["type"], is_("defines")) - def test_adds_call_node_for_call(self): + def test_adds_call_node_for_call(self) -> None: """AI: Verify a Python call expression adds a call-target node to the graph.""" extractor = self._make_extractor() call_node = make_lst_node(SemanticKind.CALL, "some_func(arg1)") @@ -160,7 +160,7 @@ def test_adds_call_node_for_call(self): assert_that(extractor.graph.nodes, has_item("some_func")) assert_that(extractor.graph.nodes["some_func"]["type"], is_("call_target")) - def test_adds_calls_edge_for_call(self): + def test_adds_calls_edge_for_call(self) -> None: """AI: Verify a Python call expression adds a calls edge from the file to the call target.""" extractor = self._make_extractor() call_node = make_lst_node(SemanticKind.CALL, "some_func(arg1)") @@ -171,7 +171,7 @@ def test_adds_calls_edge_for_call(self): assert_that(extractor.graph.has_edge("/src/foo.py", "some_func"), is_(True)) assert_that(extractor.graph.edges["/src/foo.py", "some_func"]["type"], is_("calls")) - def test_ignores_unrelated_node_kinds(self): + def test_ignores_unrelated_node_kinds(self) -> None: """AI: Verify unrelated Python node kinds are not added to the graph.""" extractor = self._make_extractor() other_node = make_lst_node("import_statement", "import os") @@ -181,7 +181,7 @@ def test_ignores_unrelated_node_kinds(self): assert_that(extractor.graph.nodes, not_(has_item("import os"))) - def test_multiple_functions_all_added(self): + def test_multiple_functions_all_added(self) -> None: """AI: Verify multiple Python function definitions are all added as separate nodes.""" extractor = self._make_extractor() nodes = [ @@ -209,7 +209,7 @@ def _make_extractor(): with patch("renaissance.integrations.tree_sitter.adapter.TreeSitterAdapter"): return JavaCodeGraphExtractor("java", tree_sitter_python) - def test_adds_file_and_folder_nodes(self): + def test_adds_file_and_folder_nodes(self) -> None: """AI: Verify processing a Java file adds file and folder nodes to the graph.""" extractor = self._make_extractor() lst = self.make_lst([]) @@ -219,7 +219,7 @@ def test_adds_file_and_folder_nodes(self): assert_that(extractor.graph.nodes, has_item("/project/src/Main.java")) assert_that(extractor.graph.nodes, has_item("/project/src")) - def test_adds_method_node_for_method_declaration(self): + def test_adds_method_node_for_method_declaration(self) -> None: """AI: Verify a Java method declaration adds a method node to the graph.""" extractor = self._make_extractor() method_node = make_lst_node(SemanticKind.FUNCTION, "void doSomething(){}", name="doSomething") @@ -230,7 +230,7 @@ def test_adds_method_node_for_method_declaration(self): assert_that(extractor.graph.nodes, has_item("doSomething")) assert_that(extractor.graph.nodes["doSomething"]["type"], is_("method")) - def test_method_node_uses_default_name_when_missing(self): + def test_method_node_uses_default_name_when_missing(self) -> None: """AI: Verify a Java method node falls back to a default name when the name property is missing.""" extractor = self._make_extractor() method_node = make_lst_node(SemanticKind.FUNCTION, "void doSomething(){}") @@ -241,7 +241,7 @@ def test_method_node_uses_default_name_when_missing(self): assert_that(extractor.graph.nodes, has_item("method")) - def test_adds_defines_edge_for_method(self): + def test_adds_defines_edge_for_method(self) -> None: """AI: Verify a Java method declaration adds a defines edge from the file to the method.""" extractor = self._make_extractor() method_node = make_lst_node(SemanticKind.FUNCTION, "void doSomething()", name="doSomething") @@ -252,7 +252,7 @@ def test_adds_defines_edge_for_method(self): assert_that(extractor.graph.has_edge("/src/Main.java", "doSomething"), is_(True)) assert_that(extractor.graph.edges["/src/Main.java", "doSomething"]["type"], is_("defines")) - def test_adds_method_invocation_node(self): + def test_adds_method_invocation_node(self) -> None: """AI: Verify a Java method invocation adds a method-target node to the graph.""" extractor = self._make_extractor() invocation_node = make_lst_node(SemanticKind.CALL, "obj.doSomething(arg)") @@ -263,7 +263,7 @@ def test_adds_method_invocation_node(self): assert_that(extractor.graph.nodes, has_item("obj.doSomething")) assert_that(extractor.graph.nodes["obj.doSomething"]["type"], is_("method_target")) - def test_adds_calls_edge_for_invocation(self): + def test_adds_calls_edge_for_invocation(self) -> None: """AI: Verify a Java method invocation adds a calls edge from the file to the method target.""" extractor = self._make_extractor() invocation_node = make_lst_node(SemanticKind.CALL, "obj.doSomething(arg)") @@ -288,7 +288,7 @@ def _make_extractor(): with patch("renaissance.integrations.tree_sitter.adapter.TreeSitterAdapter"): return CppCodeGraphExtractor("cpp", tree_sitter_python) - def test_adds_file_and_folder_nodes(self): + def test_adds_file_and_folder_nodes(self) -> None: """AI: Verify processing a C++ file adds file and folder nodes to the graph.""" extractor = self._make_extractor() lst = self.make_lst([]) @@ -298,7 +298,7 @@ def test_adds_file_and_folder_nodes(self): assert_that(extractor.graph.nodes, has_item("/project/src/main.cpp")) assert_that(extractor.graph.nodes, has_item("/project/src")) - def test_adds_function_node_for_function_definition(self): + def test_adds_function_node_for_function_definition(self) -> None: """AI: Verify a C++ function definition adds a function node to the graph.""" extractor = self._make_extractor() func_node = make_lst_node(SemanticKind.FUNCTION, "int main()", name="main") @@ -309,7 +309,7 @@ def test_adds_function_node_for_function_definition(self): assert_that(extractor.graph.nodes, has_item("main")) assert_that(extractor.graph.nodes["main"]["type"], is_("function")) - def test_function_node_uses_default_name_when_missing(self): + def test_function_node_uses_default_name_when_missing(self) -> None: """AI: Verify a C++ function node falls back to a default name when the name property is missing.""" extractor = self._make_extractor() func_node = make_lst_node(SemanticKind.FUNCTION, "int main()") @@ -320,7 +320,7 @@ def test_function_node_uses_default_name_when_missing(self): assert_that(extractor.graph.nodes, has_item("func")) - def test_adds_defines_edge_for_function(self): + def test_adds_defines_edge_for_function(self) -> None: """AI: Verify a C++ function definition adds a defines edge from the file to the function.""" extractor = self._make_extractor() func_node = make_lst_node(SemanticKind.FUNCTION, "int main()", name="main") @@ -331,7 +331,7 @@ def test_adds_defines_edge_for_function(self): assert_that(extractor.graph.has_edge("/src/main.cpp", "main"), is_(True)) assert_that(extractor.graph.edges["/src/main.cpp", "main"]["type"], is_("defines")) - def test_adds_call_expression_node(self): + def test_adds_call_expression_node(self) -> None: """AI: Verify a C++ call expression adds a call-target node to the graph.""" extractor = self._make_extractor() call_node = make_lst_node(SemanticKind.CALL, "printf(fmt)") @@ -342,7 +342,7 @@ def test_adds_call_expression_node(self): assert_that(extractor.graph.nodes, has_item("printf")) assert_that(extractor.graph.nodes["printf"]["type"], is_("call_target")) - def test_adds_calls_edge_for_call_expression(self): + def test_adds_calls_edge_for_call_expression(self) -> None: """AI: Verify a C++ call expression adds a calls edge from the file to the call target.""" extractor = self._make_extractor() call_node = make_lst_node(SemanticKind.CALL, "printf(fmt)") @@ -353,7 +353,7 @@ def test_adds_calls_edge_for_call_expression(self): assert_that(extractor.graph.has_edge("/src/main.cpp", "printf"), is_(True)) assert_that(extractor.graph.edges["/src/main.cpp", "printf"]["type"], is_("calls")) - def test_ignores_unrelated_node_kinds(self): + def test_ignores_unrelated_node_kinds(self) -> None: """AI: Verify unrelated C++ node kinds are not added to the graph.""" extractor = self._make_extractor() other_node = make_lst_node(SemanticKind.COMMENT, "// a comment") diff --git a/test/extractors/test_python_extractors.py b/test/extractors/test_python_extractors.py index f5a3209c..486f5852 100644 --- a/test/extractors/test_python_extractors.py +++ b/test/extractors/test_python_extractors.py @@ -11,12 +11,12 @@ class TestPythonExtractor: """AI: Tests for the Python AST codebase extractor.""" - def test_extractor(self): + def test_extractor(self) -> None: """AI: Verify PythonExtractor can be instantiated.""" extractor = PythonExtractor() assert_that(extractor, is_not(None)) - def test_extract_python_file(self): + def test_extract_python_file(self) -> None: """AI: Verify processing a Python file populates the extractor's codebase and graph.""" extractor = PythonExtractor() extractor.process(Path(targets.__file__).parent / "demo.py") @@ -26,7 +26,7 @@ def test_extract_python_file(self): assert_that(extractor.graph.nodes, is_not(empty())) assert_that(extractor.graph.edges, is_not(empty())) - def test_extract_python_file_and_save_graphml(self): + def test_extract_python_file_and_save_graphml(self) -> None: """AI: Verify processing a Python file and saving its graph produces a readable graphml file.""" extractor = PythonExtractor() extractor.process(Path(targets.__file__).parent / "demo.py") diff --git a/test/lst/test_clang_adapter.py b/test/lst/test_clang_adapter.py index b9ed45dc..f9be6a1d 100644 --- a/test/lst/test_clang_adapter.py +++ b/test/lst/test_clang_adapter.py @@ -14,7 +14,7 @@ class TestClangAdapter: """AI: Tests for the Clang-based LST adapter.""" - def test_parse_cpp_file(self): + def test_parse_cpp_file(self) -> None: """AI: Verify parsing a C++ example file produces a traversable LST.""" adapter = ClangAdapter() lst = adapter.parse(Path(targets.__file__).parent / "cpp_example.cpp") diff --git a/test/lst/test_clang_concrete_pattern_matcher.py b/test/lst/test_clang_concrete_pattern_matcher.py index e37f75bc..5cbf9528 100644 --- a/test/lst/test_clang_concrete_pattern_matcher.py +++ b/test/lst/test_clang_concrete_pattern_matcher.py @@ -28,7 +28,7 @@ class TestClangConcretePatternMatcher: ("int body=0; auto f = []() { return 1; };", "int $body=0; auto $f = []() { $body; };"), ], ) - def test_clang_patterns(self, code, pattern): + def test_clang_patterns(self, code, pattern) -> None: """AI: Verify a variety of C++ constructs match their corresponding pattern with placeholders.""" adapter = ClangAdapter() interface = TreeSitterPatternFactory(adapter) @@ -36,7 +36,7 @@ def test_clang_patterns(self, code, pattern): matches = extractor.run(code) assert_that(matches, is_not(empty())) - def test_clang_patterns_using_extractor(self): + def test_clang_patterns_using_extractor(self) -> None: """AI: Verify an enum pattern matches its occurrence in source via the Extractor.""" adapter = ClangAdapter() interface = TreeSitterPatternFactory(adapter) @@ -44,7 +44,7 @@ def test_clang_patterns_using_extractor(self): matches = extractor.run("int E = 0; int vals=0; enum E { A };") assert_that(matches, has_length(1)) - def test_clang_failing_pattern(self): + def test_clang_failing_pattern(self) -> None: """AI: Verify an enum statement pattern matches its occurrence via match_pattern directly on LST nodes.""" adapter = ClangAdapter() interface = TreeSitterPatternFactory(adapter) @@ -53,7 +53,7 @@ def test_clang_failing_pattern(self): matches = match_pattern(code.root.children, pattern) assert_that(matches, has_length(1)) - def test_find_variant_with_clang_failing_pattern(self): + def test_find_variant_with_clang_failing_pattern(self) -> None: """AI: Verify find_variants locates a matching variant for an enum statement pattern.""" adapter = ClangAdapter() interface = TreeSitterPatternFactory(adapter) @@ -64,7 +64,7 @@ def test_find_variant_with_clang_failing_pattern(self): assert_that(matches[0].end_index, is_not(MIS_MATCH)) @pytest.mark.skip("it should be the same really") - def test_type_property_between_code_and_pattern_are_same(self): + def test_type_property_between_code_and_pattern_are_same(self) -> None: """AI: Verify the 'type' property is the same for equivalent code and pattern enum nodes.""" adapter = ClangAdapter() interface = TreeSitterPatternFactory(adapter) @@ -87,7 +87,7 @@ def test_type_property_between_code_and_pattern_are_same(self): ("try {} catch (...) {}", "int $body, $handler;try $body catch (...) $handler"), ], ) - def test_clang_patterns_to_be_fixed(self, code, pattern): + def test_clang_patterns_to_be_fixed(self, code, pattern) -> None: """AI: Verify currently-unsupported C++ constructs don't match their pattern yet (documents known gaps).""" adapter = ClangAdapter() interface = TreeSitterPatternFactory(adapter) @@ -95,7 +95,7 @@ def test_clang_patterns_to_be_fixed(self, code, pattern): matches = extractor.run(code) assert_that(matches, has_length(0)) # but should be 1 - def test_is_match_clang_patterns_without_decl(self): + def test_is_match_clang_patterns_without_decl(self) -> None: """AI: Verify a return statement doesn't match a placeholder pattern when the placeholder isn't declared.""" adapter = ClangAdapter() interface = TreeSitterPatternFactory(adapter) @@ -103,7 +103,7 @@ def test_is_match_clang_patterns_without_decl(self): p = interface.create_statement("int main() { return $body; }") assert_that(is_match(c.children[-1], p.children[-1], {}), is_(False)) - def test_is_match_clang_patterns_with_decl(self): + def test_is_match_clang_patterns_with_decl(self) -> None: """AI: Verify a return statement matches a placeholder pattern when the placeholder is declared.""" adapter = ClangAdapter() interface = TreeSitterPatternFactory(adapter) @@ -111,7 +111,7 @@ def test_is_match_clang_patterns_with_decl(self): p = interface.create_statement("int $body=0; int main() { return $body; }") assert_that(is_match(c.children[-1], p.children[-1], {}), is_(True)) - def test_is_match_clang_tree(self): + def test_is_match_clang_tree(self) -> None: """AI: Verify is_match_tree confirms a matching statement tree against its pattern.""" adapter = ClangAdapter() interface = TreeSitterPatternFactory(adapter) @@ -119,7 +119,7 @@ def test_is_match_clang_tree(self): p = interface.create_statement("int $body=0; int main() { return $body; }") assert_that(is_match_tree([c.children[-1]], [p.children[-1]], {}), is_(True)) - def test_is_match_clang_patterns(self): + def test_is_match_clang_patterns(self) -> None: """AI: Verify MatchFinder.match_pattern finds one match for a statement against its pattern.""" adapter = ClangAdapter() interface = TreeSitterPatternFactory(adapter) diff --git a/test/lst/test_concrete_pattern_matcher.py b/test/lst/test_concrete_pattern_matcher.py index 04932498..5299ad77 100644 --- a/test/lst/test_concrete_pattern_matcher.py +++ b/test/lst/test_concrete_pattern_matcher.py @@ -38,7 +38,7 @@ class TestConcretePatternMatcher: ("import os\nx=5", "import $mod $stmt"), ], ) - def test_python_pattern(self, code, pattern): + def test_python_pattern(self, code, pattern) -> None: """AI: Verify a variety of Python constructs match their corresponding pattern with placeholders.""" adapter = TreeSitterAdapter(tree_sitter_python) interface = TreeSitterPatternFactory(adapter) @@ -47,7 +47,7 @@ def test_python_pattern(self, code, pattern): assert_that(matches, has_length(1), f"{code=} {pattern=}") - def test_is_match_python_patterns(self): + def test_is_match_python_patterns(self) -> None: """AI: Verify each try/except sub-statement matches its corresponding placeholder pattern node.""" adapter = TreeSitterAdapter(tree_sitter_python) interface = TreeSitterPatternFactory(adapter) @@ -58,7 +58,7 @@ def test_is_match_python_patterns(self): assert_that(is_match(c.children[2], p.children[2], {}), is_(True)) assert_that(is_match(c.children[3], p.children[3], {}), is_(True)) - def test_is_match_python_patterns_tree(self): + def test_is_match_python_patterns_tree(self) -> None: """AI: Verify is_match_tree confirms a matching try/except node sequence against its pattern.""" adapter = TreeSitterAdapter(tree_sitter_python) interface = TreeSitterPatternFactory(adapter) @@ -66,7 +66,7 @@ def test_is_match_python_patterns_tree(self): p = interface.create_statement("try: $b\nexcept Exception: $b") assert_that(is_match_tree(c.children, p.children, {}), is_(True)) - def test_is_match_python_patterns_1(self): + def test_is_match_python_patterns_1(self) -> None: """AI: Verify an if-statement matches its placeholder pattern via is_match and match_pattern.""" adapter = TreeSitterAdapter(tree_sitter_python) interface = TreeSitterPatternFactory(adapter) @@ -75,7 +75,7 @@ def test_is_match_python_patterns_1(self): assert_that(is_match(c, p), is_(True)) assert_that(match_pattern([c], [p]), is_not(empty())) - def test_is_match(self): + def test_is_match(self) -> None: """AI: Verify an identical function definition pattern matches itself.""" adapter = TreeSitterAdapter(tree_sitter_python) interface = TreeSitterPatternFactory(adapter) diff --git a/test/lst/test_languages.py b/test/lst/test_languages.py index 383441b5..16585c8c 100644 --- a/test/lst/test_languages.py +++ b/test/lst/test_languages.py @@ -81,7 +81,7 @@ class TestLanguages: (tscpp, "float pi = 3.14f;"), ], ) - def test_language_parsing(self, lang, code): + def test_language_parsing(self, lang, code) -> None: """AI: Verify parsing a code snippet with the given tree-sitter language grammar produces a non-empty LST.""" adapter = TreeSitterAdapter(lang) tree = adapter.parse_code(code) diff --git a/test/lst/test_matchers.py b/test/lst/test_matchers.py index 76b20b85..93fedc41 100644 --- a/test/lst/test_matchers.py +++ b/test/lst/test_matchers.py @@ -16,7 +16,7 @@ class TestMatchers: """AI: Tests for matching tree-sitter LST nodes by semantic kind.""" @pytest.fixture(autouse=True) - def setUp(self): + def setUp(self) -> None: """AI: Build if/for/while/try/class pattern nodes shared by the matcher tests.""" adapter = TreeSitterAdapter(tscpp) self.if_node = self.make_pattern("if (x > 0) print(x);", adapter) @@ -28,26 +28,26 @@ def setUp(self): ) self.class_node = self.make_pattern("class MyClass { method(self) { pass; } }", adapter) - def test_if_pattern_match(self): + def test_if_pattern_match(self) -> None: """AI: Verify an if-statement node matches an equivalent placeholder if-pattern.""" adapter = TreeSitterAdapter(tscpp) pattern = self.make_pattern("if ($x > 0) print($x);", adapter) assert_that(is_match(self.if_node, pattern)) - def test_for_pattern_match(self): + def test_for_pattern_match(self) -> None: """AI: Verify a for-statement node matches an equivalent placeholder for-pattern.""" adapter = TreeSitterAdapter(tscpp) pattern = self.make_pattern("for ($i in range(10)) print($i);", adapter) assert_that(is_match(self.for_node, pattern)) - def test_while_pattern_match(self): + def test_while_pattern_match(self) -> None: """AI: Verify a while-statement node matches an equivalent placeholder while-pattern.""" adapter = TreeSitterAdapter(tscpp) pattern = self.make_pattern("while ($x < 10) $x += 1;", adapter) assert_that(is_match(self.while_node, pattern)) - def test_try_pattern_match(self): + def test_try_pattern_match(self) -> None: """AI: Verify a try/catch statement node matches an equivalent placeholder try-pattern.""" adapter = TreeSitterAdapter(tscpp) pattern = self.make_pattern( @@ -56,18 +56,18 @@ def test_try_pattern_match(self): ) assert_that(is_match(self.try_node, pattern)) - def test_class_pattern_match(self): + def test_class_pattern_match(self) -> None: """AI: Verify a class-statement node matches an identical class-pattern.""" adapter = TreeSitterAdapter(tscpp) pattern = self.make_pattern("class MyClass { method(self) { pass; } }", adapter) assert_that(is_match(self.class_node, pattern)) - def test_node_type_match(self): + def test_node_type_match(self) -> None: """AI: Verify traversing the if-node finds exactly one CALL-kind descendant.""" matches = [node for node in traverse(self.if_node) if node.semantic_kind is SemanticKind.CALL] assert_that(matches, has_length(1)) - def test_node_type_match_exact_type(self): + def test_node_type_match_exact_type(self) -> None: """AI: Verify find_semantic_kind finds exactly one CALL-kind descendant of the if-node.""" matches = find_semantic_kind(self.if_node, SemanticKind.CALL) assert_that(matches, has_length(1)) diff --git a/test/lst/test_node_protocol_metadata.py b/test/lst/test_node_protocol_metadata.py index 73650bf1..8ddf9a19 100644 --- a/test/lst/test_node_protocol_metadata.py +++ b/test/lst/test_node_protocol_metadata.py @@ -6,7 +6,7 @@ from renaissance.syntax_tree.semantic_kind import SemanticKind -def test_tree_sitter_nodes_expose_protocol_metadata(): +def test_tree_sitter_nodes_expose_protocol_metadata() -> None: """AI: Assert a tree-sitter LST node exposes the expected parser_kind and semantic_kind.""" adapter = TreeSitterAdapter(tree_sitter_python) root = adapter.to_lst("def f():\n return 1\n", adapter.parse_code("def f():\n return 1\n")).root @@ -17,7 +17,7 @@ def test_tree_sitter_nodes_expose_protocol_metadata(): assert root.children[0].semantic_kind is SemanticKind.FUNCTION -def test_tree_sitter_kind_key_preserves_unknown_parser_identity(): +def test_tree_sitter_kind_key_preserves_unknown_parser_identity() -> None: """AI: Assert kind_key falls back to the semantic kind for unmapped tree-sitter parser kinds.""" adapter = TreeSitterAdapter(tree_sitter_python) parsed = adapter.parse_code("x = 1\n") diff --git a/test/lst/test_show_node_in_mermaid.py b/test/lst/test_show_node_in_mermaid.py index 30e20c86..ee5828e5 100644 --- a/test/lst/test_show_node_in_mermaid.py +++ b/test/lst/test_show_node_in_mermaid.py @@ -32,7 +32,7 @@ def process_code(self, grammar_module, code): ("public class Test { public static void main(String[] args) {} }", tsjava), ], ) - def test_create_diagrams(self, raw, module): + def test_create_diagrams(self, raw, module) -> None: """AI: Verify rendering source code in each language grammar produces a non-empty Mermaid diagram.""" result = self.process_code(module, raw) # with open(f"lst_output_{module.__name__}.mmd", "w", encoding="utf-8") as f: diff --git a/test/lst/test_tree_sitter_parse.py b/test/lst/test_tree_sitter_parse.py index db647ee0..e08f0f01 100644 --- a/test/lst/test_tree_sitter_parse.py +++ b/test/lst/test_tree_sitter_parse.py @@ -28,14 +28,14 @@ class TestTreeSitterParse: """AI: Tests raw tree-sitter parsing across multiple language grammars.""" - def test_parse_py_code(self): + def test_parse_py_code(self) -> None: """AI: Verify raw tree-sitter parsing of Python source round-trips through root_node.text.""" assert_that(py_code, is_(py_parser.parse(py_code).root_node.text)) - def test_parse_cpp_code(self): + def test_parse_cpp_code(self) -> None: """AI: Verify raw tree-sitter parsing of C++ source round-trips through root_node.text.""" assert_that(cpp_code, is_(cpp_parser.parse(cpp_code).root_node.text)) - def test_parse_java_code(self): + def test_parse_java_code(self) -> None: """AI: Verify raw tree-sitter parsing of Java source round-trips through root_node.text.""" assert_that(java_code, is_(java_parser.parse(java_code).root_node.text)) diff --git a/test/project/test_project_scanner.py b/test/project/test_project_scanner.py index 7b2387b1..b2af6c49 100644 --- a/test/project/test_project_scanner.py +++ b/test/project/test_project_scanner.py @@ -23,7 +23,7 @@ class TestProjectScanner: """AI: Tests the base ProjectScanner raises NotImplementedError.""" - def test_find_sources_raises_not_implemented(self): + def test_find_sources_raises_not_implemented(self) -> None: """AI: Assert the base ProjectScanner.find_sources raises NotImplementedError.""" scanner = ProjectScanner() assert_that(calling(scanner.find_sources), raises(NotImplementedError)) @@ -37,12 +37,12 @@ def test_find_sources_raises_not_implemented(self): class TestCppScanner: """AI: Tests discovering C/C++ sources from a compilation database via CppScanner.""" - def test_raises_file_not_found_when_compile_commands_missing(self, tmp_path): + def test_raises_file_not_found_when_compile_commands_missing(self, tmp_path) -> None: """AI: Assert CppScanner.find_sources raises FileNotFoundError when the compile database is missing.""" scanner = CppScanner(str(tmp_path / "compile_commands.json")) assert_that(calling(scanner.find_sources), raises(FileNotFoundError)) - def test_returns_sorted_unique_files(self, tmp_path): + def test_returns_sorted_unique_files(self, tmp_path) -> None: """AI: Assert CppScanner.find_sources returns sorted, de-duplicated file paths from the compile database.""" commands = [ {"file": "/src/b.cpp"}, @@ -57,7 +57,7 @@ def test_returns_sorted_unique_files(self, tmp_path): assert_that(result, equal_to(["/src/a.cpp", "/src/b.cpp"])) - def test_ignores_entries_without_file_key(self, tmp_path): + def test_ignores_entries_without_file_key(self, tmp_path) -> None: """AI: Assert CppScanner.find_sources skips compile-database entries lacking a "file" key.""" commands = [{"command": "cc -c foo.cpp"}, {"file": "/src/a.cpp"}] compile_commands = tmp_path / "compile_commands.json" @@ -68,7 +68,7 @@ def test_ignores_entries_without_file_key(self, tmp_path): assert_that(result, equal_to(["/src/a.cpp"])) - def test_returns_empty_list_for_empty_compile_commands(self, tmp_path): + def test_returns_empty_list_for_empty_compile_commands(self, tmp_path) -> None: """AI: Assert CppScanner.find_sources returns an empty list for an empty compile database.""" compile_commands = tmp_path / "compile_commands.json" compile_commands.write_text(json.dumps([])) @@ -78,7 +78,7 @@ def test_returns_empty_list_for_empty_compile_commands(self, tmp_path): assert_that(result, is_(empty())) - def test_default_compile_commands_path(self): + def test_default_compile_commands_path(self) -> None: """AI: Assert CppScanner defaults compile_commands_path to "compile_commands.json".""" scanner = CppScanner() assert_that(scanner.compile_commands_path, is_("compile_commands.json")) @@ -92,7 +92,7 @@ def test_default_compile_commands_path(self): class TestJavaScanner: """AI: Tests discovering Java sources under a root directory via JavaScanner.""" - def test_finds_java_files_recursively(self, tmp_path): + def test_finds_java_files_recursively(self, tmp_path) -> None: """AI: Assert JavaScanner.find_sources recursively discovers .java files under nested directories.""" (tmp_path / "src").mkdir() (tmp_path / "src" / "Main.java").write_text("class Main {}") @@ -110,7 +110,7 @@ def test_finds_java_files_recursively(self, tmp_path): ), ) - def test_returns_sorted_results(self, tmp_path): + def test_returns_sorted_results(self, tmp_path) -> None: """AI: Assert JavaScanner.find_sources returns results in sorted order.""" (tmp_path / "B.java").write_text("") (tmp_path / "A.java").write_text("") @@ -120,14 +120,14 @@ def test_returns_sorted_results(self, tmp_path): assert_that(result, equal_to(sorted(result))) - def test_returns_empty_list_when_no_java_files(self, tmp_path): + def test_returns_empty_list_when_no_java_files(self, tmp_path) -> None: """AI: Assert JavaScanner.find_sources returns an empty list when no .java files exist.""" scanner = JavaScanner(str(tmp_path)) result = scanner.find_sources() assert_that(result, is_(empty())) - def test_default_root_dir(self): + def test_default_root_dir(self) -> None: """AI: Assert JavaScanner defaults root_dir to ".".""" scanner = JavaScanner() assert_that(scanner.root_dir, is_(".")) @@ -141,7 +141,7 @@ def test_default_root_dir(self): class TestPythonScanner: """AI: Tests discovering Python sources under known package directories via PythonScanner.""" - def test_finds_python_files_in_package_dirs(self, tmp_path): + def test_finds_python_files_in_package_dirs(self, tmp_path) -> None: """AI: Assert PythonScanner.find_sources discovers .py files under configured package directories.""" src = tmp_path / "src" src.mkdir() @@ -160,14 +160,14 @@ def test_finds_python_files_in_package_dirs(self, tmp_path): ), ) - def test_skips_nonexistent_package_dirs(self, tmp_path): + def test_skips_nonexistent_package_dirs(self, tmp_path) -> None: """AI: Assert PythonScanner.find_sources skips package directories that don't exist.""" scanner = PythonScanner(str(tmp_path), package_dirs=["nonexistent"]) result = scanner.find_sources() assert_that(result, is_(empty())) - def test_returns_sorted_results(self, tmp_path): + def test_returns_sorted_results(self, tmp_path) -> None: """AI: Assert PythonScanner.find_sources returns results in sorted order.""" src = tmp_path / "src" src.mkdir() @@ -179,7 +179,7 @@ def test_returns_sorted_results(self, tmp_path): assert_that(result, equal_to(sorted(result))) - def test_searches_multiple_package_dirs(self, tmp_path): + def test_searches_multiple_package_dirs(self, tmp_path) -> None: """AI: Assert PythonScanner.find_sources searches across all configured package directories.""" (tmp_path / "src").mkdir() (tmp_path / "src" / "a.py").write_text("") @@ -197,23 +197,23 @@ def test_searches_multiple_package_dirs(self, tmp_path): ), ) - def test_default_package_dirs(self): + def test_default_package_dirs(self) -> None: """Assert PythonScanner defaults package_dirs to None.""" scanner = PythonScanner() assert_that(scanner.package_dirs, is_(None)) - def test_default_root_dir(self): + def test_default_root_dir(self) -> None: """AI: Assert PythonScanner defaults root_dir to ".".""" scanner = PythonScanner() assert_that(scanner.root_dir, is_(".")) - def test_raises_when_root_dir_does_not_exist(self, tmp_path): + def test_raises_when_root_dir_does_not_exist(self, tmp_path) -> None: """Assert PythonScanner.find_sources raises FileNotFoundError when root_dir doesn't exist.""" scanner = PythonScanner(str(tmp_path / "does-not-exist")) assert_that(calling(scanner.find_sources), raises(FileNotFoundError)) - def test_raises_when_root_dir_is_not_a_directory(self, tmp_path): + def test_raises_when_root_dir_is_not_a_directory(self, tmp_path) -> None: """Assert PythonScanner.find_sources raises NotADirectoryError when root_dir is a file.""" root_file = tmp_path / "not_a_dir.py" root_file.write_text("") @@ -222,7 +222,7 @@ def test_raises_when_root_dir_is_not_a_directory(self, tmp_path): assert_that(calling(scanner.find_sources), raises(NotADirectoryError)) @pytest.mark.parametrize("excluded_dir", sorted(PythonScanner.EXCLUDED_DIRS)) - def test_excludes_known_noise_dirs_in_default_whole_tree_scan(self, tmp_path, excluded_dir): + def test_excludes_known_noise_dirs_in_default_whole_tree_scan(self, tmp_path, excluded_dir) -> None: """Assert PythonScanner.find_sources excludes known noise directories during a default whole-tree scan.""" noise_dir = tmp_path / excluded_dir noise_dir.mkdir() @@ -235,7 +235,7 @@ def test_excludes_known_noise_dirs_in_default_whole_tree_scan(self, tmp_path, ex assert_that(result, equal_to(["kept.py"])) @pytest.mark.parametrize("excluded_dir", sorted(PythonScanner.EXCLUDED_DIRS)) - def test_excludes_known_noise_dirs_within_explicit_package_dirs(self, tmp_path, excluded_dir): + def test_excludes_known_noise_dirs_within_explicit_package_dirs(self, tmp_path, excluded_dir) -> None: """Assert PythonScanner.find_sources excludes known noise directories within an explicit package_dirs.""" src = tmp_path / "src" src.mkdir() @@ -249,7 +249,7 @@ def test_excludes_known_noise_dirs_within_explicit_package_dirs(self, tmp_path, assert_that(result, equal_to(["kept.py"])) - def test_default_package_dirs_scans_whole_root_dir(self, tmp_path): + def test_default_package_dirs_scans_whole_root_dir(self, tmp_path) -> None: """Assert PythonScanner scans the whole root_dir by default when package_dirs is not given.""" # Motivating case: source living outside src/lib/test (e.g. redis-py's redis/ layout). redis_like = tmp_path / "redis" @@ -270,7 +270,7 @@ def test_default_package_dirs_scans_whole_root_dir(self, tmp_path): class TestBearCppScanner: """AI: Tests generating a compilation database via Bear before discovering sources.""" - def test_find_sources_calls_run_bear_when_compile_commands_missing(self, tmp_path, mocker): + def test_find_sources_calls_run_bear_when_compile_commands_missing(self, tmp_path, mocker) -> None: """AI: Assert BearCppScanner.find_sources runs Bear when the compile database is missing.""" scanner = BearCppScanner( build_dir=str(tmp_path), @@ -285,7 +285,7 @@ def test_find_sources_calls_run_bear_when_compile_commands_missing(self, tmp_pat assert_that(mock_bear.call_count, is_(1)) - def test_find_sources_does_not_call_run_bear_when_compile_commands_exists(self, tmp_path, mocker): + def test_find_sources_does_not_call_run_bear_when_compile_commands_exists(self, tmp_path, mocker) -> None: """AI: Assert BearCppScanner.find_sources skips running Bear when the compile database already exists.""" commands = [{"file": "/src/main.cpp"}] compile_commands = tmp_path / "compile_commands.json" @@ -302,14 +302,14 @@ def test_find_sources_does_not_call_run_bear_when_compile_commands_exists(self, mock_bear.assert_not_called() assert_that(result, equal_to(["/src/main.cpp"])) - def test_run_bear_raises_on_nonzero_exit(self, mocker): + def test_run_bear_raises_on_nonzero_exit(self, mocker) -> None: """AI: Assert BearCppScanner.run_bear raises RuntimeError when the Bear subprocess exits non-zero.""" scanner = BearCppScanner() mocker.patch("renaissance.project.project_scanner.subprocess.run", return_value=Mock(returncode=1)) assert_that(calling(scanner.run_bear), raises(RuntimeError)) - def test_run_bear_succeeds_on_zero_exit(self, mocker): + def test_run_bear_succeeds_on_zero_exit(self, mocker) -> None: """AI: Assert BearCppScanner.run_bear does not raise when the Bear subprocess exits zero.""" scanner = BearCppScanner() mocker.patch("renaissance.project.project_scanner.subprocess.run", return_value=Mock(returncode=0)) @@ -317,7 +317,7 @@ def test_run_bear_succeeds_on_zero_exit(self, mocker): # Should not raise scanner.run_bear() - def test_default_build_dir(self): + def test_default_build_dir(self) -> None: """AI: Assert BearCppScanner defaults build_dir to ".".""" scanner = BearCppScanner() assert_that(scanner.build_dir, is_(".")) diff --git a/test/python/ast/test_patternic_style.py b/test/python/ast/test_patternic_style.py index 5f943622..a18f880f 100644 --- a/test/python/ast/test_patternic_style.py +++ b/test/python/ast/test_patternic_style.py @@ -13,7 +13,7 @@ class TestPythonicStyle: """AI: Tests for the Pythonic-style (RST) AST pattern matching.""" @pytest.fixture(autouse=True) - def setup(self): + def setup(self) -> None: """AI: Build the shared Python factory and pattern factory used by the Pythonic-style tests.""" self.factory = PythonFactory(PythonRstNode) self.pattern_factory = PythonPatternFactory(self.factory) @@ -34,7 +34,7 @@ def setup(self): ("async def fun(): pass", "AsyncFunctionDef", "function", "fun", "", 1), ], ) - def test_consistent_name_stmt(self, raw, kind, op, name, expr, body_length): + def test_consistent_name_stmt(self, raw, kind, op, name, expr, body_length) -> None: """AI: Verify compound statements (try, class, def, for, while, if, match, async) expose consistent kind/operator/name/body.""" it = PythonRstNode.load_from_text(raw).body[-1] assert_that(it.parser_kind, is_(kind)) @@ -57,7 +57,7 @@ def test_consistent_name_stmt(self, raw, kind, op, name, expr, body_length): ("pass", "Pass", None, "", "pass", None), ], ) - def test_stmt(self, raw, kind, typ, name, op, value): + def test_stmt(self, raw, kind, typ, name, op, value) -> None: """AI: Verify simple statements (AnnAssign, Assign, break, assert, etc.) expose consistent kind/name/operator/type/value.""" it = PythonRstNode.load_from_text(raw).body[-1] assert_that(it.parser_kind, is_(kind)) @@ -75,13 +75,13 @@ def test_stmt(self, raw, kind, typ, name, op, value): ], ) # ('from x import y', 'ImportFrom', None, 'x', 'import', 'y'), - def test_expr(self, raw, kind, expr): + def test_expr(self, raw, kind, expr) -> None: """AI: Verify expression statements (call, return, raise) expose the expected kind and inner expression name.""" it = PythonRstNode.load_from_text(raw).body[-1] assert_that(it.parser_kind, is_(kind)) assert_that(it.expr.name, is_(expr)) - def test_ann_assign_node(self): + def test_ann_assign_node(self) -> None: """AI: Verify an annotated assignment exposes its name, type annotation, operator, and value.""" it = PythonRstNode.load_from_text('name:str = "value"').body[-1] @@ -90,7 +90,7 @@ def test_ann_assign_node(self): assert_that(it.operator, is_("=")) assert_that(it.value, is_("value")) - def test_assign_node(self): + def test_assign_node(self) -> None: """AI: Verify a plain assignment exposes its name, no type, operator, and value.""" it = PythonRstNode.load_from_text('name = "value"').body[-1] assert_that(it.name, is_("name")) @@ -98,7 +98,7 @@ def test_assign_node(self): assert_that(it.operator, is_("=")) assert_that(it.value, is_("value")) - def test_assign_node_2(self): + def test_assign_node_2(self) -> None: """AI: Verify an augmented assignment exposes its name, no type, operator, and numeric value.""" it = PythonRstNode.load_from_text("name += 5").body[-1] assert_that(it.name, is_("name")) @@ -106,54 +106,54 @@ def test_assign_node_2(self): assert_that(it.operator, is_("+=")) assert_that(it.value, is_(5)) - def python_does_not_parse_dollar(self): + def python_does_not_parse_dollar(self) -> None: """AI: Document that a bare '$pa' text is treated as a MATCH_ONE pattern rather than plain Python code.""" it = PythonRstNode.load_from_text("$pa") assert_that(it.pattern_kind, is_(PatternKind.MATCH_ONE)) - def python_does_not_parse_dollar_dollar(self): + def python_does_not_parse_dollar_dollar(self) -> None: """AI: Document that a bare '$$pa' text is treated as a MATCH_ALL pattern rather than plain Python code.""" it = PythonRstNode.load_from_text("$$pa") assert_that(it.pattern_kind, is_(PatternKind.MATCH_ALL)) - def test_kind_is_match_all(self): + def test_kind_is_match_all(self) -> None: """AI: Verify a '$$pa' statement pattern has MATCH_ALL pattern kind.""" PythonPatternFactory(PythonFactory(PythonRstNode)) simple = self.pattern_factory.create_statement("$$pa") assert_that(simple.pattern_kind, is_(PatternKind.MATCH_ALL)) - def test_kind_is_match_one(self): + def test_kind_is_match_one(self) -> None: """AI: Verify a '$pa' statement pattern has MATCH_ONE pattern kind.""" simple = self.pattern_factory.create_statement("$pa") assert_that(simple.pattern_kind, is_(PatternKind.MATCH_ONE)) - def test_match_one_is_not_equal(self): + def test_match_one_is_not_equal(self) -> None: """AI: Verify a MATCH_ONE placeholder pattern is not equal to a concrete node it wasn't matched against.""" atu = self.factory.create_from_text("ba(55)\nca(555)\nlo(4444)\nna=55", "test.py") PythonPatternFactory(self.factory) match_one = self.pattern_factory.create("$pa") assert_that(atu.children[0], is_not(match_one)) - def test_is_match_all_stmt(self): + def test_is_match_all_stmt(self) -> None: """AI: Verify a MATCH_ALL placeholder pattern's node is contained within the parsed module.""" atu = self.factory.create_from_text("ba(55)\nca(555)\nlo(4444)\nna=55", "test.py") match_all = self.pattern_factory.create("$$pa") assert_that(match_all.node, is_in(atu)) - def test_is_exact_match(self): + def test_is_exact_match(self) -> None: """AI: Verify the first statement in the module equals an independently parsed identical statement.""" atu = self.factory.create_from_text("ba(55)\nca(555)\nlo(4444)\nna=55", "test.py") stmt = PythonRstNode.load_from_text("ba(55)")[0] assert_that(atu.children[0], is_(stmt)) - def test_match_exact_pattern(self): + def test_match_exact_pattern(self) -> None: """AI: Verify exactly one node in the module equals an exact (non-placeholder) statement pattern.""" atu = self.factory.create_from_text("ba(55)\nca(555)\nlo(4444)\nna=55", "test.py") stmt = self.pattern_factory.create_statement("ba(55)").node result = [node for node in atu if node == stmt] assert_that(result, has_length(1)) - def test_match_single_pattern(self): + def test_match_single_pattern(self) -> None: """AI: Verify a single-statement placeholder pattern matches none via equality but matches all statements via is_match.""" atu = self.factory.create_from_text("ba(55)\nca(555)\nlo(4444)\nna=55", "test.py") match_any = self.pattern_factory.create_statement("$stmt") @@ -162,14 +162,14 @@ def test_match_single_pattern(self): result = [node for node in atu if is_match(node, match_any, {})] assert_that(result, has_length(4)) - def test_match_single_call_pattern(self): + def test_match_single_call_pattern(self) -> None: """AI: Verify a call pattern with a placeholder argument matches no node via plain equality.""" atu = self.factory.create_from_text("ba(55)\nca(555)\nlo(4444)\nna=55", "test.py") match_call = self.pattern_factory.create("$call($arg)") result = [node for node in atu if node == match_call] assert_that(result, has_length(0)) - def test_find_all_using_generic_matcher(self): + def test_find_all_using_generic_matcher(self) -> None: """AI: Verify an exact statement pattern equals only the matching node among the module's siblings.""" atu = self.factory.create_from_text("ba(55)\nca(555)\nlo(4444)\nna=55", "test.py") simple = self.pattern_factory.create_statement("ca(555)").node @@ -180,7 +180,7 @@ def test_find_all_using_generic_matcher(self): result = [node for node in atu if node == simple] assert_that(result, has_length(1)) - def test_slice_call(self): + def test_slice_call(self) -> None: """AI: Verify slicing the module's statement sequence returns the expected number of nodes.""" atu = self.factory.create_from_text( "ba(55)\nna(55)\nna(55)\npa(55)\npa(55)\nba(55)\nna(55)\nna(55)\nna=55", @@ -189,7 +189,7 @@ def test_slice_call(self): node_slice = atu[0:3] assert_that(node_slice, has_length(3)) - def test_property_kind_call(self): + def test_property_kind_call(self) -> None: """AI: Verify the module node's parser kind is 'Module'.""" atu = self.factory.create_from_text( "ba(55)\nna(55)\nna(55)\npa(55)\npa(55)\nba(55)\nna(55)\nna(55)\nna=55", @@ -197,7 +197,7 @@ def test_property_kind_call(self): ) assert_that(atu.parser_kind, is_("Module")) - def test_property_name_call(self): + def test_property_name_call(self) -> None: """AI: Verify the module node's name matches the source file name it was created from.""" atu = self.factory.create_from_text( "ba(55)\nna(55)\nna(55)\npa(55)\npa(55)\nba(55)\nna(55)\nna(55)\nna=55", diff --git a/test/python/ast/test_python_ast_matcher_basic.py b/test/python/ast/test_python_ast_matcher_basic.py index 34175e3e..e556bc11 100644 --- a/test/python/ast/test_python_ast_matcher_basic.py +++ b/test/python/ast/test_python_ast_matcher_basic.py @@ -39,7 +39,7 @@ class TestPythonAstMatcherBasic: "a, b, expected", make_parametersets_of_equivalence_classes("if statement", PATTERN_FACTORY.create_statement, IF_CLASSES), ) - def test_pairs_of_equivalence_classes(self, a: NodeProtocol, b: NodeProtocol, expected: bool): + def test_pairs_of_equivalence_classes(self, a: NodeProtocol, b: NodeProtocol, expected: bool) -> None: """AI: Verify if-statement pairs from the same or different equivalence classes match as expected.""" assert_pair_equivalence(a, b, expected) diff --git a/test/python/ast/test_python_ast_matcher_representation.py b/test/python/ast/test_python_ast_matcher_representation.py index 378b01b4..999f62b9 100644 --- a/test/python/ast/test_python_ast_matcher_representation.py +++ b/test/python/ast/test_python_ast_matcher_representation.py @@ -195,7 +195,7 @@ class TestPythonAstMatcherRepresentation: + make_parametersets_of_equivalence_classes("tuple", PATTERN_FACTORY.create_statement, TUPLE_REPRESENTATIONS) + make_parametersets_of_equivalence_classes("equal", PATTERN_FACTORY.create_expression, EQUAL_REPRESENTATIONS), ) - def test_pairs_of_equivalence_classes(self, a: NodeProtocol, b: NodeProtocol, expected: bool): + def test_pairs_of_equivalence_classes(self, a: NodeProtocol, b: NodeProtocol, expected: bool) -> None: """AI: Verify literal pairs from the same or different numeric/string equivalence classes match as expected.""" assert_pair_equivalence(a, b, expected) diff --git a/test/python/ast/test_python_ast_node_ref.py b/test/python/ast/test_python_ast_node_ref.py index e37b38d2..e2d301d8 100644 --- a/test/python/ast/test_python_ast_node_ref.py +++ b/test/python/ast/test_python_ast_node_ref.py @@ -71,11 +71,11 @@ class TestPythonNode: """AI: Tests resolving references between Python AST nodes (function calls, definitions).""" @pytest.fixture(autouse=True) - def setup(self): + def setup(self) -> None: """Set up fixture state before each test method.""" self.factory = PythonFactory(PythonRstNode) - def test_def_call_references(self): + def test_def_call_references(self) -> None: """AI: Verify function f's references to functions a and b are resolved, each referenced back by f.""" # Function f() refers to Function a() ast = PythonRstNode.load_from_text(content2) @@ -102,7 +102,7 @@ def test_def_call_references(self): assert_that(referenced_by1, has_length(1)) # Function b referenced by function f. assert_that(func_def in [ast.translation_unit._nodes[r.node_id] for r in referenced_by]) - def test_type_reference(self): + def test_type_reference(self) -> None: """AI: Verify a type-annotated name resolves its reference to the imported name it annotates.""" # Name z refers to Name a ast = self.factory.create_from_text("from abc import a\nx = a()\nz: a = x", "content3.py") @@ -121,7 +121,7 @@ def test_type_reference(self): assert_that(referenced_by, has_length(greater_than(0))) assert_that(type_node in [ast.translation_unit._nodes[r.node_id] for r in referenced_by]) - def test_class_reference(self): + def test_class_reference(self) -> None: """AI: Verify a subclass resolves its reference to its base class.""" # Class A refers to Class B ast = self.factory.create_from_text(content3, "content3.py") @@ -141,7 +141,7 @@ def test_class_reference(self): assert_that(referenced_by, has_length(2)) assert_that(class_node in [ast.translation_unit._nodes[r.node_id] for r in referenced_by]) - def test_param_reference(self): + def test_param_reference(self) -> None: """AI: Verify a parameter's type annotation resolves its reference to the class defined in the same file.""" # param obj refers to its type, if type definition in the same file, refers to def, otherwise refers to Name ast = self.factory.create_from_text(content, "content.py") @@ -162,7 +162,7 @@ def test_param_reference(self): types = [r.node_id for r in referenced_by] assert_that(param_node[0].name, is_in(types)) - def test_function_reference(self): + def test_function_reference(self) -> None: """AI: Verify a method call resolves its reference to the method it calls.""" ast = self.factory.create_from_text(content, "content.py") with tempfile.TemporaryDirectory(delete=True) as temp_dir: @@ -179,13 +179,13 @@ def test_function_reference(self): assert_that(referenced_by, has_length(1)) assert_that(call_node in [ast.translation_unit._nodes[r.node_id] for r in referenced_by]) - def test_ref_node_to_str(self): + def test_ref_node_to_str(self) -> None: """AI: Verify PythonRSTReference's string representation combines its message and kind.""" it = PythonRSTReference("it is ", "kind", {}) assert_that(it, has_string("it is :kind")) @pytest.mark.parametrize("def_keyword", ["def", "async def"]) - def test_return_type_reference(self, def_keyword): + def test_return_type_reference(self, def_keyword) -> None: """AI: Verify a function's return type annotation resolves its reference to the annotated class.""" # Function make_config()'s return annotation refers to Class Config. code = f"class Config:\n pass\n\n{def_keyword} make_config() -> Config:\n pass\n" @@ -197,7 +197,7 @@ def test_return_type_reference(self, def_keyword): ref_node = ast.translation_unit._nodes[refs[0].node_id] assert_that(ref_node.semantic_kind is SemanticKind.CLASS, is_(True)) - def test_function_without_return_annotation_has_no_type_reference(self): + def test_function_without_return_annotation_has_no_type_reference(self) -> None: """AI: Verify a function without a return annotation has no references.""" ast = self.factory.create_from_text("def f():\n pass\n", "content.py") func_node = first(n for n in traverse(ast) if n.name == "f") diff --git a/test/python/ast/test_python_astshower.py b/test/python/ast/test_python_astshower.py index cdb06589..ea8578f4 100644 --- a/test/python/ast/test_python_astshower.py +++ b/test/python/ast/test_python_astshower.py @@ -12,35 +12,35 @@ class TestPythonShower: """AI: Tests for rendering Python AST nodes via ASTShower.""" @pytest.fixture(autouse=True) - def setup(self): + def setup(self) -> None: """AI: Build the shared Python factory, sample AST, and pattern factory used by the ASTShower tests.""" self.factory = PythonFactory(PythonRstNode) self.atu = self.factory.create_from_text("ba(55)\nca(555)\nlo(4444)\nna=55", "test.py") self.pattern_factory = PythonPatternFactory(self.factory) - def test_show_call_using_repr(self): + def test_show_call_using_repr(self) -> None: """AI: Verify rendering a call pattern shows its parser kind and placeholder-mangled names.""" pattern = self.pattern_factory.create_statement("$pa($55)") text = ASTShower.get_node(pattern.node, display_parser_kind=True) assert_that(text, contains_string("(Expr,")) assert_that(text, contains_string("_MatchOne__pa(_MatchOne__55)")) - def test_show_module(self): + def test_show_module(self) -> None: """AI: Verify rendering the module node starts with its parser kind and contains the source text.""" text = ASTShower.get_node(self.atu, display_parser_kind=True) assert_that(text, starts_with("(Module,")) assert_that(text, contains_string("ba(55)")) - def test_show_body(self): + def test_show_body(self) -> None: """AI: Verify stringifying the module's children includes the source text.""" assert_that(str(self.atu.children), contains_string("ba(55)")) - def test_show_ast_filter_implicit_node(self): + def test_show_ast_filter_implicit_node(self) -> None: """AI: Verify rendering without parser kinds omits implicit-node markers.""" ptext = ASTShower.get_node(self.atu) assert_that(ptext, not_(contains_string("(ImplicitNode"))) - def test_show_ast(self): + def test_show_ast(self) -> None: """AI: Verify rendering the module shows nested call/name/constant nodes with their parser kinds.""" text = ASTShower.get_node(self.atu, display_parser_kind=True) assert_that(text, contains_string("(Module,")) @@ -48,7 +48,7 @@ def test_show_ast(self): assert_that(text, contains_string("(Name, ba,")) assert_that(text, contains_string("(Constant, 55,")) - def test_show_if_else(self): + def test_show_if_else(self) -> None: """AI: Verify rendering an if/else statement's AST produces the expected node structure.""" factory = PythonFactory(PythonRstNode) atu = factory.create_from_text( diff --git a/test/python/ast/test_python_cst_node.py b/test/python/ast/test_python_cst_node.py index f65ad2a7..74987161 100644 --- a/test/python/ast/test_python_cst_node.py +++ b/test/python/ast/test_python_cst_node.py @@ -23,14 +23,14 @@ class TestPythonCstNode: """AI: Tests for the CST-based Python AST node implementation.""" @pytest.fixture(autouse=True) - def setup(self): + def setup(self) -> None: """AI: Build the CST-backed Python factory, sample AST, and pattern factory used by the CST node tests.""" self.factory = PythonFactory(PythonCstNode) self.atu = self.factory.create_from_text("a = 0", "all.py") # create a pattern factory atu is passed to the pattern factory for use of all # includes, #defines and declarations self.pattern_factory = PythonPatternFactory(self.factory) - def test_slice(self): + def test_slice(self) -> None: """AI: Verify a subscript slice expression's children expose the expected CST parser kinds in order.""" it = self.pattern_factory.create_expression("items[1:2:3]") @@ -40,14 +40,14 @@ def test_slice(self): assert_that(it.children[3].parser_kind, is_("SubscriptElement")) assert_that(it.children[4].parser_kind, is_("RightSquareBracket")) - def test_attribute_signature_has_at(self): + def test_attribute_signature_has_at(self) -> None: """AI: Verify a decorated function's decorator node signature includes the leading @ syntax.""" src = self.pattern_factory.create_statement("@TUAT\ndef ba(): pass") ASTShower.show_node(src) attr = src.children[0] assert_that(attr.signature, is_("@TUAT\n")) - def test_node_family(self): + def test_node_family(self) -> None: """AI: Verify a method node exposes its name, sibling methods, parent class, and children count.""" src = PythonCstNode.load_from_text( textwrap.dedent(""" @@ -75,22 +75,22 @@ def next_me(): # all children are mashed together assert_that(me.children, has_length(8)) - def test_load_file_with_ignored_types(self): + def test_load_file_with_ignored_types(self) -> None: """AI: Verify loading source with a '# type: ignore' comment still produces a valid translation unit.""" atu = PythonCstNode.load_from_text("x = 1 # type: ignore", "bogus.py") assert_that(atu.translation_unit, is_not(None)) - def test_load_file(self): + def test_load_file(self) -> None: """AI: Verify loading a demo Python file from disk produces a valid node.""" atu = PythonCstNode.load(Path(targets.__file__).parent / "demo.py") assert_that(atu, is_not(None)) - def test_load_invalid_file(self): + def test_load_invalid_file(self) -> None: """AI: Verify loading a syntactically invalid Python file raises a ParserSyntaxError.""" with pytest.raises(ParserSyntaxError, match="Syntax Error"): PythonCstNode.load(Path(targets.__file__).parent / "invalid.py") - def test_ann_fun_to_str2(self): + def test_ann_fun_to_str2(self) -> None: """AI: Verify a decorated function's offset and signature reflect the leading decorator text.""" ann_fun = textwrap.dedent(""" @parameterized.expand(Factories.extend(['$x;$y;'])) @@ -105,7 +105,7 @@ def test(_): assert_that(it.offset, is_(1)) assert_that(it.signature, contains_string("@parameterized.expand")) - def test_ann_fun_to_str(self): + def test_ann_fun_to_str(self) -> None: """AI: Verify a decorated function's signature includes the function definition text.""" ann_fun = textwrap.dedent(""" @parameterized.expand(Factories.extend(['$x;$y;'])) diff --git a/test/python/ast/test_python_lst_node.py b/test/python/ast/test_python_lst_node.py index 76044f02..995b3658 100644 --- a/test/python/ast/test_python_lst_node.py +++ b/test/python/ast/test_python_lst_node.py @@ -16,12 +16,12 @@ class TestPythonLstNode: """AI: Tests for the LST-based Python AST node implementation.""" @pytest.fixture(autouse=True) - def setup(self): + def setup(self) -> None: """AI: Build the LST-backed Python factory and pattern factory used by the LST node tests.""" self.factory = PythonFactory(LSTNode) self.pattern_factory = PythonPatternFactory(self.factory) - def test_stmt_kind(self): + def test_stmt_kind(self) -> None: """AI: Verify two equivalent statements differing only in whitespace produce equal LST nodes.""" src = self.factory.create_from_text("x =1") target = self.factory.create_from_text("x = 1") @@ -30,7 +30,7 @@ def test_stmt_kind(self): @pytest.mark.hypothesisslow @given(code=hypothesmith.from_node(libcst.BaseStatement)) @settings(max_examples=250, suppress_health_check=list(HealthCheck)) - def test_from_cst_returns_statement(self, code): + def test_from_cst_returns_statement(self, code) -> None: """AI: Verify creating a node from arbitrary hypothesis-generated CST code yields a non-NODE-kind child.""" reject_unsupported_code(code) factory = PythonFactory(LSTNode) @@ -44,7 +44,7 @@ def test_from_cst_returns_statement(self, code): "membership checks silently fail even though the object is still the set's only member.", strict=True, ) - def test_hash_stays_stable_after_add_child(self): + def test_hash_stays_stable_after_add_child(self) -> None: """AI: Document that LSTNode.__hash__ becomes unstable after add_child mutates the node's children.""" parent = LSTNode("block", {}, "block text") child = LSTNode("stmt", {"name": "a"}, "a;") diff --git a/test/python/ast/test_python_matcher.py b/test/python/ast/test_python_matcher.py index 4d98b032..ac45921d 100644 --- a/test/python/ast/test_python_matcher.py +++ b/test/python/ast/test_python_matcher.py @@ -22,12 +22,12 @@ class TestPythonMatcher: """AI: Tests for matching Python AST patterns via MatchFinder.""" @pytest.fixture(autouse=True) - def setup(self): + def setup(self) -> None: """AI: Build the shared Python factory and pattern factory used by the matcher tests.""" self.factory = PythonFactory(PythonRstNode) self.pattern_factory = PythonPatternFactory(self.factory) - def test_if_statements(self): + def test_if_statements(self) -> None: """Document the semantic power of [the Python parser ast](https://docs.python.org/3/library/ast.html) for if statements. In particular, syntax differences between `elif` and `else if` are not semantically relevant. @@ -63,7 +63,7 @@ def test_if_statements(self): assert_that(is_match(if_then_else_if_statement, if_then_elif_statement), is_(True)) assert_that(is_match(if_then_else_if_statement, if_then_else_if_statement), is_(True)) - def test_is_match_if_statements(self): + def test_is_match_if_statements(self) -> None: """AI: Verify variant_in_match_stmt finds no variants matching a bare if against an if/else-if statement's else branch.""" code_if_then_statement = "if c1:\n pass" code_if_then_else_if_statement = "if c1:\n pass\nelse:\n if c2:\n pass" @@ -98,13 +98,13 @@ def test_is_match_if_statements(self): ("return 1, 2, 3,", "return $$expressions", True), ], ) - def test_placeholder_return_stmt(self, stmt_txt: str, pattern_txt: str, expected: bool): + def test_placeholder_return_stmt(self, stmt_txt: str, pattern_txt: str, expected: bool) -> None: """AI: Verify return statements with 0, 1, or multiple values match bare/single/multi-placeholder patterns.""" stmt = self.pattern_factory.create_statement(stmt_txt) pattern = self.pattern_factory.create_statement(pattern_txt) assert_that(is_match(stmt, pattern, {}), is_(expected)) - def test_generic_is_match_any_stmt(self): + def test_generic_is_match_any_stmt(self) -> None: """AI: Verify a call statement matches a single-placeholder-callee pattern.""" atu = self.factory.create_from_text("ba(55)", "test.py") @@ -113,21 +113,21 @@ def test_generic_is_match_any_stmt(self): assert_that(simple.parser_kind, is_("Expr")) assert_that(is_match(atu.children[0], simple, {}), is_(True)) - def test_generic_is_match_any_assignment(self): + def test_generic_is_match_any_assignment(self) -> None: """AI: Verify an assignment statement matches a single MATCH_ONE placeholder pattern.""" atu = self.factory.create_from_text("na=55", "test.py") simple = self.pattern_factory.create_statement("$pa") assert_that(simple.pattern_kind, is_(PatternKind.MATCH_ONE)) assert_that(is_match(atu.children[0], simple, {}), is_(True)) - def test_match_multiple_single_stmt(self): + def test_match_multiple_single_stmt(self) -> None: """AI: Verify a single-placeholder pattern matches all four statements in the module.""" atu = self.factory.create_from_text("ba(55)\nca(555)\nlo(4444)\nna=55", "test.py") simple = self.pattern_factory.create_statements("$pa") result = MatchFinder.match_pattern(atu.children, simple) assert_that(result, has_length(4)) - def test_match_fix_stmt_fix_param(self): + def test_match_fix_stmt_fix_param(self) -> None: """AI: Verify an exact statement pattern matches exactly one of several similar statements.""" atu = self.factory.create_from_text("ba(55)\nca(555)\nlo(4444)\nna=55", "test.py") @@ -135,7 +135,7 @@ def test_match_fix_stmt_fix_param(self): result = MatchFinder.match_pattern(atu.children, simple) assert_that(result, has_length(1)) - def test_is_match_any_stmt_with_fix_param_in_detail(self): + def test_is_match_any_stmt_with_fix_param_in_detail(self) -> None: """AI: Verify a call pattern with a fixed argument matches only the statement with that exact argument.""" atu = self.factory.create_from_text("ba(55)\nca(555)\nlo(4444)\nna=55", "test.py") @@ -147,7 +147,7 @@ def test_is_match_any_stmt_with_fix_param_in_detail(self): result = MatchFinder.match_pattern(atu.children, [simple]) assert_that(result, has_length(1)) - def test_is_match_any_stmt_with_any_param(self): + def test_is_match_any_stmt_with_any_param(self) -> None: """AI: Verify a call pattern with a placeholder callee and argument matches all matching calls.""" atu = self.factory.create_from_text("ba(55)\nca(555)\nlo(4444)\nna=55", "test.py") @@ -155,21 +155,21 @@ def test_is_match_any_stmt_with_any_param(self): result = match_pattern(atu.children, simple) assert_that(result, has_length(3)) - def test_match_multi_fix_stmts(self): + def test_match_multi_fix_stmts(self) -> None: """AI: Verify a fixed two-statement pattern matches exactly once against the module's statements.""" atu = self.factory.create_from_text("ba(55)\nca(555)\nlo(4444)\nna=55", "test.py") simple = self.pattern_factory.create_statements("ba(55)\nca(555)") result = match_pattern(atu.children, simple) assert_that(result, has_length(1)) - def test_match_fix_stmt_with_multi_result(self): + def test_match_fix_stmt_with_multi_result(self) -> None: """AI: Verify an exact single-statement pattern matches all repeated occurrences of that statement.""" atu = self.factory.create_from_text("pa(55)\npa(55)\npa(55)\npa=55", "test.py") simple = self.pattern_factory.create_statement("pa(55)") results = MatchFinder.match_pattern(atu.children, [simple]) assert_that(results, has_length(3)) - def test_match_multi_fix_stmt_with_multi_result(self): + def test_match_multi_fix_stmt_with_multi_result(self) -> None: """AI: Verify a repeated multi-statement pattern with placeholders matches multiple sequences of 3 nodes each.""" atu = self.factory.create_from_text("ba(55)\nna(55)\nna(55)\npa(55)\npa(55)\nba(55)\nna(55)\nna(55)\nna=55") simple = self.pattern_factory.create_statements("ba($a)\nna($b)\nna($c)") @@ -177,7 +177,7 @@ def test_match_multi_fix_stmt_with_multi_result(self): assert_that(results, has_length(2)) assert_that(results[0].nodes, has_length(3)) - def test_match_multi_fix_stmt_with_multi_different_result(self): + def test_match_multi_fix_stmt_with_multi_different_result(self) -> None: """AI: Verify a repeated multi-statement pattern with placeholders finds multiple non-overlapping matches, each with 3 nodes.""" atu = self.factory.create_from_text( "ba(51)\nna(52)\nna(53)\npa(54)\npa(55)\nba(56)\nna(57)\nna(58)\nna=59\nba(51)\nna(52)\nna(53)\n", @@ -189,7 +189,7 @@ def test_match_multi_fix_stmt_with_multi_different_result(self): assert_that(results[1].nodes, has_length(3)) assert_that(results[2].nodes, has_length(3)) - def test_match_stmts_in_children(self): + def test_match_stmts_in_children(self) -> None: """AI: Verify a multi-statement pattern with placeholders matches within if/else branches, each with 3 nodes.""" atu = self.factory.create_from_text( "ba(51)\nna(52)\nna(53)\npa(54)\nif pa(55):\n ba(51)\n na(52)\n na(53)\n na=59\nelse:\n ba(51)\n na(52)\n na(53)\n", @@ -199,7 +199,7 @@ def test_match_stmts_in_children(self): assert_that(results, has_length(3)) assert_that(results[0].nodes, has_length(3)) - def test_match_placeholder_with_args(self): + def test_match_placeholder_with_args(self) -> None: """AI: Verify a pattern with a fixed prefix/suffix and a MATCH_ALL middle placeholder matches once with the expected 3 nodes.""" atu = self.factory.create_from_text("ba()\nna()\nba()\npa(54)\nba()\nna()\nba()\nna()\nna=59\nba(1)\nna()\nba(1)") simple = self.pattern_factory.create_statements("ba($a)\n$$na\nba($c)") @@ -207,7 +207,7 @@ def test_match_placeholder_with_args(self): assert_that(results, has_length(1)) assert_that(results[0].nodes, has_length(3)) - def test_match_sandwitch_pattern_with_different_content(self): + def test_match_sandwitch_pattern_with_different_content(self) -> None: """AI: Verify a sandwich pattern (fixed prefix/middle/suffix) matches 3 sequences of 5 nodes each in nested branches.""" atu = self.factory.create_from_text( textwrap.dedent(""" @@ -237,7 +237,7 @@ def test_match_sandwitch_pattern_with_different_content(self): assert_that(results, has_length(3)) assert_that(results[0].nodes, has_length(5)) - def test_match_any_placeholder_but_in_child(self): + def test_match_any_placeholder_but_in_child(self) -> None: """AI: Verify a sandwich pattern with fixed empty calls matches 3 sequences of varying length across nested branches.""" atu = self.factory.create_from_text( textwrap.dedent(""" @@ -271,7 +271,7 @@ def test_match_any_placeholder_but_in_child(self): assert_that(results[2].nodes, has_length(2)) # can only return one match - def test_match_all_epxression(self): + def test_match_all_epxression(self) -> None: """AI: Verify an exact expression pattern matches all 6 occurrences within nested if-statements.""" atu = self.factory.create_from_text("pa(55)\npa(55)\nif pa(55):\n pa(55)\n if pa(55):\n pa(55)\n pa=55") @@ -279,7 +279,7 @@ def test_match_all_epxression(self): results = MatchFinder.match_pattern(atu.children, [simple]) assert_that(results, has_length(6)) - def test_match_all_statement(self): + def test_match_all_statement(self) -> None: """AI: Verify an exact statement pattern matches all 3 occurrences within nested if-statements.""" atu = self.factory.create_from_text("pa(55)\nif pa(55):\n pa(55)\n if pa(55):\n pa(55)\n pa=55") @@ -287,30 +287,30 @@ def test_match_all_statement(self): results = match_pattern(atu.children, simple) assert_that(results, has_length(3)) - def test_ast_name(self): + def test_ast_name(self) -> None: """AI: Verify a call statement pattern's name reflects the full call signature.""" simple = self.pattern_factory.create_statement("pa(55)") assert_that(simple.name, is_("pa(55)")) - def test_python_ast_name(self): + def test_python_ast_name(self) -> None: """AI: Verify the raw Python ast module exposes the called function's id via value.func.id.""" simple = ast.parse("pa(55)").body[0] assert_that(simple.value.func.id, is_("pa")) - def test_equal_nodes(self): + def test_equal_nodes(self) -> None: """AI: Verify an exact statement pattern equals the identical statement node in the parsed module.""" atu = self.factory.create_from_text("pa(55)\nif pa(55):\n pa(55)\n pa=55", "test.py") simple = self.pattern_factory.create_statement("pa(55)") assert_that(simple, is_(atu.children[0])) - def test_equal_nodes_different_args(self): + def test_equal_nodes_different_args(self) -> None: """AI: Verify a statement pattern with a different argument is not equal to the parsed module's statement.""" atu = self.factory.create_from_text("pa(55)\nif pa(55):\n pa(55)\n pa=55", "test.py") simple = self.pattern_factory.create_statement("pa(66)") assert_that(simple, is_not(atu.children[0])) - def test_replace_multiple_different_nodes(self): + def test_replace_multiple_different_nodes(self) -> None: """AI: Verify a multi-statement module with imports and nested if/else branches parses successfully.""" example_code = textwrap.dedent(""" from module import foo, bar, baz, quux @@ -335,7 +335,7 @@ def test_replace_multiple_different_nodes(self): atu = self.factory.create_from_text(example_code) assert_that(atu, is_not(None)) - def test_find_pattern_four_depth(self): + def test_find_pattern_four_depth(self) -> None: """AI: Verify a keyword-argument call pattern matches occurrences nested 4 levels deep inside a class method.""" example_code = """class CommonTestUtils(): def foo(): @@ -348,7 +348,7 @@ def foo(): pattern = self.pattern_factory.create_expression("TestDoubles($a=ImprovedStub($b))") assert_that(match_pattern(atu.children, [pattern]), has_length(2)) - def test_find_pattern_one_expr(self): + def test_find_pattern_one_expr(self) -> None: """AI: Verify a keyword-argument call pattern matches a single occurrence inside a list expression.""" example_code = textwrap.dedent(""" [TestDoubles(b=ImprovedStub(write))] @@ -357,7 +357,7 @@ def test_find_pattern_one_expr(self): pattern = self.pattern_factory.create_expression("TestDoubles($a=ImprovedStub($b))") assert_that(match_pattern(atu.children, [pattern]), has_length(1)) - def test_find_pattern_one_stmt(self): + def test_find_pattern_one_stmt(self) -> None: """AI: Verify a keyword-argument call pattern matches a single occurrence as a bare statement.""" example_code = textwrap.dedent(""" TestDoubles(b=ImprovedStub(write)) @@ -366,7 +366,7 @@ def test_find_pattern_one_stmt(self): pattern = self.pattern_factory.create_statement("TestDoubles($a=ImprovedStub($b))") assert_that(match_pattern(atu.children, [pattern]), has_length(1)) - def test_variable_length_match_variant_x(self): + def test_variable_length_match_variant_x(self) -> None: """AI: Verify a variable-length pattern with a fixed marker finds variants with correct before/after slice lengths.""" example_code = textwrap.dedent("0\n1\n2\n3\n4\n5\n3") atu = self.factory.create_from_text(example_code) @@ -381,14 +381,14 @@ def test_variable_length_match_variant_x(self): assert_that(variants[1].exp["$$before"], has_length(3)) assert_that(variants[1].exp["$$after"], has_length(3)) - def test_simple_match_with_variant(self): + def test_simple_match_with_variant(self) -> None: """AI: Verify an exact multi-statement pattern finds exactly one variant.""" example_code = textwrap.dedent("0\n1\n2\n") atu = self.factory.create_from_text(example_code) pattern = self.pattern_factory.create_statements("0\n1\n2\n") assert_that(find_variants(atu.children, pattern), has_length(1)) - def test_variable_length_matcher_as_valid_variants(self): + def test_variable_length_matcher_as_valid_variants(self) -> None: """AI: Verify a variable-length-prefix-plus-single-placeholder pattern finds a variant for each statement.""" example_code = textwrap.dedent(""" 0 @@ -404,7 +404,7 @@ def test_variable_length_matcher_as_valid_variants(self): variants = find_variants(atu.children, pattern) assert_that(variants, has_length(7)) - def test_variable_length_matcherat_start_end_end_as_variants(self): + def test_variable_length_matcherat_start_end_end_as_variants(self) -> None: """AI: Verify a variable-length prefix/middle/suffix pattern finds a variant for each statement.""" example_code = textwrap.dedent(""" 0 @@ -420,7 +420,7 @@ def test_variable_length_matcherat_start_end_end_as_variants(self): variants = find_variants(atu.children, pattern) assert_that(variants, has_length(7)) - def test_match_pattern_needs_variants(self): + def test_match_pattern_needs_variants(self) -> None: """AI: Verify a doubled variable-length pattern with a separator finds one variant with correctly bound placeholders.""" example_code = textwrap.dedent("0\n1\n2\n8\n0\n7\n2") atu = self.factory.create_from_text(example_code) @@ -432,7 +432,7 @@ def test_match_pattern_needs_variants(self): assert_that(variants[0].exp["$dido"], has_length(1)) assert_that(variants[0].exp["$$after"], has_length(1)) - def test_trim_variants(self): + def test_trim_variants(self) -> None: """AI: Verify find_variants trims a doubled variable-length pattern down to exactly one variant.""" example_code = textwrap.dedent("0\n1\n2\n8\n0\n7\n2") atu = self.factory.create_from_text(example_code) @@ -440,7 +440,7 @@ def test_trim_variants(self): variants = find_variants(atu.children, pattern) assert_that(variants, has_length(1)) - def test_mismatch_with_double_match_all(self): + def test_mismatch_with_double_match_all(self) -> None: """AI: Verify a pattern with two MATCH_ALL placeholders sandwiching a fixed marker finds no variants when it can't match.""" example_code = textwrap.dedent("0\n1\n2\n3\n0\n7\n2") atu = self.factory.create_from_text(example_code) @@ -448,7 +448,7 @@ def test_mismatch_with_double_match_all(self): variants = find_variants(atu.children, pattern) assert_that(variants, has_length(0)) - def test_trim_variants_with_double_match_all(self): + def test_trim_variants_with_double_match_all(self) -> None: """AI: Verify a doubled variable-length pattern with two MATCH_ALL groups finds 3 trimmed variants.""" example_code = textwrap.dedent("0\n1\n2\n0\n7\n2") atu = self.factory.create_from_text(example_code) @@ -459,7 +459,7 @@ def test_trim_variants_with_double_match_all(self): # assert_that(trimmed_variants[1], has_length(3)) # [] 0 [1] [] 2 missing 1 # assert_that(trimmed_variants[2], has_length(5)) - def test_match_variant_in_args(self): + def test_match_variant_in_args(self) -> None: """AI: Verify find_variants finds multiple variants when matching a sandwich pattern against call arguments.""" example_code = textwrap.dedent("fc(1,2,3,4,5)") atu = self.factory.create_from_text(example_code) @@ -467,7 +467,7 @@ def test_match_variant_in_args(self): variants = find_variants(atu.body[0].expression.children[1].children, pattern.children[1].children, {}) assert_that(variants, has_length(greater_than(1))) - def test_variant_in_args(self): + def test_variant_in_args(self) -> None: """AI: Verify variant_in_match_stmt finds multiple variants when matching a sandwich pattern against a call node.""" example_code = textwrap.dedent("fc(1,2,3,4,5)") atu = self.factory.create_from_text(example_code) @@ -475,7 +475,7 @@ def test_variant_in_args(self): variants = variant_in_match_stmt(atu.body[0].expression.children[1], pattern.children[1], {}) assert_that(variants, has_length(greater_than(1))) - def test_variant_in_children_function(self): + def test_variant_in_children_function(self) -> None: """AI: Verify variant_in_match_stmt finds all 5 variants of a call's placeholder pattern with correctly bound placeholders.""" example_code = textwrap.dedent("fc(1,2,3,4,5)") atu = self.factory.create_from_text(example_code) @@ -486,7 +486,7 @@ def test_variant_in_children_function(self): assert_that(variants[2].exp["$a"][0].signature, is_("3")) assert_that(variants[2].exp["$$after"], has_length(2)) - def test_variant_in_children_function_with_expansion(self): + def test_variant_in_children_function_with_expansion(self) -> None: """AI: Verify a previously bound placeholder ($a) can be reused as context to bind a new placeholder ($b) on a second call.""" atu = self.factory.create_from_text("fc(1,2,3,4,5)") pattern = self.pattern_factory.create_statements("$f($$before, $a, $$after)") @@ -499,7 +499,7 @@ def test_variant_in_children_function_with_expansion(self): assert_that(variants, has_length(1)) assert_that(variants[0].exp["$b"][0].name, is_("6")) - def test_find_variant_in_children_function(self): + def test_find_variant_in_children_function(self) -> None: """AI: Verify find_variants finds more than one variant for a placeholder call pattern among the module's top-level children.""" example_code = textwrap.dedent("fc(1,2,3,4,5)") atu = self.factory.create_from_text(example_code) @@ -507,7 +507,7 @@ def test_find_variant_in_children_function(self): variants = find_variants(atu.body, pattern, {}) assert_that(variants, has_length(greater_than(1))) - def test_only_one_variant_in_children_functions(self): + def test_only_one_variant_in_children_functions(self) -> None: """AI: Verify a two-statement pattern with shared and distinct placeholders finds exactly one variant across two calls.""" example_code = textwrap.dedent("fc(1,2,3,4,5)\nfc(1,2,6,4,5)") atu = self.factory.create_from_text(example_code) @@ -516,7 +516,7 @@ def test_only_one_variant_in_children_functions(self): # should be 1 assert_that(variants, has_length(1)) - def test_variant_in_children(self): + def test_variant_in_children(self) -> None: """AI: Verify find_variants finds more than one variant when matching a placeholder call pattern against the module's children.""" example_code = textwrap.dedent("fc(1,2,3,4,5)") atu = self.factory.create_from_text(example_code) @@ -525,7 +525,7 @@ def test_variant_in_children(self): print(variants) assert_that(variants, has_length(greater_than(1))) - def test_variable_length_matcher(self): + def test_variable_length_matcher(self) -> None: """AI: Verify a two-statement pattern with shared and distinct placeholders finds a non-empty variant list and a single match.""" example_code = textwrap.dedent(""" fc(1,2,3,4,5) @@ -541,7 +541,7 @@ def test_variable_length_matcher(self): assert_that(variants, is_not(empty())) assert_that(match_pattern(atu.children, pattern), has_length(1)) - def test_match_multi_fun_using_generic_matcher2(self): + def test_match_multi_fun_using_generic_matcher2(self) -> None: """AI: Verify a fixed two-statement pattern matches exactly once against the module's statements (second variant).""" atu = self.factory.create_from_text("ba(55)\nca(555)\nlo(4444)\nna=55", "test.py") simple = self.pattern_factory.create_statements("ba(55)\nca(555)") diff --git a/test/python/ast/test_python_matcher_basic.py b/test/python/ast/test_python_matcher_basic.py index 4b0793d5..e692556c 100644 --- a/test/python/ast/test_python_matcher_basic.py +++ b/test/python/ast/test_python_matcher_basic.py @@ -48,7 +48,7 @@ class TestPythonMatcherBasic: "a, b, expected", make_parametersets_of_equivalence_classes("trivia", PATTERN_FACTORY.create_statements, TRIVIA_CLASSES), ) - def test_pairs_of_equivalence_classes(self, a: Sequence[NodeProtocol], b: Sequence[NodeProtocol], expected: bool): + def test_pairs_of_equivalence_classes(self, a: Sequence[NodeProtocol], b: Sequence[NodeProtocol], expected: bool) -> None: """AI: Verify code snippet pairs differing only in comments/whitespace match as expected.""" assert_pair_equivalence(a, b, expected) diff --git a/test/python/ast/test_python_matcher_representation.py b/test/python/ast/test_python_matcher_representation.py index 2a8058a5..cfdddb19 100644 --- a/test/python/ast/test_python_matcher_representation.py +++ b/test/python/ast/test_python_matcher_representation.py @@ -11,12 +11,12 @@ class TestPythonMatcherRepresentation: """AI: Tests documenting the representation of Python AST pattern matches.""" @pytest.fixture(autouse=True) - def setup(self): + def setup(self) -> None: """AI: Build the shared Python AST factory and pattern factory used by the representation tests.""" self.factory = PythonFactory(PythonRstNode) self.pattern_factory = PythonPatternFactory(self.factory) - def test_literal_whole_numbers_representation(self): + def test_literal_whole_numbers_representation(self) -> None: """Document the semantic power of [the Python parser ast](https://docs.python.org/3/library/ast.html) for whole-number literals. Different representations of literal instances of whole numbers are not semantically relevant. @@ -70,7 +70,7 @@ def test_literal_whole_numbers_representation(self): for expression in expressions: assert_that(expression_signed, is_not(expression)) - def test_literal_real_numbers_representation(self): + def test_literal_real_numbers_representation(self) -> None: """Document how the different representations of literal instances of real numbers are handled by the parser.""" normal = "0.123456" more_significant_digits = "0.123456000" @@ -103,7 +103,7 @@ def test_literal_real_numbers_representation(self): for expression in expressions: assert_that(expression_fraction, is_not(expression)) - def test_character_representation(self): + def test_character_representation(self) -> None: """Document the semantic power of [the Python parser ast](https://docs.python.org/3/library/ast.html) for character representations. Different character representations are not semantically relevant. @@ -134,7 +134,7 @@ def test_character_representation(self): for expression2 in expressions: assert_that(expression1, is_(expression2)) - def test_string_representation(self): + def test_string_representation(self) -> None: """Document how the different string representations are handled by the parser.""" normal_single = "'abcdef'" normal_double = '"abcdef"' @@ -179,7 +179,7 @@ def test_string_representation(self): for expression in expressions: assert_that(expression_explicit_concatenated, is_not(expression)) - def test_statements_with_comment_and_whitespace(self): + def test_statements_with_comment_and_whitespace(self) -> None: """Document how statements with comments and whitespace are handled by the parser.""" statement = "x = 1" statement_with_comment = "x = 1 # This is a comment" diff --git a/test/python/ast/test_python_nodes.py b/test/python/ast/test_python_nodes.py index 95d86200..47d66a29 100644 --- a/test/python/ast/test_python_nodes.py +++ b/test/python/ast/test_python_nodes.py @@ -58,7 +58,7 @@ class TestPythonNodes: ], ), ) - def test_stmt_kind(self, _, factory, raw, kind): + def test_stmt_kind(self, _, factory, raw, kind) -> None: """AI: Verify each statement kind (AnnAssign, Assert, For, If, Try, etc.) parses to the expected kind across all backends.""" pattern_factory = PythonPatternFactory(factory) it = pattern_factory.create_statement(raw) @@ -94,13 +94,13 @@ def inner(): ], ), ) - def test_stmt_kind_in_context(self, _, factory, raw, kind): + def test_stmt_kind_in_context(self, _, factory, raw, kind) -> None: """AI: Verify context-dependent statement kinds (With, Await, BinOp, Nonlocal, etc.) are found somewhere in the parsed tree.""" it = factory.create_from_text(raw, "context.py") assert_that(any(has_parser_or_semantic_kind(node, kind) for node in traverse(it) if hasattr(node, "parser_kind")), is_(True)) @pytest.mark.parametrize("_, factory, raw, kind", Factories.extend([("global x", "Global")])) - def test_global_stmt(self, _, factory, raw, kind): + def test_global_stmt(self, _, factory, raw, kind) -> None: """AI: Verify a global statement parses to the expected parser/semantic kind.""" pattern_factory = PythonPatternFactory(factory) it = pattern_factory.create_statement(raw) @@ -129,7 +129,7 @@ def test_global_stmt(self, _, factory, raw, kind): ], ), ) - def test_expr_kind(self, _, factory, raw, kind): + def test_expr_kind(self, _, factory, raw, kind) -> None: """AI: Verify each expression kind (Call, Dict, Lambda, Subscript, Yield, etc.) parses to the expected parser/semantic kind.""" pattern_factory = PythonPatternFactory(factory) it = pattern_factory.create_expression(raw) @@ -152,7 +152,7 @@ def test_expr_kind(self, _, factory, raw, kind): ], ), ) - def test_comperator_operator(self, _, factory, raw, kind): + def test_comperator_operator(self, _, factory, raw, kind) -> None: """AI: Verify comparator operators (==, in, is, <, >, etc.) parse as binary operation expressions.""" pattern_factory = PythonPatternFactory(factory) it = pattern_factory.create_expression(raw) @@ -179,7 +179,7 @@ def test_comperator_operator(self, _, factory, raw, kind): ], ), ) - def test_match_patterns(self, _, factory, raw, kind): + def test_match_patterns(self, _, factory, raw, kind) -> None: """AI: Verify each match-case pattern kind (MatchSingleton, MatchOr, MatchClass, etc.) parses to the expected parser kind.""" pattern_factory = PythonPatternFactory(factory) sample_code = f"match data:\n {raw}\n case _: pass" @@ -211,7 +211,7 @@ def test_match_patterns(self, _, factory, raw, kind): ], ), ) - def test_binary_operator(self, _, factory, raw, kind): + def test_binary_operator(self, _, factory, raw, kind) -> None: """AI: Verify each binary operator (%, /, //, <<, *, **, -, +) parses as a binary operation expression.""" pattern_factory = PythonPatternFactory(factory) it = pattern_factory.create_expression(raw) @@ -228,7 +228,7 @@ def test_binary_operator(self, _, factory, raw, kind): ], ), ) - def test_unary_operator(self, _, factory, raw, kind): + def test_unary_operator(self, _, factory, raw, kind) -> None: """AI: Verify each unary operator (+, -, ~, not) parses as a unary operation expression.""" pattern_factory = PythonPatternFactory(factory) it = pattern_factory.create_expression(raw) diff --git a/test/python/ast/test_python_pattern_factory.py b/test/python/ast/test_python_pattern_factory.py index 8c9dbfe8..93701e97 100644 --- a/test/python/ast/test_python_pattern_factory.py +++ b/test/python/ast/test_python_pattern_factory.py @@ -298,7 +298,7 @@ def test_misalignment(self, _, factory, raw, expected) -> None: expression = pattern_factory.create_expression(raw) assert_that(expression.parser_kind, is_in(expected)) - def test_function_with_multi_patterns(self): + def test_function_with_multi_patterns(self) -> None: """AI: Verify a call pattern mixing MATCH_ONE and MATCH_ALL placeholders assigns the expected pattern kinds to each child.""" pattern = self.pattern_factory.create_expression("$f($$before, $a, $$after)") assert_that(pattern.parser_kind, is_("Call")) diff --git a/test/python/ast/test_python_protocol_metadata.py b/test/python/ast/test_python_protocol_metadata.py index d231983f..ee8883cb 100644 --- a/test/python/ast/test_python_protocol_metadata.py +++ b/test/python/ast/test_python_protocol_metadata.py @@ -8,7 +8,7 @@ from renaissance.syntax_tree.semantic_kind import SemanticKind -def test_native_ast_exposes_protocol_metadata(): +def test_native_ast_exposes_protocol_metadata() -> None: """AI: Assert a native ast-based Python node exposes the expected parser_kind and semantic_kind.""" node = PythonFactory(ast.AST).create_from_text("def f(): pass").body[0] @@ -16,7 +16,7 @@ def test_native_ast_exposes_protocol_metadata(): assert node.semantic_kind is SemanticKind.FUNCTION -def test_python_cst_exposes_protocol_metadata(): +def test_python_cst_exposes_protocol_metadata() -> None: """AI: Assert a Python CST node exposes the expected parser_kind and semantic_kind.""" root = PythonCstNode.load_from_text("def f(): pass") @@ -25,7 +25,7 @@ def test_python_cst_exposes_protocol_metadata(): assert root.children[0].semantic_kind is SemanticKind.FUNCTION -def test_python_rst_exposes_protocol_metadata(): +def test_python_rst_exposes_protocol_metadata() -> None: """AI: Assert a Python RST node exposes the expected parser_kind and semantic_kind.""" root = PythonRstNode.load_from_text("f(1)") @@ -33,7 +33,7 @@ def test_python_rst_exposes_protocol_metadata(): assert root.semantic_kind is SemanticKind.TRANSLATION_UNIT -def test_python_rst_kind_key_preserves_unknown_parser_identity(): +def test_python_rst_kind_key_preserves_unknown_parser_identity() -> None: """AI: Assert kind_key falls back to the parser kind for an RST node with no mapped semantic kind.""" node_type = "NotARealNode" node = type(node_type, (), {"_fields": []})() diff --git a/test/python/ast/test_python_rst_node.py b/test/python/ast/test_python_rst_node.py index 038dcff5..12ec59c2 100644 --- a/test/python/ast/test_python_rst_node.py +++ b/test/python/ast/test_python_rst_node.py @@ -28,19 +28,19 @@ class TestPythonRstNode: """AI: Tests for the RST-based Python AST node implementation.""" @pytest.fixture(autouse=True) - def setup(self): + def setup(self) -> None: """AI: Build the shared Python factory, sample AST, and pattern factory used by the RST node tests.""" self.factory = PythonFactory(PythonRstNode) self.atu = self.factory.create_from_text("a = 0", "all.py") # create a pattern factory atu is passed to the pattern factory for use of all # includes, #defines and declarations self.pattern_factory = PythonPatternFactory(self.factory) - def test_type_alias(self): + def test_type_alias(self) -> None: """AI: Verify a type-alias statement parses with parser kind 'TypeAlias'.""" it = self.factory.create_from_text("type UserId = int", "context.py") assert_that(it.children[0].parser_kind, is_("TypeAlias")) - def test_exposes_parser_and_semantic_kinds(self): + def test_exposes_parser_and_semantic_kinds(self) -> None: """AI: Verify a function definition's nodes expose the expected parser and semantic kinds for function, parameter, and name.""" root = self.factory.create_from_text("def f(value):\n return value\n") function = root.children[0] @@ -52,7 +52,7 @@ def test_exposes_parser_and_semantic_kinds(self): assert parameter.semantic_kind is SemanticKind.PARAMETER assert returned_name.semantic_kind is SemanticKind.NAME - def test_unknown_python_kind_keeps_parser_name(self): + def test_unknown_python_kind_keeps_parser_name(self) -> None: """AI: Verify a set literal keeps its parser kind 'Set' while its semantic kind falls back to NODE.""" root = self.factory.create_from_text("x = {1, 2}") set_node = root.children[0].children[1] @@ -60,38 +60,38 @@ def test_unknown_python_kind_keeps_parser_name(self): assert set_node.parser_kind == "Set" assert set_node.semantic_kind is SemanticKind.NODE - def test_slice(self): + def test_slice(self) -> None: """AI: Verify a subscript slice expression's second child has parser kind 'Slice'.""" it = self.pattern_factory.create_expression("items[1:2:3]") assert_that(it.children[1].parser_kind, is_("Slice")) - def test_named_expr(self): + def test_named_expr(self) -> None: """AI: Verify a walrus-operator condition in an if-statement parses with parser kind 'NamedExpr'.""" it = self.pattern_factory.create_statement("if n:= len(items): pass") assert_that(it.children[0].parser_kind, is_("NamedExpr")) - def test_named_expr_simple(self): + def test_named_expr_simple(self) -> None: """AI: Verify a bare walrus-operator expression parses with parser kind 'NamedExpr'.""" it = self.pattern_factory.create_statement("(n:= 3)") assert_that(it.children[0].parser_kind, is_("NamedExpr")) # why not ""? - def test_starred(self): + def test_starred(self) -> None: """AI: Verify a starred-unpacking assignment target parses with parser kind 'Starred'.""" it = self.pattern_factory.create_statement("*x =[1,2]") assert_that(it.children[0].children[0].parser_kind, is_("Starred")) - def test_formatted_value(self): + def test_formatted_value(self) -> None: """AI: Verify an f-string's embedded expression parses with parser kind 'FormattedValue'.""" it = self.pattern_factory.create_expression('f"{one}two"') assert_that(it.children[0].parser_kind, is_("FormattedValue")) - def test_except_handler(self): + def test_except_handler(self) -> None: """AI: Verify a try/except statement's except clause parses with parser kind 'ExceptHandler'.""" it = self.pattern_factory.create_statement("try: pass\nexcept NameError:pass") assert_that(it.children[1].children[0].parser_kind, is_("ExceptHandler")) - def test_match_stmt(self): + def test_match_stmt(self) -> None: """AI: Verify a match-case statement with sequence unpacking parses with the expected nested parser kinds.""" sample_code = ( 'match data:\n case [first, *rest]: return f"List with first element {first} and {len(rest)} more items"\n case _: pass' @@ -102,7 +102,7 @@ def test_match_stmt(self): assert_that(stmt.children[1].children[0].children[0].children[1].parser_kind, is_("MatchStar")) assert_that(stmt.children[1].children[0].children[0].children[0].parser_kind, is_("MatchAs")) - def test_show_call(self): + def test_show_call(self) -> None: """AI: Verify a statement node exposes correct offset, length, filename, and shared translation unit.""" atu = self.factory.create_from_text("ba(55)\nca(555)\nlo(4444)\nna=55", "apple.py") second_stmt = atu.children[1] @@ -111,14 +111,14 @@ def test_show_call(self): assert_that(second_stmt.filename, is_("apple.py")) assert_that(atu.translation_unit, is_(second_stmt.translation_unit)) - def test_attribute_signature_has_at(self): + def test_attribute_signature_has_at(self) -> None: """AI: Verify a decorated function's decorator node signature includes the leading @ syntax.""" src = self.pattern_factory.create_statement("@TUAT\ndef ba(): pass") ASTShower.show_node(src) attr = src.children[2].children[0] assert_that(attr.signature, is_("@TUAT")) - def test_node_family(self): + def test_node_family(self) -> None: """AI: Verify a method node exposes its name, sibling methods, parent class, and children count.""" src = PythonRstNode.load_from_text( textwrap.dedent(""" @@ -145,22 +145,22 @@ def next_me(): assert_that(me.children[1].children, has_length(4)) @pytest.mark.skip("don't use ast comment parser") - def test_load_file_with_ignored_types(self): + def test_load_file_with_ignored_types(self) -> None: """AI: Verify loading source with a '# type: ignore' comment records it in the AST's type_ignores.""" atu = PythonRstNode.load_from_text("x = 1 # type: ignore", "bogus.py") assert_that(atu.translation_unit.atu.type_ignores, has_length(1)) - def test_load_file(self): + def test_load_file(self) -> None: """AI: Verify loading a demo Python file from disk produces no type_ignores.""" atu = PythonRstNode.load(Path(targets.__file__).parent / "demo.py") assert_that(atu.translation_unit.atu.type_ignores, is_(empty())) - def test_load_invalid_file(self): + def test_load_invalid_file(self) -> None: """AI: Verify loading a syntactically invalid Python file raises an IndentationError.""" with pytest.raises(IndentationError, match="unexpected indent"): PythonRstNode.load(Path(targets.__file__).parent / "invalid.py") - def test_ann_fun_to_str2(self): + def test_ann_fun_to_str2(self) -> None: """AI: Verify a decorated function's offset and signature reflect the leading decorator text.""" ann_fun = textwrap.dedent(""" @parameterized.expand(Factories.extend(['$x;$y;'])) @@ -176,7 +176,7 @@ def test(_): assert_that(it.signature, contains_string("@parameterized.expand")) # @pytest.mark.skip("it was working before") - def test_ann_fun_to_str(self): + def test_ann_fun_to_str(self) -> None: """AI: Verify a decorated function's signature round-trips back to the original source text.""" ann_fun = textwrap.dedent(""" @parameterized.expand(Factories.extend(['$x;$y;'])) @@ -194,7 +194,7 @@ def test(_): @pytest.mark.hypothesisslow @given(code=hypothesmith.from_node(libcst.BaseStatement)) @settings(max_examples=50, suppress_health_check=list(HealthCheck)) - def test_from_cst_returns_statement(self, code): + def test_from_cst_returns_statement(self, code) -> None: """AI: Verify creating a node from arbitrary hypothesis-generated code yields a non-NODE-kind child.""" reject_unsupported_code(code) factory = PythonFactory(PythonRstNode) @@ -202,7 +202,7 @@ def test_from_cst_returns_statement(self, code): print(f"testing {code=} with PythonRstNode") assert_that(node.children[0].semantic_kind is not SemanticKind.NODE, is_(True), f"{code=}") - def test_corner_case(self): + def test_corner_case(self) -> None: """AI: Verify a class name made of exotic unicode characters yields a non-NODE-kind child.""" factory = PythonFactory(PythonRstNode) node = factory.create_from_text("class ŻP𭻊鲖ÉØ_ąň𣑗: pass\n") @@ -216,7 +216,7 @@ def test_corner_case(self): ("async def f():\n async for a, (b, c) in x():\n pass\n", "AsyncFor"), ], ) - def test_nested_tuple_unpacking_for_target(self, code, expected_kind, capsys): + def test_nested_tuple_unpacking_for_target(self, code, expected_kind, capsys) -> None: """AI: Verify nested-tuple for-loop targets parse without AttributeError and produce the expected kind.""" root = PythonRstNode.load_from_text(code) @@ -231,7 +231,7 @@ def test_nested_tuple_unpacking_for_target(self, code, expected_kind, capsys): ("def f():\n def g():\n nonlocal x\n", "Nonlocal"), ], ) - def test_global_nonlocal_names_not_dropped(self, code, expected_kind, capsys): + def test_global_nonlocal_names_not_dropped(self, code, expected_kind, capsys) -> None: """AI: Verify global/nonlocal statements keep their names without raising AttributeError.""" root = PythonRstNode.load_from_text(code) @@ -242,7 +242,7 @@ def test_global_nonlocal_names_not_dropped(self, code, expected_kind, capsys): reason="PythonRstNode does not inherit from ASTNode, so get_ancestor() is not available on it", strict=True, ) - def test_get_ancestor_finds_enclosing_function(self): + def test_get_ancestor_finds_enclosing_function(self) -> None: """get_ancestor() walks up .parent to find the nearest FunctionDef.""" root = PythonRstNode.load_from_text("def f():\n x = 1\n") target = root.children[0].children[0] diff --git a/test/python/ast/test_pythonic_node.py b/test/python/ast/test_pythonic_node.py index 7679fdb0..cbaee3aa 100644 --- a/test/python/ast/test_pythonic_node.py +++ b/test/python/ast/test_pythonic_node.py @@ -10,17 +10,17 @@ class TestPythonicNode: """AI: Tests for the Pythonic RST AST node wrapper.""" - def test_it_can_be_created(self): + def test_it_can_be_created(self) -> None: """AI: Verify a PythonRstNode can be created from a bare ast.Pass node.""" it = PythonRstNode(ast.Pass()) assert_that(it, is_(not_none())) - def test_it_has_elements(self): + def test_it_has_elements(self) -> None: """AI: Verify indexing a PythonRstNode returns the same element as its children property.""" it = PythonRstNode(ast.parse("def fun(): pass")) assert_that(it[0], is_(it.children[0])) - def test_it_has_multiple_elements(self): + def test_it_has_multiple_elements(self) -> None: """AI: Verify slicing a PythonRstNode returns the same slice as its children property.""" it = PythonRstNode(ast.parse("def fun(): pass")) it = PythonRstNode(ast.parse("0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n")) diff --git a/test/recipes/test_cleanup_refactoring.py b/test/recipes/test_cleanup_refactoring.py index 6aa10c78..6abf1e93 100644 --- a/test/recipes/test_cleanup_refactoring.py +++ b/test/recipes/test_cleanup_refactoring.py @@ -32,7 +32,7 @@ class TestCleanupRefactoring: ), ), ) - def test_remove_unused_variables(self, name, factory: ASTFactory, input_code, expected_code): + def test_remove_unused_variables(self, name, factory: ASTFactory, input_code, expected_code) -> None: """AI: Verify unused local variable declarations are removed while used ones are preserved.""" atu = factory.create_from_text(input_code, "test.c") ASTShower.show_node(atu) @@ -41,7 +41,7 @@ def test_remove_unused_variables(self, name, factory: ASTFactory, input_code, ex result = ast_refactor.commit().apply_to_string() assert_that(result, is_(expected_code)) - def test_should_not_be_instantiable(self): + def test_should_not_be_instantiable(self) -> None: """AI: Verify CleanupRefactoring cannot be instantiated directly.""" assert_that(calling(CleanupRefactoring), raises(Exception)) diff --git a/test/recipes/test_python_refactoring.py b/test/recipes/test_python_refactoring.py index e6df8c88..87766459 100644 --- a/test/recipes/test_python_refactoring.py +++ b/test/recipes/test_python_refactoring.py @@ -36,7 +36,7 @@ class TestPythonRefactoring: .filter(lambda name: not keyword.iskeyword(name)) ) - def _patch_factory(self, mocker, text="pass", filename="test_foo.py"): + def _patch_factory(self, mocker, text="pass", filename="test_foo.py") -> None: code = textwrap.dedent(text) mocker.patch( "renaissance.integrations.python.ast.factory.PythonFactory.create", @@ -47,7 +47,7 @@ def _patch_factory(self, mocker, text="pass", filename="test_foo.py"): # __init__ / replace_stmt # ------------------------------------------------------------------ - def test_init_sets_default_list_patterns(self, mocker): + def test_init_sets_default_list_patterns(self, mocker) -> None: """AI: Verify a subclass's __init__ sets its black/white list patterns from subclass defaults.""" self._patch_factory(mocker) subject = UnitToPytest("test_foo.py") @@ -55,7 +55,7 @@ def test_init_sets_default_list_patterns(self, mocker): assert_that(subject.black_list_pattern, is_("utils_for_test")) assert_that(subject.white_list_pattern, is_("test")) - def test_replace_stmt_rewrites_matching_pattern(self, mocker): + def test_replace_stmt_rewrites_matching_pattern(self, mocker) -> None: """AI: Verify replace_stmt rewrites a statement matching an exact pattern.""" self._patch_factory( mocker, @@ -70,7 +70,7 @@ def test_replace_stmt_rewrites_matching_pattern(self, mocker): assert_that(subject.apply_to_string(), contains_string("import pytest")) assert_that(subject.apply_to_string(), contains_string("from hamcrest import *")) - def test_replace_stmt_expands_variadic_captures(self, mocker): + def test_replace_stmt_expands_variadic_captures(self, mocker) -> None: """AI: Verify replace_stmt rewrites a statement matched via a variadic ($$symbols) capture.""" self._patch_factory( mocker, @@ -91,7 +91,7 @@ def test_replace_stmt_expands_variadic_captures(self, mocker): # extract_call_arguments() # ------------------------------------------------------------------ - def test_extract_call_arguments_positional_only(self, mocker): + def test_extract_call_arguments_positional_only(self, mocker) -> None: """Verify extract_call_arguments returns only positional values for positional-only calls.""" self._patch_factory(mocker, "fun(1, 'x')") subject = UnitToPytest("test_foo.py") @@ -102,7 +102,7 @@ def test_extract_call_arguments_positional_only(self, mocker): assert_that(positional, is_(["1", "'x'"])) assert_that(keyword, is_({})) - def test_extract_call_arguments_keyword_only(self, mocker): + def test_extract_call_arguments_keyword_only(self, mocker) -> None: """Verify extract_call_arguments returns only keyword values for keyword-only calls.""" self._patch_factory(mocker, "fun(a=1, b='x')") subject = UnitToPytest("test_foo.py") @@ -113,7 +113,7 @@ def test_extract_call_arguments_keyword_only(self, mocker): assert_that(positional, is_([])) assert_that(keyword, is_({"a": "1", "b": "'x'"})) - def test_extract_call_arguments_mixed(self, mocker): + def test_extract_call_arguments_mixed(self, mocker) -> None: """Verify extract_call_arguments splits mixed positional and keyword arguments correctly.""" self._patch_factory(mocker, "fun(1, 2, b='x', c=other)") subject = UnitToPytest("test_foo.py") @@ -124,7 +124,7 @@ def test_extract_call_arguments_mixed(self, mocker): assert_that(positional, is_(["1", "2"])) assert_that(keyword, is_({"b": "'x'", "c": "other"})) - def test_extract_call_arguments_accepts_node_inside_call(self, mocker): + def test_extract_call_arguments_accepts_node_inside_call(self, mocker) -> None: """Verify extract_call_arguments works when given a child node nested inside a call.""" self._patch_factory(mocker, "fun(1, k='v')") subject = UnitToPytest("test_foo.py") @@ -136,7 +136,7 @@ def test_extract_call_arguments_accepts_node_inside_call(self, mocker): assert_that(positional, is_(["1"])) assert_that(keyword, is_({"k": "'v'"})) - def test_extract_call_arguments_walks_parent_chain_to_first_call(self, mocker): + def test_extract_call_arguments_walks_parent_chain_to_first_call(self, mocker) -> None: """Verify extract_call_arguments climbs ancestors and uses the first enclosing call node.""" self._patch_factory(mocker, "outer(inner(1), k=2)") subject = UnitToPytest("test_foo.py") @@ -150,7 +150,7 @@ def test_extract_call_arguments_walks_parent_chain_to_first_call(self, mocker): assert_that(positional, is_(["1"])) assert_that(keyword, is_({})) - def test_extract_call_arguments_returns_empty_for_non_call_node(self, mocker): + def test_extract_call_arguments_returns_empty_for_non_call_node(self, mocker) -> None: """Verify extract_call_arguments returns empty positional/keyword results for non-call nodes.""" self._patch_factory(mocker, "x = 1") subject = UnitToPytest("test_foo.py") @@ -166,7 +166,7 @@ def test_extract_call_arguments_returns_empty_for_non_call_node(self, mocker): positional_args=st.lists(_EXPR_STRATEGY, max_size=4), keyword_args=st.dictionaries(keys=_IDENTIFIER_STRATEGY, values=_EXPR_STRATEGY, max_size=4), ) - def test_extract_call_arguments_hypothesis_roundtrip(self, positional_args, keyword_args): + def test_extract_call_arguments_hypothesis_roundtrip(self, positional_args, keyword_args) -> None: """Property: extraction round-trips generated positional and keyword call arguments.""" rendered_kwargs = [f"{name}={value}" for name, value in keyword_args.items()] source = f"fun({', '.join([*positional_args, *rendered_kwargs])})" @@ -188,7 +188,7 @@ def test_extract_call_arguments_hypothesis_roundtrip(self, positional_args, keyw # class_declares_base() / class_base_arguments() # ------------------------------------------------------------------ - def test_class_base_arguments_returns_declared_bases(self, mocker): + def test_class_base_arguments_returns_declared_bases(self, mocker) -> None: """Verify class_base_arguments returns all explicitly declared base classes.""" self._patch_factory(mocker, "class Child(Base1, Base2):\n pass") subject = UnitToPytest("test_foo.py") @@ -198,7 +198,7 @@ def test_class_base_arguments_returns_declared_bases(self, mocker): assert_that(bases, is_(["Base1", "Base2"])) - def test_class_base_arguments_returns_empty_without_bases(self, mocker): + def test_class_base_arguments_returns_empty_without_bases(self, mocker) -> None: """Verify class_base_arguments returns an empty list for classes without base classes.""" self._patch_factory(mocker, "class Child:\n pass") subject = UnitToPytest("test_foo.py") @@ -208,7 +208,7 @@ def test_class_base_arguments_returns_empty_without_bases(self, mocker): assert_that(bases, is_([])) - def test_class_declares_base_checks_base_membership(self, mocker): + def test_class_declares_base_checks_base_membership(self, mocker) -> None: """Verify class_declares_base reports whether a requested base class is present.""" self._patch_factory(mocker, "class Child(Base):\n pass") subject = UnitToPytest("test_foo.py") @@ -217,7 +217,7 @@ def test_class_declares_base_checks_base_membership(self, mocker): assert_that(subject.class_declares_base(class_node, "Base"), is_(True)) assert_that(subject.class_declares_base(class_node, "Other"), is_(False)) - def test_class_declares_base_does_not_follow_transitive_inheritance(self, mocker): + def test_class_declares_base_does_not_follow_transitive_inheritance(self, mocker) -> None: """Verify class_declares_base checks only direct bases, not transitive ancestors.""" self._patch_factory( mocker, @@ -243,7 +243,7 @@ class Bottom(Middle): # process() — skip branch # ------------------------------------------------------------------ - def test_process_skips_file_matching_black_list(self, mocker, capsys): + def test_process_skips_file_matching_black_list(self, mocker, capsys) -> None: """AI: Verify process() skips and does not run the refactor when the filename matches the black list.""" self._patch_factory(mocker, "pass", "utils_for_test_foo.py") run_spy = mocker.patch("renaissance.recipes.unit_to_pytest.UnitToPytest.run") @@ -252,7 +252,7 @@ def test_process_skips_file_matching_black_list(self, mocker, capsys): assert_that(captured.out, contains_string("skipping")) assert_that(run_spy.call_count, is_(0)) - def test_process_skips_file_not_matching_white_list(self, mocker, capsys): + def test_process_skips_file_not_matching_white_list(self, mocker, capsys) -> None: """AI: Verify process() skips and does not run the refactor when the filename doesn't match the white list.""" self._patch_factory(mocker, "pass", "my_module.py") run_spy = mocker.patch("renaissance.recipes.unit_to_pytest.UnitToPytest.run") @@ -265,7 +265,7 @@ def test_process_skips_file_not_matching_white_list(self, mocker, capsys): # process() — run branch # ------------------------------------------------------------------ - def test_process_runs_refactor_on_matching_file(self, mocker, capsys): + def test_process_runs_refactor_on_matching_file(self, mocker, capsys) -> None: """AI: Verify process() runs the refactor when the filename matches both white and black list patterns.""" self._patch_factory(mocker, "pass", "test_foo.py") run_spy = mocker.patch("renaissance.recipes.unit_to_pytest.UnitToPytest.run") @@ -278,7 +278,7 @@ def test_process_runs_refactor_on_matching_file(self, mocker, capsys): # body property # ------------------------------------------------------------------ - def test_body_returns_module_level_statements(self, mocker): + def test_body_returns_module_level_statements(self, mocker) -> None: """AI: Verify the body property returns the module's top-level statements.""" self._patch_factory( mocker, diff --git a/test/recipes/test_refactor_with_rewrite.py b/test/recipes/test_refactor_with_rewrite.py index 2db30e2b..1719b81a 100644 --- a/test/recipes/test_refactor_with_rewrite.py +++ b/test/recipes/test_refactor_with_rewrite.py @@ -23,7 +23,7 @@ def _create(self, mocker, text) -> PythonRefactoring: return subject @pytest.mark.skip("comment are not correctly calculated") - def test_refactor_with_comment_and_spaces(self, mocker): + def test_refactor_with_comment_and_spaces(self, mocker) -> None: """AI: Verify rewrite-based refactoring preserves kept comments while removing others (comment placement TBD).""" refactoring = self._create( mocker, @@ -71,7 +71,7 @@ def test_functions(self): # end comments to keep"""), ) - def test_refactor_replace_multi_placeholder(self, mocker): + def test_refactor_replace_multi_placeholder(self, mocker) -> None: """Test case showing a replacement of a multi placeholder that matches a non-empty list of AST nodes in the code.""" refactoring = self._create(mocker, "def f(a):\n f(2, 0)") function_call = refactoring.pattern_factory.create_expression("f($$params, 0)") @@ -82,7 +82,7 @@ def test_refactor_replace_multi_placeholder(self, mocker): assert_that(refactoring.apply_to_string(), is_("def f(a):\n f(1, 0)")) @pytest.mark.skip("empty array can't be detected") - def test_refactor_replace_multi_placeholder_empty(self, mocker): + def test_refactor_replace_multi_placeholder_empty(self, mocker) -> None: """Test case showing a replacement of a multi placeholder that matches an empty list of AST nodes in the code.""" # TODO: is this the behaviour we want? # Can $$params be empty and a comma absent, while present in the pattern. diff --git a/test/recipes/test_simplify_renaissance.py b/test/recipes/test_simplify_renaissance.py index 15bdcb71..fa2f0159 100644 --- a/test/recipes/test_simplify_renaissance.py +++ b/test/recipes/test_simplify_renaissance.py @@ -21,13 +21,13 @@ def _create(self, mocker, text) -> SimplifyRenaissance: subject.in_memory = True return subject - def test_init_sets_white_and_black_list(self, mocker): + def test_init_sets_white_and_black_list(self, mocker) -> None: """AI: Verify __init__ sets the white and black list patterns from the recipe's own defaults.""" subject = self._create(mocker, "pass") assert_that(subject.white_list_pattern, is_("unit_to_pytest")) assert_that(subject.black_list_pattern, is_("SimplifyRenaissance")) - def test_run_skips_file_matching_black_list(self, mocker, capsys): + def test_run_skips_file_matching_black_list(self, mocker, capsys) -> None: """AI: Verify run() skips a file whose name matches the black list pattern.""" mocker.patch( "renaissance.integrations.python.ast.factory.PythonFactory.create", @@ -39,7 +39,7 @@ def test_run_skips_file_matching_black_list(self, mocker, capsys): captured = capsys.readouterr() assert_that(captured.out, contains_string("skipping")) - def test_run_skips_file_not_matching_white_list(self, mocker, capsys): + def test_run_skips_file_not_matching_white_list(self, mocker, capsys) -> None: """AI: Verify run() skips a file whose name doesn't match the white list pattern.""" mocker.patch( "renaissance.integrations.python.ast.factory.PythonFactory.create", @@ -51,7 +51,7 @@ def test_run_skips_file_not_matching_white_list(self, mocker, capsys): captured = capsys.readouterr() assert_that(captured.out, contains_string("skipping")) - def test_run_rewrites_expansion_signature_access(self, mocker): + def test_run_rewrites_expansion_signature_access(self, mocker) -> None: """AI: Verify a manual replace_stmt call rewrites .expansions[...] signature access to bracket indexing.""" subject = self._create( mocker, @@ -64,7 +64,7 @@ def foo(): assert_that(subject.apply_to_string(), contains_string('val= match["$key"]')) assert_that(subject.apply_to_string(), not_(contains_string(".expansions"))) - def test_run_rewrites_factory_create_from_text(self, mocker): + def test_run_rewrites_factory_create_from_text(self, mocker) -> None: """AI: Verify run() rewrites ASTFactory.create_from_text calls to the simplified load_from_text form.""" subject = self._create( mocker, @@ -78,7 +78,7 @@ def foo(): assert_that(subject.apply_to_string(), contains_string("PythonASTNode.load_from_text(code, name)")) assert_that(subject.apply_to_string(), not_(contains_string("ASTFactory"))) - def test_run_processes_matching_file(self, mocker, capsys): + def test_run_processes_matching_file(self, mocker, capsys) -> None: """AI: Verify run() processes a matching file and reports it via stdout.""" subject = self._create(mocker, "pass") subject.run() diff --git a/test/recipes/test_taut_to_python_unittest.py b/test/recipes/test_taut_to_python_unittest.py index 6b26cfca..93209049 100644 --- a/test/recipes/test_taut_to_python_unittest.py +++ b/test/recipes/test_taut_to_python_unittest.py @@ -20,7 +20,7 @@ class TestTautToPythonUnittest: """AI: Tests for the TautToPythonUnittest recipe.""" - def test_init(self): + def test_init(self) -> None: """AI: Verify the recipe's filename attribute reflects the constructed path.""" subject = TautToPythonUnittest(Path(targets.__file__).parent / "taut/taut_test.py") assert_that(subject.filename, ends_with("taut_test.py")) @@ -44,7 +44,7 @@ def _create(self, mocker, text) -> TautToPythonUnittest: ), ], ) - def test_remove_import(self, input_code, expected_code, mocker): + def test_remove_import(self, input_code, expected_code, mocker) -> None: """AI: Verify remove_taut_import removes the TAUT import while keeping other imports.""" subject = self._create(mocker, input_code) subject.remove_taut_import() @@ -64,7 +64,7 @@ def test_remove_import(self, input_code, expected_code, mocker): ), ], ) - def test_replace_taut(self, input_code, expected_code, mocker): + def test_replace_taut(self, input_code, expected_code, mocker) -> None: """AI: Verify replace_taut rewrites TAUT.TestCase base classes to unittest.TestCase.""" subject = self._create(mocker, input_code) subject.replace_taut() @@ -80,7 +80,7 @@ def test_replace_taut(self, input_code, expected_code, mocker): ), ], ) - def test_replace_skip(self, input_code, expected_code, mocker): + def test_replace_skip(self, input_code, expected_code, mocker) -> None: """AI: Verify replace_taut_skip rewrites @TAUT.skip_test to @unittest.skip.""" subject = self._create(mocker, input_code) subject.replace_taut_skip() @@ -94,7 +94,7 @@ def test_replace_skip(self, input_code, expected_code, mocker): (tst_testdoubles.test_indent_fun, tst_testdoubles.test_indent_fun_new, " "), ], ) - def test_indentation(self, input_code, expected_code, indent, mocker): + def test_indentation(self, input_code, expected_code, indent, mocker) -> None: """AI: Verify move_indent adjusts a statement's indentation to the given prefix.""" subject = self._create(mocker, input_code) subject.move_indent(indent) @@ -110,7 +110,7 @@ def test_indentation(self, input_code, expected_code, indent, mocker): ), ], ) - def test_replace_import(self, input_code, expected_code, mocker): + def test_replace_import(self, input_code, expected_code, mocker) -> None: """AI: Verify replace_taut_import rewrites a mock/TAUT import to the unittest.mock/mock fallback try-import.""" subject = self._create(mocker, input_code) subject.replace_taut_import() @@ -131,7 +131,7 @@ def test_replace_import(self, input_code, expected_code, mocker): # ('EMRWxREAD.emrwxread.set_retval(0)', 'self.emrwxread.set_retval(0)') ], ) - def test_add_self(self, input_code, expected_code, mocker): + def test_add_self(self, input_code, expected_code, mocker) -> None: """AI: Verify add_self prefixes bare test-double variable names with self.""" subject = self._create(mocker, input_code) subject.add_self() @@ -147,7 +147,7 @@ def test_add_self(self, input_code, expected_code, mocker): ), ], ) - def test_remove_decorator(self, input_code, expected_code, mocker): + def test_remove_decorator(self, input_code, expected_code, mocker) -> None: """AI: Verify remove_decorator strips the @TAUT.log_stub decorator from a function.""" subject = self._create(mocker, input_code) subject.remove_decorator() @@ -162,7 +162,7 @@ def test_remove_decorator(self, input_code, expected_code, mocker): ("self.assert_true(len(listA), 5)", "self.assertTrue(len(listA), 5)"), ], ) - def test_convert_assert(self, input_code, expected_code, mocker): + def test_convert_assert(self, input_code, expected_code, mocker) -> None: """AI: Verify convert_assert rewrites snake_case assert_* calls to unittest's assertEqual/assertFalse/assertTrue.""" subject = self._create(mocker, input_code) subject.convert_assert() @@ -170,7 +170,7 @@ def test_convert_assert(self, input_code, expected_code, mocker): assert_that(result, is_(expected_code)) @pytest.mark.parametrize("input_code, expected_code", [(tst_code.taut_code, tst_code.result_code)]) - def test_log_abcdxtl(self, input_code, expected_code, mocker): + def test_log_abcdxtl(self, input_code, expected_code, mocker) -> None: """AI: Verify replace_log_compxtl rewrites the ABCDxTL log-component call in the sample TAUT code.""" subject = self._create(mocker, input_code) subject.in_memory = True @@ -179,7 +179,7 @@ def test_log_abcdxtl(self, input_code, expected_code, mocker): assert_that(result, is_(expected_code)) @pytest.mark.parametrize("input_code, insert_code", [(tst_insert.input_code, tst_insert.insert_code)]) - def test_insert_class(self, input_code, insert_code, mocker): + def test_insert_class(self, input_code, insert_code, mocker) -> None: """AI: Verify insert_class appends the generated class boilerplate after the existing code.""" subject = self._create(mocker, input_code) subject.insert_class() @@ -187,14 +187,14 @@ def test_insert_class(self, input_code, insert_code, mocker): assert_that(result, is_(input_code + insert_code)) @pytest.mark.parametrize("input_code, expected_code", [(tst_class.set_up, tst_class.new_set_up)]) - def test_setup(self, input_code, expected_code, mocker): + def test_setup(self, input_code, expected_code, mocker) -> None: """AI: Verify convert_setup rewrites the sample set_up method to unittest's setUp form.""" subject = self._create(mocker, input_code) subject.convert_setup() result = subject.apply_to_string() assert result == expected_code - def test_teardown(self, mocker): + def test_teardown(self, mocker) -> None: """AI: Verify convert_teardown rewrites a simple tear_down method to unittest's tearDown form.""" subject = self._create(mocker, tst_class.tear_down_simple) subject.convert_teardown() @@ -202,7 +202,7 @@ def test_teardown(self, mocker): assert result == tst_class.tear_down_simple_new @pytest.mark.parametrize("input_code, expected_code", [(tst_class.tear_down, tst_class.new_tear_down)]) - def test_teardown_refactor(self, input_code, expected_code, mocker): + def test_teardown_refactor(self, input_code, expected_code, mocker) -> None: """AI: Verify refactor_teardown rewrites the sample tear_down method body into its expected form.""" subject = self._create(mocker, input_code) subject.refactor_teardown() @@ -210,7 +210,7 @@ def test_teardown_refactor(self, input_code, expected_code, mocker): assert_that(result, is_(expected_code)) @pytest.mark.parametrize("input_code, expected_code", [(tst_testdoubles.test_doubles_fun, tst_testdoubles.test_doubles_fun_new)]) - def test_testdoubles_fun(self, input_code, expected_code, mocker): + def test_testdoubles_fun(self, input_code, expected_code, mocker) -> None: """AI: Verify refactor_testdoubles_fun rewrites the sample test-doubles function-level usage.""" subject = self._create(mocker, input_code) subject.refactor_testdoubles_fun() @@ -218,7 +218,7 @@ def test_testdoubles_fun(self, input_code, expected_code, mocker): assert_that(result, is_(expected_code)) @pytest.mark.parametrize("input_code, expected_code", [(tst_testdoubles.test_doubles_class, tst_testdoubles.test_doubles_class_new)]) - def test_testdoubles_class(self, input_code, expected_code, mocker): + def test_testdoubles_class(self, input_code, expected_code, mocker) -> None: """AI: Verify refactor_testdoubles_class rewrites the sample test-doubles class-level usage.""" subject = self._create(mocker, input_code) subject.refactor_testdoubles_class() @@ -232,14 +232,14 @@ def test_testdoubles_class(self, input_code, expected_code, mocker): ("a = mock.patch(arg)", "a = mock.patch(arg)"), ], ) - def test_remove_mock(self, input_code, expected_code, mocker): + def test_remove_mock(self, input_code, expected_code, mocker) -> None: """AI: Verify replace_mock strips the mock. prefix from mock.patch calls/decorators.""" subject = self._create(mocker, input_code) subject.replace_mock() result = subject.apply_to_string() assert_that(result, is_(expected_code)) - def test_remove_stubserver(self, mocker): + def test_remove_stubserver(self, mocker) -> None: """AI: Verify remove_stubserver strips the @TAUT.StubServer decorator from a function.""" subject = self._create(mocker, "@TAUT.StubServer\ndef test():\n pass\n") expected_code = "\ndef test():\n pass\n" @@ -254,7 +254,7 @@ def test_remove_stubserver(self, mocker): ("self.tds.append(TestDoubles(a=ImprovedStub(b)))", "self.a = ImprovedStub(b)"), ], ) - def test_convert_tds(self, input_code, expected_code, mocker): + def test_convert_tds(self, input_code, expected_code, mocker) -> None: """AI: Verify convert_tds rewrites TestDoubles/ImprovedStub append calls into add_patcher/attribute assignment.""" subject = self._create(mocker, input_code) subject.convert_tds() @@ -268,7 +268,7 @@ def test_convert_tds(self, input_code, expected_code, mocker): ("def a():\n assert_double_equal(l.x, 0.0)", "def a():\n self.assert_double_equal(l.x, 0.0)"), ], ) - def test_assert_doubles(self, input_code, expected_code, mocker): + def test_assert_doubles(self, input_code, expected_code, mocker) -> None: """AI: Verify manually replacing assert_double_equal name references prefixes them with self.""" subject = self._create(mocker, input_code) [ @@ -279,7 +279,7 @@ def test_assert_doubles(self, input_code, expected_code, mocker): result = subject.apply_to_string() assert_that(result, is_(expected_code)) - def test_import_verify(self, mocker): + def test_import_verify(self, mocker) -> None: """AI: Verify convert_import_verify rewrites import_and_verify_module calls into an import plus assertIsNotNone.""" subject = self._create(mocker, "def test_import(self):\n self.import_and_verify_module('ABCDxTL')") expected_code = "def test_import(self):\n import ABCDxTL\n self.assertIsNotNone(ABCDxTL)" @@ -287,7 +287,7 @@ def test_import_verify(self, mocker): result = subject.apply_to_string() assert_that(result, is_(expected_code)) - def test_insert_asserter(self, mocker): + def test_insert_asserter(self, mocker) -> None: """AI: Verify insert_asserter plus remove_assert_func inserts the asserter helper and removes the original function.""" subject = self._create(mocker, "def assert_double_equal(a, br=c):\n pass") expected_code = tst_insert.insert_code @@ -296,7 +296,7 @@ def test_insert_asserter(self, mocker): result = subject.apply_to_string() assert_that(result, is_(expected_code)) - def test_replace_unittest_asserter(self, mocker): + def test_replace_unittest_asserter(self, mocker) -> None: """AI: Verify replace_unittest_with_asserter rewrites TAUT.TestCase base class to Asserter.""" subject = self._create(mocker, "class A(TAUT.TestCase):\n def b(self):\n self.assert_raises(a, b=c)") expected_code = "class A(Asserter):\n def b(self):\n self.assert_raises(a, b=c)" @@ -311,49 +311,49 @@ def test_replace_unittest_asserter(self, mocker): ("assert_double_equal", "self.assert_double_equal"), ], ) - def test_assert_func(self, mocker, input_code, expected_code): + def test_assert_func(self, mocker, input_code, expected_code) -> None: """AI: Verify assert_func prefixes bare assert_raises/assert_double_equal calls with self.""" subject = self._create(mocker, input_code) subject.assert_func() result = subject.apply_to_string() assert_that(result, is_(expected_code)) - def test_convert_testdoubles_func(self, mocker): + def test_convert_testdoubles_func(self, mocker) -> None: """AI: Verify convert_testdoubles_fun rewrites the multi-line sample test-doubles class usage.""" subject = self._create(mocker, tst_testdoubles.test_taut_doubles_class) subject.convert_testdoubles_fun() result = subject.apply_to_string() assert_that(result, is_(tst_testdoubles.test_taut_doubles_class_new)) - def test_convert_testdoubles_func_single_line(self, mocker): + def test_convert_testdoubles_func_single_line(self, mocker) -> None: """AI: Verify convert_testdoubles_fun rewrites the single-line sample test-doubles class usage.""" subject = self._create(mocker, tst_testdoubles.test_taut_doubles_class_single_line) subject.convert_testdoubles_fun() result = subject.apply_to_string() assert_that(result, is_(tst_testdoubles.test_taut_doubles_class_single_line_new)) - def test_setup_common(self, mocker): + def test_setup_common(self, mocker) -> None: """AI: Verify convert_setup_common rewrites the shared set_up_common method into its expected form.""" subject = self._create(mocker, tst_class.set_up_common) subject.convert_setup_common() result = subject.apply_to_string() assert_that(result, is_(tst_class.set_up_common_new)) - def test_teardown_common(self, mocker): + def test_teardown_common(self, mocker) -> None: """AI: Verify convert_teardown_common rewrites the shared tear_down_common method into its expected form.""" subject = self._create(mocker, tst_class.tear_down_common) subject.convert_teardown_common() result = subject.apply_to_string() assert_that(result, is_(tst_class.tear_down_common_new)) - def test_add_patcher(self, mocker): + def test_add_patcher(self, mocker) -> None: """AI: Verify convert_add_patcher appends the add_patcher helper method to the class.""" subject = self._create(mocker, tst_class.tear_down_common_new) subject.convert_add_patcher() result = subject.apply_to_string() assert_that(result, is_(tst_class.tear_down_common_new + tst_class.insert_add_patcher + "\n")) - def test_shared_setup(self, mocker): + def test_shared_setup(self, mocker) -> None: """AI: Verify shared_setup renames sharedSetUp to setUp.""" subject = self._create(mocker, "class A():\n def sharedSetUp(self):\n pass") expected_code = "class A():\n def setUp(self):\n pass" @@ -361,7 +361,7 @@ def test_shared_setup(self, mocker): result = subject.apply_to_string() assert_that(result, is_(expected_code)) - def test_with_testdoubles(self, mocker): + def test_with_testdoubles(self, mocker) -> None: """AI: Verify with_testdoubles rewrites a TAUT.TestDoubles with-statement into patch.object usage.""" subject = self._create(mocker, "with TAUT.TestDoubles(module=mod, b=c):\n pass") expected_code = "with patch.object(mod, 'b', new=c):\n pass" @@ -369,7 +369,7 @@ def test_with_testdoubles(self, mocker): result = subject.apply_to_string() assert_that(result, is_(expected_code)) - def test_insert_patch_import(self, mocker): + def test_insert_patch_import(self, mocker) -> None: """AI: Verify insert_patch_import inserts the unittest.mock/mock fallback try-import after the unittest import.""" subject = self._create(mocker, "import unittest\nself.patches = []") expected_code = ( diff --git a/test/recipes/test_unit_to_pytest.py b/test/recipes/test_unit_to_pytest.py index e213f84a..79f68696 100644 --- a/test/recipes/test_unit_to_pytest.py +++ b/test/recipes/test_unit_to_pytest.py @@ -13,12 +13,12 @@ class TestUnitToPytest: """AI: Tests for the UnitToPytest recipe.""" - def test_init(self): + def test_init(self) -> None: """AI: Verify the recipe's filename attribute reflects the constructed path.""" subject = UnitToPytest(Path(targets.__file__).parent / "demo.py") assert_that(subject.filename, ends_with("demo.py")) - def test_commit_does_nothing_when_not_changed(self, mocker): + def test_commit_does_nothing_when_not_changed(self, mocker) -> None: """AI: Verify has_changed() returns False when the code hasn't been modified.""" subject = self._create( mocker, @@ -28,7 +28,7 @@ def test_commit_does_nothing_when_not_changed(self, mocker): ) assert_that(subject.has_changed(), is_(False)) - def test_convert_test_class_updates_only_testcase_bases(self, mocker): + def test_convert_test_class_updates_only_testcase_bases(self, mocker) -> None: """AI: Verify convert_test_class rewrites only classes that extend TestCase, and normalizes their names.""" subject = self._create( mocker, @@ -54,7 +54,7 @@ def _create(self, mocker, text) -> UnitToPytest: subject.in_memory = True return subject - def test_convert_plain_assert_same_length_rewrites_to_has_length(self, mocker): + def test_convert_plain_assert_same_length_rewrites_to_has_length(self, mocker) -> None: """AI: Verify convert_plain_assert_same_length rewrites a length-equality assert into assert_that/has_length.""" expected = textwrap.dedent(""" def test_asert(): @@ -74,7 +74,7 @@ def test_asert(): subject.convert_plain_assert_same_length() assert_that(subject.apply_to_string(), is_(expected)) - def test_restructure_module_injects_methods_when_class_exists(self, mocker): + def test_restructure_module_injects_methods_when_class_exists(self, mocker) -> None: """AI: Verify restructure_module moves a module-level function into the existing test class as a method.""" subject = self._create( mocker, @@ -93,7 +93,7 @@ def parse(a): assert_that(subject.apply_to_string(), contains_string("def parse(self,a):")) - def test_convert(self, mocker): + def test_convert(self, mocker) -> None: """AI: Verify run() invokes convert_test_class, convert_test_setup, and replace_stmt the expected number of times.""" sut = self._create( mocker, @@ -113,7 +113,7 @@ def test_fun(self): assert_that(spy2.call_count, is_(1)) assert_that(spy3.call_count, is_(26)) - def test_convert_assert(self, mocker): + def test_convert_assert(self, mocker) -> None: """AI: Verify convert_assert rewrites assertEqual calls into assert_that/is_, regardless of argument order.""" sut = self._create( mocker, @@ -128,7 +128,7 @@ def test_fun(self): assert_that(sut.apply_to_string(), contains_string("assert_that(call()")) assert_that(sut.apply_to_string(), not_(contains_string("assert_that(1"))) - def test_to_assertthat(self, mocker): + def test_to_assertthat(self, mocker) -> None: """AI: Verify a manual replace_stmt call rewrites a plain assert into assert_that(..., is_(True), ...).""" sut = self._create( mocker, @@ -142,7 +142,7 @@ def test_fun(): assert_that(sut.apply_to_string(), contains_string("assert_that(call()")) assert_that(sut.apply_to_string(), not_(contains_string("assert_that(1"))) - def test_convert_test_class_renames_class_ending_with_test(self, mocker): + def test_convert_test_class_renames_class_ending_with_test(self, mocker) -> None: """AI: Verify convert_test_class renames a FooTest class to TestFoo.""" subject = self._create( mocker, @@ -154,7 +154,7 @@ class FooTest(TestCase): subject.convert_test_class() assert_that(subject.apply_to_string(), contains_string("class TestFoo:")) - def test_convert_parameterized_test_at_top_level(self, mocker): + def test_convert_parameterized_test_at_top_level(self, mocker) -> None: """AI: Verify convert_parameterized_test rewrites a top-level @parameterized.expand into @pytest.mark.parametrize.""" subject = self._create( mocker, @@ -168,7 +168,7 @@ def test_fun(self, val): subject.convert_parameterized_test() assert_that(subject.apply_to_string(), contains_string("@pytest.mark.parametrize")) - def test_convert_parameterized_test_inside_class(self, mocker): + def test_convert_parameterized_test_inside_class(self, mocker) -> None: """AI: Verify convert_parameterized_test rewrites a class-nested @parameterized.expand into @pytest.mark.parametrize.""" subject = self._create( mocker, @@ -183,7 +183,7 @@ def test_fun(self, val): subject.convert_parameterized_test() assert_that(subject.apply_to_string(), contains_string("@pytest.mark.parametrize")) - def test_remove_print_removes_entire_function_when_only_statement(self, mocker): + def test_remove_print_removes_entire_function_when_only_statement(self, mocker) -> None: """AI: Verify remove_print removes the entire function when print() is its only statement.""" subject = self._create( mocker, @@ -195,7 +195,7 @@ def test_foo(self): subject.remove_print() assert_that(subject.apply_to_string(), not_(contains_string("test_foo"))) - def test_remove_print_removes_only_print_when_other_statements_exist(self, mocker): + def test_remove_print_removes_only_print_when_other_statements_exist(self, mocker) -> None: """AI: Verify remove_print removes only the print() call, keeping other statements in the function.""" subject = self._create( mocker, @@ -209,7 +209,7 @@ def test_foo(self): assert_that(subject.apply_to_string(), not_(contains_string("print"))) assert_that(subject.apply_to_string(), contains_string("assert 1 == 1")) - def test_convert_plain_assert_same_length_when_not_swapped(self, mocker): + def test_convert_plain_assert_same_length_when_not_swapped(self, mocker) -> None: """AI: Verify convert_plain_assert_same_length still rewrites to has_length when operands aren't swapped.""" subject = self._create( mocker, @@ -223,7 +223,7 @@ def test_foo(): subject.convert_plain_assert_same_length() assert_that(subject.apply_to_string(), contains_string("has_length")) - def test_convert_skip_test_replaces_unittest_skip(self, mocker): + def test_convert_skip_test_replaces_unittest_skip(self, mocker) -> None: """AI: Verify convert_skip_test rewrites @unittest.skip to @pytest.mark.skip.""" subject = self._create( mocker, @@ -237,7 +237,7 @@ def test_foo(self): assert_that(subject.apply_to_string(), contains_string("pytest.mark.skip")) assert_that(subject.apply_to_string(), not_(contains_string("unittest.skip"))) - def test_swap_expected_and_actual_swaps_when_literal_is_expected(self, mocker): + def test_swap_expected_and_actual_swaps_when_literal_is_expected(self, mocker) -> None: """AI: Verify swap_expected_and_actual reorders assert_that args so the literal becomes the expected value.""" subject = self._create( mocker, @@ -249,7 +249,7 @@ def test_foo(self): subject.swap_expected_and_actual() assert_that(subject.apply_to_string(), contains_string("assert_that(call(), is_(1))")) - def test_restructure_module_moves_functions_into_existing_test_class(self, mocker): + def test_restructure_module_moves_functions_into_existing_test_class(self, mocker) -> None: """AI: Verify restructure_module moves a standalone helper function into the existing TestFoo class.""" subject = self._create( mocker, @@ -266,7 +266,7 @@ def helper(a): subject.commit() assert_that(subject.apply_to_string(), contains_string("def helper(self,a):")) - def test_remove_duplicate_import_removes_middle_duplicates(self, mocker): + def test_remove_duplicate_import_removes_middle_duplicates(self, mocker) -> None: """AI: Verify remove_duplicate_import collapses repeated identical import statements down to the first and last.""" subject = self._create( mocker, @@ -285,7 +285,7 @@ def test_foo(): result = subject.apply_to_string() assert_that(result.count("import pytest"), is_(2)) - def test_convert_test_setup_adds_pytest_fixture(self, mocker): + def test_convert_test_setup_adds_pytest_fixture(self, mocker) -> None: """AI: Verify convert_test_setup rewrites setUp into a pytest autouse fixture named setup.""" subject = self._create( mocker, @@ -301,7 +301,7 @@ def test_foo(self): assert_that(subject.apply_to_string(), contains_string("@pytest.fixture(autouse=True)")) assert_that(subject.apply_to_string(), contains_string("def setup(self)")) - def test_convert_parameterized_test_with_vargs(self, mocker): + def test_convert_parameterized_test_with_vargs(self, mocker) -> None: """AI: Verify convert_parameterized_test preserves *rest varargs when rewriting to @pytest.mark.parametrize.""" subject = self._create( mocker, @@ -316,7 +316,7 @@ def test_fun(self, val, *rest): assert_that(subject.apply_to_string(), contains_string("@pytest.mark.parametrize")) assert_that(subject.apply_to_string(), contains_string("*rest")) - def test_restructure_module_rewrites_call_sites_in_existing_class(self, mocker): + def test_restructure_module_rewrites_call_sites_in_existing_class(self, mocker) -> None: """AI: Verify restructure_module rewrites call sites of a moved helper function to use self.""" subject = self._create( mocker, @@ -333,13 +333,13 @@ def helper(a): subject.commit() assert_that(subject.apply_to_string(), contains_string("self.helper(1)")) - def test_convert_file_to_test_class_strips_trailing_test(self, mocker): + def test_convert_file_to_test_class_strips_trailing_test(self, mocker) -> None: """AI: Verify convert_file_to_test_class strips a trailing '_test' suffix when deriving the class name.""" subject = self._create(mocker, "pass") mocker.patch.object(type(subject), "filename", new_callable=lambda: property(lambda self: "my_module_test.py")) assert_that(subject.convert_file_to_test_class(), is_("TestMyModule")) - def test_convert_file_to_test_class_keeps_test_prefix(self, mocker): + def test_convert_file_to_test_class_keeps_test_prefix(self, mocker) -> None: """AI: Verify convert_file_to_test_class keeps a leading 'test_' prefix when deriving the class name.""" subject = self._create(mocker, "pass") mocker.patch.object(type(subject), "filename", new_callable=lambda: property(lambda self: "test_my_module.py")) diff --git a/test/search_strategies/test_python_arguments.py b/test/search_strategies/test_python_arguments.py index 5f7f7eb3..5aed675d 100644 --- a/test/search_strategies/test_python_arguments.py +++ b/test/search_strategies/test_python_arguments.py @@ -21,21 +21,21 @@ def _collect_names(a: ast.arguments) -> list[str]: @given(gen_arguments()) -def test_gen_arguments_names_unique(a: ast.arguments): +def test_gen_arguments_names_unique(a: ast.arguments) -> None: """AI: Assert generated argument names are all unique.""" names = _collect_names(a) assert len(names) == len(set(names)) @given(gen_arguments()) -def test_gen_arguments_defaults_valid(a: ast.arguments): +def test_gen_arguments_defaults_valid(a: ast.arguments) -> None: """AI: Assert the number of positional defaults never exceeds the number of positional arguments.""" total_pos = len(a.args) + len(a.posonlyargs) assert len(a.defaults) <= total_pos @given(gen_arguments()) -def test_gen_arguments_compilable(a: ast.arguments): +def test_gen_arguments_compilable(a: ast.arguments) -> None: """AI: Assert a function built from generated arguments compiles successfully.""" f = ast.FunctionDef(name="f", args=a, body=[ast.Pass()], decorator_list=[], returns=None) m = ast.Module(body=[f], type_ignores=[]) @@ -44,7 +44,7 @@ def test_gen_arguments_compilable(a: ast.arguments): @given(gen_arguments()) -def test_gen_arguments_unparsable_parsable(a: ast.arguments): +def test_gen_arguments_unparsable_parsable(a: ast.arguments) -> None: """AI: Assert generated arguments unparse to source that can be re-parsed.""" code = ast.unparse(a) ast.parse(f""" diff --git a/test/syntax_tree/infra_text_segment.py b/test/syntax_tree/infra_text_segment.py index cef9304e..53dbab97 100644 --- a/test/syntax_tree/infra_text_segment.py +++ b/test/syntax_tree/infra_text_segment.py @@ -143,7 +143,7 @@ def _check_column_range( column: int, line_starts: tuple[int, ...], description: str, -): +) -> None: start_line = line_starts[line] end_line = ( length_full_text + 1 ## column must be able to point beyond last character of full text to include that character as well. diff --git a/test/syntax_tree/test_ast_processor.py b/test/syntax_tree/test_ast_processor.py index 471d7269..9dec5ae4 100644 --- a/test/syntax_tree/test_ast_processor.py +++ b/test/syntax_tree/test_ast_processor.py @@ -9,7 +9,7 @@ class TestAstProcessor: """AI: Tests for the ASTProcessor pattern-match/find functionality.""" - def test_find_match(self, mocker): + def test_find_match(self, mocker) -> None: """AI: Verify find_match delegates to the match_finder.find_all function.""" node = mocker.Mock() pattern_match = PatternMatch([node, node, node], {}, []) diff --git a/test/syntax_tree/test_ast_processor_semantic_kind.py b/test/syntax_tree/test_ast_processor_semantic_kind.py index 9ebe6bdd..3f680074 100644 --- a/test/syntax_tree/test_ast_processor_semantic_kind.py +++ b/test/syntax_tree/test_ast_processor_semantic_kind.py @@ -6,7 +6,7 @@ from renaissance.syntax_tree.semantic_kind import SemanticKind -def test_ast_processor_finds_nodes_by_semantic_kind(): +def test_ast_processor_finds_nodes_by_semantic_kind() -> None: """AI: Assert ASTProcessor.find_semantic_kind locates nodes by their semantic kind.""" root = PythonRstNode.load_from_text("def f():\n return 1\n") processor = ASTProcessor(root, PythonFactory(PythonRstNode), in_memory=True) diff --git a/test/syntax_tree/test_ast_refactor_actions.py b/test/syntax_tree/test_ast_refactor_actions.py index f4dd2f93..0c0a429a 100644 --- a/test/syntax_tree/test_ast_refactor_actions.py +++ b/test/syntax_tree/test_ast_refactor_actions.py @@ -9,14 +9,14 @@ class TestASTRefactorActions: """AI: Tests for the ASTRefactorActions helper.""" - def test_it_can_be_created(self, mocker): + def test_it_can_be_created(self, mocker) -> None: """AI: Verify ASTRefactorActions can be constructed from a processor and factory.""" proc = mocker.Mock() factory = mocker.Mock() refactor_actions = ASTRefactorActions(proc, factory) assert_that(refactor_actions, not is_(None)) - def test_replace_expr(self, mocker): + def test_replace_expr(self, mocker) -> None: """AI: Verify replace_expr delegates to the processor's find_all method.""" proc = mocker.Mock() proc.find_all.return_value = [] @@ -25,7 +25,7 @@ def test_replace_expr(self, mocker): refactor_actions.replace_expr("name", "my_awsome_name", SemanticKind.NAME) assert_that(proc.find_all.called) - def test_replace_name(self, mocker): + def test_replace_name(self, mocker) -> None: """AI: Verify replace_name finds a matching name node and calls the processor's replace method.""" node = mocker.Mock() node.offset = 1 @@ -40,7 +40,7 @@ def test_replace_name(self, mocker): assert_that(proc.replace.called) - def test_replace_text(self, mocker): + def test_replace_text(self, mocker) -> None: """AI: Verify replace_text finds matching literal nodes and calls the processor's replace method.""" node = mocker.Mock() node.semantic_kind = SemanticKind.LITERAL @@ -55,7 +55,7 @@ def test_replace_text(self, mocker): assert_that(proc.replace.called) - def test_replace_declaration(self, mocker): + def test_replace_declaration(self, mocker) -> None: """AI: Verify replace_declaration finds the declaration and calls the processor's replace method.""" node = mocker.Mock() proc = mocker.Mock() @@ -67,7 +67,7 @@ def test_replace_declaration(self, mocker): assert_that(proc.replace.called) - def test_replace_patterns(self, mocker): + def test_replace_patterns(self, mocker) -> None: """AI: Verify _replace_patterns matches the pattern and calls the processor's replace method.""" node = mocker.Mock() proc = mocker.Mock() @@ -80,7 +80,7 @@ def test_replace_patterns(self, mocker): assert_that(proc.replace.called) assert_that(is_match_mock.called) - def test_find_declaration(self, mocker): + def test_find_declaration(self, mocker) -> None: """AI: Verify find_declaration delegates to the processor's find_match method.""" proc = mocker.Mock() factory = mocker.Mock() @@ -88,7 +88,7 @@ def test_find_declaration(self, mocker): refactor_actions.find_declaration("decl_pattern") assert_that(proc.find_match.called) - def test_collect(self, mocker): + def test_collect(self, mocker) -> None: """AI: Verify collect delegates to the processor's find_match method.""" proc = mocker.Mock() proc.find_match.return_value = [] diff --git a/test/syntax_tree/test_ast_rewriter.py b/test/syntax_tree/test_ast_rewriter.py index bc4d869e..11787a1b 100644 --- a/test/syntax_tree/test_ast_rewriter.py +++ b/test/syntax_tree/test_ast_rewriter.py @@ -97,7 +97,7 @@ def test( stop_offset: int, content: bytes, expected: tuple[int, int], - ): + ) -> None: """AI: Verify _get_comment_location finds the expected comment span for each comment-style scenario.""" result = ASTRewriter._get_comment_location(start_offset, stop_offset, content) # converted print but what to do it true??? @@ -108,7 +108,7 @@ def test( class TestRewrites: """AI: Base class with shared helpers for ASTRewriter rewrite-action tests.""" - def test_passing_case_in_clang(self): + def test_passing_case_in_clang(self) -> None: """AI: Verify insert_before places replacement text between two adjacent comments in a C declaration.""" # action: Callable[[ASTRewriter, str, Sequence[ASTNode], bool, bool], None], # factory: ASTFactory,code: str, replacement: str, include_whitespace: bool, include_comments: bool, expected: str): @@ -127,7 +127,7 @@ def test_passing_case_in_clang(self): is_("void f() { /* c1 */ int b=4;int c=5;\n /* c2 */ int a=3;\n}"), ) - def test_failing_case(self): + def test_failing_case(self) -> None: """AI: Duplicate of test_passing_case_in_clang, kept as a placeholder for a failing-case scenario.""" # action: Callable[[ASTRewriter, str, Sequence[ASTNode], bool, bool], None], # factory: ASTFactory,code: str, replacement: str, include_whitespace: bool, include_comments: bool, expected: str): @@ -155,7 +155,7 @@ def do_test( include_whitespace: bool, include_comments: bool, expected: str, - ): + ) -> None: """AI: Apply the given rewrite action to the matched declaration and assert the resulting text matches expected.""" atu = factory.create_from_text(code, "test.cpp") pattern_factory = CPatternFactory(factory) @@ -210,7 +210,7 @@ def test( include_whitespace: Any, include_comments: Any, expected: Any, - ): + ) -> None: """AI: Apply ASTRewriter.remove to the matched declaration and assert the resulting text matches expected.""" def reemove(s, _, n, ws, cm): @@ -226,7 +226,7 @@ def reemove(s, _, n, ws, cm): expected, ) - def test_remove_last_node_at_eof_without_trailing_newline(self): + def test_remove_last_node_at_eof_without_trailing_newline(self) -> None: """AI: Removing a final node at EOF should not crash and should keep surrounding content intact.""" factory = PythonFactory(PythonRstNode) atu = factory.create_from_text("import DDXA\nfrom TAUT import TestDoubles", "test.py") @@ -364,7 +364,7 @@ def test( include_whitespace: Any, include_comments: Any, expected: Any, - ): + ) -> None: """AI: Apply ASTRewriter.replace to the matched declaration and assert the resulting text matches expected.""" self.do_test( ASTRewriter.replace, @@ -499,7 +499,7 @@ def test( include_whitespace: Any, include_comments: Any, expected: Any, - ): + ) -> None: """AI: Apply ASTRewriter.insert_before with a single-line insertion and assert the resulting text matches expected.""" self.do_test( ASTRewriter.insert_before, @@ -652,7 +652,7 @@ def test( include_whitespace: Any, include_comments: Any, expected: Any, - ): + ) -> None: """AI: Apply ASTRewriter.insert_before with a multi-line insertion and assert the resulting text matches expected.""" self.do_test( ASTRewriter.insert_before, @@ -787,7 +787,7 @@ def test( include_whitespace: Any, include_comments: Any, expected: Any, - ): + ) -> None: """AI: Apply ASTRewriter.insert_after with a single-line insertion and assert the resulting text matches expected.""" self.do_test( ASTRewriter.insert_after, @@ -940,7 +940,7 @@ def test( include_whitespace: Any, include_comments: Any, expected: Any, - ): + ) -> None: """AI: Apply ASTRewriter.insert_after with a multi-line insertion and assert the resulting text matches expected.""" self.do_test( ASTRewriter.insert_after, @@ -975,7 +975,7 @@ def test_args( statements: Any, extra_declarations: Any, replacement: Any, - ): + ) -> None: """AI: Verify replace rewrites a matched if/else pattern into a ternary assignment using the placeholder bindings.""" code = """ int a = 1; @@ -1007,7 +1007,7 @@ def test_args( actual = rewriter.apply_to_string() assert_that(compress(expected), is_(compress(actual))) - def test_get_node_in_match_pattern(self, mocker): + def test_get_node_in_match_pattern(self, mocker) -> None: """AI: Verify _RewriteAction._get_nodes resolves a node's referenced_by chain back to the original node.""" node = mocker.Mock() reference = mocker.Mock() @@ -1018,13 +1018,13 @@ def test_get_node_in_match_pattern(self, mocker): assert_that(n, is_(node)) @pytest.mark.skip("fail on empty nodes") - def test_get_node_in_match_pattern_on_empty_pattern(self): + def test_get_node_in_match_pattern_on_empty_pattern(self) -> None: """AI: Verify __get_texts raises/returns gracefully when given an empty node list (currently fails).""" it = _RewriteActions([], sys.getfilesystemencoding(), True) text = it._RewriteActions__get_texts([]) assert_that(text, is_("node")) - def test_get_text_from_rewrite(self, mocker): + def test_get_text_from_rewrite(self, mocker) -> None: """AI: Verify __get_texts extracts the node's text slice from its binary file content.""" node = mocker.Mock() node.root = node @@ -1044,7 +1044,7 @@ class TestAroundComposition: @pytest.mark.skip( "TODO: Test fails due to two issues\n 1. order of inserts ([ )]\n 2. insert around whole pattern, not placeholder.", ) - def test_around(self): + def test_around(self) -> None: """AI: Verify two composed insert_before/insert_after pairs around the same placeholder nest correctly (currently fails).""" # set up factory = PythonFactory(PythonRstNode) @@ -1092,7 +1092,7 @@ def setup(self) -> tuple[ASTRewriter, PatternMatch]: return rewriter, match @pytest.mark.skip("TODO: fix impl.") - def test_replace_contained_replace(self): + def test_replace_contained_replace(self) -> None: """AI: Verify replacing the parent then the contained child collapses to the parent's replacement (currently fails).""" rewriter, match = self.setup() rewriter.replace("product", match.nodes) @@ -1100,7 +1100,7 @@ def test_replace_contained_replace(self): assert rewriter.apply_to_string() == "x = product", "Unexpected replacement" @pytest.mark.skip("TODO: fix impl.") - def test_contained_replace_replace(self): + def test_contained_replace_replace(self) -> None: """AI: Verify replacing the contained child then the parent collapses to the parent's replacement (currently fails).""" rewriter, match = self.setup() rewriter.replace("term", match.expansions["$a"]) @@ -1108,7 +1108,7 @@ def test_contained_replace_replace(self): assert rewriter.apply_to_string() == "x = product", "Unexpected replacement" @pytest.mark.skip("TODO: fix impl.") - def test_replace_contained_remove(self): + def test_replace_contained_remove(self) -> None: """AI: Verify replacing the parent then removing a contained child leaves only the parent's replacement (currently fails).""" rewriter, match = self.setup() rewriter.replace("product", match.nodes) @@ -1116,7 +1116,7 @@ def test_replace_contained_remove(self): assert rewriter.apply_to_string() == "x = product", "Unexpected replacement" @pytest.mark.skip("TODO: fix impl.") - def test_contained_remove_replace(self): + def test_contained_remove_replace(self) -> None: """AI: Verify removing a contained child then replacing the parent leaves only the parent's replacement (currently fails).""" rewriter, match = self.setup() rewriter.remove(match.expansions["$a"]) @@ -1124,7 +1124,7 @@ def test_contained_remove_replace(self): assert rewriter.apply_to_string() == "x = product", "Unexpected replacement" @pytest.mark.skip("TODO: fix impl.") - def test_replace_contained_prepend(self): + def test_replace_contained_prepend(self) -> None: """AI: Verify replacing the parent then prepending to a contained child leaves only the parent's replacement (currently fails).""" rewriter, match = self.setup() rewriter.replace("product", match.nodes) @@ -1132,7 +1132,7 @@ def test_replace_contained_prepend(self): assert rewriter.apply_to_string() == "x = product", "Unexpected replacement" @pytest.mark.skip("TODO: fix impl.") - def test_contained_prepend_replace(self): + def test_contained_prepend_replace(self) -> None: """AI: Verify prepending to a contained child then replacing the parent leaves only the parent's replacement (currently fails).""" rewriter, match = self.setup() rewriter.insert_before("term", match.expansions["$a"]) @@ -1140,7 +1140,7 @@ def test_contained_prepend_replace(self): assert rewriter.apply_to_string() == "x = product", "Unexpected replacement" @pytest.mark.skip("TODO: fix impl.") - def test_replace_contained_append(self): + def test_replace_contained_append(self) -> None: """AI: Verify replacing the parent then appending to a contained child leaves only the parent's replacement (currently fails).""" rewriter, match = self.setup() rewriter.replace("product", match.nodes) @@ -1148,7 +1148,7 @@ def test_replace_contained_append(self): assert rewriter.apply_to_string() == "x = product", "Unexpected replacement" @pytest.mark.skip("TODO: fix impl.") - def test_contained_append_replace(self): + def test_contained_append_replace(self) -> None: """AI: Verify appending to a contained child then replacing the parent leaves only the parent's replacement (currently fails).""" rewriter, match = self.setup() rewriter.insert_after("term", match.expansions["$a"]) @@ -1189,7 +1189,7 @@ def f($a,$b,$c): @pytest.mark.skip( "it is not correctly implementing: https://github.com/TNO/Renaissance-Experiments/wiki/Transform-%E2%80%90-AST%E2%80%90aware-changes#scenario-contained-changes", ) - def test_overlapping_replaces(self): + def test_overlapping_replaces(self) -> None: """AI: Verify apply_to_string raises when two replace operations target partially overlapping placeholder ranges.""" rewriter, match = self.setup() placeholder_a = match.expansions["$a"] @@ -1228,7 +1228,7 @@ def setup(self) -> tuple[ASTRewriter, PatternMatch]: return rewriter, match @pytest.mark.skip("TODO: Test fails as prepend of child appears before prepend of parent") - def test_prepend_child_parent(self): + def test_prepend_child_parent(self) -> None: """AI: Verify prepending to the child then the parent nests both insertions in child-before-parent order (currently fails).""" rewriter, match = self.setup() rewriter.insert_before("4 *", match.expansions["$a"]) @@ -1237,7 +1237,7 @@ def test_prepend_child_parent(self): # TODO: Test fails as prepend of child appears before prepend of parent @pytest.mark.skip("TODO: Test fails as prepend of child appears before prepend of parent") - def test_prepend_parent_child(self): + def test_prepend_parent_child(self) -> None: """AI: Verify prepending to the parent then the child nests both insertions in child-before-parent order.""" rewriter, match = self.setup() rewriter.insert_before("6 +", match.nodes) @@ -1245,7 +1245,7 @@ def test_prepend_parent_child(self): assert rewriter.apply_to_string() == "x = 6 + 4 * a * b", "Unexpected replacement" @pytest.mark.skip("TODO: Test fails as prepend of child appears before prepend of parent") - def test_append_child_parent(self): + def test_append_child_parent(self) -> None: """AI: Verify appending to the child then the parent nests both insertions in child-before-parent order.""" rewriter, match = self.setup() rewriter.insert_after("* 4", match.expansions["$b"]) @@ -1253,7 +1253,7 @@ def test_append_child_parent(self): assert rewriter.apply_to_string() == "x = a * b * 4 + 6", "Unexpected replacement" @pytest.mark.skip("TODO: Test fails as prepend of child appears before prepend of parent") - def test_append_parent_child(self): + def test_append_parent_child(self) -> None: """AI: Verify appending to the parent then the child nests both insertions in child-before-parent order (currently fails).""" rewriter, match = self.setup() rewriter.insert_after("+ 6", match.nodes) @@ -1292,7 +1292,7 @@ def setup(self, factory: ASTFactory): return rewriter, match @pytest.mark.skip("TODO: implement accordingly") - def test_first_append_prepend_second(self): + def test_first_append_prepend_second(self) -> None: """AI: Verify appending after the first statement then prepending before the second nests in source order (not yet implemented).""" # setup rewriter, match = self.setup(ASTFactory(ClangASTNode)) @@ -1306,7 +1306,7 @@ def test_first_append_prepend_second(self): @pytest.mark.parametrize("name, factory", Factories.factories) @pytest.mark.skip("TODO: implement accordingly") - def test_prepend_second_first_append(self, name: str, factory: ASTFactory): + def test_prepend_second_first_append(self, name: str, factory: ASTFactory) -> None: """AI: Verify prepend-before-second then append-after-first nests in source order, across factories (not yet implemented).""" # setup rewriter, match = self.setup(factory) diff --git a/test/syntax_tree/test_batch_ast_processor.py b/test/syntax_tree/test_batch_ast_processor.py index 975f6464..b832a676 100644 --- a/test/syntax_tree/test_batch_ast_processor.py +++ b/test/syntax_tree/test_batch_ast_processor.py @@ -9,13 +9,13 @@ class TestBatchASTProcessor: """AI: Tests for the BatchASTProcessor.""" - def test_it(self): + def test_it(self) -> None: """AI: Verify BatchASTProcessor stores its in_memory flag and max_processes count.""" it = BatchASTProcessor(True, 8) assert_that(it.in_memory) assert_that(it.max_processes, is_(8)) - def test_once(self, mocker): + def test_once(self, mocker) -> None: """AI: Verify once() invokes the processor's internal __process method.""" processor = BatchASTProcessor(True, 8) iterable_items = [mocker.Mock()] @@ -24,7 +24,7 @@ def test_once(self, mocker): processor.once(lambda: iterable_items, actions_mock) assert_that(process_method_spy.called) - def test_repeat(self, mocker): + def test_repeat(self, mocker) -> None: """AI: Verify repeat() invokes the processor's internal __process method.""" processor = BatchASTProcessor(True, 8) iterable_items = [mocker.Mock()] @@ -35,7 +35,7 @@ def test_repeat(self, mocker): assert_that(process_method_spy.called) - def test__process(self, mocker): + def test__process(self, mocker) -> None: """AI: Verify the internal __process method invokes process_atu for each item.""" processor = BatchASTProcessor(True, 8) dummy_atu_item = (mocker.Mock(), mocker.Mock()) @@ -45,7 +45,7 @@ def test__process(self, mocker): processor._BatchASTProcessor__process(atu_items, actions_list) assert_that(process_atu_spy.called) - def test_replace_if_in_memory(self, mocker): + def test_replace_if_in_memory(self, mocker) -> None: """AI: Verify _replace_if_in_memory swaps in in-memory file content when registered, and passes through otherwise.""" processor = BatchASTProcessor(True, 8) fake_factory = mocker.Mock() @@ -67,7 +67,7 @@ def test_replace_if_in_memory(self, mocker): assert_that(result_with_in_memory[1], is_(sentinel_atu)) fake_factory.create_from_text.assert_called_with(in_memory_content, fake_node.filename) - def test_process_atu(self, mocker): + def test_process_atu(self, mocker) -> None: """AI: Verify process_atu runs the given action on the ASTProcessor and returns its results.""" processor = BatchASTProcessor(True, 8) diff --git a/test/syntax_tree/test_match_dict.py b/test/syntax_tree/test_match_dict.py index 2758d32d..07b9adde 100644 --- a/test/syntax_tree/test_match_dict.py +++ b/test/syntax_tree/test_match_dict.py @@ -8,25 +8,25 @@ class TestIsMatchDict: """AI: Tests for the is_match_dict dictionary matching helper.""" - def test_is_same_dict(self): + def test_is_same_dict(self) -> None: """AI: Verify two identical dicts match.""" src = {"a": "asd", "b": "zxc"} cmp = {"a": "asd", "b": "zxc"} assert_that(is_match_dict(src, cmp, {})) - def test_is_same_dict_different_key(self): + def test_is_same_dict_different_key(self) -> None: """AI: Verify dicts with a differing key do not match.""" src = {"a": "asd", "b": "zxc"} cmp = {"a": "asd", "c": "zxc"} assert_that(is_match_dict(src, cmp), is_(False)) - def test_is_same_dict_extra_key(self): + def test_is_same_dict_extra_key(self) -> None: """AI: Verify src having an extra key not present in cmp causes a mismatch.""" src = {"a": "asd", "b": "zxc", "extra": "zxc"} cmp = {"a": "asd", "b": "zxc"} assert_that(is_match_dict(src, cmp), is_(False)) - def test_is_same_dict_missing_key(self): + def test_is_same_dict_missing_key(self) -> None: """AI: Verify src missing a key present in cmp causes a mismatch.""" src = {"a": "asd", "b": "zxc"} cmp = {"a": "asd", "b": "zxc", "extra": "zxc"} @@ -38,7 +38,7 @@ def test_is_same_dict_missing_key(self): is_(False), ) - def test_is_same_dict_extra_irelevent_key(self): + def test_is_same_dict_extra_irelevent_key(self) -> None: """AI: Verify an irrelevant 'macro_expansion' key in src is ignored when matching against cmp.""" src = {"a": "asd", "b": "zxc", "macro_expansion": "zxc"} cmp = { @@ -47,7 +47,7 @@ def test_is_same_dict_extra_irelevent_key(self): } assert_that(is_match_dict(src, cmp, {}), is_(True)) - def test_is_same_dict_key_in_expansion(self): + def test_is_same_dict_key_in_expansion(self) -> None: """AI: Verify a placeholder value in cmp matches when it's bound to the corresponding value in the expansion map.""" src = { "a": "asd", @@ -59,7 +59,7 @@ def test_is_same_dict_key_in_expansion(self): } assert_that(is_match_dict(src, cmp, {"$var": ["zxc"]}), is_(True)) - def test_is_same_dict_key_no_expansion(self): + def test_is_same_dict_key_no_expansion(self) -> None: """AI: Verify a placeholder value in cmp matches src when no expansion map is provided.""" src = { "a": "asd", @@ -71,7 +71,7 @@ def test_is_same_dict_key_no_expansion(self): } assert_that(is_match_dict(src, cmp), is_(True)) - def test_is_same_dict_key_in_expansion_with_different_value(self): + def test_is_same_dict_key_in_expansion_with_different_value(self) -> None: """AI: Verify a placeholder value in cmp mismatches when the expansion map binds a different value.""" src = { "a": "asd", @@ -83,7 +83,7 @@ def test_is_same_dict_key_in_expansion_with_different_value(self): } assert_that(is_match_dict(src, cmp, {"$var": "_xc"}), is_(False)) - def test_is_same_dict_key_in_expansion_in_src_should_not_happen(self): + def test_is_same_dict_key_in_expansion_in_src_should_not_happen(self) -> None: """AI: Verify a placeholder value appearing in src (not cmp) does not match a literal value in cmp.""" src = { "a": "asd", diff --git a/test/syntax_tree/test_match_finder.py b/test/syntax_tree/test_match_finder.py index d1304e3e..cb62767e 100644 --- a/test/syntax_tree/test_match_finder.py +++ b/test/syntax_tree/test_match_finder.py @@ -34,7 +34,7 @@ class TestMatchFinder: """AI: Tests for the MatchFinder pattern matching.""" - def test_find_in_tree_one_and_all_params(self): + def test_find_in_tree_one_and_all_params(self) -> None: """AI: Verify find_in_list finds the pattern at position 0 in the full statement list.""" factory = ASTFactory(ClangASTNode, []) patterns = [CPatternFactory(factory).create_statements(statements, extra_declarations=extra_declarations)] @@ -44,7 +44,7 @@ def test_find_in_tree_one_and_all_params(self): found_position = find_in_list(src, patterns[0], {}) assert_that(found_position, is_(0)) - def test_find_in_tree_one_and_all_params_2(self): + def test_find_in_tree_one_and_all_params_2(self) -> None: """AI: Verify find_in_list finds the pattern at position 0 when the list starts one statement later.""" factory = ASTFactory(ClangASTNode, []) patterns = [CPatternFactory(factory).create_statements(statements, extra_declarations=extra_declarations)] @@ -54,7 +54,7 @@ def test_find_in_tree_one_and_all_params_2(self): found_position = find_in_list(src[1:], patterns[0], {}) assert_that(found_position, is_(0)) - def test_find_in_tree_one_and_all_params_3(self): + def test_find_in_tree_one_and_all_params_3(self) -> None: """AI: Verify find_in_list finds the pattern at position 0 when the list starts two statements later.""" factory = ASTFactory(ClangASTNode, []) patterns = [CPatternFactory(factory).create_statements(statements, extra_declarations=extra_declarations)] @@ -64,7 +64,7 @@ def test_find_in_tree_one_and_all_params_3(self): found_position = find_in_list(src[2:], patterns[0], {}) assert_that(found_position, is_(0)) - def test_match_one_and_all_params(self): + def test_match_one_and_all_params(self) -> None: """AI: Verify a single-placeholder-plus-variadic pattern matches all call statements in the sample code.""" factory = ASTFactory(ClangASTNode, []) patterns = [CPatternFactory(factory).create_statements(statements, extra_declarations=extra_declarations)] diff --git a/test/syntax_tree/test_match_finder_multi_assignments.py b/test/syntax_tree/test_match_finder_multi_assignments.py index 7d1b54a1..8f363760 100644 --- a/test/syntax_tree/test_match_finder_multi_assignments.py +++ b/test/syntax_tree/test_match_finder_multi_assignments.py @@ -21,7 +21,7 @@ class TestMatchFinderMultiAssignments: """AI: Tests for MatchFinder matching multiple assignment variants.""" @pytest.mark.skip("not impl. yet") - def test_find_multi_assignments(self): + def test_find_multi_assignments(self) -> None: """AI: Verify find_variants produces two distinct assignment variants for a placeholder call pattern (not yet implemented).""" # set up factory = PythonFactory(PythonRstNode) diff --git a/test/syntax_tree/test_match_tree.py b/test/syntax_tree/test_match_tree.py index dde53dc4..f5d7c6af 100644 --- a/test/syntax_tree/test_match_tree.py +++ b/test/syntax_tree/test_match_tree.py @@ -33,85 +33,85 @@ class TestMatchTree: """AI: Tests for matching AST patterns containing placeholder variants.""" @pytest.fixture(autouse=True) - def setup(self): + def setup(self) -> None: """AI: Build the shared Python factory and pattern factory used by the match-tree tests.""" self.factory = PythonFactory(PythonRstNode) self.pattern_factory = PythonPatternFactory(self.factory) - def test_none_with_none_is_not_allowed(self): + def test_none_with_none_is_not_allowed(self) -> None: """AI: Verify is_match_tree raises when both source and pattern are None.""" assert_that(calling(lambda: is_match_tree(None, None)), raises(Exception)) - def test_none_with_list(self): + def test_none_with_list(self) -> None: """AI: Verify is_match_tree raises when the source is None but the pattern is a list.""" pattern = self.pattern_factory.create_statements("1") assert_that(calling(lambda: is_match_tree(None, pattern)), raises(Exception)) - def test_list_with_none(self): + def test_list_with_none(self) -> None: """AI: Verify is_match_tree returns False when the source is a list but the pattern is None.""" src = self.pattern_factory.create_statements("1") pattern = None assert_that(is_match_tree(src, pattern), is_(False)) - def test_empty_lists_with_empty_pattern(self): + def test_empty_lists_with_empty_pattern(self) -> None: """AI: Verify is_match_tree returns True for two empty lists.""" src = [] pattern = [] assert_that(is_match_tree(src, pattern), is_(True)) - def test_lists_with_empty_pattern(self): + def test_lists_with_empty_pattern(self) -> None: """AI: Verify is_match_tree returns False when the source is non-empty but the pattern is empty.""" src = self.pattern_factory.create_statements("1") pattern = [] assert_that(is_match_tree(src, pattern), is_(False)) - def test_is_match_tree_between_list_and_other(self): + def test_is_match_tree_between_list_and_other(self) -> None: """AI: Verify is_match_tree returns False when the pattern list contains a raw non-node object.""" src = self.pattern_factory.create_statements("1") pattern = ast.Name("name") assert_that(is_match_tree(src, [pattern]), is_(False)) - def test_empty_lists_with_pattern(self): + def test_empty_lists_with_pattern(self) -> None: """AI: Verify is_match_tree returns False when the source is empty but the pattern is non-empty.""" src = [] pattern = self.pattern_factory.create_statements("1") assert_that(is_match_tree(src, pattern), is_(False)) - def test_lists_with_list(self): + def test_lists_with_list(self) -> None: """AI: Verify is_match_tree matches when source and pattern are identical statement lists.""" src = self.pattern_factory.create_statements("1\n2\n3\n4\n5\n6") pattern = self.pattern_factory.create_statements("1\n2\n3\n4\n5\n6") assert_that(is_match_tree(src, pattern), is_(True)) - def test_lists_with_matcher(self): + def test_lists_with_matcher(self) -> None: """AI: Verify is_match_tree matches any statement list against a bare multi-placeholder pattern.""" src = self.pattern_factory.create_statements("1\n2\n3\n4\n5\n6") pattern = self.pattern_factory.create_statements("$$name") assert_that(is_match_tree(src, pattern), is_(True)) - def test_lists_with_list_with_matcher_at_end(self): + def test_lists_with_list_with_matcher_at_end(self) -> None: """AI: Verify is_match_tree matches when a multi-placeholder trails a fixed prefix.""" src = self.pattern_factory.create_statements("1\n2\n3\n4\n5\n6") pattern = self.pattern_factory.create_statements("1\n2\n$$name") assert_that(is_match_tree(src, pattern, {}), is_(True)) - def test_lists_with_list_with_matcher_at_start(self): + def test_lists_with_list_with_matcher_at_start(self) -> None: """AI: Verify is_match_tree matches when a multi-placeholder leads a fixed suffix.""" src = self.pattern_factory.create_statements("1\n2\n3\n4\n5\n6") pattern = self.pattern_factory.create_statements("$$name\n5\n6") assert_that(is_match_tree(src, pattern, {}), is_(True)) - def test_lists_with_list_with_multi_single(self): + def test_lists_with_list_with_multi_single(self) -> None: """AI: Verify a multi-placeholder followed by a single-placeholder captures the expected split of statements.""" src = self.pattern_factory.create_statements("1\n2\n3\n4\n5\n6") pattern = self.pattern_factory.create_statements("$$name\n$name") @@ -122,7 +122,7 @@ def test_lists_with_list_with_multi_single(self): assert_that(exp["$$name"], has_length(5)) assert_that(exp["$name"], has_length(1)) - def test_lists_with_list_with_list_multi_single(self): + def test_lists_with_list_with_list_multi_single(self) -> None: """AI: Verify a fixed prefix, multi-placeholder, then single-placeholder captures the expected split.""" src = self.pattern_factory.create_statements("1\n2\n3\n4\n5\n6") pattern = self.pattern_factory.create_statements("1\n2\n$$name\n$name") @@ -133,70 +133,70 @@ def test_lists_with_list_with_list_multi_single(self): assert_that(exp["$$name"], has_length(3)) assert_that(exp["$name"], has_length(1)) - def test_lists_with_list_with_matcher_in_the_middle(self): + def test_lists_with_list_with_matcher_in_the_middle(self) -> None: """AI: Verify is_match_tree matches when a multi-placeholder sits between a fixed prefix and suffix.""" src = self.pattern_factory.create_statements("1\n2\n3\n4\n5\n6") pattern = self.pattern_factory.create_statements("1\n$$name\n6") assert_that(is_match_tree(src, pattern, {}), is_(True)) - def test_lists_with_list_with_matcher_in_both_end(self): + def test_lists_with_list_with_matcher_in_both_end(self) -> None: """AI: Verify is_match_tree matches when multi-placeholders bracket a fixed middle statement.""" src = self.pattern_factory.create_statements("1\n2\n3\n4\n5\n6") pattern = self.pattern_factory.create_statements("$$start\n3\n$$end") assert_that(is_match_tree(src, pattern, {}), is_(True)) - def test_lists_with_list_with_matcher_in_both_end_empty_list_at_start(self): + def test_lists_with_list_with_matcher_in_both_end_empty_list_at_start(self) -> None: """AI: Verify is_match_tree matches when the leading multi-placeholder captures zero statements.""" src = self.pattern_factory.create_statements("1\n2\n3\n4\n5\n6") pattern = self.pattern_factory.create_statements("$$start\n1\n$$end") assert_that(is_match_tree(src, pattern, {}), is_(True)) - def test_lists_with_list_with_matcher_in_both_end_empty_list_at_the_end(self): + def test_lists_with_list_with_matcher_in_both_end_empty_list_at_the_end(self) -> None: """AI: Verify is_match_tree matches when the trailing multi-placeholder captures zero statements.""" src = self.pattern_factory.create_statements("1\n2\n3\n4\n5\n6") pattern = self.pattern_factory.create_statements("$$start\n6\n$$end") assert_that(is_match_tree(src, pattern, {}), is_(True)) - def test_lists_with_list_with_matcher_in_both_end__mismatch(self): + def test_lists_with_list_with_matcher_in_both_end__mismatch(self) -> None: """AI: Verify is_match_tree returns False when two same-named multi-placeholders can't reconcile a mismatched split.""" src = self.pattern_factory.create_statements("1\n2\n3\n4\n5\n61\n2\n3\n4\n5\n6") pattern = self.pattern_factory.create_statements("$$seq\n61\n$$seq") assert_that(is_match_tree(src, pattern, {}), is_(False)) - def test_lists_with_list_with_matcher_in_both_end_same_pattern(self): + def test_lists_with_list_with_matcher_in_both_end_same_pattern(self) -> None: """AI: Verify is_match_tree returns False when the source lacks the trailing statement required by the pattern.""" src = self.pattern_factory.create_statements("1\n2\n3\n4\n5\n61\n2\n3\n4\n5") pattern = self.pattern_factory.create_statements("$$seq\n61\n$$seq") assert_that(is_match_tree(src, pattern, {}), is_(False)) - def test_lists_with_list_with_matcher_in_matcher_in_between(self): + def test_lists_with_list_with_matcher_in_matcher_in_between(self) -> None: """AI: Verify is_match_tree matches a same-named multi-placeholder pair sandwiched between fixed statements.""" src = self.pattern_factory.create_statements("2\n3\n4\n5\n61\n2\n3\n4\n5\n7\n8\n9") pattern = self.pattern_factory.create_statements("$$seq\n61\n$$seq\n7\n8\n9") assert_that(is_match_tree(src, pattern, {}), is_(True)) - def test_lists_with_list_with_matcher_in_matcher_in_between_but_has_leftover(self): + def test_lists_with_list_with_matcher_in_matcher_in_between_but_has_leftover(self) -> None: """AI: Verify is_match_tree returns False when trailing source statements are left unmatched after the pattern.""" src = self.pattern_factory.create_statements("2\n3\n4\n5\n61\n2\n3\n4\n5\n7\n8\n9") pattern = self.pattern_factory.create_statements("$$seq\n61\n$$seq") assert_that(is_match_tree(src, pattern, {}), is_(False)) - def test_find_in_list(self): + def test_find_in_list(self) -> None: """AI: Verify find_in_list returns the index of the first matching statement.""" src = self.pattern_factory.create_statements("2\n3\n4\n5\n61\n2\n3\n4\n5\n7\n8\n9") pattern = self.pattern_factory.create_statements("2") assert_that(find_in_list(src, pattern, {}), is_(0)) - def test_find_in_list_with_expansion(self): + def test_find_in_list_with_expansion(self) -> None: """AI: Verify find_in_list returns the match index and populates the expansion dict for a single-placeholder.""" src = self.pattern_factory.create_statements("2\n3\n4\n5\n61\n2\n3\n4\n5\n7\n8\n9") pattern = self.pattern_factory.create_statements("2\n$3\n4") @@ -205,48 +205,48 @@ def test_find_in_list_with_expansion(self): assert_that(find_in_list(src, pattern, exp), is_(2)) assert_that(exp["$3"][0].name, is_("3")) - def test_can_t_find_in_list(self): + def test_can_t_find_in_list(self) -> None: """AI: Verify find_in_list returns a negative index when the pattern has no match.""" src = self.pattern_factory.create_statements("2\n3\n4\n5\n61\n2\n3\n4\n5\n7\n8\n9") pattern = self.pattern_factory.create_statements("1") assert_that(find_in_list(src, pattern, {}), less_than(0)) - def test_find_in_list_returns_last_pos(self): + def test_find_in_list_returns_last_pos(self) -> None: """AI: Verify find_in_list returns the last valid index when the pattern matches at the end of the list.""" src = self.pattern_factory.create_statements("0\n1\n2\n3\n4\n5\n61\n2\n3\n4\n5\n7\n8\n9") pattern = self.pattern_factory.create_statements("0\n1\n2\n3\n4\n5") assert_that(find_in_list(src, pattern, {}), is_(5)) - def test_find_with_match_all_returns_last_pos(self): + def test_find_with_match_all_returns_last_pos(self) -> None: """AI: Verify find_in_list returns the last index when the pattern ends with a trailing multi-placeholder.""" src = self.pattern_factory.create_statements("0\n1\n2\n3\n4\n5\n61\n2\n3\n4\n5\n7\n8\n9") pattern = self.pattern_factory.create_statements("0\n1\n2\n3\n4\n5\n$$seq") assert_that(find_in_list(src, pattern, {}), is_(len(src) - 1)) - def test_lists_with_list_with_matcher_in_both_end_mismatch2(self): + def test_lists_with_list_with_matcher_in_both_end_mismatch2(self) -> None: """AI: Verify is_match_tree returns False when the source is one statement shorter than the pattern requires.""" src = self.pattern_factory.create_statements("1\n2\n3\n4\n5\n61\n2\n3\n4\n5") pattern = self.pattern_factory.create_statements("$$seq\n61\n$$seq") assert_that(is_match_tree(src, pattern, {}), is_(False)) - def test_find_function_with_any_param_python(self): + def test_find_function_with_any_param_python(self) -> None: """AI: Verify find_in_list locates a Python call statement against a variadic-argument pattern.""" atu = self.factory.create_from_text("ca(13,14,15)", "test.py") src = atu.children pattern = self.pattern_factory.create_statements("ca($$all)") assert_that(find_in_list(src, pattern, {}), is_(0)) - def test_find_function_with_any_param_and_all_param_in_python(self): + def test_find_function_with_any_param_and_all_param_in_python(self) -> None: """AI: Verify find_in_list locates a Python call statement matched by a single-plus-variadic argument pattern.""" atu = self.factory.create_from_text("ca(13,14,15)", "test.py") src = atu.children pattern = self.pattern_factory.create_statements("$f($a,$$all)") assert_that(find_in_list(src, pattern, {}), is_(0)) - def test_match_all_function_with_any_param_clang(self): + def test_match_all_function_with_any_param_clang(self) -> None: """AI: Verify match_pattern finds both matching C function calls against a variadic-argument pattern.""" factory = ASTFactory(ClangASTNode, []) atu = factory.create_from_text("void ca(int a,int b,int c){ca(13,14,15); ca(13,14,15);}", "fut.c") @@ -254,7 +254,7 @@ def test_match_all_function_with_any_param_clang(self): pattern = factory.create_from_text("int $a,$$all;void $f(int a,int b){$f($a, $$all);}", "pat.c").children[-1].children[-1].children assert_that(match_pattern(src, pattern), has_length(2)) - def test_find_all_in_list_with_expansion(self): + def test_find_all_in_list_with_expansion(self) -> None: """AI: Verify match_pattern finds all matches and captures the single-placeholder expansion for each.""" src = self.pattern_factory.create_statements("2\n3\n4\n5\n61\n2\n3\n4\n5\n7\n8\n9") pattern = self.pattern_factory.create_statements("2\n$3\n4") @@ -262,7 +262,7 @@ def test_find_all_in_list_with_expansion(self): assert_that(matches, has_length(2)) assert_that(matches[0].expansions["$3"][0].name, is_("3")) - def test_find_all_in_python_list_with_expansion(self): + def test_find_all_in_python_list_with_expansion(self) -> None: """AI: Verify match_pattern finds a TestCase subclass and captures its class-name expansion.""" atu = self.factory.create_from_text( textwrap.dedent(""" @@ -290,7 +290,7 @@ def test_case_example(self): assert_that(matches, has_length(1)) assert_that(matches[0].expansions["$name"][0], is_("TestExample")) - def test_find_all_in_python_arg_list_with_expansion1(self): + def test_find_all_in_python_arg_list_with_expansion1(self) -> None: """AI: Verify match_pattern finds a call matched by a bare variadic-argument pattern and captures a non-empty expansion.""" self.factory.create_from_text("class klass: pass", "test_file.py") statement = self.pattern_factory.create_statements("assertEqual(1,2,34,5,6,7,7,8)") @@ -299,7 +299,7 @@ def test_find_all_in_python_arg_list_with_expansion1(self): assert_that(matches, has_length(1)) assert_that(matches[0].expansions["$$args"], is_not(empty())) - def test_find_all_in_python_arg_list_with_expansion2(self): + def test_find_all_in_python_arg_list_with_expansion2(self) -> None: """AI: Verify match_pattern finds a function def matched by a variadic-parameter pattern and captures a non-empty expansion.""" atu = self.factory.create_from_text("class klass:\n def fun(a,b,c,d,f): pass", "test_file.py") pattern = self.pattern_factory.create_statements("def fun($$args): pass") @@ -307,7 +307,7 @@ def test_find_all_in_python_arg_list_with_expansion2(self): assert_that(matches, has_length(1)) assert_that(matches[0].expansions["$$args"], is_not(empty())) - def test_find_all_in_clang_list_with_expansion(self): + def test_find_all_in_clang_list_with_expansion(self) -> None: """AI: Verify match_pattern finds both matching C comparisons and captures a non-empty expansion for the first.""" factory = ASTFactory(ClangASTNode, []) pattern = CPatternFactory(factory).create_statements("a == $x;") @@ -317,7 +317,7 @@ def test_find_all_in_clang_list_with_expansion(self): assert_that(matches[0].expansions["$x"], is_not(empty())) @pytest.mark.skip - def test_match_one_and_all_params(self): + def test_match_one_and_all_params(self) -> None: """AI: Verify MatchFinder.match_pattern finds a keyword-argument call matched by a single kwarg pattern.""" sample = textwrap.dedent(""" context_stub=0 @@ -334,7 +334,7 @@ def setUp(self): matches = MatchFinder.match_pattern(atu.children, kwargs) assert_that(matches, has_length(1)) - def test_match_pattern_for_parameterized_finds_one_match1(self): + def test_match_pattern_for_parameterized_finds_one_match1(self) -> None: """AI: Verify match_pattern finds a single @parameterized.expand-decorated test method.""" code = textwrap.dedent(""" from parameterized import parameterized @@ -350,7 +350,7 @@ def test_definition_declaration_references(self, _, factory, code, *args): found = match_pattern(atu.children, unittest) assert_that(found, has_length(1)) - def test_match_pattern_for_parameterized_finds_one_match2(self): + def test_match_pattern_for_parameterized_finds_one_match2(self) -> None: """AI: Verify match_pattern finds one match even when the result is materialized via list().""" code = textwrap.dedent(""" from parameterized import parameterized diff --git a/test/syntax_tree/test_node_finder.py b/test/syntax_tree/test_node_finder.py index 8fd890d5..8b145dee 100644 --- a/test/syntax_tree/test_node_finder.py +++ b/test/syntax_tree/test_node_finder.py @@ -5,7 +5,7 @@ from renaissance.syntax_tree.semantic_kind import SemanticKind -def test_find_nodes_accepts_protocol_predicate(): +def test_find_nodes_accepts_protocol_predicate() -> None: """AI: Assert find_nodes locates nodes matching an arbitrary NodeProtocol predicate.""" root = PythonRstNode.load_from_text("def f():\n return 1\n") diff --git a/test/syntax_tree/test_node_protocol.py b/test/syntax_tree/test_node_protocol.py index dbe5ec6c..86d61482 100644 --- a/test/syntax_tree/test_node_protocol.py +++ b/test/syntax_tree/test_node_protocol.py @@ -16,12 +16,12 @@ class FakeNode: name = "" -def test_structural_node_satisfies_protocol(): +def test_structural_node_satisfies_protocol() -> None: """AI: Assert a duck-typed structural node satisfies NodeProtocol via isinstance.""" assert isinstance(FakeNode(), NodeProtocol) -def test_matcher_prefers_semantic_kind_over_legacy_type(): +def test_matcher_prefers_semantic_kind_over_legacy_type() -> None: """AI: Assert is_match prefers matching by semantic_kind over the legacy parser_kind field.""" source = FakeNode() pattern = FakeNode() diff --git a/test/syntax_tree/test_pattern_kind.py b/test/syntax_tree/test_pattern_kind.py index 2f70436b..d5fa475e 100644 --- a/test/syntax_tree/test_pattern_kind.py +++ b/test/syntax_tree/test_pattern_kind.py @@ -3,6 +3,6 @@ from renaissance.syntax_tree.pattern_kind import PatternKind -def test_pattern_kind_values_are_distinct(): +def test_pattern_kind_values_are_distinct() -> None: """AI: Assert distinct PatternKind enum members compare unequal.""" assert PatternKind.MATCH_ONE != PatternKind.MATCH_ALL diff --git a/test/syntax_tree/test_pattern_match.py b/test/syntax_tree/test_pattern_match.py index b0e97230..dd8fa3ec 100644 --- a/test/syntax_tree/test_pattern_match.py +++ b/test/syntax_tree/test_pattern_match.py @@ -16,13 +16,13 @@ class TestPatternMatch: """AI: Tests for the PatternMatch container returned by pattern matching.""" @pytest.fixture(autouse=True) - def setup(self): + def setup(self) -> None: """AI: Build the shared Python factory and pattern factory used by the pattern-match tests.""" self.factory = PythonFactory(PythonRstNode) self.pattern_factory = PythonPatternFactory(self.factory) @pytest.mark.skip("length on empty node") - def test_empty_expansion_has_offset(self): + def test_empty_expansion_has_offset(self) -> None: """AI: Verify an empty ($$empty) expansion reports an empty value, no expansions, and the correct offset/length.""" example_code = textwrap.dedent(""" 1 @@ -43,7 +43,7 @@ def test_empty_expansion_has_offset(self): assert_that(match.offset_of("$$empty"), is_(5)) assert_that(match.length_of("$$empty"), is_(0)) - def test_single_expansion_has_offset(self): + def test_single_expansion_has_offset(self) -> None: """AI: Verify a single-value ($3) expansion reports its value and correct offset/length.""" example_code = textwrap.dedent(""" 1 @@ -64,7 +64,7 @@ def test_single_expansion_has_offset(self): assert_that(match.offset_of("$3"), is_(5)) assert_that(match.length_of("$3"), is_(1)) - def test_multi_expansion_has_offset(self): + def test_multi_expansion_has_offset(self) -> None: """AI: Verify a multi-value ($$other) expansion reports its joined value and correct offset/length.""" example_code = textwrap.dedent(""" 1 @@ -85,7 +85,7 @@ def test_multi_expansion_has_offset(self): assert_that(match.offset_of("$$other"), is_(3)) assert_that(match.length_of("$$other"), is_(7)) - def test_match_referenced_by(self, mocker): + def test_match_referenced_by(self, mocker) -> None: """AI: Verify match_referenced_by calls match_pattern once per referencing node.""" node = mocker.Mock() reference = mocker.Mock() @@ -99,7 +99,7 @@ def test_match_referenced_by(self, mocker): pattern_match.match_referenced_by([[node]], False) assert_that(mock_matcher.call_count, is_(6)) - def test_get_key_redirect_to_expansion_signature(self, mocker): + def test_get_key_redirect_to_expansion_signature(self, mocker) -> None: """AI: Verify __getitem__ resolves plain keys, placeholder node signatures, and missing keys to an empty string.""" node = mocker.Mock() node.signature = "name_1" diff --git a/test/syntax_tree/test_recipe_ast_processor.py b/test/syntax_tree/test_recipe_ast_processor.py index 5a37f973..2860c7ac 100644 --- a/test/syntax_tree/test_recipe_ast_processor.py +++ b/test/syntax_tree/test_recipe_ast_processor.py @@ -15,28 +15,28 @@ class TestRecipeASTProcessor: """AI: Tests for the RecipeASTProcessor and its decorator helpers.""" - def test_receipe_proc(self): + def test_receipe_proc(self) -> None: """AI: Verify RecipeASTProcessor can be constructed from a recipe, iterable provider, and path.""" it = RecipeASTProcessor(lambda n: n, lambda: (), "") assert_that(it, is_(RecipeASTProcessor)) - def test_run(self, mocker): + def test_run(self, mocker) -> None: """AI: Verify run() invokes each recipe_step-decorated method via BatchASTProcessor.repeat.""" # define a simple recipe class with one recipe_step class SimpleRecipe: - def __init__(self): + def __init__(self) -> None: self.ran = [] @recipe_step(order=0) def do_step(self, _): - def work(): + def work() -> None: self.ran.append("done") return work # patch BatchASTProcessor.repeat to immediately invoke actions with a dummy ASTProcessor - def fake_repeat(_, _1, actions, _2): + def fake_repeat(_, _1, actions, _2) -> None: dummy = mocker.Mock() dummy.repeat_step = 0 for action in actions: @@ -52,7 +52,7 @@ def fake_repeat(_, _1, actions, _2): assert_that(recipe.ran, is_(["done"])) - def test_annotate_decorator(self): + def test_annotate_decorator(self) -> None: """AI: Verify annotate_decorator preserves the wrapped decorator's name and attaches a recipe_action name.""" def foreign(f): @@ -64,29 +64,29 @@ def foreign(f): # when applied to a function, the decorator attaches the recipe_action name @decorator - def sample(): + def sample() -> int: return 1 assert_that(sample.recipe_action, is_("test_decorator")) - def test_get_methods_with_decorator(self): + def test_get_methods_with_decorator(self) -> None: """AI: Verify get_methods_with_decorator finds methods decorated with @recipe_step.""" class Sample: @recipe_step() - def step1(self): + def step1(self) -> None: pass methods = list(get_methods_with_decorator(Sample, recipe_step)) assert_that(methods, has_length(1)) assert_that(methods[0].__name__, is_("step1")) - def test_final_action(self): + def test_final_action(self) -> None: """AI: Verify get_methods_with_decorator finds methods decorated with @final_action.""" class Sample: @final_action() - def final(self): + def final(self) -> None: pass methods = list(get_methods_with_decorator(Sample, final_action)) diff --git a/test/syntax_tree/test_removed_legacy_api.py b/test/syntax_tree/test_removed_legacy_api.py index add49044..0dbdecb4 100644 --- a/test/syntax_tree/test_removed_legacy_api.py +++ b/test/syntax_tree/test_removed_legacy_api.py @@ -5,7 +5,7 @@ import pytest -def test_legacy_type_module_is_removed(): +def test_legacy_type_module_is_removed() -> None: """AI: Assert the removed legacy renaissance.integrations.types module can no longer be imported.""" with pytest.raises(ModuleNotFoundError): importlib.import_module("renaissance.integrations.types") diff --git a/test/syntax_tree/test_semantic_finder.py b/test/syntax_tree/test_semantic_finder.py index eb0d7f4d..12fb9e8b 100644 --- a/test/syntax_tree/test_semantic_finder.py +++ b/test/syntax_tree/test_semantic_finder.py @@ -5,7 +5,7 @@ from renaissance.syntax_tree.semantic_kind import SemanticKind -def test_find_semantic_kind_uses_protocol_metadata(): +def test_find_semantic_kind_uses_protocol_metadata() -> None: """AI: Assert find_semantic_kind locates nodes by their semantic kind via protocol metadata.""" root = PythonRstNode.load_from_text("def f():\n return 1\n") diff --git a/test/syntax_tree/test_text_segment.py b/test/syntax_tree/test_text_segment.py index a5dbbd79..dfa6a871 100644 --- a/test/syntax_tree/test_text_segment.py +++ b/test/syntax_tree/test_text_segment.py @@ -115,47 +115,47 @@ class BadTypesButProtocolLike: """ @property - def full_text(self): # not str + def full_text(self) -> int: # not str """AI: Return a value of the wrong type (int) for full_text, to trigger a validation failure.""" return 123 @property - def location(self): # not str + def location(self) -> None: # not str """AI: Return a value of the wrong type (None) for location, to trigger a validation failure.""" return None @property - def start_line(self): # not int + def start_line(self) -> str: # not int """AI: Return a value of the wrong type (str) for start_line, to trigger a validation failure.""" return "hello" @property - def start_column(self): # not int + def start_column(self) -> str: # not int """AI: Return a value of the wrong type (str) for start_column, to trigger a validation failure.""" return "hello" @property - def start_offset(self): # not int + def start_offset(self) -> str: # not int """AI: Return a value of the wrong type (str) for start_offset, to trigger a validation failure.""" return "hello" @property - def end_line(self): # not int + def end_line(self) -> str: # not int """AI: Return a value of the wrong type (str) for end_line, to trigger a validation failure.""" return "hello" @property - def end_column(self): # not int + def end_column(self) -> str: # not int """AI: Return a value of the wrong type (str) for end_column, to trigger a validation failure.""" return "hello" @property - def end_offset(self): # not int + def end_offset(self) -> str: # not int """AI: Return a value of the wrong type (str) for end_offset, to trigger a validation failure.""" return "hello" @property - def text_segment(self): # not str + def text_segment(self) -> int: # not str """AI: Return a value of the wrong type (int) for text_segment, to trigger a validation failure.""" return 456 diff --git a/test/test_pyproject_config.py b/test/test_pyproject_config.py index 7daf85de..7acb19f6 100644 --- a/test/test_pyproject_config.py +++ b/test/test_pyproject_config.py @@ -19,7 +19,7 @@ def _load_pyproject() -> dict: return tomllib.load(f) -def test_source_roots_are_consistent_across_tool_configs(): +def test_source_roots_are_consistent_across_tool_configs() -> None: """AI: Assert the source-root list is identical across pytest, ruff, and pyright tool configs.""" config = _load_pyproject() pytest_pythonpath = config["tool"]["pytest"]["ini_options"]["pythonpath"] diff --git a/test/tools/test_lint_budget.py b/test/tools/test_lint_budget.py index acb02a90..2284a44f 100644 --- a/test/tools/test_lint_budget.py +++ b/test/tools/test_lint_budget.py @@ -33,17 +33,17 @@ def budget_file(tmp_path, monkeypatch): return path -def _stub_counts(monkeypatch, ruff: dict[str, int], pyright: dict[str, int]): +def _stub_counts(monkeypatch, ruff: dict[str, int], pyright: dict[str, int]) -> None: """AI: Replace the ruff and pyright runs by fixed issue counts.""" monkeypatch.setattr(lint_budget, "COUNTERS", {"ruff": lambda: Counter(ruff), "pyright": lambda: Counter(pyright)}) -def test_unchanged_counts_are_within_budget(): +def test_unchanged_counts_are_within_budget() -> None: """AI: Assert equal counts are neither exceeded nor improved.""" assert lint_budget.compare("ruff", Counter({"E501": 3}), {"E501": 3}) == ([], []) -def test_more_issues_of_a_budgeted_kind_exceed_the_budget(): +def test_more_issues_of_a_budgeted_kind_exceed_the_budget() -> None: """AI: Assert an extra issue of an existing kind is reported as exceeding its budget.""" exceeded, improved = lint_budget.compare("ruff", Counter({"E501": 4}), {"E501": 3}) assert len(exceeded) == 1 @@ -51,7 +51,7 @@ def test_more_issues_of_a_budgeted_kind_exceed_the_budget(): assert improved == [] -def test_a_new_kind_of_issue_exceeds_its_zero_budget(): +def test_a_new_kind_of_issue_exceeds_its_zero_budget() -> None: """AI: Assert a kind without a budget entry may not occur at all.""" exceeded, improved = lint_budget.compare("ruff", Counter({"E501": 3, "D100": 1}), {"E501": 3}) assert len(exceeded) == 1 @@ -59,14 +59,14 @@ def test_a_new_kind_of_issue_exceeds_its_zero_budget(): assert improved == [] -def test_fewer_issues_are_reported_as_improvement(): +def test_fewer_issues_are_reported_as_improvement() -> None: """AI: Assert a reduced count is reported as an improvement rather than a failure.""" exceeded, improved = lint_budget.compare("ruff", Counter({"E501": 1}), {"E501": 3, "D100": 2}) assert exceeded == [] assert len(improved) == 2 -def test_check_fails_when_a_new_issue_is_introduced(budget_file, monkeypatch): +def test_check_fails_when_a_new_issue_is_introduced(budget_file, monkeypatch) -> None: """AI: Assert the gate fails and keeps the budget when a pull request adds an issue.""" budget_file.write_text(json.dumps({"ruff": {"E501": 1}, "pyright": {}}), encoding="utf-8") _stub_counts(monkeypatch, ruff={"E501": 2}, pyright={}) @@ -75,7 +75,7 @@ def test_check_fails_when_a_new_issue_is_introduced(budget_file, monkeypatch): assert json.loads(budget_file.read_text(encoding="utf-8"))["ruff"] == {"E501": 1} -def test_reduced_counts_are_adopted_as_the_new_budget(budget_file, monkeypatch): +def test_reduced_counts_are_adopted_as_the_new_budget(budget_file, monkeypatch) -> None: """AI: Assert the budget ratchets down to the reduced counts.""" budget_file.write_text(json.dumps({"ruff": {"E501": 5}, "pyright": {"reportUnusedVariable": 2}}), encoding="utf-8") _stub_counts(monkeypatch, ruff={"E501": 3}, pyright={"reportUnusedVariable": 2}) @@ -84,7 +84,7 @@ def test_reduced_counts_are_adopted_as_the_new_budget(budget_file, monkeypatch): assert json.loads(budget_file.read_text(encoding="utf-8")) == {"ruff": {"E501": 3}, "pyright": {"reportUnusedVariable": 2}} -def test_check_mode_reports_a_stale_budget_without_writing(budget_file, monkeypatch): +def test_check_mode_reports_a_stale_budget_without_writing(budget_file, monkeypatch) -> None: """AI: Assert CI mode never writes the budget file and asks for the reduction to be committed.""" budget_file.write_text(json.dumps({"ruff": {"E501": 5}, "pyright": {}}), encoding="utf-8") _stub_counts(monkeypatch, ruff={"E501": 3}, pyright={}) @@ -93,7 +93,7 @@ def test_check_mode_reports_a_stale_budget_without_writing(budget_file, monkeypa assert json.loads(budget_file.read_text(encoding="utf-8"))["ruff"] == {"E501": 5} -def test_removal_is_advised_when_no_issues_are_left(budget_file, monkeypatch, capsys): +def test_removal_is_advised_when_no_issues_are_left(budget_file, monkeypatch, capsys) -> None: """AI: Assert the user is told the budget functionality can be removed once all issues are gone.""" budget_file.write_text(json.dumps({"ruff": {"E501": 1}, "pyright": {}}), encoding="utf-8") _stub_counts(monkeypatch, ruff={}, pyright={}) diff --git a/test/tree_sitter/test_tree_sitter_structural_matcher.py b/test/tree_sitter/test_tree_sitter_structural_matcher.py index b503362c..b9eb6208 100644 --- a/test/tree_sitter/test_tree_sitter_structural_matcher.py +++ b/test/tree_sitter/test_tree_sitter_structural_matcher.py @@ -37,7 +37,7 @@ class TestTreeSitterStructuralMatcher: ("import os", "import $os"), ], ) - def test_python_patterns(self, code, pattern): + def test_python_patterns(self, code, pattern) -> None: """AI: Assert a placeholder Python pattern matches its corresponding concrete code snippet.""" adapter = TreeSitterAdapter(tspython) ast = adapter.parse_code(code) @@ -81,7 +81,7 @@ def test_python_patterns(self, code, pattern): ("foo();", "$foo();"), ], ) - def test_cpp_patterns(self, code, pattern): + def test_cpp_patterns(self, code, pattern) -> None: """AI: Assert a placeholder C++ pattern matches its corresponding concrete code snippet.""" adapter = TreeSitterAdapter(tscpp) ast = adapter.parse_code(code) diff --git a/test/utils/test_text_utils.py b/test/utils/test_text_utils.py index 61b560b8..e40be8a1 100644 --- a/test/utils/test_text_utils.py +++ b/test/utils/test_text_utils.py @@ -24,6 +24,6 @@ class TestSnakeCase: ("TestSnakeCase", "test_snake_case"), ], ) - def test_snake_case(self, input_str, expected): + def test_snake_case(self, input_str, expected) -> None: """AI: Assert snake_case converts camelCase/PascalCase strings to snake_case, preserving acronyms.""" assert_that(snake_case(input_str), is_(expected)) diff --git a/test/utils_for_tests.py b/test/utils_for_tests.py index 3c9d0bd8..25172df0 100644 --- a/test/utils_for_tests.py +++ b/test/utils_for_tests.py @@ -24,7 +24,7 @@ def compress(s: str): return skip_whitespace.strip() -def show_node(node: ASTNode, title: str = ""): +def show_node(node: ASTNode, title: str = "") -> None: """AI: Print an ASTNode's structure via ASTShower when VERBOSE mode is enabled.""" if VERBOSE: if title: @@ -32,7 +32,7 @@ def show_node(node: ASTNode, title: str = ""): ASTShower.show_node(node) -def debug_mismatch(debug_mismatches, atu, patterns: list[ASTNode], matches: list[PatternMatch]): +def debug_mismatch(debug_mismatches, atu, patterns: list[ASTNode], matches: list[PatternMatch]) -> None: """AI: Print details of mismatched pattern matches for debugging when debug_mismatches is enabled.""" if debug_mismatches: for idx, pattern in enumerate(patterns): @@ -59,7 +59,7 @@ def debug_print( expected_result: ASTNode, include_comments: bool, include_whitespace: bool, -): +) -> None: """AI: Print original, expected, and actual AST/text output for debugging test comparisons.""" if AST_SHOWER: print("Original:")