add cr tool

This commit is contained in:
shenchucheng 2024-06-27 16:01:16 +08:00
parent 4dfe8524a1
commit 4cb0696df0
9 changed files with 781 additions and 0 deletions

View file

@ -0,0 +1,3 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Desc :

View 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

View 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
View file

@ -0,0 +1,275 @@
[
{
"id": 2,
"text": "避免未使用的临时变量",
"language": "java",
"file_path": "",
"start_line": 0,
"end_line": 0,
"detail": "缺陷类型避免未使用的临时变量对应FixerUnusedLocalVariableFixer修复方案删除未使用的临时变量",
"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 去打印对应FixerSystemPrintlnFixer修复方案注释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 语句块不能为空对应FixerEmptyIfStmtFixer修复方案删除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": "缺陷类型循环体不能为空对应FixerEmptyStatementNotInLoopFixer修复方案删除对应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 语句块不能为空对应FixerEmptyCatchBlockFixer修复方案在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": "缺陷类型:避免不必要的永真/永假判断对应FixerUnconditionalIfStatement 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 必须放在最后对应FixerDefaultLabelNotLastInSwitchStmtFixer修复方案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 作比较对应FixerUnSynStaticDateFormatter 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": "缺陷类型:禁止在日志中直接使用字符串输出异常,请使用占位符传递异常对象 输出异常对应FixerConcatExceptionFixer修复方案使用占位符传递异常对象",
"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 语句块不能为空对应FixerEmptyFinallyBlockFixer修复方案删除空 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 语句块不能为空对应FixerEmptyTryBlockFixer修复方案删除整个 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 检查对应FixerLogicalOpNpeFixer修复方案删除对对象不必要的 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 初始化对应FixerEmptyInitializerFixer修复方案删除整个空初始化块",
"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()对应FixerClassCastExpWithToArrayF 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 做比较对应FixerEqualsNullFixer修复方案使用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 语句块不能为空对应FixerEmptySwitchStatementsFix修复方案删除整个空 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>"
}
]

View file

View 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)

View 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