From 24d7230acf9ee0457e412497491ad97187fdab7a Mon Sep 17 00:00:00 2001 From: Tristan Manchester Date: Fri, 17 Apr 2026 09:21:50 +0200 Subject: [PATCH] Ignore TypeScript comments in syntax scan --- .../typescript/fixers/syntax_scan.py | 68 ++++++++++++++----- .../typescript/tests/test_ts_fixers.py | 52 +++++++++++++- 2 files changed, 101 insertions(+), 19 deletions(-) diff --git a/desloppify/languages/typescript/fixers/syntax_scan.py b/desloppify/languages/typescript/fixers/syntax_scan.py index f6902275c..244beff87 100644 --- a/desloppify/languages/typescript/fixers/syntax_scan.py +++ b/desloppify/languages/typescript/fixers/syntax_scan.py @@ -2,7 +2,10 @@ from __future__ import annotations -from desloppify.languages.typescript.detectors.smells.helpers import scan_code +from desloppify.languages.typescript.detectors.smells.helpers import ( + _strip_ts_comments, + scan_code, +) _CHAR_DEPTH_DELTA: dict[str, tuple[str, int]] = { "(": ("parens", 1), @@ -14,33 +17,62 @@ } +def _iter_code_chars( + lines: list[str], start: int, stop: int +) -> tuple[int, str, bool]: + in_block_comment = False + for idx in range(start, stop): + line = lines[idx] + if in_block_comment: + close = line.find("*/") + if close == -1: + continue + line = line[close + 2 :] + in_block_comment = False + + while True: + block_start = line.find("/*") + if block_start == -1: + break + block_end = line.find("*/", block_start + 2) + if block_end == -1: + line = line[:block_start] + in_block_comment = True + break + line = line[:block_start] + line[block_end + 2 :] + + for _, ch, in_s in scan_code(_strip_ts_comments(line)): + yield idx, ch, in_s + + def find_balanced_end( lines: list[str], start: int, *, track: str = "parens", max_lines: int = 80 ) -> int | None: """Find the line where brackets opened at *start* balance to zero.""" depths = {"parens": 0, "braces": 0, "brackets": 0} - for idx in range(start, min(start + max_lines, len(lines))): - for _, ch, in_s in scan_code(lines[idx]): - if in_s: - continue - delta_spec = _CHAR_DEPTH_DELTA.get(ch) - if delta_spec is None: - continue - key, delta = delta_spec - depths[key] += delta - if delta > 0: - continue - if track == "parens" and key == "parens" and depths["parens"] <= 0: - return idx - if track == "braces" and key == "braces" and depths["braces"] <= 0: - return idx - if track == "all" and key == "parens" and depths["parens"] <= 0: - return idx + stop = min(start + max_lines, len(lines)) + for idx, ch, in_s in _iter_code_chars(lines, start, stop): + if in_s: + continue + delta_spec = _CHAR_DEPTH_DELTA.get(ch) + if delta_spec is None: + continue + key, delta = delta_spec + depths[key] += delta + if delta > 0: + continue + if track == "parens" and key == "parens" and depths["parens"] <= 0: + return idx + if track == "braces" and key == "braces" and depths["braces"] <= 0: + return idx + if track == "all" and key == "parens" and depths["parens"] <= 0: + return idx return None def extract_body_between_braces(text: str, search_after: str = "") -> str | None: """Extract content between the first ``{`` and its matching ``}``.""" + text = _strip_ts_comments(text) start_pos = 0 if search_after: pos = text.find(search_after) diff --git a/desloppify/languages/typescript/tests/test_ts_fixers.py b/desloppify/languages/typescript/tests/test_ts_fixers.py index c5145b804..5f85e1812 100644 --- a/desloppify/languages/typescript/tests/test_ts_fixers.py +++ b/desloppify/languages/typescript/tests/test_ts_fixers.py @@ -96,6 +96,25 @@ def test_returns_none_when_unbalanced(self): lines = ["foo(\n", " bar\n"] assert find_balanced_end(lines, 0, track="parens") is None + def test_ignores_closing_parens_in_line_comment(self): + """Line comments must not terminate a multiline call early.""" + lines = [ + "console.log( // ))\n", + " '[DEBUG] value',\n", + " someVar,\n", + ");\n", + ] + assert find_balanced_end(lines, 0, track="parens") == 3 + + def test_ignores_closing_braces_in_block_comment(self): + """Block comments must not terminate brace tracking early.""" + lines = [ + "if (x) { /* }} */\n", + " return 1;\n", + "}\n", + ] + assert find_balanced_end(lines, 0, track="braces") == 2 + class TestCommonExtractBody: """Tests for extract_body_between_braces().""" @@ -127,6 +146,13 @@ def test_search_after_not_found_returns_none(self): """Returns None if search_after marker not found.""" assert extract_body_between_braces("no marker", search_after="=>") is None + def test_ignores_comment_braces_when_extracting_body(self): + """Comment braces must not truncate the extracted body.""" + text = "const f = () => { /* } */ return 42; }" + body = extract_body_between_braces(text, search_after="=>") + assert body is not None + assert "return 42;" in body + class TestCommonCollapseBlankLines: """Tests for collapse_blank_lines().""" @@ -495,6 +521,31 @@ def test_remove_multiline_log(self, tmp_path): assert "console.log" not in content assert "return 1;" in content + def test_remove_multiline_log_ignores_comment_delimiters(self, tmp_path): + """Comment delimiters on the opening line must not truncate removal.""" + ts_file = tmp_path / "app.ts" + ts_file.write_text( + textwrap.dedent("""\ + function foo() { + console.log( // )) + '[DEBUG] multi', + someVar + ); + return 1; + } + """) + ) + entries = [ + {"file": str(ts_file), "line": 2, "tag": "DEBUG", "content": "console.log("} + ] + result = fix_debug_logs(entries, dry_run=False) + assert len(result.entries) == 1 + content = ts_file.read_text() + assert "console.log" not in content + assert "'[DEBUG] multi'" not in content + assert "someVar" not in content + assert "return 1;" in content + def test_removes_orphaned_debug_comment(self, tmp_path): """A preceding // DEBUG comment is removed along with the log.""" ts_file = tmp_path / "app.ts" @@ -719,4 +770,3 @@ def test_dry_run(self, tmp_path): _ = fix_unused_params(entries, dry_run=True) assert ts_file.read_text() == original -