mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-04-30 19:36:24 +02:00
add cr tool
This commit is contained in:
parent
4dfe8524a1
commit
4cb0696df0
9 changed files with 781 additions and 0 deletions
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
|
||||
114
metagpt/ext/cr/actions/modify_code.py
Normal file
114
metagpt/ext/cr/actions/modify_code.py
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
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
|
||||
if comment["point_id"] in [24, 25, 26]:
|
||||
comment["point_detail"] = comment["point_detail"] + "\n" + comment["comment"]
|
||||
# 去掉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
|
||||
275
metagpt/ext/cr/points.json
Normal file
275
metagpt/ext/cr/points.json
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
[
|
||||
{
|
||||
"id": 2,
|
||||
"text": "避免未使用的临时变量",
|
||||
"language": "java",
|
||||
"file_path": "",
|
||||
"start_line": 0,
|
||||
"end_line": 0,
|
||||
"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": 3,
|
||||
"text": "不要使用 System.out.println 去打印",
|
||||
"language": "java",
|
||||
"file_path": "",
|
||||
"start_line": 0,
|
||||
"end_line": 0,
|
||||
"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": 4,
|
||||
"text": "避免函数中未使用的形参",
|
||||
"language": "java",
|
||||
"file_path": "",
|
||||
"start_line": 0,
|
||||
"end_line": 0,
|
||||
"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": 5,
|
||||
"text": "if语句块不能为空",
|
||||
"language": "java",
|
||||
"file_path": "",
|
||||
"start_line": 0,
|
||||
"end_line": 0,
|
||||
"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": 6,
|
||||
"text": "循环体不能为空",
|
||||
"language": "java",
|
||||
"file_path": "",
|
||||
"start_line": 0,
|
||||
"end_line": 0,
|
||||
"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": 7,
|
||||
"text": "避免使用 printStackTrace(),应该使用日志的方式去记录",
|
||||
"language": "java",
|
||||
"file_path": "",
|
||||
"start_line": 0,
|
||||
"end_line": 0,
|
||||
"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": 8,
|
||||
"text": "catch 语句块不能为空",
|
||||
"language": "java",
|
||||
"file_path": "",
|
||||
"start_line": 0,
|
||||
"end_line": 0,
|
||||
"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": 9,
|
||||
"text": "避免不必要的永真/永假判断",
|
||||
"language": "java",
|
||||
"file_path": "",
|
||||
"start_line": 0,
|
||||
"end_line": 0,
|
||||
"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": 10,
|
||||
"text": "switch 中 default 必须放在最后",
|
||||
"language": "java",
|
||||
"file_path": "",
|
||||
"start_line": 0,
|
||||
"end_line": 0,
|
||||
"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": 11,
|
||||
"text": "未使用equals()函数对 String 作比较",
|
||||
"language": "java",
|
||||
"file_path": "",
|
||||
"start_line": 0,
|
||||
"end_line": 0,
|
||||
"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": 12,
|
||||
"text": "禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象",
|
||||
"language": "java",
|
||||
"file_path": "",
|
||||
"start_line": 0,
|
||||
"end_line": 0,
|
||||
"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": 13,
|
||||
"text": "finally 语句块不能为空",
|
||||
"language": "java",
|
||||
"file_path": "",
|
||||
"start_line": 0,
|
||||
"end_line": 0,
|
||||
"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": 14,
|
||||
"text": "try 语句块不能为空",
|
||||
"language": "java",
|
||||
"file_path": "",
|
||||
"start_line": 0,
|
||||
"end_line": 0,
|
||||
"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": 15,
|
||||
"text": "避免对象进行不必要的 NULL或者null 检查",
|
||||
"language": "java",
|
||||
"file_path": "",
|
||||
"start_line": 0,
|
||||
"end_line": 0,
|
||||
"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": 17,
|
||||
"text": "避免 finally 块中出现 return",
|
||||
"language": "java",
|
||||
"file_path": "",
|
||||
"start_line": 0,
|
||||
"end_line": 0,
|
||||
"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": 18,
|
||||
"text": "避免空的 static 初始化",
|
||||
"language": "java",
|
||||
"file_path": "",
|
||||
"start_line": 0,
|
||||
"end_line": 0,
|
||||
"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": 19,
|
||||
"text": "避免日历类用法不当风险",
|
||||
"language": "java",
|
||||
"file_path": "",
|
||||
"start_line": 0,
|
||||
"end_line": 0,
|
||||
"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": 21,
|
||||
"text": "使用集合转数组的方法,必须使用集合的toArray(T[]array),传入的是类型完全一样的数组,大小就是list.size()",
|
||||
"language": "java",
|
||||
"file_path": "",
|
||||
"start_line": 0,
|
||||
"end_line": 0,
|
||||
"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": 22,
|
||||
"text": "禁止在 equals()中使用 NULL或者null 做比较",
|
||||
"language": "java",
|
||||
"file_path": "",
|
||||
"start_line": 0,
|
||||
"end_line": 0,
|
||||
"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": 23,
|
||||
"text": "switch 语句块不能为空",
|
||||
"language": "java",
|
||||
"file_path": "",
|
||||
"start_line": 0,
|
||||
"end_line": 0,
|
||||
"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": 24,
|
||||
"text": "变量/方法/类 命名,要有效清晰表示对应的实际含义",
|
||||
"language": "java",
|
||||
"file_path": "",
|
||||
"start_line": 0,
|
||||
"end_line": 0,
|
||||
"detail": "缺陷类型:变量/方法/类 命名,要有效清晰表示对应的实际含义;修复方案:修改成合适的变量/方法/类名,有效表示对应的实体含义",
|
||||
"yes_example": "### 被判定为\"变量/方法/类 命名,要有效清晰表示对应的实际含义\"的例子\n<例子1>\nint a = 1;\nif (a == 1) {\n\treturn a;\n}这段代码中的变量a的命名无法从命名中理解a代表什么,所以这个被判定为\"变量/方法/类 命名,要有效清晰表示对应的实际含义\"\n</例子1>\n\n<例子2>\nStringBuilder sb = new StringBuilder();\nsb.append(\"{!\").append(tag).append(\"=\").append(tagField);\n这段代码中的变量sb的命名无法从命名中理解a代表什么,所以这个被判定为\"变量/方法/类 命名,要有效清晰表示对应的实际含义\"\n</例子2>\n\n<例子3>\nPage<Owner> findAll(Pageable pageable);\n这段代码findAll是一个interface声明,接口名findAll无法清晰的理解这个接口要做什么,所以这个被判定为\"变量/方法/类 命名,要有效清晰表示对应的实际含义\"\n</例子3>",
|
||||
"no_example": "### 不能被判定为\"变量/方法/类 命名,要有效清晰表示对应的实际含义\"的例子\n<例子1>\nint persionNum = 5;\nif (persionNum > 3) {\n\treturn True;\n}这段代码中的变量persionNum的命名可以比较清楚知道persionNum代表人员的数量,所以这个不能被判定为\"变量/方法/类 命名,要有效清晰表示对应的实际含义\"\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 25,
|
||||
"text": "判断逻辑应简洁,不能有重复或者无意义的判断",
|
||||
"language": "java",
|
||||
"file_path": "",
|
||||
"start_line": 0,
|
||||
"end_line": 0,
|
||||
"detail": "缺陷类型:判断逻辑应简洁,不能有重复或者无意义的判断;修复方案:简化或删除一些重复的,无意义的判断逻辑",
|
||||
"yes_example": "### 被判定为\"判断逻辑应简洁,不能有重复或者无意义的判断\"\n<例子1>\nif (task == null) {\n\ttask = 1\n\tif (task != null) {\n\t\tInteger ok = 0;\n\t\tif (task == 1) {\n\t\t\tok=1;\n\t\t}\n\t\tif (ok == 0) {\n\t\t\ttask = null;\n\t\t}\n\t}\n}这段代码中的关于变量ok的判断逻辑不够简洁,变量ok其实可以被去掉进行逻辑简化,可以被简化为\nif (task == null) {\n\ttask = 1\n\tif (task != null) {\n\t\tif (task == 1) {\n\t\t task = null;\n\t\t}\n\t}\n},所以这个被判定为\"判断逻辑应简洁,不能有重复或者无意义的判断\"\n</例子1>\n\n<例子2>\nif (!(a == 0) && a == 1 || a == 2){\n\treturn True;\n}这段代码的if判断逻辑过于复杂,可简化为if (0<a≤2),所以这个被判定为\"判断逻辑应简洁,不能有重复或者无意义的判断\"\n</例子2>\n\n<例子3>\na = 1;\nif (a > 1){\n\treturn True;\n}这段代码的变量a是确定的值1,这样就不会出现a > 1,所以if的判断无意义,所以这个被判定为\"判断逻辑应简洁,不能有重复或者无意义的判断\"\n</例子3>",
|
||||
"no_example": "### 不能被判定为\"判断逻辑应简洁,不能有重复或者无意义的判断\"\n<例子1>\na = 1;\nb = 2;\nif (0 < a ≤ 2){\n\treturn a + b;\n}这段代码的判断条件比较简洁明了,无重复的判断,所以这个不能被判定为\"判断逻辑应简洁,不能有重复或者无意义的判断\"\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 26,
|
||||
"text": "代码结构冗余,避免重复代码",
|
||||
"language": "java",
|
||||
"file_path": "",
|
||||
"start_line": 0,
|
||||
"end_line": 0,
|
||||
"detail": "缺陷类型:代码结构冗余,避免重复代码;修复方案:忽略",
|
||||
"yes_example": "### 被判定为\"代码结构冗余,避免重复代码\"的例子\n<例子1>\npublic void trigger(String executionId, Map<String, Object> processVariables) {\n commandExecutor.execute(new TriggerCmd(executionId, processVariables));\n}\npublic void trigger(String executionId, Map<String, Object> processVariables, Map<String, Object> transientVariables) {\n\tcommandExecutor.execute(new TriggerCmd(executionId, processVariables, transientVariables)); \n}这段代码中的2个trigger存在代码都是触发器的逻辑,存在重复,可以合并成1个函数,所以这个被判定为\"代码结构冗余,避免重复代码\"\n</例子1>\n\n<例子2>\npublic int sum(int a, int b){\n\treturn a + b;\n}\npublic int merge(int a, int b){\n\treturn a + b;\n}这段代码中的2个函数sum和merge的逻辑是一样的,可以合并成1个函数,所以这个被判定为\"代码结构冗余,避免重复代码\"\n</例子2>",
|
||||
"no_example": "### 不能被判定为\"代码结构冗余,避免重复代码\"的例子\n<例子1>\npublic int append(int a, int b){\n\treturn a + b;\n}\npublic int add(int a, int b){\n\treturn (a + b) - b;\n}这段代码中的2个函数sum和add的代码结构看上去差不多,但是具体的处理逻辑是不一样的,所以这个不能被判定为\"代码结构冗余,避免重复代码\"\n</例子1>"
|
||||
},
|
||||
{
|
||||
"id": 27,
|
||||
"text": "不能有多余的分号",
|
||||
"file_path": "\"\"",
|
||||
"start_line": 0,
|
||||
"end_line": 0,
|
||||
"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": 28,
|
||||
"text": "非线程安全的 SimpleDateFormat 使用,必须在函数或代码块级别使用synchronized",
|
||||
"file_path": "\"\"",
|
||||
"start_line": 0,
|
||||
"end_line": 0,
|
||||
"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>"
|
||||
}
|
||||
]
|
||||
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"]) -> 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue