mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-06-05 14:55:18 +02:00
Merge branch 'feat-cr-tool' into 'mgx_ops'
Feat cr tool See merge request pub/MetaGPT!191
This commit is contained in:
commit
c246d9a63b
10 changed files with 1169 additions and 1 deletions
15
examples/cr.py
Normal file
15
examples/cr.py
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import fire
|
||||
|
||||
from metagpt.roles.di.engineer2 import Engineer2
|
||||
from metagpt.tools.libs.cr import CodeReview
|
||||
|
||||
|
||||
async def main(msg):
|
||||
role = Engineer2(tools=["Plan", "Editor:write,read", "RoleZero", "ReviewAndRewriteCode", "CodeReview"])
|
||||
cr = CodeReview()
|
||||
role.tool_execution_map.update({"CodeReview.review": cr.review, "CodeReview.fix": cr.fix})
|
||||
await role.run(msg)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
fire.Fire(main)
|
||||
3
metagpt/ext/cr/__init__.py
Normal file
3
metagpt/ext/cr/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Desc :
|
||||
196
metagpt/ext/cr/actions/code_review.py
Normal file
196
metagpt/ext/cr/actions/code_review.py
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Desc :
|
||||
|
||||
import json
|
||||
import re
|
||||
|
||||
from unidiff import PatchedFile, PatchSet
|
||||
|
||||
from metagpt.actions.action import Action
|
||||
from metagpt.ext.cr.utils.cleaner import (
|
||||
add_line_num_on_patch,
|
||||
get_code_block_from_patch,
|
||||
rm_patch_useless_part,
|
||||
)
|
||||
from metagpt.ext.cr.utils.schema import Point
|
||||
from metagpt.logs import logger
|
||||
from metagpt.utils.common import parse_json_code_block
|
||||
|
||||
CODE_REVIEW_PROMPT_TEMPLATE = """
|
||||
NOTICE
|
||||
With the given pull-request(PR) Patch, and referenced Points(Code Standards), you should compare each point with the code one-by-one.
|
||||
|
||||
The Patch code has added line number at the first character each line for reading, but the review should focus on new added code inside the `Patch` (lines starting with line number and '+').
|
||||
Each point is start with a line number and follows with the point description.
|
||||
|
||||
## Patch
|
||||
```
|
||||
{patch}
|
||||
```
|
||||
|
||||
## Points
|
||||
{points}
|
||||
|
||||
## Output Format
|
||||
```json
|
||||
[
|
||||
{{
|
||||
"commented_file": "The file path which you give a comment from the patch",
|
||||
"comment": "The chinese comment of code which do not meet point description and give modify suggestions",
|
||||
"code_start_line": "the code start line number like `10` in the Patch of current comment,",
|
||||
"code_end_line": "the code end line number like `15` in the Patch of current comment",
|
||||
"point_id": "The point id which the `comment` references to"
|
||||
}}
|
||||
]
|
||||
```
|
||||
|
||||
CodeReview guidelines:
|
||||
- Generate code `comment` that do not meet the point description.
|
||||
- Each `comment` should be restricted inside the `commented_file`
|
||||
- Try to provide diverse and insightful comments across different `commented_file`.
|
||||
- Don't suggest to add docstring unless it's necessary indeed.
|
||||
- If the same code error occurs multiple times, it cannot be omitted, and all places need to be identified.But Don't duplicate at the same place with the same comment!
|
||||
- Every line of code in the patch needs to be carefully checked, and laziness cannot be omitted. It is necessary to find out all the places.
|
||||
|
||||
Just print the PR Patch comments in json format like **Output Format**.
|
||||
"""
|
||||
|
||||
CODE_REVIEW_COMFIRM_SYSTEM_PROMPT = """
|
||||
You are a professional engineer with Java stack, and good at code review comment result judgement.
|
||||
"""
|
||||
|
||||
CODE_REVIEW_COMFIRM_TEMPLATE = """
|
||||
## Code
|
||||
```
|
||||
{code}
|
||||
```
|
||||
## Code Review Comments
|
||||
{comment}
|
||||
|
||||
## Description of Defects
|
||||
{desc}
|
||||
|
||||
## Reference Example for Judgment
|
||||
{example}
|
||||
|
||||
## Your Task:
|
||||
1. First, check if the code meets the requirements and does not violate any defects. If it meets the requirements and does not violate any defects, print `False` and do not proceed with further judgment.
|
||||
2. If the check in step 1 does not print `False`, proceed to further judgment. Based on the "Reference Example for Judgment" provided, determine if the "Code" and "Code Review Comments" match. If they match, print `True`; otherwise, print `False`.
|
||||
|
||||
Note: Your output should only be `True` or `False` without any explanations.
|
||||
"""
|
||||
|
||||
|
||||
class CodeReview(Action):
|
||||
name: str = "CodeReview"
|
||||
|
||||
def format_comments(self, comments: list[dict], points: list[Point], patch: PatchSet):
|
||||
new_comments = []
|
||||
logger.debug(f"original comments: {comments}")
|
||||
for cmt in comments:
|
||||
for p in points:
|
||||
if int(cmt.get("point_id", -1)) == p.id:
|
||||
code_start_line = cmt.get("code_start_line")
|
||||
code_end_line = cmt.get("code_end_line")
|
||||
code = get_code_block_from_patch(patch, code_start_line, code_end_line)
|
||||
|
||||
new_comments.append(
|
||||
{
|
||||
"commented_file": cmt.get("commented_file"),
|
||||
"code": code,
|
||||
"code_start_line": code_start_line,
|
||||
"code_end_line": code_end_line,
|
||||
"comment": cmt.get("comment"),
|
||||
"point_id": p.id,
|
||||
"point": p.text,
|
||||
"point_detail": p.detail,
|
||||
}
|
||||
)
|
||||
break
|
||||
|
||||
logger.debug(f"new_comments: {new_comments}")
|
||||
return new_comments
|
||||
|
||||
async def confirm_comments(self, patch: PatchSet, comments: list[dict], points: list[Point]) -> list[dict]:
|
||||
points_dict = {point.id: point for point in points}
|
||||
new_comments = []
|
||||
for cmt in comments:
|
||||
point = points_dict[cmt.get("point_id")]
|
||||
|
||||
code_start_line = cmt.get("code_start_line")
|
||||
code_end_line = cmt.get("code_end_line")
|
||||
# 如果代码位置为空的话,那么就将这条记录丢弃掉
|
||||
if not code_start_line or not code_end_line:
|
||||
logger.info("False")
|
||||
continue
|
||||
|
||||
# 代码增加上下文,提升confirm的准确率
|
||||
code = get_code_block_from_patch(patch, str(max(1, int(code_start_line) - 3)), str(int(code_end_line) + 3))
|
||||
pattern = r"^[ \t\n\r(){}[\];,]*$"
|
||||
if re.match(pattern, code):
|
||||
code = get_code_block_from_patch(
|
||||
patch, str(max(1, int(code_start_line) - 5)), str(int(code_end_line) + 5)
|
||||
)
|
||||
prompt = CODE_REVIEW_COMFIRM_TEMPLATE.format(
|
||||
code=code,
|
||||
comment=cmt.get("comment"),
|
||||
desc=point.text,
|
||||
example=point.yes_example + "\n" + point.no_example,
|
||||
)
|
||||
resp = await self.llm.aask(prompt, system_msgs=[CODE_REVIEW_COMFIRM_SYSTEM_PROMPT])
|
||||
if "True" in resp or "true" in resp:
|
||||
new_comments.append(cmt)
|
||||
logger.info(f"original comments num: {len(comments)}, confirmed comments num: {len(new_comments)}")
|
||||
return new_comments
|
||||
|
||||
async def cr_by_full_points(self, patch: PatchSet, points: list[Point]):
|
||||
comments = []
|
||||
points_str = "\n".join([f"{p.id} {p.text}" for p in points])
|
||||
for patched_file in patch:
|
||||
if patched_file.path.endswith(".py"):
|
||||
points_str = "\n".join([f"{p.id} {p.text}" for p in points if p.language == "Python"])
|
||||
elif patched_file.path.endswith(".java"):
|
||||
points_str = "\n".join([f"{p.id} {p.text}" for p in points if p.language == "Java"])
|
||||
else:
|
||||
continue
|
||||
if len(str(patched_file).splitlines()) >= 50:
|
||||
cr_by_segment_points_comments = await self.cr_by_segment_points(
|
||||
patched_file=patched_file, points=points
|
||||
)
|
||||
comments += cr_by_segment_points_comments
|
||||
continue
|
||||
prompt = CODE_REVIEW_PROMPT_TEMPLATE.format(patch=str(patched_file), points=points_str)
|
||||
resp = await self.llm.aask(prompt)
|
||||
json_str = parse_json_code_block(resp)[0]
|
||||
comments += json.loads(json_str)
|
||||
|
||||
return comments
|
||||
|
||||
async def cr_by_segment_points(self, patched_file: PatchedFile, points: list[Point]):
|
||||
comments = []
|
||||
group_points = [points[i : i + 3] for i in range(0, len(points), 3)]
|
||||
for group_point in group_points:
|
||||
points_str = "\n".join([f"{p.id} {p.text}" for p in group_point])
|
||||
prompt = CODE_REVIEW_PROMPT_TEMPLATE.format(patch=str(patched_file), points=points_str)
|
||||
resp = await self.llm.aask(prompt)
|
||||
json_str = parse_json_code_block(resp)[0]
|
||||
comments_batch = json.loads(json_str)
|
||||
comments += comments_batch
|
||||
|
||||
return comments
|
||||
|
||||
async def run(self, patch: PatchSet, points: list[Point]):
|
||||
patch: PatchSet = rm_patch_useless_part(patch)
|
||||
patch: PatchSet = add_line_num_on_patch(patch)
|
||||
|
||||
result = []
|
||||
comments = await self.cr_by_full_points(patch=patch, points=points)
|
||||
if len(comments) != 0:
|
||||
comments = self.format_comments(comments, points, patch)
|
||||
comments = await self.confirm_comments(patch=patch, comments=comments, points=points)
|
||||
for comment in comments:
|
||||
if comment["code"]:
|
||||
if not (comment["code"].startswith("-") or comment["code"].isspace()):
|
||||
result.append(comment)
|
||||
return result
|
||||
112
metagpt/ext/cr/actions/modify_code.py
Normal file
112
metagpt/ext/cr/actions/modify_code.py
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
import datetime
|
||||
import itertools
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from unidiff import PatchSet
|
||||
|
||||
from metagpt.actions.action import Action
|
||||
from metagpt.const import DEFAULT_WORKSPACE_ROOT
|
||||
from metagpt.ext.cr.utils.cleaner import (
|
||||
add_line_num_on_patch,
|
||||
get_code_block_from_patch,
|
||||
rm_patch_useless_part,
|
||||
)
|
||||
from metagpt.utils.common import CodeParser
|
||||
from metagpt.utils.report import EditorReporter
|
||||
|
||||
SYSTEM_MSGS_PROMPT = """
|
||||
You're an adaptive software developer who excels at refining code based on user inputs. You're proficient in creating Git patches to represent code modifications.
|
||||
"""
|
||||
|
||||
MODIFY_CODE_PROMPT = """
|
||||
NOTICE
|
||||
With the given pull-request(PR) Patch, and referenced Comments(Code Standards), you should modify the code according the Comments.
|
||||
|
||||
The Patch code has added line no at the first character each line for reading, but the modification should focus on new added code inside the `Patch` (lines starting with line no and '+').
|
||||
|
||||
## Patch
|
||||
```
|
||||
{patch}
|
||||
```
|
||||
|
||||
## Comments
|
||||
{comments}
|
||||
|
||||
## Output Format
|
||||
<the standard git patch>
|
||||
|
||||
|
||||
Code Modification guidelines:
|
||||
- Look at `point_detail`, modify the code by `point_detail`, use `code_start_line` and `code_end_line` to locate the problematic code, fix the problematic code by `point_detail` in Comments.Strictly,must handle the fix plan given by `point_detail` in every comment.
|
||||
- Create a patch that satifies the git patch standard and your fixes need to be marked with '+' and '-',but notice:don't change the hunk header!
|
||||
- Do not print line no in the new patch code.
|
||||
|
||||
Just print the Patch in the format like **Output Format**.
|
||||
"""
|
||||
|
||||
|
||||
class ModifyCode(Action):
|
||||
name: str = "Modify Code"
|
||||
pr: str
|
||||
|
||||
async def run(self, patch: PatchSet, comments: list[dict], output_dir: Optional[str] = None) -> str:
|
||||
patch: PatchSet = rm_patch_useless_part(patch)
|
||||
patch: PatchSet = add_line_num_on_patch(patch)
|
||||
|
||||
#
|
||||
for comment in comments:
|
||||
code_start_line = comment.get("code_start_line")
|
||||
code_end_line = comment.get("code_end_line")
|
||||
# 如果代码位置为空的话,那么就将这条记录丢弃掉
|
||||
if code_start_line and code_end_line:
|
||||
code = get_code_block_from_patch(
|
||||
patch, str(max(1, int(code_start_line) - 3)), str(int(code_end_line) + 3)
|
||||
)
|
||||
pattern = r"^[ \t\n\r(){}[\];,]*$"
|
||||
if re.match(pattern, code):
|
||||
code = get_code_block_from_patch(
|
||||
patch, str(max(1, int(code_start_line) - 5)), str(int(code_end_line) + 5)
|
||||
)
|
||||
# 代码增加上下文,提升代码修复的准确率
|
||||
comment["code"] = code
|
||||
# 去掉CR时LLM给的comment的影响,应该使用既定的修复方案
|
||||
comment.pop("comment")
|
||||
|
||||
# 按照 commented_file 字段进行分组
|
||||
comments.sort(key=lambda x: x["commented_file"])
|
||||
grouped_comments = {
|
||||
key: list(group) for key, group in itertools.groupby(comments, key=lambda x: x["commented_file"])
|
||||
}
|
||||
resp = None
|
||||
for patched_file in patch:
|
||||
patch_target_file_name = str(patched_file.target_file).split("/", maxsplit=1)[-1]
|
||||
if patch_target_file_name not in grouped_comments:
|
||||
continue
|
||||
comments_prompt = ""
|
||||
index = 1
|
||||
for grouped_comment in grouped_comments[patch_target_file_name]:
|
||||
comments_prompt += f"""
|
||||
<comment{index}>
|
||||
{grouped_comment}
|
||||
</comment{index}>\n
|
||||
"""
|
||||
prompt = MODIFY_CODE_PROMPT.format(patch=patched_file, comments=comments_prompt)
|
||||
output_dir = (
|
||||
Path(output_dir)
|
||||
if output_dir
|
||||
else DEFAULT_WORKSPACE_ROOT / "modify_code" / str(datetime.date.today()) / self.pr
|
||||
)
|
||||
patch_file = output_dir / f"{patch_target_file_name}.patch"
|
||||
patch_file.parent.mkdir(exist_ok=True, parents=True)
|
||||
async with EditorReporter(enable_llm_stream=True) as reporter:
|
||||
await reporter.async_report(
|
||||
{"type": "Patch", "src_path": str(patch_file), "filename": patch_file.name}, "meta"
|
||||
)
|
||||
resp = await self.llm.aask(msg=prompt, system_msgs=[SYSTEM_MSGS_PROMPT])
|
||||
resp = CodeParser.parse_code(resp, "diff")
|
||||
with open(patch_file, "w", encoding="utf-8") as file:
|
||||
file.writelines(resp)
|
||||
await reporter.async_report(patch_file)
|
||||
return resp
|
||||
664
metagpt/ext/cr/points.json
Normal file
664
metagpt/ext/cr/points.json
Normal file
|
|
@ -0,0 +1,664 @@
|
|||
[
|
||||
{
|
||||
"id": 1,
|
||||
"text": "避免未使用的临时变量",
|
||||
"language": "java",
|
||||
"detail": "缺陷类型:避免未使用的临时变量;对应Fixer:UnusedLocalVariableFixer;修复方案:删除未使用的临时变量",
|
||||
"yes_example": "### 被判定为\"避免未使用的临时变量\"的例子\n<例子1>\npublic String initCreationForm(Map<String, Object> model) {\n\t\tOwner owner = new Owner();\n\t\tmodel.put(\"owner\", owner);\n\t\tint unusedVar = 10;\n\t\treturn VIEWS_OWNER_CREATE_OR_UPDATE_FORM;\n\t}\n上述代码中unusedVar变量未被使用,所以这个被判定为\"避免未使用的临时变量\"\n</例子1>\n<例子2>\nint unusedVariable = 10;\nSystem.out.println(\"Hello, World!\");\n这段代码的变量\"unusedVariable\"未被使用或者引用,所以这个不能判定为\"避免未使用的临时变量\"\n</例子2>",
|
||||
"no_example": "### 不能被判定为\"避免未使用的临时变量\"的例子\n<例子1>\npublic void setTransientVariablesLocal(Map<String, Object> transientVariables) {\nthrow new UnsupportedOperationException(\"No execution active, no variables can be set\");\n}\n这段代码的\"transientVariables\"是函数参数而不是临时变量,虽然transientVariables没有被使用或者引用,但是这个也不能判定为\"避免未使用的临时变量\"\n</例子1>\n\n<例子2>\npublic class TriggerCmd extends NeedsActiveExecutionCmd<Object> {\n protected Map<String, Object> transientVariables;\n public TriggerCmd(Map<String, Object> transientVariables) {\n this.transientVariables = transientVariables;\n }\n}\n上述代码中transientVariables不属于临时变量,它是类属性,且它在构造函数中被使用,所以这个不能被判定为\"避免未使用的临时变量\"\n</例子2>"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"text": "不要使用 System.out.println 去打印",
|
||||
"language": "java",
|
||||
"detail": "缺陷类型:不要使用 System.out.println 去打印;对应Fixer:SystemPrintlnFixer;修复方案:注释System.out.println代码",
|
||||
"yes_example": "### 被判定为\"不要使用 System.out.println 去打印\"的例子\n<例子1>\nSystem.out.println(\"Initializing new owner form.\");\n上述代码使用了\"System.out.println\"进行打印,所以这个被判定为\"不要使用 System.out.println 去打印\"\n</例子1>",
|
||||
"no_example": "### 不能被判定为\"不要使用 System.out.println 去打印\"的例子\n<例子1>\nthrow new IllegalStateException(\"There is no authenticated user, we need a user authenticated to find tasks\");\n上述代码是抛出异常的代码,没有使用\"System.out.print\",所以这个不能被判定为\"不要使用 System.out.println 去打印\"\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"text": "避免函数中未使用的形参",
|
||||
"language": "java",
|
||||
"detail": "缺陷类型:避免函数中未使用的形参;修复方案:忽略",
|
||||
"yes_example": "### 被判定为\"避免函数中未使用的形参\"的例子\n<例子1>\npublic void setTransientVariablesLocal(Map<String, Object> transientVariables) {\n throw new UnsupportedOperationException(\"No execution active, no variables can be set\");\n}这段代码中的形参\"transientVariables\"未在函数体内出现,所以这个被判定为\"避免函数中未使用的形参\"\n</例子1>\n\n<例子2>\nprotected void modifyFetchPersistencePackageRequest(PersistencePackageRequest ppr, Map<String, String> pathVars) {}\n这段代码中的形参\"ppr\"和\"pathVars\"未在函数体内出现,所以这个被判定为\"避免函数中未使用的形参\"\n</例子2>",
|
||||
"no_example": "### 不能被判定为\"避免函数中未使用的形参\"的例子\n<例子1>\npublic String processFindForm(@RequestParam(value = \"pageNo\", defaultValue = \"1\") int pageNo) {\n\tlastName = owner.getLastName();\n\treturn addPaginationModel(pageNo, paginationModel, lastName, ownersResults);\n}上述代码中的形参\"pageNo\"在当前函数'processFindForm'内被'return addPaginationModel(pageNo, paginationModel, lastName, ownersResults);'这一句被使用,虽然pageNo没有被用于逻辑计算,但作为了函数调用其他函数的参数使用了,所以这个不能被判定为\"避免函数中未使用的形参\"\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"text": "if语句块不能为空",
|
||||
"language": "java",
|
||||
"detail": "缺陷类型:if 语句块不能为空;对应Fixer:EmptyIfStmtFixer;修复方案:删除if语句块 或 适当的逻辑处理 或 注释说明为何为空",
|
||||
"yes_example": "### 被判定为\"if语句块不能为空\"的例子\n<例子1>\npublic void emptyIfStatement() {\n\tif (getSpecialties().isEmpty()) {\n\t}\n}这段代码中的if语句块内容是空的,所以这个被判定为\"if语句块不能为空\"\n</例子1>\n\n<例子2>\npublic void judgePersion() {\n\tif (persion != null) {\n\t\t// judge persion if not null\n\t}\n}\n这段代码中的if语句块虽然有内容,但是\"// judge persion if not null\"只是代码注释,if语句块内并没有实际的逻辑代码,所以这个被判定为\"if语句块不能为空\"\n</例子2>",
|
||||
"no_example": "### 不能被判定为\"if语句块不能为空\"的例子\n<例子1>\npublic void judgePersion() {\n\tif (persion != null) {\n\t\treturn 0;\n\t}\n}这段代码中的if语句块里有内容,且里面有非注释代码的逻辑代码\"return 0;\",所以这个不能被判定为\"if语句块不能为空\"\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"text": "循环体不能为空",
|
||||
"language": "java",
|
||||
"detail": "缺陷类型:循环体不能为空;对应Fixer:EmptyStatementNotInLoopFixer;修复方案:删除对应while、for、foreach 循环体 或 添加适当的逻辑处理或者注释说明为何为空",
|
||||
"yes_example": "### 被判定为\"循环体不能为空\"的例子\n<例子1>\npublic void emptyLoopBody() {\n\tfor (Specialty specialty : getSpecialties()) {\n\t}\n}这段代码中的for循环体的内容是空的,所以这个被判定为\"循环体不能为空\"\n</例子1>\n\n<例子2>\npublic void emptyLoopBody() {\n\twhile (True) {\n\t\t// this is a code example\n\t}\n}这段代码中的while循环体的内容虽然不是空的,但内容只是代码注释,无逻辑内容,所以这个被判定为\"循环体不能为空\"\n</例子2>\n\n<例子3>\npublic void emptyLoopBody() {\n\twhile (True) {\n\t\t\n\t}\n}这段代码中的while循环体内容是空的,所以这个被判定为\"循环体不能为空\"\n</例子3>",
|
||||
"no_example": "### 不能被判定为\"循环体不能为空\"的例子\n<例子1>\npublic void emptyLoopBody() {\n\tfor (Specialty specialty : getSpecialties()) {\n\t\ta = 1;\n\t\tif (a == 1) {\n\t\t\tretrun a;\n\t\t}\n\t}\n}上述代码的for循环体的内容不为空,且内容不全是代码注释,所以这个不能被判定为\"循环体不能为空\"\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"text": "避免使用 printStackTrace(),应该使用日志的方式去记录",
|
||||
"language": "java",
|
||||
"detail": "缺陷类型:避免使用 printStackTrace(),应该使 用日志的方式去记录;修复方案:用日志的方式去记录",
|
||||
"yes_example": "### 被判定为\"避免使用 printStackTrace(),应该使用日志的方式去记录\"的例子\n<例子1>\npublic void usePrintStackTrace() {\n\ttry {\n\t\tthrow new Exception(\"Fake exception\");\n\t} catch (Exception e) {\n\t\te.printStackTrace();\n\t}\n}这段代码中的catch语句中使用了printStackTrace(),所以这个被判定为\"避免使用 printStackTrace(),应该使用日志的方式去记录\"\n</例子1>",
|
||||
"no_example": "### 不能被判定为\"避免使用 printStackTrace(),应该使用日志的方式去记录\"的例子\n<例子1>\npublic void usePrintStackTrace() {\n\ttry {\n\t\tthrow new Exception(\"Fake exception\");\n\t} catch (Exception e) {\n\t\tlogging.info(\"info\");\n\t}\n}这段代码的catch语句中使用的是日志记录的方式,所以这个不能被判定为\"避免使用 printStackTrace(),应该使用日志的方式去记录\"\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"text": "catch 语句块不能为空",
|
||||
"language": "java",
|
||||
"detail": "缺陷类型:catch 语句块不能为空;对应Fixer:EmptyCatchBlockFixer;修复方案:在catch里面添加注释",
|
||||
"yes_example": "### 被判定为\"catch语句块不能为空\"的例子\n<例子1>\ntry {\n int[] array = new int[5];\n int number = array[10];\n} catch (ArrayIndexOutOfBoundsException e) {\n \n}\n这段代码中的catch语句中没有内容,所以这个被判定为\"catch语句块不能为空\"\n</例子1>\n\n<例子2>\ntry {\n String str = null;\n str.length();\n} catch (NullPointerException e) {\n \n}这段代码中的catch语句中没有内容,所以这个被判定为\"catch语句块不能为空\"\n</例子2>\n\n<例子3>\npublic class EmptyCatchExample {\n public static void main(String[] args) {\n try {\n // 尝试除以零引发异常\n int result = 10 / 0;\n } catch (ArithmeticException e) {\n \n }\n }\n}这段代码中的catch语句中没有内容,所以这个被判定为\"catch语句块不能为空\"\n</例子3>\n<例子4>\ntry {\n FileReader file = new FileReader(\"nonexistentfile.txt\");\n} catch (FileNotFoundException e) {\n \n}这段代码中的catch语句中没有内容,所以这个被判定为\"catch语句块不能为空\"\n</例子4>\n<例子5>\ntry {\n Object obj = \"string\";\n Integer num = (Integer) obj;\n} catch (ClassCastException e) {\n\t\n}这段代码中的catch语句中没有内容,所以这个被判定为\"catch语句块不能为空\"\n</例子5>",
|
||||
"no_example": "### 不能被判定为\"catch语句块不能为空\"的例子\n<例子1>\npersionNum = 1\ntry {\n\treturn True;\n} catch (Exception e) {\n\t// 如果人数为1则返回false\n\tif (persionNum == 1){\n\t\treturn False;\n\t}\n}这段代码的catch语句中不为空,所以不能把这个被判定为\"catch语句块不能为空\"\n</例子1>\n\n<例子2>\ntry {\n\tthrow new Exception(\"Fake exception\");\n} catch (Exception e) {\n\te.printStackTrace();\n}这段代码的catch语句中虽然只有\"e.printStackTrace();\"但确实不为空,所以不能把这个被判定为\"catch语句块不能为空\"\n</例子2>"
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"text": "避免不必要的永真/永假判断",
|
||||
"language": "java",
|
||||
"detail": "缺陷类型:避免不必要的永真/永假判断;对应Fixer:UnconditionalIfStatement Fixer;修复方案:删除永真/永假判断逻辑",
|
||||
"yes_example": "### 被判定为\"避免不必要的永真/永假判断\"的例子\n<例子1>\npublic void someMethod() {\n\twhile (true) {\n\t}\n}这段代码中的\"while (true)\"是一个使用true做判断条件,但是没有循环结束标记,所以这个被判定为\"避免不必要的永真/永假判断\"\n</例子1>\n\n<例子2>\nif (true) {\n\tSystem.out.println(\"This is always true\");\n}这段代码中的\"if (true)\"是一个使用true条件做条件,但是没有循环结束标记,所以这个被判定为\"避免不必要的永真/永假判断\"\n</例子2>\n\n<例子3>\na = 1;\nwhile(a > 0){\n\ta = a + 1\n}这段代码初始化a=1,是大于0的,while循环体的逻辑是每次加1,那么判断条件a > 0会永远是真的,不会退出循环,所以这个被判定为\"避免不必要的永真/永假判断\"\n<例子3>",
|
||||
"no_example": "### 不能被判定为\"避免不必要的永真/永假判断\"的例子\n<例子1>\na = 0;\nwhile (a < 5) {\n\ta = a + 1;\n}这段代码中的a<5是一个判断,当执行了5次while语句中的逻辑a=a+1之后,a会满足a < 5,就会退出循环,所以这个能被判定为\"避免不必要的永真/永假判断\"\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"text": "switch 中 default 必须放在最后",
|
||||
"language": "java",
|
||||
"detail": "缺陷类型:switch 中 default 必须放在最后;对应Fixer:DefaultLabelNotLastInSwitchStmtFixer;修复方案:switch 中 default 放在最后",
|
||||
"yes_example": "### 被判定为\"switch 中 default 必须放在最后\"的例子\n<例子1>\nswitch (number) {\n\tdefault:\n\t\tSystem.out.println(\"This is the default block, which is incorrectly placed here.\");\n\t\tbreak;\n\tcase 1:\n\t\tSystem.out.println(\"Number one\");\n\t\tbreak;\n\tcase 2:\n\t\tSystem.out.println(\"Number two\");\n\t\tbreak;\n}这段代码是一个switch语句,但是里面的default没有放在最后,所以这个被判定为\"switch 中 default 必须放在最后\"\n</例子1>",
|
||||
"no_example": "### 不能被判定为\"switch 中 default 必须放在最后\"的例子\n<例子1>\nswitch (number) {\ncase 3:\n\tSystem.out.println(\"Number one\");\n\tbreak;\ncase 4:\n\tSystem.out.println(\"Number two\");\n\tbreak;\ndefault:\n\tSystem.out.println(\"This is the default block, which is incorrectly placed here.\");\n\tbreak;\n}这段代码是一个switch语句且里面的default放在了最后,所以这个不能被判定为\"switch 中 default 必须放在最后\"\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"text": "未使用equals()函数对 String 作比较",
|
||||
"language": "java",
|
||||
"detail": "缺陷类型:未使用equals()函数对 String 作比较;对应Fixer:UnSynStaticDateFormatter Fixer;修复方案:使用equals()函数对 String 作比较",
|
||||
"yes_example": "### 被判定为\"未使用equals()函数对 String 作比较\"的例子\n<例子1>\nif (existingPet != null && existingPet.getName() == petName) {\n\tresult.rejectValue(\"name\", \"duplicate\", \"already exists\");\n}这段代码中所涉及的existingPet.getName()和petName均是字符串,但是在if语句里做比较的时候使用了==而没有使用equals()对string做比较,所以这个被判定为\"未使用equals()函数对 String 作比较\"\n</例子1>\n\n<例子2>\nString isOk = \"ok\";\nif (\"ok\" == isOk) {\n\tresult.rejectValue(\"name\", \"duplicate\", \"already exists\");\n}这段代码中的isOk是个字符串,但在if判断中与\"ok\"比较的时候使用的是==,未使用equals()对string做比较,所以这个被判定为\"未使用equals()函数对 String 作比较\"\n</例子2>",
|
||||
"no_example": "### 不能被判定为\"未使用equals()函数对 String 作比较\"的例子\n<例子1>\nif (PROPERTY_VALUE_YES.equalsIgnoreCase(readWriteReqNode))\n formProperty.setRequired(true);\n这段代码中的PROPERTY_VALUE_YES和readWriteReqNode均是字符串,在if语句里比较PROPERTY_VALUE_YES和readWriteReqNode的使用的是equalsIgnoreCase(字符串比较忽略大小写),所以equalsIgnoreCase也是符合使用equals()函数对 String 作比较的,所以这个不能被判定为\"未使用equals()函数对 String 作比较\"\n</例子1>\n\n<例子2>\nString isOk = \"ok\";\nif (\"ok\".equals(isOk)) {\n\tresult.rejectValue(\"name\", \"duplicate\", \"already exists\");\n}这段代码中的isOk是个字符串,在if判断中与\"ok\"比较的时候使用的是equals()对string做比较,所以这个不能被判定为\"未使用equals()函数对 String 作比较\"\n</例子2>"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"text": "禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象",
|
||||
"language": "java",
|
||||
"detail": "缺陷类型:禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象 输出异常;对应Fixer:ConcatExceptionFixer;修复方案:使用占位符传递异常对象",
|
||||
"yes_example": "### 被判定为\"禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象\"的例子\n<例子1>\ntry {\n listenersNode = objectMapper.readTree(listenersNode.asText());\n} catch (Exception e) {\n LOGGER.info(\"Listeners node can not be read\", e);\n}这段代码中日志输出内容内容是直接使用字符串\"Listeners node can not be read\"拼接,日志输出异常时,应使用占位符输出异常信息,而不是直接使用字符串拼接,所以这个被判定为\"禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象\"\n</例子1>",
|
||||
"no_example": "### 不能被判定为\"禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象\"的例子\n<例子1>\nPersion persion = persionService.getPersion(1);\nif (persion == null){\n\tLOGGER.error(PERSION_NOT_EXIT);\n}这段代码中的PERSION_NOT_EXIT是一个用户自定义的异常常量,代表persion不存在,没有直接使用字符串\"persion not exit\"拼接,所以这个不能被判定为\"禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象\"\n<例子1>\n\n<例子2>\ntry {\n a = a + 1;\n} catch (Exception e) {\n Persion persion = persionService.getPersion(1);\n LOGGER.info(persion);\n}这段代码中输出日志没有直接使用字符串拼接,而是使用的Persion对象输出,所以这个不能被判定为\"禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象\"\n</例子2>"
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"text": "finally 语句块不能为空",
|
||||
"language": "java",
|
||||
"detail": "缺陷类型:finally 语句块不能为空;对应Fixer:EmptyFinallyBlockFixer;修复方案:删除空 finally 语句块",
|
||||
"yes_example": "### 被判定为\"finally 语句块不能为空\"的例子\n<例子1>\ntry {\n\tPersion persion = persionService.getPersion(1);\n\treturn persion;\n} finally {\n\t\n}这段代码中的finally语句块内没有内容,所以这个被判定为\"finally 语句块不能为空\"\n</例子1>\n\n<例子2>\ntry {\n\tSystem.out.println(\"Inside try block\");\n} finally {\n\t// 空的finally块,没有任何语句,这是一个缺陷\n}这段代码中的finally语句块内没有内容,所以这个被判定为\"finally 语句块不能为空\"\n</例子2>\n\n<例子3>\ntry {\n int result = 10 / 0;\n} catch (ArithmeticException e) {\n e.printStackTrace();\n} finally {\n \n}这段代码中的finally语句块内没有内容,所以这个被判定为\"finally 语句块不能为空\"\n</例子3>\n\n<例子4>\ntry {\n String str = null;\n System.out.println(str.length());\n} catch (NullPointerException e) {\n e.printStackTrace();\n} finally {\n \n}这段代码中的finally语句块内没有内容,所以这个被判定为\"finally 语句块不能为空\"\n</例子4>\n\n<例子5>\ntry {\n int[] array = new int[5];\n int number = array[10];\n} catch (ArrayIndexOutOfBoundsException e) {\n e.printStackTrace();\n} finally {\n // 只有注释的 finally 语句块\n // 这是一个空的 finally 块\n}这段代码中的finally语句块内没有内容,所以这个被判定为\"finally 语句块不能为空\"\n</例子5>\n\n<例子6>\ntry {\n FileReader file = new FileReader(\"nonexistentfile.txt\");\n} catch (FileNotFoundException e) {\n e.printStackTrace();\n} finally {\n // 只有空行的 finally 语句块\n \n}这段代码中的finally语句块内没有内容,所以这个被判定为\"finally 语句块不能为空\"\n</例子6>",
|
||||
"no_example": "### 不能被判定为\"finally 语句块不能为空\"的例子\n<例子1>\npublic void getPersion() {\n\ttry {\n\t\tPersion persion = persionService.getPersion(1);\n\t\tif (persion != null){ \n\t\t\treturn persion;\n\t\t}\n\t} finally {\n\t\treturn null;\n\t}\n}这段代码中的finally语句块中有非注释意外的内容\"return null;\",所以这个不能被判定为\"finally 语句块不能为空\"\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"text": "try 语句块不能为空",
|
||||
"language": "java",
|
||||
"detail": "缺陷类型:try 语句块不能为空;对应Fixer:EmptyTryBlockFixer;修复方案:删除整个 try 语句",
|
||||
"yes_example": "### 被判定为\"try 语句块不能为空\"的例子\n<例子1>\npublic void getPersion() {\n\ttry {\n\n\t}\n\treturn null;\n}这段代码中的try语句块内没有内容,所以这个被判定为\"try 语句块不能为空\"\n</例子1>\n\n<例子2>\npublic void demoFinallyBlock() {\n\ttry {\n\n\t} finally {\n\t\treturn null;\n\t}\n}这段代码中的try语句块内没有内容,所以这个被判定为\"try 语句块不能为空\"\n</例子2>\n\n<例子3>\ntry {\n \n} catch (Exception e) {\n e.printStackTrace();\n}这段代码中的try语句块内没有内容,所以这个被判定为\"try 语句块不能为空\"\n</例子3>\n\n<例子4>\ntry {\n // 只有注释的 try 语句块\n\t\n} catch (Exception e) {\n e.printStackTrace();\n}这段代码中的try语句块内只有注释和空行,也可以认定为这种情况是try语句块内没有内容,所以这个被判定为\"try 语句块不能为空\"\n</例子4>",
|
||||
"no_example": "### 不能被判定为\"try 语句块不能为空\"的例子\n<例子1>\ntry {\n\ta = a + 1;\n} catch (Exception e) {\n\te.printStackTrace();\n}\n这段代码中的try语句块中有非注释意外的内容\"return null;\",所以这个不能被判定为\"try 语句块不能为空\"\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"text": "避免对象进行不必要的 NULL或者null 检查",
|
||||
"language": "java",
|
||||
"detail": "缺陷类型:避免对象进行不必要的 NULL或者null 检查;对应Fixer:LogicalOpNpeFixer;修复方案:删除对对象不必要的 NULL 检查的逻辑",
|
||||
"yes_example": "### 被判定为\"避免对象进行不必要的 NULL或者null 检查\"的例子\n<例子1>\na = \"dog\";\nif (a != null){\n\treturn a;\n}这段代码中的对象a已经是确定的值\"dog\",所以if条件句的判断\"a != null\"是不必要的,所以这个被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n</例子1>\n\n<例子2>\nif (authenticatedUserId != null && !authenticatedUserId.isEmpty() && userGroupManager!=null){\n\treturn authenticatedUserId;\n}这段代码中的\"authenticatedUserId != null\"和\"!authenticatedUserId.isEmpty()\"都是对\"authenticatedUserId\"的空判断,重复了,所以这个被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n</例子2>\n\n<例子3>\nList<Integer> list = new ArrayList<>();\nif (list != null) {\n list.add(1);\n}这段代码中的list已经被初始化,不需要进行 null 检查,所以这个被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n</例子3>\n\n<例子4>\nif (this.type != null && this.type.getName() != null) {\n\tSystem.out.println(\"Type name is not null\");\n}这段代码中的对象type已经检查过非null,再次检查getName()是否为null是不必要的,所以这个被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n\n</例子4>\n\n<例子5>\nif (\"dog\".equals(null)){\n\treturn a;\n}这段代码中的\"dog\"是个确定的字符串,不需要进行null 检查,所以这个被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n</例子5>\n\n<例子6>\nInteger num = 10;\nif (num != null) {\n System.out.println(num);\n}这段代码中的num 已经被初始化,不需要进行 null 检查,所以这个被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n</例子6>",
|
||||
"no_example": "### 不能被判定为\"避免对象进行不必要的 NULL或者null 检查\"的例子\n<例子1>\nCat cat = catService.get(1);\nif (cat != null){\n\tretrun cat;\n}这段代码中的对象\"cat\"是通过service获取到的,不确定是否为空,所以if条件句的判断的\"cat != null\"是必要的,所以这个不能被判定为\"避免对象进行不必要的 NULL或者null 检查\"\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"text": "避免 finally 块中出现 return",
|
||||
"language": "java",
|
||||
"detail": "缺陷类型:避免 finally 块中出现 return;修复方案:无需修复",
|
||||
"yes_example": "### 被判定为\"避免 finally 块中出现 return\"的例子\n<例子1>\npublic void getPersion() {\n\ttry {\n\t\tPersion persion = persionService.getPersion(1);\n\t\tif (persion != null){ \n\t\t\treturn persion;\n\t\t}\n\t} finally {\n\t\treturn null;\n\t}\n}这段代码中的finally语句块内容包含\"return\",所以这个被判定为\"避免 finally 块中出现 return\"\n</例子1>",
|
||||
"no_example": "### 不能被判定为\"避免 finally 块中出现 return\"的例子\n<例子1>\npublic void getPersion() {\n\ttry {\n\t\tPersion persion = persionService.getPersion(1);\n\t\tif (persion != null){ \n\t\t\treturn persion;\n\t\t}\n\t} finally {\n\t\tLOGGER.info(PERSION_NOT_EXIT);\n\t}\n}这段代码中的finally语句块中内容不包含\"return\",所以这个不能被判定为\"避免 finally 块中出现 return\"\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"text": "避免空的 static 初始化",
|
||||
"language": "java",
|
||||
"detail": "缺陷类型:避免空的 static 初始化;对应Fixer:EmptyInitializerFixer;修复方案:删除整个空初始化块",
|
||||
"yes_example": "### 被判定为\"避免空的 static 初始化\"的例子\n<例子1>\npublic class PetValidator implements Validator {\n\tstatic {\n\n\t}\n}这段代码中的static语句块没有内容,是空的,所以这个被判定为\"避免空的 static 初始化\"\n</例子1>\n\n<例子2>\npublic class Persion {\n\tstatic {\n\t\t// 初始化的静态块\n\t}\n}这段代码中的static语句块是有内容的,不是空的,但是static初始化语句块中只有注释代码,没有实际的逻辑,所以这个被判定为\"避免空的 static 初始化\"\n</例子2>",
|
||||
"no_example": "### 不能被判定为\"避免空的 static 初始化\"的例子\n<例子1>\npublic class Cat {\n\tstatic {\n\t\t// 初始化的静态块\n\t\tcat = null;\n\t}\n}这段代码中的static语句块是有内容的,不是空的,且static初始化语句块中有非注释代码,有实际的逻辑,所以这个不能被判定为\"避免空的 static 初始化\"\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"text": "避免日历类用法不当风险",
|
||||
"language": "java",
|
||||
"detail": "缺陷类型:避免日历类用法不当风险;修复方案:使用Java 8 及以上版本中的 java.time 包的LocalDate",
|
||||
"yes_example": "### 被判定为\"避免日历类用法不当风险\"的例子\n<例子1>\nprivate static final Calendar calendar = new GregorianCalendar(2020, Calendar.JANUARY, 1);\n这段代码中的Calendar和GregorianCalendar是线程不安全的,所以这个被判定为\"避免日历类用法不当风险\"\n</例子1>",
|
||||
"no_example": "### 不能被判定为\"避免日历类用法不当风险\"的例子\n<例子1>\nprivate static final LocalDate calendar = LocalDate.of(2020, 1, 1);\n这段代码中的LocalDate使用的是Java 8 及以上版本中的 java.time 包,LocalDate 是不可变的并且是线程安全的,不会有线程安全和性能方面的问题,所以这个不能被判定为\"避免日历类用法不当风险\"\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"text": "使用集合转数组的方法,必须使用集合的toArray(T[]array),传入的是类型完全一样的数组,大小就是list.size()",
|
||||
"language": "java",
|
||||
"detail": "缺陷类型:使用集合转数组的方法,必须使用集合的toArray(T[]array),传入的是类型完全一样的数组,大小就是list.size();对应Fixer:ClassCastExpWithToArrayF ixer;修复方案:使用集合的toArray(T[]array),且传入的是类型完全一样的数组",
|
||||
"yes_example": "### 被判定为\"使用集合转数组的方法,必须使用集合的toArray(T[]array),传入的是类型完全一样的数组,大小就是list.size()\"的例子\n<例子1>\nList<String> stringList = new ArrayList<>();\nstringList.add(\"Apple\");\nstringList.add(\"Banana\");\nObject[] objectArray = stringList.toArray(new Object[5]);\n这段代码使用集合转数组的方法的时候使用了toArray(new Object[5]),但是传入的数组类型不一致,所以这个被判定为\"使用集合转数组的方法,必须使用集合的toArray(T[]array),传入的是类型完全一样的数组,大小就是list.size()\"\n</例子1>",
|
||||
"no_example": "### 不能被判定为\"使用集合转数组的方法,必须使用集合的toArray(T[]array),传入的是类型完全一样的数组,大小就是list.size()\"的例子\n<例子1>\nList<String> stringList = new ArrayList<>();\nstringList.add(\"Apple\");\nstringList.add(\"Banana\");\nString[] stringArray = stringList.toArray(new String[stringList.size()]);\n这段代码使用集合转数组的方法的时候使用了toArray(new String[stringList.size()]),传入的是类型完全一样的数组,所以这个不能被判定为\"使用集合转数组的方法,必须使用集合的toArray(T[]array),传入的是类型完全一样的数组,大小就是list.size()\"\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"text": "禁止在 equals()中使用 NULL或者null 做比较",
|
||||
"language": "java",
|
||||
"detail": "缺陷类型:禁止在 equals()中使用 NULL或者null 做比较;对应Fixer:EqualsNullFixer;修复方案:使用Object的判空函数 做比较",
|
||||
"yes_example": "### 被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"的例子\n<例子1>\nif (\"test\".equals(null)) {\n\tSystem.out.println(\"test\");\n}这段代码中if条件中的代码\"test\".equals(null)使用equals()函数与null进行了比较,所以这个被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n</例子1>\n\n<例子2>\nif (!rangeValues[1].equals(\"null\")) {\n\tmaxValue = new BigDecimal(rangeValues[1]);\n}这段代码中if条件中的代码!rangeValues[1].equals(\"null\")使用equals()函数与Nnull进行了比较,所以这个被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n</例子2>\n\n<例子3>\nString str1 = \"example\";\nif (str1.equals(\"null\")) {\n System.out.println(\"str1 is null\");\n}这段代码中if条件中的代码str1.equals(null)使用equals()函数与null进行了比较,所以这个被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n</例子3>\n\n<例子4>\nString str3 = \"example\";\nif (str3 != null && str3.equals(\"null\")) {\n System.out.println(\"str3 is null\");\n}这段代码中if条件中的代码str3.equals(\"null\")使用equals()函数与\"null\"进行了比较,所以这个被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n</例子4>\n\n<例子5>\nInteger num1 = 10;\nif (num1.equals(null)) {\n System.out.println(\"num1 is null\");\n}这段代码中if条件中的代码num1.equals(null)使用equals()函数与\"null\"进行了比较,所以这个被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n</例子5>\n\n<例子6>\nObject obj = new Object();\nif (obj.equals(null)) {\n System.out.println(\"obj is null\");\n}这段代码中if条件中的代码obj.equals(null)使用equals()函数与\"null\"进行了比较,所以这个被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n</例子6>",
|
||||
"no_example": "### 不能被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"的例子\n<例子1>\na = \"test\";\nif (a.equals(\"test\")) {\n\tSystem.out.println(\"test\");\n}这段代码中if条件中的代码a.equals(\"test\")使用equals()函数与\"test\"进行了比较,所以这个不能被判定为\"禁止在 equals()中使用 NULL或者null 做比较\"\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"text": "switch 语句块不能为空",
|
||||
"language": "java",
|
||||
"detail": "缺陷类型:switch 语句块不能为空;对应Fixer:EmptySwitchStatementsFix;修复方案:删除整个空 switch 语句块",
|
||||
"yes_example": "### 被判定为\"switch 语句块不能为空\"的例子\n<例子1>\nswitch (number) {\n\t\n}这段代码是一个switch语句块,但是里面没有内容,所以这个被判定为\"switch 语句块不能为空\"\n</例子1>\n\n<例子2>\nswitch (number) {\n\t// 这是一个switch语句块\n}这段代码是一个switch语句块,里面虽然有内容,但是内容仅仅是注释内容,没有实际的逻辑,所以这个被判定为\"switch 语句块不能为空\"\n</例子2>",
|
||||
"no_example": "### 不能被判定为\"switch 语句块不能为空\"的例子\n<例子1>\nswitch (number) {\n\tcase 1:\n\t\tSystem.out.println(\"Number one\");\n\t\tbreak;\n\tdefault:\n\t\tSystem.out.println(\"This is the default block, which is incorrectly placed here.\");\n\t\tbreak;\n}这段代码是一个switch语句块,里面有内容,而且内容里有非注释的代码,有实际的逻辑,所以这个不能被判定为\"switch 语句块不能为空\"\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"text": "在进行类型强制转换时,右括号与强制转换值之间不需要任何空格隔开",
|
||||
"detail": "缺陷类型:类型强制转换时空格使用不当;修复方案:在进行类型强制转换时,右括号与强制转换值之间不需要任何空格隔开。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为\"在进行类型强制转换时,右括号与强制转换值之间不需要任何空格隔开\"的例子\n<例子1>\nint a = (int) 3.5;\n</例子1>",
|
||||
"no_example": "### 不能被判定为\"在进行类型强制转换时,右括号与强制转换值之间不需要任何空格隔开\"的例子\n<例子1>\nint a = (int)3.5;\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 22,
|
||||
"text": "方法参数在定义和传入时,多个参数逗号后面必须加空格",
|
||||
"detail": "缺陷类型:方法参数逗号后缺少空格;修复方案:方法参数在定义和传入时,多个参数逗号后面必须加空格。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为\"方法参数在定义和传入时,多个参数逗号后面必须加空格\"的例子\n<例子1>\npublic void exampleMethod(int a,int b,int c) {}\n</例子1>",
|
||||
"no_example": "### 不能被判定为\"方法参数在定义和传入时,多个参数逗号后面必须加空格\"的例子\n<例子1>\npublic void exampleMethod(int a, int b, int c) {}\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 23,
|
||||
"text": "禁止使用构造方法 BigDecimal(double) 的方式把 double 值转化为 BigDecimal 对象",
|
||||
"detail": "缺陷类型:使用不推荐的 BigDecimal 构造方法;修复方案:禁止使用构造方法 BigDecimal(double) 的方式把 double 值转化为 BigDecimal 对象,推荐使用 BigDecimal 的 valueOf 方法。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为\"禁止使用构造方法 BigDecimal(double) 的方式把 double 值转化为 BigDecimal 对象\"的例子\n<例子1>\nBigDecimal bd = new BigDecimal(0.1);\n</例子1>",
|
||||
"no_example": "### 不能被判定为\"禁止使用构造方法 BigDecimal(double) 的方式把 double 值转化为 BigDecimal 对象\"的例子\n<例子1>\nBigDecimal bd = BigDecimal.valueOf(0.1);\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 24,
|
||||
"text": "不能有多余的分号",
|
||||
"detail": "缺陷类型:多余的分号;修复方案:删除多余的分号",
|
||||
"yes_example": "### 被判定为\"不能有多余的分号\"的例子\n<例子1>\npublic void trigger(String executionId, Map<String, Object> processVariables) {\n commandExecutor.execute(new TriggerCmd(executionId, processVariables));\n}\n;\na = 1;\nb = 2;\nsum = a + b;\n这段代码中包含一个多余的分号\";\",所以这个被判定为\"不能有多余的分号\"\n</例子1>",
|
||||
"no_example": "### 不能被判定为\"不能有多余的分号\"的例子\n<例子1>\nwhile (True) {\n\ta = a + 1;\n\tbreak;\n}这段代码每个分号都是必须要的,所以这个能被判定为\"不能有多余的分号\"\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 25,
|
||||
"text": "非线程安全的 SimpleDateFormat 使用,必须在函数或代码块级别使用synchronized",
|
||||
"detail": "缺陷类型:非线程安全的 SimpleDateFormat 使用;修复方案:在函数或代码块级别加上synchronized修饰 或 使用其他线程安全的方式",
|
||||
"yes_example": "### 被判定为\"非线程安全的 SimpleDateFormat 使用,必须在函数或代码块级别使用synchronized\"的例子\n<例子1>\npublic void formatDate(Date date) {\n\tSimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\");\n\tSystem.out.println(\"Formatted date: \" + sdf.format(date));\n}这段代码中的函数formatDate在未使用synchronized同步修饰的情况下使用了SimpleDateFormat,这是线程不安全的,所以这个被判定为\"非线程安全的 SimpleDateFormat 使用,必须在函数或代码块级别使用synchronized\"\n</例子1>",
|
||||
"no_example": "### 不能被判定为\"非线程安全的 SimpleDateFormat 使用,必须在函数或代码块级别使用synchronized\"的例子\n<例子1>\npublic synchronized void formatDate(Date date) {\n\tSimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\");\n\tSystem.out.println(\"Formatted date: \" + sdf.format(date));\n}这段代码是在synchronized同步块对函数'formatDate'进行保护,保证了线程安全,所以这个不能被判定为\"非线程安全的 SimpleDateFormat 使用,必须在函数或代码块级别使用synchronized\"\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 26,
|
||||
"text": "类名使用驼峰式UpperCamelCase风格, 方法名、参数名、成员变量、局部变量都统一使用lowerCamelCase风格",
|
||||
"detail": "缺陷类型:命名规范;修复方案:类名使用UpperCamelCase风格,方法名、参数名、成员变量、局部变量使用lowerCamelCase风格。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为命名规范的例子\n<例子1>\npublic class myClass {\n private int MyVariable;\n public void MyMethod() {}\n}\n这段代码中的类名、成员变量和方法名没有遵循驼峰命名法,所以被判定为命名规范问题。\n</例子1>",
|
||||
"no_example": "### 不能被判定为命名规范的例子\n<例子1>\npublic class MyClass {\n private int myVariable;\n public void myMethod() {}\n}\n这段代码中的类名、成员变量和方法名都遵循了驼峰命名法,所以不能被判定为命名规范问题。\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 27,
|
||||
"text": "抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾,测试类命名以它要测试的类的名称开始,以 Test 结尾",
|
||||
"detail": "缺陷类型:命名规范;修复方案:抽象类命名使用 Abstract 或 Base 开头,异常类命名使用 Exception 结尾,测试类命名以它要测试的类的名称开始,以 Test 结尾。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为命名规范的例子\n<例子1>\npublic class MyAbstractClass {}\npublic class MyExceptionClass {}\npublic class TestMyClass {}\n这段代码中的抽象类、异常类和测试类的命名不符合规范,所以被判定为命名规范问题。\n</例子1>",
|
||||
"no_example": "### 不能被判定为命名规范的例子\n<例子1>\npublic abstract class AbstractMyClass {}\npublic class MyCustomException extends Exception {}\npublic class MyClassTest {}\n这段代码中的抽象类、异常类和测试类的命名都符合规范,所以不能被判定为命名规范问题。\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 28,
|
||||
"text": "POJO 类中的任何布尔类型的变量,都不要加 is 前缀",
|
||||
"detail": "缺陷类型:命名规范;修复方案:POJO 类中的布尔类型变量不要加 is 前缀。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为命名规范的例子\n<例子1>\npublic class User {\n private boolean isActive;\n}\n这段代码中的布尔类型变量加了 is 前缀,所以被判定为命名规范问题。\n</例子1>",
|
||||
"no_example": "### 不能被判定为命名规范的例子\n<例子1>\npublic class User {\n private boolean active;\n}\n这段代码中的布尔类型变量没有加 is 前缀,所以不能被判定为命名规范问题。\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 29,
|
||||
"text": "杜绝完全不规范的英文缩写,避免望文不知义。",
|
||||
"detail": "缺陷类型:命名规范;修复方案:避免使用不规范的英文缩写,确保代码可读性。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为命名规范的例子\n<例子1>\npublic class CfgMgr {\n private int cnt;\n}\n这段代码中的类名和变量名使用了不规范的英文缩写,所以被判定为命名规范问题。\n</例子1>",
|
||||
"no_example": "### 不能被判定为命名规范的例子\n<例子1>\npublic class ConfigManager {\n private int count;\n}\n这段代码中的类名和变量名没有使用不规范的英文缩写,所以不能被判定为命名规范问题。\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 30,
|
||||
"text": "不允许任何魔法值(即未经预先定义的常量)直接出现在代码中",
|
||||
"detail": "缺陷类型:代码规范;修复方案:将魔法值定义为常量。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为代码规范的例子\n<例子1>\npublic class MagicNumberExample {\n public void calculate() {\n int result = 42 * 2;\n }\n}\n这段代码中直接使用了魔法值 42,所以被判定为代码规范问题。\n</例子1>",
|
||||
"no_example": "### 不能被判定为代码规范的例子\n<例子1>\npublic class MagicNumberExample {\n private static final int MULTIPLIER = 42;\n public void calculate() {\n int result = MULTIPLIER * 2;\n }\n}\n这段代码中将魔法值定义为了常量,所以不能被判定为代码规范问题。\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 31,
|
||||
"text": "long 或 Long 赋值时,数值后使用大写 L,不能是小写 l,浮点数类型的数值后缀统一为大写的 D 或 F",
|
||||
"detail": "缺陷类型:代码规范;修复方案:long 或 Long 赋值时使用大写 L,浮点数类型的数值后缀使用大写的 D 或 F。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为代码规范的例子\n<例子1>\npublic class NumberExample {\n private long value = 1000l;\n private double pi = 3.14d;\n}\n这段代码中使用了小写的 l 和 d,所以被判定为代码规范问题。\n</例子1>",
|
||||
"no_example": "### 不能被判定为代码规范的例子\n<例子1>\npublic class NumberExample {\n private long value = 1000L;\n private double pi = 3.14D;\n}\n这段代码中使用了大写的 L 和 D,所以不能被判定为代码规范问题。\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 32,
|
||||
"text": "如果大括号内为空,简洁地写成{}即可,大括号中间无需换行和空格;如果是非空代码块,则:1)左大括号前不换行。2)左大括号后换行。3)右大括号前换行。4)右大括号后还有 else 等代码则不换行;表示终止的右大括号后必须换行。",
|
||||
"detail": "缺陷类型:代码格式;修复方案:遵循大括号的使用规范。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为代码格式的例子\n<例子1>\npublic class BracketExample{public void method(){\n if (true) {\n }}\n}\n这段代码中的大括号使用不符合规范,所以被判定为代码格式问题。\n</例子1>",
|
||||
"no_example": "### 不能被判定为代码格式的例子\n<例子1>\npublic class BracketExample {\n public void method() {\n if (true) {\n // do something\n }\n }\n}\n这段代码中的大括号使用符合规范,所以不能被判定为代码格式问题。\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 33,
|
||||
"text": "左小括号和右边相邻字符之间不需要空格;右小括号和左边相邻字符之间也不需要空格;而左大括号前需要加空格。",
|
||||
"detail": "缺陷类型:代码格式;修复方案:遵循括号和空格的使用规范。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为代码格式的例子\n<例子1>\npublic class SpaceExample {\n public void method (){\n }\n}\n这段代码中的括号和空格使用不符合规范,所以被判定为代码格式问题。\n</例子1>",
|
||||
"no_example": "### 不能被判定为代码格式的例子\n<例子1>\npublic class SpaceExample {\n public void method() {}\n}\n这段代码中的括号和空格使用符合规范,所以不能被判定为代码格式问题。\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 34,
|
||||
"text": "if / for / while / switch / do 等保留字与左右括号之间都必须加空格。",
|
||||
"detail": "缺陷类型:代码格式;修复方案:保留字与左右括号之间加空格。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为代码格式的例子\n<例子1>\npublic class KeywordExample {\n public void method() {\n if(true) {\n }\n }\n}\n这段代码中的 if 关键字与括号之间没有空格,所以被判定为代码格式问题。\n</例子1>",
|
||||
"no_example": "### 不能被判定为代码格式的例子\n<例子1>\npublic class KeywordExample {\n public void method() {\n if (true) {\n }\n }\n}\n这段代码中的 if 关键字与括号之间有空格,所以不能被判定为代码格式问题。\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 35,
|
||||
"text": "所有整型包装类对象之间值的比较,全部使用 equals 方法比较",
|
||||
"detail": "缺陷类型:代码规范;修复方案:整型包装类对象之间的值比较使用 equals 方法。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为代码规范的例子\n<例子1>\npublic class IntegerComparison {\n public void compare() {\n Integer a = 100;\n Integer b = 100;\n if (a == b) {\n }\n }\n}\n这段代码中使用了 == 比较整型包装类对象,所以被判定为代码规范问题。\n</例子1>",
|
||||
"no_example": "### 不能被判定为代码规范的例子\n<例子1>\npublic class IntegerComparison {\n public void compare() {\n Integer a = 100;\n Integer b = 100;\n if (a.equals(b)) {\n }\n }\n}\n这段代码中使用了 equals 方法比较整型包装类对象,所以不能被判定为代码规范问题。\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 36,
|
||||
"text": "BigDecimal 的等值比较应使用 compareTo() 方法,而不是 equals() 方法。",
|
||||
"detail": "缺陷类型:BigDecimal 等值比较错误;修复方案:使用 compareTo() 方法进行比较。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为\"BigDecimal 的等值比较应使用 compareTo() 方法,而不是 equals() 方法\"的例子\n<例子1>\nBigDecimal a = new BigDecimal(\"1.0\");\nBigDecimal b = new BigDecimal(\"1.00\");\nif (a.equals(b)) {\n // 这段代码会返回 false,因为 equals() 方法会比较精度\n}\n</例子1>",
|
||||
"no_example": "### 不能被判定为\"BigDecimal 的等值比较应使用 compareTo() 方法,而不是 equals() 方法\"的例子\n<例子1>\nBigDecimal a = new BigDecimal(\"1.0\");\nBigDecimal b = new BigDecimal(\"1.00\");\nif (a.compareTo(b) == 0) {\n // 这段代码会返回 true,因为 compareTo() 方法只比较数值\n}\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 37,
|
||||
"text": "禁止在 POJO 类中,同时存在对应属性 xxx 的 isXxx() 和 getXxx() 方法。",
|
||||
"detail": "缺陷类型:POJO 类中存在重复的 getter 方法;修复方案:确保只存在一个 getter 方法。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为\"禁止在 POJO 类中,同时存在对应属性 xxx 的 isXxx() 和 getXxx() 方法\"的例子\n<例子1>\npublic class User {\n private boolean active;\n public boolean isActive() {\n return active;\n }\n public boolean getActive() {\n return active;\n }\n}\n</例子1>",
|
||||
"no_example": "### 不能被判定为\"禁止在 POJO 类中,同时存在对应属性 xxx 的 isXxx() 和 getXxx() 方法\"的例子\n<例子1>\npublic class User {\n private int age;\n public int getAge() {\n return age;\n }\n}\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 38,
|
||||
"text": "日期格式化时,传入 pattern 中表示年份统一使用小写的 y。",
|
||||
"detail": "缺陷类型:日期格式化错误;修复方案:使用小写的 y 表示年份。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为\"日期格式化时,传入 pattern 中表示年份统一使用小写的 y\"的例子\n<例子1>\nSimpleDateFormat sdf = new SimpleDateFormat(\"YYYY-MM-dd\");\n</例子1>",
|
||||
"no_example": "### 不能被判定为\"日期格式化时,传入 pattern 中表示年份统一使用小写的 y\"的例子\n<例子1>\nSimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\");\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 39,
|
||||
"text": "不允许在程序任何地方中使用:1)java.sql.Date 2)java.sql.Time 3)java.sql.Timestamp。",
|
||||
"detail": "缺陷类型:使用了 java.sql 包中的日期类;修复方案:使用 java.time 包中的日期类。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为\"不允许在程序任何地方中使用:1)java.sql.Date 2)java.sql.Time 3)java.sql.Timestamp\"的例子\n<例子1>\njava.sql.Date sqlDate = new java.sql.Date(System.currentTimeMillis());\n</例子1>",
|
||||
"no_example": "### 不能被判定为\"不允许在程序任何地方中使用:1)java.sql.Date 2)java.sql.Time 3)java.sql.Timestamp\"的例子\n<例子1>\njava.time.LocalDate localDate = java.time.LocalDate.now();\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 40,
|
||||
"text": "判断所有集合内部的元素是否为空,使用 isEmpty() 方法,而不是 size() == 0 的方式。",
|
||||
"detail": "缺陷类型:集合判空方式错误;修复方案:使用 isEmpty() 方法。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为\"判断所有集合内部的元素是否为空,使用 isEmpty() 方法,而不是 size() == 0 的方式\"的例子\n<例子1>\nList<String> list = new ArrayList<>();\nif (list.size() == 0) {\n // 判空逻辑\n}\n</例子1>",
|
||||
"no_example": "### 不能被判定为\"判断所有集合内部的元素是否为空,使用 isEmpty() 方法,而不是 size() == 0 的方式\"的例子\n<例子1>\nList<String> list = new ArrayList<>();\nif (list.isEmpty()) {\n // 判空逻辑\n}\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 41,
|
||||
"text": "只要覆写 equals,就必须覆写 hashCode。",
|
||||
"detail": "缺陷类型:未覆写 hashCode 方法;修复方案:同时覆写 equals 和 hashCode 方法。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为\"只要覆写 equals,就必须覆写 hashCode\"的例子\n<例子1>\npublic class User {\n private String name;\n @Override\n public boolean equals(Object o) {\n if (this == o) return true;\n if (o == null || getClass() != o.getClass()) return false;\n User user = (User) o;\n return Objects.equals(name, user.name);\n }\n}\n</例子1>",
|
||||
"no_example": "### 不能被判定为\"只要覆写 equals,就必须覆写 hashCode\"的例子\n<例子1>\npublic class User {\n private String name;\n @Override\n public boolean equals(Object o) {\n if (this == o) return true;\n if (o == null || getClass() != o.getClass()) return false;\n User user = (User) o;\n return Objects.equals(name, user.name);\n }\n @Override\n public int hashCode() {\n return Objects.hash(name);\n }\n}\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 42,
|
||||
"text": "使用 Map 的方法 keySet() / values() / entrySet() 返回集合对象时,不可以对其进行添加元素操作,否则会抛出 UnsupportedOperationException 异常。",
|
||||
"detail": "缺陷类型:对 Map 的 keySet() / values() / entrySet() 返回的集合进行添加操作;修复方案:避免对这些集合进行添加操作。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为\"使用 Map 的方法 keySet() / values() / entrySet() 返回集合对象时,不可以对其进行添加元素操作,否则会抛出 UnsupportedOperationException 异常\"的例子\n<例子1>\nMap<String, String> map = new HashMap<>();\nmap.put(\"key1\", \"value1\");\nSet<String> keys = map.keySet();\nkeys.add(\"key2\");\n</例子1>",
|
||||
"no_example": "### 不能被判定为\"使用 Map 的方法 keySet() / values() / entrySet() 返回集合对象时,不可以对其进行添加元素操作,否则会抛出 UnsupportedOperationException 异常\"的例子\n<例子1>\nMap<String, String> map = new HashMap<>();\nmap.put(\"key1\", \"value1\");\nSet<String> keys = map.keySet();\n// 不进行添加操作\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 43,
|
||||
"text": "不要在 foreach 循环里进行元素的 remove / add 操作。remove 元素请使用 iterator 方式,如果并发操作,需要对 iterator",
|
||||
"detail": "缺陷类型:在 foreach 循环中进行元素的 remove / add 操作;修复方案:使用 iterator 进行元素的 remove 操作。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为\"不要在 foreach 循环里进行元素的 remove / add 操作。remove 元素请使用 iterator 方式,如果并发操作,需要对 iterator\"的例子\n<例子1>\nList<String> list = new ArrayList<>(Arrays.asList(\"a\", \"b\", \"c\"));\nfor (String s : list) {\n if (s.equals(\"a\")) {\n list.remove(s);\n }\n}\n</例子1>",
|
||||
"no_example": "### 不能被判定为\"不要在 foreach 循环里进行元素的 remove / add 操作。remove 元素请使用 iterator 方式,如果并发操作,需要对 iterator\"的例子\n<例子1>\nList<String> list = new ArrayList<>(Arrays.asList(\"a\", \"b\", \"c\"));\nIterator<String> iterator = list.iterator();\nwhile (iterator.hasNext()) {\n String s = iterator.next();\n if (s.equals(\"a\")) {\n iterator.remove();\n }\n}\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 44,
|
||||
"text": "类、类属性、类方法的注释必须使用 Javadoc 规范,使用 /** 内容 */ 格式,不得使用 // xxx方式。",
|
||||
"detail": "缺陷类型:注释不符合 Javadoc 规范;修复方案:使用 Javadoc 规范的注释格式。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为\"类、类属性、类方法的注释必须使用 Javadoc 规范,使用 /** 内容 */ 格式,不得使用 // xxx方式\"的例子\n<例子1>\npublic class Example {\n // 这是一个类注释\n private String name;\n // 这是一个属性注释\n public String getName() {\n return name;\n }\n // 这是一个方法注释\n}\n</例子1>",
|
||||
"no_example": "### 不能被判定为\"类、类属性、类方法的注释必须使用 Javadoc 规范,使用 /** 内容 */ 格式,不得使用 // xxx方式\"的例子\n<例子1>\n/**\n * 这是一个类注释\n */\npublic class Example {\n /**\n * 这是一个属性注释\n */\n private String name;\n /**\n * 这是一个方法注释\n */\n public String getName() {\n return name;\n }\n}\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 45,
|
||||
"text": "所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释",
|
||||
"detail": "缺陷类型:缺少 Javadoc 注释;修复方案:为所有的抽象方法(包括接口中的方法)添加 Javadoc 注释,除了返回值、参数异常说明外,还必须指出该方法做什么事情,实现什么功能。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为缺少 Javadoc 注释的例子\n<例子1>\npublic interface MyInterface {\n void doSomething();\n}\n这段代码中的接口方法 doSomething() 没有 Javadoc 注释,所以被判定为缺少 Javadoc 注释。\n</例子1>",
|
||||
"no_example": "### 不能被判定为缺少 Javadoc 注释的例子\n<例子1>\n/**\n * 执行某个操作\n * @param param 参数说明\n * @return 返回值说明\n * @throws Exception 异常说明\n */\npublic interface MyInterface {\n void doSomething(String param) throws Exception;\n}\n这段代码中的接口方法 doSomething() 有完整的 Javadoc 注释,所以不能被判定为缺少 Javadoc 注释。\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 46,
|
||||
"text": "方法内部单行注释和多行注释的使用规范",
|
||||
"detail": "缺陷类型:注释使用不规范;修复方案:方法内部单行注释,在被注释语句上方另起一行,使用 // 注释。方法内部多行注释使用 /* */注释,注意与代码对齐。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为注释使用不规范的例子\n<例子1>\npublic void exampleMethod() {\n int a = 1; // 初始化变量a\n int b = 2; /* 初始化变量b */\n}\n这段代码中的单行注释和多行注释没有按照规范使用,所以被判定为注释使用不规范。\n</例子1>",
|
||||
"no_example": "### 不能被判定为注释使用不规范的例子\n<例子1>\npublic void exampleMethod() {\n // 初始化变量a\n int a = 1;\n /*\n * 初始化变量b\n */\n int b = 2;\n}\n这段代码中的单行注释和多行注释按照规范使用,所以不能被判定为注释使用不规范。\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 47,
|
||||
"text": "所有的枚举类型字段必须要有注释",
|
||||
"detail": "缺陷类型:枚举类型字段缺少注释;修复方案:为所有的枚举类型字段添加注释,说明每个数据项的用途。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为枚举类型字段缺少注释的例子\n<例子1>\npublic enum Status {\n ACTIVE,\n INACTIVE\n}\n这段代码中的枚举类型字段没有注释,所以被判定为枚举类型字段缺少注释。\n</例子1>",
|
||||
"no_example": "### 不能被判定为枚举类型字段缺少注释的例子\n<例子1>\npublic enum Status {\n /**\n * 活跃状态\n */\n ACTIVE,\n /**\n * 非活跃状态\n */\n INACTIVE\n}\n这段代码中的枚举类型字段有注释,所以不能被判定为枚举类型字段缺少注释。\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 48,
|
||||
"text": "finally 块必须对资源对象、流对象进行关闭",
|
||||
"detail": "缺陷类型:资源对象、流对象未在 finally 块中关闭;修复方案:在 finally 块中对资源对象、流对象进行关闭,有异常也要做 try-catch。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为资源对象、流对象未在 finally 块中关闭的例子\n<例子1>\npublic void readFile() {\n FileInputStream fis = null;\n try {\n fis = new FileInputStream(\"file.txt\");\n // 读取文件内容\n } catch (IOException e) {\n e.printStackTrace();\n }\n}\n这段代码中的 FileInputStream 对象没有在 finally 块中关闭,所以被判定为资源对象、流对象未在 finally 块中关闭。\n</例子1>",
|
||||
"no_example": "### 不能被判定为资源对象、流对象未在 finally 块中关闭的例子\n<例子1>\npublic void readFile() {\n FileInputStream fis = null;\n try {\n fis = new FileInputStream(\"file.txt\");\n // 读取文件内容\n } catch (IOException e) {\n e.printStackTrace();\n } finally {\n if (fis != null) {\n try {\n fis.close();\n } catch (IOException e) {\n e.printStackTrace();\n }\n }\n }\n}\n这段代码中的 FileInputStream 对象在 finally 块中关闭,所以不能被判定为资源对象、流对象未在 finally 块中关闭。\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 49,
|
||||
"text": "常量命名应该全部大写,单词间用下划线隔开",
|
||||
"detail": "缺陷类型:常量命名不规范;修复方案:常量命名应该全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为\"常量命名应该全部大写,单词间用下划线隔开\"的例子\n<例子1>\npublic static final int maxCount = 100;\n</例子1>",
|
||||
"no_example": "### 不能被判定为\"常量命名应该全部大写,单词间用下划线隔开\"的例子\n<例子1>\npublic static final int MAX_COUNT = 100;\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 50,
|
||||
"text": "任何二目、三目运算符的左右两边都需要加一个空格",
|
||||
"detail": "缺陷类型:运算符两边缺少空格;修复方案:任何二目、三目运算符的左右两边都需要加一个空格。",
|
||||
"language": "Java",
|
||||
"yes_example": "### 被判定为\"任何二目、三目运算符的左右两边都需要加一个空格\"的例子\n<例子1>\nint a=b+c;\n</例子1>",
|
||||
"no_example": "### 不能被判定为\"任何二目、三目运算符的左右两边都需要加一个空格\"的例子\n<例子1>\nint a = b + c;\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"text": "避免使用from <module> import *",
|
||||
"detail": "缺陷类型:避免使用from <module> import *,导入所有内容会造成命名冲突;修复方案:每个使用到的子依赖需分别导入。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'避免使用from <module> import *'的例子\n<例子1>\nfrom math import *\n</例子1>",
|
||||
"no_example": "### 不能被判定为'避免使用from <module> import *'的例子\n<例子1>\nfrom math import sqrt, pi\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"text": "避免使用__import__()函数动态导入模块",
|
||||
"detail": "缺陷类型:避免使用__import__()函数动态导入模块;修复方案:使用标准的import语句。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'避免使用__import__()函数动态导入模块'的例子\n<例子1>\nmodule = __import__('math')\n</例子1>",
|
||||
"no_example": "### 不能被判定为'避免使用__import__()函数动态导入模块'的例子\n<例子1>\nimport math\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"text": "导入语句未按标准库导入、相关第三方导入、本地应用/库特定导入的顺序分组",
|
||||
"detail": "缺陷类型:导入语句未按标准库导入、相关第三方导入、本地应用/库特定导入的顺序分组;修复方案:按顺序分组导入语句。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'导入语句未按标准库导入、相关第三方导入、本地应用/库特定导入的顺序分组'的例子\n<例子1>\nimport requests\n\nimport mymodule\n\nimport os\n</例子1>",
|
||||
"no_example": "### 不能被判定为'导入语句未按标准库导入、相关第三方导入、本地应用/库特定导入的顺序分组'的例子\n<例子1>\nimport os\nimport requests\n\nimport mymodule\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"text": "避免未使用的函数形参",
|
||||
"detail": "缺陷类型:存在未使用的函数形参;修复方案:移除未使用的函数形参。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'避免未使用的函数形参'的例子\n<例子1>\ndef func(a, b):\n return a\n</例子1>",
|
||||
"no_example": "### 不能被判定为'避免未使用的函数形参'的例子\n<例子1>\ndef func(a):\n return a\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"text": "使用is not None来检查一个变量是否不是None",
|
||||
"detail": "缺陷类型:未使用is not None来检查一个变量是否不是None;修复方案:使用is not None来检查。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'使用is not None来检查一个变量是否不是None'的例子\n<例子1>\nif variable != None:\n pass\n</例子1>",
|
||||
"no_example": "### 不能被判定为'使用is not None来检查一个变量是否不是None'的例子\n<例子1>\nif variable is not None:\n pass\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"text": "避免使用==或!=来比较实例的等价性",
|
||||
"detail": "缺陷类型:使用==或!=来比较实例的等价性;修复方案:应使用equals比较。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'避免使用==或!=来比较实例的等价性'的例子\n<例子1>\nif obj1 == obj2:\n pass\n</例子1>",
|
||||
"no_example": "### 不能被判定为'避免使用==或!=来比较实例的等价性'的例子\n<例子1>\nif obj1.equals(obj2):\n pass\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"text": "使用描述性变量名,避免使用单字母变量名",
|
||||
"detail": "缺陷类型:使用单字母变量名;修复方案:使用描述性变量名。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'使用描述性变量名,避免使用单字母变量名'的例子\n<例子1>\nx = 10\n</例子1>",
|
||||
"no_example": "### 不能被判定为'使用描述性变量名,避免使用单字母变量名'的例子\n<例子1>\ncount = 10\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"text": "常量命名使用全大写字母,并用下划线分隔",
|
||||
"detail": "缺陷类型:常量命名未使用全大写字母或未用下划线分隔;修复方案:常量命名使用全大写字母,并用下划线分隔。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'常量命名未使用全大写字母,并用下划线分隔'的例子\n<例子1>\npi = 3.14\n</例子1>",
|
||||
"no_example": "### 不能被判定为'常量命名未使用全大写字母,并用下划线分隔'的例子\n<例子1>\nPI = 3.14\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"text": "类名应该使用驼峰式命名(CamelCase)",
|
||||
"detail": "缺陷类型:类名未使用驼峰式命名;修复方案:类名使用驼峰式命名。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'类名未使用驼峰式命名(CamelCase)'的例子\n<例子1>\nclass my_class:\n pass\n</例子1>",
|
||||
"no_example": "### 不能被判定为'类名未使用驼峰式命名(CamelCase)'的例子\n<例子1>\nclass MyClass:\n pass\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"text": "尽量使用with语句来管理资源",
|
||||
"detail": "缺陷类型:未使用with语句来管理资源;修复方案:使用with语句来管理资源。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'未使用with语句来管理资源'的例子\n<例子1>\nfile = open('file.txt', 'r')\ncontent = file.read()\nfile.close()\n</例子1>",
|
||||
"no_example": "### 不能被判定为'未使用with语句来管理资源'的例子\n<例子1>\nwith open('file.txt', 'r') as file:\n content = file.read()\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"text": "避免使用except:来捕获所有异常,应该指定异常类型",
|
||||
"detail": "缺陷类型:捕获所有异常;修复方案:指定具体的异常类型。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'避免使用except:来捕获所有异常,应该指定异常类型'的例子\n<例子1>\ntry:\n # some code\nexcept:\n handle_error()\n这段代码中使用了except:来捕获所有异常,所以这个被判定为'避免使用except:来捕获所有异常,应该指定异常类型'\n</例子1>",
|
||||
"no_example": "### 不能被判定为'避免使用except:来捕获所有异常,应该指定异常类型'的例子\n<例子1>\ntry:\n # some code\nexcept ValueError:\n handle_value_error()\n这段代码中指定了具体的异常类型ValueError,所以这个不能被判定为'避免使用except:来捕获所有异常,应该指定异常类型'\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"text": "尽量避免手动拼接字符串",
|
||||
"detail": "缺陷类型:手动拼接字符串;修复方案:使用格式化字符串或join方法。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'尽量避免手动拼接字符串'的例子\n<例子1>\nname = 'John'\ngreeting = 'Hello, ' + name + '!'\n这段代码中使用了手动拼接字符串,所以这个被判定为'尽量避免手动拼接字符串'\n</例子1>",
|
||||
"no_example": "### 不能被判定为'尽量避免手动拼接字符串'的例子\n<例子1>\nname = 'John'\ngreeting = f'Hello, {name}!'\n这段代码中使用了格式化字符串,所以这个不能被判定为'尽量避免手动拼接字符串'\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"text": "尽量避免出现魔法字符和数字,声明为常量",
|
||||
"detail": "缺陷类型:使用魔法字符和数字;修复方案:将其声明为常量。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'尽量避免出现魔法字符和数字,声明为常量'的例子\n<例子1>\nif status == 1:\n print('Active')\n这段代码中使用了魔法数字1,所以这个被判定为'尽量避免出现魔法字符和数字,声明为常量'\n</例子1>",
|
||||
"no_example": "### 不能被判定为'尽量避免出现魔法字符和数字,声明为常量'的例子\n<例子1>\nACTIVE_STATUS = 1\nif status == ACTIVE_STATUS:\n print('Active')\n这段代码中将魔法数字声明为了常量ACTIVE_STATUS,所以这个不能被判定为'尽量避免出现魔法字符和数字,声明为常量'\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"text": "boolean变量判断无需显式比较",
|
||||
"detail": "缺陷类型:显式比较boolean变量;修复方案:直接使用boolean变量进行判断。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'boolean变量判断无需显式比较'的例子\n<例子1>\nflag = True\nif flag == True:\n print('Flag is true')\n这段代码中对boolean变量进行了显式比较,所以这个被判定为'boolean变量判断无需显式比较'\n</例子1>",
|
||||
"no_example": "### 不能被判定为'boolean变量判断无需显式比较'的例子\n<例子1>\nflag = True\nif flag:\n print('Flag is true')\n这段代码中直接使用了boolean变量进行判断,所以这个不能被判定为'boolean变量判断无需显式比较'\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"text": "使用isinstance()来检查对象的类型",
|
||||
"detail": "缺陷类型:使用type()检查对象类型;修复方案:使用isinstance()函数。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'使用isinstance()来检查对象的类型'的例子\n<例子1>\nif type(obj) == list:\n print('obj is a list')\n这段代码中使用了type()来检查对象类型,所以这个被判定为'使用isinstance()来检查对象的类型'\n</例子1>",
|
||||
"no_example": "### 不能被判定为'使用isinstance()来检查对象的类型'的例子\n<例子1>\nif isinstance(obj, list):\n print('obj is a list')\n这段代码中使用了isinstance()来检查对象类型,所以这个不能被判定为'使用isinstance()来检查对象的类型'\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"text": "避免使用os.system()来调用外部命令",
|
||||
"detail": "缺陷类型:使用os.system()调用外部命令;修复方案:使用subprocess模块。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'避免使用os.system()来调用外部命令'的例子\n<例子1>\nos.system('ls -l')\n这段代码中使用了os.system()来调用外部命令,所以这个被判定为'避免使用os.system()来调用外部命令'\n</例子1>",
|
||||
"no_example": "### 不能被判定为'避免使用os.system()来调用外部命令'的例子\n<例子1>\nimport subprocess\nsubprocess.run(['ls', '-l'])\n这段代码中使用了subprocess模块来调用外部命令,所以这个不能被判定为'避免使用os.system()来调用外部命令'\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"text": "只使用@property装饰器来创建只读属性,而非修改属性",
|
||||
"detail": "缺陷类型:使用@property装饰器创建可修改属性;修复方案:只使用@property装饰器创建只读属性。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'只使用@property装饰器来创建只读属性,而非修改属性'的例子\n<例子1>\nclass MyClass:\n def __init__(self, value):\n self._value = value\n\n @property\n def value(self, new_value):\n self._value = new_value\n这段代码中使用@property装饰器创建了可修改属性,所以这个被判定为'只使用@property装饰器来创建只读属性,而非修改属性'\n</例子1>",
|
||||
"no_example": "### 不能被判定为'只使用@property装饰器来创建只读属性,而非修改属性'的例子\n<例子1>\nclass MyClass:\n def __init__(self, value):\n self._value = value\n\n @property\n def value(self):\n return self._value\n这段代码中使用@property装饰器创建了只读属性,所以这个不能被判定为'只使用@property装饰器来创建只读属性,而非修改属性'\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"text": "在使用索引或切片时,不要在方括号或冒号内加空格",
|
||||
"detail": "缺陷类型:在索引或切片的方括号或冒号内加空格;修复方案:去掉方括号或冒号内的空格。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'在使用索引或切片时,不要在方括号或冒号内加空格'的例子\n<例子1>\nlist = [1, 2, 3, 4]\nsublist = list[ 1 : 3 ]\n这段代码中在索引或切片的方括号或冒号内加了空格,所以这个被判定为'在使用索引或切片时,不要在方括号或冒号内加空格'\n</例子1>",
|
||||
"no_example": "### 不能被判定为'在使用索引或切片时,不要在方括号或冒号内加空格'的例子\n<例子1>\nlist = [1, 2, 3, 4]\nsublist = list[1:3]\n这段代码中在索引或切片的方括号或冒号内没有加空格,所以这个不能被判定为'在使用索引或切片时,不要在方括号或冒号内加空格'\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"text": "在逗号、分号或冒号前不要加空格,但在它们之后要加空格",
|
||||
"detail": "缺陷类型:在逗号、分号或冒号前加空格或在它们之后不加空格;修复方案:在逗号、分号或冒号前不要加空格,但在它们之后要加空格。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'在逗号、分号或冒号前不要加空格,但在它们之后要加空格'的例子\n<例子1>\nif x == 4 : print(x , y)\n这段代码中在逗号、分号或冒号前加了空格或在它们之后没有加空格,所以这个被判定为'在逗号、分号或冒号前不要加空格,但在它们之后要加空格'\n</例子1>",
|
||||
"no_example": "### 不能被判定为'在逗号、分号或冒号前不要加空格,但在它们之后要加空格'的例子\n<例子1>\nif x == 4: print(x, y)\n这段代码中在逗号、分号或冒号前没有加空格且在它们之后加了空格,所以这个不能被判定为'在逗号、分号或冒号前不要加空格,但在它们之后要加空格'\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"text": "对于二元操作符,两边都应有空格,例如 a = b + 1",
|
||||
"detail": "缺陷类型:二元操作符两边没有空格;修复方案:在二元操作符两边加空格。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'对于二元操作符,两边都应有空格,例如 a = b + 1'的例子\n<例子1>\na=b+1\n这段代码中二元操作符两边没有空格,所以这个被判定为'对于二元操作符,两边都应有空格,例如 a = b + 1'\n</例子1>",
|
||||
"no_example": "### 不能被判定为'对于二元操作符,两边都应有空格,例如 a = b + 1'的例子\n<例子1>\na = b + 1\n这段代码中二元操作符两边有空格,所以这个不能被判定为'对于二元操作符,两边都应有空格,例如 a = b + 1'\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"text": "避免使用Python关键字作为变量名或函数名",
|
||||
"detail": "缺陷类型:使用Python关键字作为变量名或函数名;修复方案:使用非关键字的名称。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'避免使用Python关键字作为变量名或函数名'的例子\n<例子1>\ndef class():\n pass\n</例子1>\n<例子2>\nfor = 5\n</例子2>",
|
||||
"no_example": "### 不能被判定为'避免使用Python关键字作为变量名或函数名'的例子\n<例子1>\ndef my_function():\n pass\n</例子1>\n<例子2>\nnumber = 5\n</例子2>"
|
||||
},
|
||||
{
|
||||
"id": 22,
|
||||
"text": "避免使用特殊字符作为变量名,例如$或@",
|
||||
"detail": "缺陷类型:使用特殊字符作为变量名;修复方案:使用合法的变量名。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'避免使用特殊字符作为变量名,例如$或@'的例子\n<例子1>\nmy$var = 10\n</例子1>\n<例子2>\n@var = 20\n</例子2>",
|
||||
"no_example": "### 不能被判定为'避免使用特殊字符作为变量名,例如$或@'的例子\n<例子1>\nmy_var = 10\n</例子1>\n<例子2>\nvar_20 = 20\n</例子2>"
|
||||
},
|
||||
{
|
||||
"id": 23,
|
||||
"text": "避免使用raise来重新抛出当前的异常,这会丢失原始的栈跟踪",
|
||||
"detail": "缺陷类型:使用raise重新抛出当前异常;修复方案:使用raise ... from ...语法。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'避免使用raise来重新抛出当前的异常,这会丢失原始的栈跟踪'的例子\n<例子1>\ntry:\n 1 / 0\nexcept ZeroDivisionError:\n raise\n</例子1>",
|
||||
"no_example": "### 不能被判定为'避免使用raise来重新抛出当前的异常,这会丢失原始的栈跟踪'的例子\n<例子1>\ntry:\n 1 / 0\nexcept ZeroDivisionError as e:\n raise RuntimeError('Error occurred') from e\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 24,
|
||||
"text": "避免在except块中使用pass,这会捕获并忽略异常",
|
||||
"detail": "缺陷类型:在except块中使用pass;修复方案:处理异常或记录日志。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'避免在except块中使用pass,这会捕获并忽略异常'的例子\n<例子1>\ntry:\n 1 / 0\nexcept ZeroDivisionError:\n pass\n</例子1>",
|
||||
"no_example": "### 不能被判定为'避免在except块中使用pass,这会捕获并忽略异常'的例子\n<例子1>\ntry:\n 1 / 0\nexcept ZeroDivisionError as e:\n logging.error('Error occurred: %s', e)\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 25,
|
||||
"text": "避免使用assert语句来执行重要的运行时检查",
|
||||
"detail": "缺陷类型:使用assert语句执行重要的运行时检查;修复方案:使用显式的条件检查和异常处理。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'避免使用assert语句来执行重要的运行时检查'的例子\n<例子1>\ndef divide(a, b):\n assert b != 0\n return a / b\n</例子1>",
|
||||
"no_example": "### 不能被判定为'避免使用assert语句来执行重要的运行时检查'的例子\n<例子1>\ndef divide(a, b):\n if b == 0:\n raise ValueError('b cannot be zero')\n return a / b\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 26,
|
||||
"text": "避免使用eval()和exec(),这些函数可能会带来安全风险",
|
||||
"detail": "缺陷类型:使用eval()和exec()函数;修复方案:使用安全的替代方案。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'避免使用eval()和exec(),这些函数可能会带来安全风险'的例子\n<例子1>\neval('print(1)')\n</例子1>\n<例子2>\nexec('a = 1')\n</例子2>",
|
||||
"no_example": "### 不能被判定为'避免使用eval()和exec(),这些函数可能会带来安全风险'的例子\n<例子1>\ncompiled_code = compile('print(1)', '<string>', 'exec')\nexec(compiled_code)\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 27,
|
||||
"text": "避免使用open()函数的exec模式,这可能会带来安全风险",
|
||||
"detail": "缺陷类型:使用open()函数的exec模式;修复方案:使用安全的文件操作模式。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'避免使用open()函数的exec模式,这可能会带来安全风险'的例子\n<例子1>\nopen('file.txt', 'w+').write('data')\n</例子1>",
|
||||
"no_example": "### 不能被判定为'避免使用open()函数的exec模式,这可能会带来安全风险'的例子\n<例子1>\nwith open('file.txt', 'w') as f:\n f.write('data')\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 28,
|
||||
"text": "避免使用sys.exit(),应使用异常来控制程序的退出",
|
||||
"detail": "缺陷类型:使用sys.exit()退出程序;修复方案:使用异常处理机制。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'避免使用sys.exit(),应使用异常来控制程序的退出'的例子\n<例子1>\nimport sys\nsys.exit(1)\n</例子1>",
|
||||
"no_example": "### 不能被判定为'避免使用sys.exit(),应使用异常来控制程序的退出'的例子\n<例子1>\nraise SystemExit(1)\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 29,
|
||||
"text": "避免使用time.sleep()进行线程同步,应使用同步原语,如锁或事件",
|
||||
"detail": "缺陷类型:使用time.sleep()进行线程同步;修复方案:使用同步原语。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'避免使用time.sleep()进行线程同步,应使用同步原语,如锁或事件'的例子\n<例子1>\nimport time\n\ndef worker():\n time.sleep(1)\n</例子1>",
|
||||
"no_example": "### 不能被判定为'避免使用time.sleep()进行线程同步,应使用同步原语,如锁或事件'的例子\n<例子1>\nimport threading\n\nevent = threading.Event()\n\ndef worker():\n event.wait()\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 30,
|
||||
"text": "每行代码尽量不超过79个字符",
|
||||
"detail": "缺陷类型:每行代码超过79个字符;修复方案:将长行代码拆分为多行。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'每行代码尽量不超过79个字符'的例子\n<例子1>\nprint('This is a very long line of code that exceeds the 79 characters limit')\n</例子1>",
|
||||
"no_example": "### 不能被判定为'每行代码尽量不超过79个字符'的例子\n<例子1>\nprint('This is a very long line of code that exceeds the 79 characters limit' +\n ' but it is split into two lines')\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 31,
|
||||
"text": "模块级别的函数和类定义之间用两个空行分隔,类内部的方法定义之间用一个空行分隔",
|
||||
"detail": "缺陷类型:模块级别的函数和类定义之间没有用两个空行分隔,类内部的方法定义之间没有用一个空行分隔;修复方案:按照规范添加空行。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'模块级别的函数和类定义之间用两个空行分隔,类内部的方法定义之间用一个空行分隔'的例子\n<例子1>\ndef func1():\n pass\ndef func2():\n pass\n</例子1>\n<例子2>\nclass MyClass:\n def method1(self):\n pass\n def method2(self):\n pass\n</例子2>",
|
||||
"no_example": "### 不能被判定为'模块级别的函数和类定义之间用两个空行分隔,类内部的方法定义之间用一个空行分隔'的例子\n<例子1>\ndef func1():\n pass\n\n\ndef func2():\n pass\n</例子1>\n<例子2>\nclass MyClass:\n def method1(self):\n pass\n\n def method2(self):\n pass\n</例子2>"
|
||||
},
|
||||
{
|
||||
"id": 32,
|
||||
"text": "使用小写字母和下划线分隔的方式命名变量和函数",
|
||||
"detail": "缺陷类型:变量和函数命名不符合小写字母和下划线分隔的方式;修复方案:使用小写字母和下划线分隔的方式命名。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'使用小写字母和下划线分隔的方式命名变量和函数'的例子\n<例子1>\ndef myFunction():\n pass\n</例子1>\n<例子2>\nmyVariable = 10\n</例子2>",
|
||||
"no_example": "### 不能被判定为'使用小写字母和下划线分隔的方式命名变量和函数'的例子\n<例子1>\ndef my_function():\n pass\n</例子1>\n<例子2>\nmy_variable = 10\n</例子2>"
|
||||
},
|
||||
{
|
||||
"id": 33,
|
||||
"text": "不允许使用print()函数来记录日志,使用logging模块等来记录日志",
|
||||
"detail": "缺陷类型:使用print()函数记录日志;修复方案:使用logging模块记录日志。",
|
||||
"language": "Python",
|
||||
"yes_example": "### 被判定为'不允许使用print()函数来记录日志,使用logging模块等来记录日志'的例子\n<例子1>\nprint('Error occurred')\n</例子1>",
|
||||
"no_example": "### 不能被判定为'不允许使用print()函数来记录日志,使用logging模块等来记录日志'的例子\n<例子1>\nimport logging\nlogging.error('Error occurred')\n</例子1>"
|
||||
}
|
||||
]
|
||||
0
metagpt/ext/cr/utils/__init__.py
Normal file
0
metagpt/ext/cr/utils/__init__.py
Normal file
68
metagpt/ext/cr/utils/cleaner.py
Normal file
68
metagpt/ext/cr/utils/cleaner.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
"""Cleaner."""
|
||||
|
||||
from unidiff import Hunk, PatchedFile, PatchSet
|
||||
|
||||
from metagpt.logs import logger
|
||||
|
||||
|
||||
def rm_patch_useless_part(patch: PatchSet, used_suffix: list[str] = ["java", "py"]) -> PatchSet:
|
||||
new_patch = PatchSet("")
|
||||
useless_files = []
|
||||
for pfile in patch:
|
||||
suffix = str(pfile.target_file).split(".")[-1]
|
||||
if suffix not in used_suffix or pfile.is_removed_file or "test" in pfile.target_file.casefold():
|
||||
useless_files.append(pfile.path)
|
||||
continue
|
||||
new_patch.append(pfile)
|
||||
logger.info(f"total file num: {len(patch)}, used file num: {len(new_patch)}, useless_files: {useless_files}")
|
||||
return new_patch
|
||||
|
||||
|
||||
def add_line_num_on_patch(patch: PatchSet, start_line_num: int = 1) -> PatchSet:
|
||||
new_patch = PatchSet("")
|
||||
lineno = start_line_num
|
||||
for pfile in patch:
|
||||
new_pfile = PatchedFile(
|
||||
source=pfile.source_file,
|
||||
target=pfile.target_file,
|
||||
source_timestamp=pfile.source_timestamp,
|
||||
target_timestamp=pfile.target_timestamp,
|
||||
)
|
||||
for hunk in pfile:
|
||||
arr = [str(line) for line in hunk]
|
||||
new_hunk = Hunk(
|
||||
src_start=hunk.source_start,
|
||||
src_len=hunk.source_length,
|
||||
tgt_start=hunk.target_start,
|
||||
tgt_len=hunk.target_length,
|
||||
section_header=hunk.section_header,
|
||||
)
|
||||
|
||||
for line in arr:
|
||||
# if len(line) > 0 and line[0] in ["+", "-"]:
|
||||
# line = f"{lineno} {line}"
|
||||
# lineno += 1
|
||||
line = f"{lineno} {line}"
|
||||
lineno += 1
|
||||
new_hunk.append(line)
|
||||
new_pfile.append(new_hunk)
|
||||
new_patch.append(new_pfile)
|
||||
return new_patch
|
||||
|
||||
|
||||
def get_code_block_from_patch(patch: PatchSet, code_start_line: str, code_end_line: str) -> str:
|
||||
line_arr = str(patch).split("\n")
|
||||
code_arr = []
|
||||
add_line_tag = False
|
||||
for line in line_arr:
|
||||
if line.startswith(f"{code_start_line} "):
|
||||
add_line_tag = True
|
||||
|
||||
if add_line_tag:
|
||||
new_line = " ".join(line.split(" ")[1:]) # rm line-no tag
|
||||
code_arr.append(new_line)
|
||||
|
||||
if line.startswith(f"{code_end_line} "):
|
||||
add_line_tag = False
|
||||
|
||||
return "\n".join(code_arr)
|
||||
20
metagpt/ext/cr/utils/schema.py
Normal file
20
metagpt/ext/cr/utils/schema.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class Point(BaseModel):
|
||||
id: int = Field(default=0, description="ID of the point.")
|
||||
text: str = Field(default="", description="Content of the point.")
|
||||
language: Literal["Python", "Java"] = Field(
|
||||
default="Python", description="The programming language that the point corresponds to."
|
||||
)
|
||||
file_path: str = Field(default="", description="The file that the points come from.")
|
||||
start_line: int = Field(default=0, description="The starting line number that the point refers to.")
|
||||
end_line: int = Field(default=0, description="The ending line number that the point refers to.")
|
||||
detail: str = Field(default="", description="File content from start_line to end_line.")
|
||||
yes_example: str = Field(default="", description="yes of point examples")
|
||||
no_example: str = Field(default="", description="no of point examples")
|
||||
|
||||
def rag_key(self) -> str:
|
||||
return self.text
|
||||
|
|
@ -12,7 +12,7 @@ class Engineer2(RoleZero):
|
|||
goal: str = "Take on game, app, and web development"
|
||||
instruction: str = ENGINEER2_INSTRUCTION
|
||||
|
||||
tools: str = ["Plan", "Editor:write,read", "RoleZero", "ReviewAndRewriteCode"]
|
||||
tools: list[str] = ["Plan", "Editor:write,read", "RoleZero", "ReviewAndRewriteCode"]
|
||||
|
||||
def _update_tool_execution(self):
|
||||
review = ReviewAndRewriteCode()
|
||||
|
|
|
|||
90
metagpt/tools/libs/cr.py
Normal file
90
metagpt/tools/libs/cr.py
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
import json
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
import aiofiles
|
||||
from unidiff import PatchSet
|
||||
|
||||
import metagpt.ext.cr
|
||||
from metagpt.ext.cr.actions.code_review import CodeReview as CodeReview_
|
||||
from metagpt.ext.cr.actions.modify_code import ModifyCode
|
||||
from metagpt.ext.cr.utils.schema import Point
|
||||
from metagpt.tools.libs.browser import Browser
|
||||
from metagpt.tools.tool_registry import register_tool
|
||||
from metagpt.utils.report import EditorReporter
|
||||
|
||||
|
||||
@register_tool(tags=["codereview"], include_functions=["review", "fix"])
|
||||
class CodeReview:
|
||||
"""Review and fix the patch content from the pull request URL or a file."""
|
||||
|
||||
async def review(
|
||||
self,
|
||||
patch_path: str,
|
||||
cr_output_file: str,
|
||||
cr_point_file: Optional[str] = None,
|
||||
) -> str:
|
||||
"""Review a PR and save code review comments.
|
||||
|
||||
Args:
|
||||
patch_path: The local path of the patch file or the url of the pull request. Example: "/data/xxx-pr-1.patch", "https://github.com/xx/XX/pull/1362"
|
||||
cr_output_file: Output file path where code review comments will be saved. Example: "cr/xxx-pr-1.json"
|
||||
cr_point_file: File path for specifying code review points. Defaults to a predefined file.
|
||||
"""
|
||||
patch = await self._get_patch_content(patch_path)
|
||||
cr_point_file = cr_point_file if cr_point_file else Path(metagpt.ext.cr.__file__).parent / "points.json"
|
||||
async with aiofiles.open(cr_point_file, "rb") as f:
|
||||
cr_point_content = await f.read()
|
||||
cr_points = [Point(**i) for i in json.loads(cr_point_content)]
|
||||
|
||||
async with EditorReporter(enable_llm_stream=True) as reporter:
|
||||
src_path = cr_output_file
|
||||
cr_output_path = Path(cr_output_file)
|
||||
await reporter.async_report(
|
||||
{"type": "CodeReview", "src_path": src_path, "filename": cr_output_path.name}, "meta"
|
||||
)
|
||||
comments = await CodeReview_().run(patch, cr_points)
|
||||
cr_output_path.parent.mkdir(exist_ok=True, parents=True)
|
||||
async with aiofiles.open(cr_output_path, "w") as f:
|
||||
await f.write(json.dumps(comments, ensure_ascii=False))
|
||||
await reporter.async_report(cr_output_path)
|
||||
|
||||
return f"The number of defects: {len(comments)} and the comments are stored in {cr_output_file}"
|
||||
|
||||
async def fix(
|
||||
self,
|
||||
patch_path: str,
|
||||
cr_file: str,
|
||||
output_dir: str,
|
||||
) -> str:
|
||||
"""Fix the patch content based on code review comments.
|
||||
|
||||
Args:
|
||||
patch_path: The local path of the patch file or the url of the pull request.
|
||||
cr_file: File path where code review comments are stored.
|
||||
output_dir: File path where code review comments are stored.
|
||||
"""
|
||||
patch = await self._get_patch_content(patch_path)
|
||||
async with aiofiles.open(cr_file, "r") as f:
|
||||
comments = json.loads(await f.read())
|
||||
await ModifyCode(pr="").run(patch, comments, output_dir)
|
||||
return f"The fixed patch files store in {output_dir}"
|
||||
|
||||
async def _get_patch_content(self, patch_path):
|
||||
if patch_path.startswith(("https://", "http://")):
|
||||
# async with aiohttp.ClientSession(trust_env=True) as client:
|
||||
# async with client.get(f"{patch_path}.diff", ) as resp:
|
||||
# patch_file_content = await resp.text()
|
||||
browser = Browser()
|
||||
browser.proxy = {"server": "http://127.0.0.1:20172"}
|
||||
async with browser:
|
||||
await browser.goto(f"{patch_path}.diff")
|
||||
patch_file_content = await browser.page.content()
|
||||
|
||||
else:
|
||||
async with aiofiles.open(patch_path) as f:
|
||||
patch_file_content = await f.read()
|
||||
await EditorReporter().async_report(patch_path)
|
||||
|
||||
patch: PatchSet = PatchSet(patch_file_content)
|
||||
return patch
|
||||
Loading…
Add table
Add a link
Reference in a new issue