R013: 注释的废弃代码

SkillDev tools

A skill for dev tools by openharmonyinsight.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the R013: 注释的废弃代码 skill

What this skill tells your AI

The instructions your AI receives, as published by openharmonyinsight/openharmony-skills in skills/check-test-code-quality/rules/R013/SKILL.md and read by ahel’s review.

规则信息

属性
规则编号R013
问题类型注释的废弃代码
严重级别Warning
规则复杂度complex
扫描范围测试文件(.test.ets, .test.ts, .test.js
testcase字段所属的it()名称,不在it()块内时为-

问题描述

存在大量注释的废弃代码。测试文件中包含连续多行的注释代码块,这些代码已经不再使用但仍然保留在文件中,影响代码可读性。

修复建议

直接删除注释的废弃代码。使用版本控制系统(如Git)保留历史记录,不需要在代码中注释保留。

扫描逻辑

Step 1: 筛选测试文件

import os

TEST_EXTENSIONS = ('.test.ets', '.test.ts', '.test.js')

def find_test_files(scan_root):
    test_files = []
    for dirpath, dirnames, filenames in os.walk(scan_root):
        for fn in filenames:
            if fn.endswith(TEST_EXTENSIONS):
                test_files.append(os.path.join(dirpath, fn))
    return test_files

Step 2: 识别连续注释块

检测连续3行及以上的注释行组成的代码块。

import re

def find_comment_blocks(content):
    lines = content.split('\n')
    blocks = []
    current_block = []

    for i, line in enumerate(lines, 1):
        stripped = line.strip()
        is_comment = (
            stripped.startswith('//') or
            stripped.startswith('*') or
            stripped.startswith('/*') or
            (current_block and stripped.startswith('*/'))
        )

        if is_comment:
            current_block.append((i, line))
        else:
            if len(current_block) >= 3:
                blocks.append(list(current_block))
            current_block = []

    if len(current_block) >= 3:
        blocks.append(list(current_block))

    return blocks

Step 3: 检测代码特征

判断注释块内容是否包含代码特征(而非普通文档注释)。

CODE_PATTERNS = [
    r'\bfunction\b',
    r'\bvar\b',
    r'\blet\b',
    r'\bconst\b',
    r'\breturn\b',
    r'\bif\s*\(',
    r'\bfor\s*\(',
    r'\bwhile\s*\(',
    r'\bswitch\s*\(',
    r'\bcase\b',
    r'\bbreak\b',
    r'\bclass\b',
    r'\bimport\b',
    r'\bexport\b',
    r'\basync\b',
    r'\bawait\b',
    r'\btry\b',
    r'\bcatch\b',
    r'\bthrow\b',
    r'\bnew\b',
    r'\bthis\b',
    r'\bexpect\b',
    r'\bit\s*\(',
    r'\bdescribe\s*\(',
    r'\{',
    r'\}',
    r';',
    r'\=\>',
    r'\.\w+\s*\(',
]

def extract_comment_text(block):
    texts = []
    for _, line in block:
        stripped = line.strip()
        if stripped.startswith('//'):
            texts.append(stripped[2:].strip())
        elif stripped.startswith('*') and not stripped.startswith('*/'):
            texts.append(stripped[1:].strip())
        elif stripped.startswith('/*'):
            texts.append(stripped[2:].strip())
        elif stripped.startswith('*/'):
            texts.append(stripped[2:].strip())
        else:
            texts.append(stripped)
    return '\n'.join(texts)

def has_code_characteristics(comment_text):
    pattern_count = 0
    for pattern in CODE_PATTERNS:
        if re.search(pattern, comment_text):
            pattern_count += 1
    return pattern_count >= 2

Step 4: 检测完整函数定义或测试用例

检查注释块中是否包含完整的函数定义或测试用例声明。

def has_complete_function(comment_text):
    patterns = [
        r'function\s+\w+\s*\([^)]*\)\s*\{',
        r'(?:async\s+)?\w+\s*=\s*(?:async\s+)?\([^)]*\)\s*(?:=>|\{)',
        r'it\s*\(\s*["\'][^"\']+["\']',
        r'describe\s*\(\s*["\'][^"\']+["\']',
        r'\bclass\s+\w+',
    ]
    match_count = sum(1 for p in patterns if re.search(p, comment_text))
    return match_count >= 1

def is_javadoc_like(comment_text):
    javadoc_markers = [
        r'@tc\.name',
        r'@tc\.number',
        r'@tc\.desc',
        r'@tc\.size',
        r'@tc\.type',
        r'@tc\.level',
        r'@param',
        r'@return',
        r'@throws',
        r'@since',
        r'@deprecated',
    ]
    match_count = sum(1 for m in javadoc_markers if re.search(m, comment_text))
    return match_count >= 2

Step 5: 判断所属testcase

解析文件中所有it()块的范围,判断注释块的起始行落在哪个it()块内。

def find_it_blocks(content):
    it_pattern = re.compile(
        r"it\s*\(\s*['\"]([^'\"]+)['\"]",
        re.MULTILINE
    )
    it_blocks = []
    for match in it_pattern.finditer(content):
        name = match.group(1)
        start_line = content[:match.start()].count('\n') + 1
        it_blocks.append((start_line, name))
    return sorted(it_blocks, key=lambda x: x[0])

def get_testcase_for_line(line_num, it_blocks):
    testcase = '-'
    for i, (start, name) in enumerate(it_blocks):
        if i + 1 < len(it_blocks):
            next_start = it_blocks[i + 1][0]
            if start < line_num < next_start:
                testcase = name
                break
        else:
            if line_num > start:
                testcase = name
                break
    return testcase

Step 6: 生成问题报告

def scan_r013(scan_root, base_dir):
    issues = []
    test_files = find_test_files(scan_root)

    for file_path in test_files:
        with open(file_path, 'r', encoding='utf-8') as f:
            content = f.read()

        it_blocks = find_it_blocks(content)
        comment_blocks = find_comment_blocks(content)

        for block in comment_blocks:
            start_line = block[0][0]
            end_line = block[-1][0]
            comment_text = extract_comment_text(block)

            if is_javadoc_like(comment_text):
                continue

            if not has_code_characteristics(comment_text):
                continue

            is_function = has_complete_function(comment_text)
            if not is_function and len(block) < 5:
                continue

            rel_path = os.path.relpath(file_path, base_dir)
            testcase = get_testcase_for_line(start_line, it_blocks)

            block_preview = '\n'.join(line for _, line in block[:5])
            if len(block) > 5:
                block_preview += '\n  ... (省略' + str(len(block) - 5) + '行)'

            issues.append({
                'rule': 'R013',
                'type': '注释的废弃代码',
                'severity': 'Warning',
                'file': rel_path,
                'line': start_line,
                'testcase': testcase,
                'snippet': block_preview,
                'suggestion': (
                    f'第{start_line}-{end_line}行存在注释的废弃代码(共{len(block)}行)。'
                    f'建议直接删除,使用版本控制系统保留历史记录。'
                ),
            })

    return issues

错误示例

// 错误1:大量注释的废弃代码
// 废弃的旧方法 - 不要删除,保留参考
// function oldMethod() {
//   let value = testFunction();
//   expect(value).assertEqual('expected');
//   return value;
// }

function newMethod() {
  let newValue = newTestFunction();
  expect(newValue).assertEqual('newExpected');
}
// 错误2:注释掉的完整测试用例
it('test001', () => {
  // 废弃的测试逻辑
  // let value = testFunction();
  // expect(value).assertEqual('expected');
  // done();

  let newValue = newTestFunction();
  expect(newValue).assertEqual('newExpected');
});
// 错误3:注释掉的函数定义(3行以上)
// function oldTestMethod(done: Function) {
//   console.info('old test start');
//   let result = doSomething();
//   expect(result).assertTrue();
//   done();
// }

正确示例

// 正确:直接删除废弃代码,保持代码简洁
it('test001', () => {
  let newValue = newTestFunction();
  expect(newValue).assertEqual('newExpected');
});
// 正确:如果需要保留旧逻辑作为参考,使用Git历史记录
function newMethod() {
  let currentValue = currentFunction();
  expect(currentValue).assertEqual('expected');
}

排除规则

以下注释块不应被报告:

  1. JSDoc文档注释:包含@tc.name@tc.number@param@return等标记的注释
  2. 少于3行的注释:不视为"大量"注释
  3. 无代码特征的注释:纯文字说明、TODO、FIXME等
  4. 单行代码片段:如仅// let x = 1;一行,不构成"废弃代码块"

输出格式

每条issue的字段:

字段
ruleR013
type注释的废弃代码
severityWarning
file相对路径(如xxx/test.test.ets
line注释块起始行号
testcase所属的it()名称,不在it()块内时为-
snippet注释块前5行预览
suggestion第X-Y行存在注释的废弃代码(共N行)。建议直接删除,使用版本控制系统保留历史记录。

Signals

GitHub stars
34
Forks
7
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
r013
Source
github.com/openharmonyinsight/openharmony-skills