jarvis-ai-assistant 0.7.8__py3-none-any.whl → 1.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (279) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +567 -222
  3. jarvis/jarvis_agent/agent_manager.py +19 -12
  4. jarvis/jarvis_agent/builtin_input_handler.py +79 -11
  5. jarvis/jarvis_agent/config_editor.py +7 -2
  6. jarvis/jarvis_agent/event_bus.py +24 -13
  7. jarvis/jarvis_agent/events.py +19 -1
  8. jarvis/jarvis_agent/file_context_handler.py +67 -64
  9. jarvis/jarvis_agent/file_methodology_manager.py +38 -24
  10. jarvis/jarvis_agent/jarvis.py +186 -114
  11. jarvis/jarvis_agent/language_extractors/__init__.py +8 -1
  12. jarvis/jarvis_agent/language_extractors/c_extractor.py +7 -4
  13. jarvis/jarvis_agent/language_extractors/cpp_extractor.py +9 -4
  14. jarvis/jarvis_agent/language_extractors/go_extractor.py +7 -4
  15. jarvis/jarvis_agent/language_extractors/java_extractor.py +27 -20
  16. jarvis/jarvis_agent/language_extractors/javascript_extractor.py +22 -17
  17. jarvis/jarvis_agent/language_extractors/python_extractor.py +7 -4
  18. jarvis/jarvis_agent/language_extractors/rust_extractor.py +7 -4
  19. jarvis/jarvis_agent/language_extractors/typescript_extractor.py +22 -17
  20. jarvis/jarvis_agent/language_support_info.py +250 -219
  21. jarvis/jarvis_agent/main.py +19 -23
  22. jarvis/jarvis_agent/memory_manager.py +9 -6
  23. jarvis/jarvis_agent/methodology_share_manager.py +21 -15
  24. jarvis/jarvis_agent/output_handler.py +4 -2
  25. jarvis/jarvis_agent/prompt_builder.py +7 -6
  26. jarvis/jarvis_agent/prompt_manager.py +113 -8
  27. jarvis/jarvis_agent/prompts.py +317 -85
  28. jarvis/jarvis_agent/protocols.py +5 -2
  29. jarvis/jarvis_agent/run_loop.py +192 -32
  30. jarvis/jarvis_agent/session_manager.py +7 -3
  31. jarvis/jarvis_agent/share_manager.py +23 -13
  32. jarvis/jarvis_agent/shell_input_handler.py +12 -8
  33. jarvis/jarvis_agent/stdio_redirect.py +25 -26
  34. jarvis/jarvis_agent/task_analyzer.py +29 -23
  35. jarvis/jarvis_agent/task_list.py +869 -0
  36. jarvis/jarvis_agent/task_manager.py +26 -23
  37. jarvis/jarvis_agent/tool_executor.py +6 -5
  38. jarvis/jarvis_agent/tool_share_manager.py +24 -14
  39. jarvis/jarvis_agent/user_interaction.py +3 -3
  40. jarvis/jarvis_agent/utils.py +9 -1
  41. jarvis/jarvis_agent/web_bridge.py +37 -17
  42. jarvis/jarvis_agent/web_output_sink.py +5 -2
  43. jarvis/jarvis_agent/web_server.py +165 -36
  44. jarvis/jarvis_c2rust/__init__.py +1 -1
  45. jarvis/jarvis_c2rust/cli.py +260 -141
  46. jarvis/jarvis_c2rust/collector.py +37 -18
  47. jarvis/jarvis_c2rust/constants.py +60 -0
  48. jarvis/jarvis_c2rust/library_replacer.py +242 -1010
  49. jarvis/jarvis_c2rust/library_replacer_checkpoint.py +133 -0
  50. jarvis/jarvis_c2rust/library_replacer_llm.py +287 -0
  51. jarvis/jarvis_c2rust/library_replacer_loader.py +191 -0
  52. jarvis/jarvis_c2rust/library_replacer_output.py +134 -0
  53. jarvis/jarvis_c2rust/library_replacer_prompts.py +124 -0
  54. jarvis/jarvis_c2rust/library_replacer_utils.py +188 -0
  55. jarvis/jarvis_c2rust/llm_module_agent.py +98 -1044
  56. jarvis/jarvis_c2rust/llm_module_agent_apply.py +170 -0
  57. jarvis/jarvis_c2rust/llm_module_agent_executor.py +288 -0
  58. jarvis/jarvis_c2rust/llm_module_agent_loader.py +170 -0
  59. jarvis/jarvis_c2rust/llm_module_agent_prompts.py +268 -0
  60. jarvis/jarvis_c2rust/llm_module_agent_types.py +57 -0
  61. jarvis/jarvis_c2rust/llm_module_agent_utils.py +150 -0
  62. jarvis/jarvis_c2rust/llm_module_agent_validator.py +119 -0
  63. jarvis/jarvis_c2rust/loaders.py +28 -10
  64. jarvis/jarvis_c2rust/models.py +5 -2
  65. jarvis/jarvis_c2rust/optimizer.py +192 -1974
  66. jarvis/jarvis_c2rust/optimizer_build_fix.py +286 -0
  67. jarvis/jarvis_c2rust/optimizer_clippy.py +766 -0
  68. jarvis/jarvis_c2rust/optimizer_config.py +49 -0
  69. jarvis/jarvis_c2rust/optimizer_docs.py +183 -0
  70. jarvis/jarvis_c2rust/optimizer_options.py +48 -0
  71. jarvis/jarvis_c2rust/optimizer_progress.py +469 -0
  72. jarvis/jarvis_c2rust/optimizer_report.py +52 -0
  73. jarvis/jarvis_c2rust/optimizer_unsafe.py +309 -0
  74. jarvis/jarvis_c2rust/optimizer_utils.py +469 -0
  75. jarvis/jarvis_c2rust/optimizer_visibility.py +185 -0
  76. jarvis/jarvis_c2rust/scanner.py +229 -166
  77. jarvis/jarvis_c2rust/transpiler.py +531 -2732
  78. jarvis/jarvis_c2rust/transpiler_agents.py +503 -0
  79. jarvis/jarvis_c2rust/transpiler_build.py +1294 -0
  80. jarvis/jarvis_c2rust/transpiler_codegen.py +204 -0
  81. jarvis/jarvis_c2rust/transpiler_compile.py +146 -0
  82. jarvis/jarvis_c2rust/transpiler_config.py +178 -0
  83. jarvis/jarvis_c2rust/transpiler_context.py +122 -0
  84. jarvis/jarvis_c2rust/transpiler_executor.py +516 -0
  85. jarvis/jarvis_c2rust/transpiler_generation.py +278 -0
  86. jarvis/jarvis_c2rust/transpiler_git.py +163 -0
  87. jarvis/jarvis_c2rust/transpiler_mod_utils.py +225 -0
  88. jarvis/jarvis_c2rust/transpiler_modules.py +336 -0
  89. jarvis/jarvis_c2rust/transpiler_planning.py +394 -0
  90. jarvis/jarvis_c2rust/transpiler_review.py +1196 -0
  91. jarvis/jarvis_c2rust/transpiler_symbols.py +176 -0
  92. jarvis/jarvis_c2rust/utils.py +269 -79
  93. jarvis/jarvis_code_agent/after_change.py +233 -0
  94. jarvis/jarvis_code_agent/build_validation_config.py +37 -30
  95. jarvis/jarvis_code_agent/builtin_rules.py +68 -0
  96. jarvis/jarvis_code_agent/code_agent.py +976 -1517
  97. jarvis/jarvis_code_agent/code_agent_build.py +227 -0
  98. jarvis/jarvis_code_agent/code_agent_diff.py +246 -0
  99. jarvis/jarvis_code_agent/code_agent_git.py +525 -0
  100. jarvis/jarvis_code_agent/code_agent_impact.py +177 -0
  101. jarvis/jarvis_code_agent/code_agent_lint.py +283 -0
  102. jarvis/jarvis_code_agent/code_agent_llm.py +159 -0
  103. jarvis/jarvis_code_agent/code_agent_postprocess.py +105 -0
  104. jarvis/jarvis_code_agent/code_agent_prompts.py +46 -0
  105. jarvis/jarvis_code_agent/code_agent_rules.py +305 -0
  106. jarvis/jarvis_code_agent/code_analyzer/__init__.py +52 -48
  107. jarvis/jarvis_code_agent/code_analyzer/base_language.py +12 -10
  108. jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +12 -11
  109. jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +16 -12
  110. jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +26 -17
  111. jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +558 -104
  112. jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +27 -16
  113. jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +22 -18
  114. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +21 -16
  115. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +20 -16
  116. jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +27 -16
  117. jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +47 -23
  118. jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +71 -37
  119. jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +162 -35
  120. jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +111 -57
  121. jarvis/jarvis_code_agent/code_analyzer/build_validator.py +18 -12
  122. jarvis/jarvis_code_agent/code_analyzer/context_manager.py +185 -183
  123. jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +2 -1
  124. jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +24 -15
  125. jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +227 -141
  126. jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +321 -247
  127. jarvis/jarvis_code_agent/code_analyzer/language_registry.py +37 -29
  128. jarvis/jarvis_code_agent/code_analyzer/language_support.py +21 -13
  129. jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +15 -9
  130. jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +75 -45
  131. jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +87 -52
  132. jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +84 -51
  133. jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +94 -64
  134. jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +109 -71
  135. jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +97 -63
  136. jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +103 -69
  137. jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +271 -268
  138. jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +76 -64
  139. jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +92 -19
  140. jarvis/jarvis_code_agent/diff_visualizer.py +998 -0
  141. jarvis/jarvis_code_agent/lint.py +223 -524
  142. jarvis/jarvis_code_agent/rule_share_manager.py +158 -0
  143. jarvis/jarvis_code_agent/rules/clean_code.md +144 -0
  144. jarvis/jarvis_code_agent/rules/code_review.md +115 -0
  145. jarvis/jarvis_code_agent/rules/documentation.md +165 -0
  146. jarvis/jarvis_code_agent/rules/generate_rules.md +52 -0
  147. jarvis/jarvis_code_agent/rules/performance.md +158 -0
  148. jarvis/jarvis_code_agent/rules/refactoring.md +139 -0
  149. jarvis/jarvis_code_agent/rules/security.md +160 -0
  150. jarvis/jarvis_code_agent/rules/tdd.md +78 -0
  151. jarvis/jarvis_code_agent/test_rules/cpp_test.md +118 -0
  152. jarvis/jarvis_code_agent/test_rules/go_test.md +98 -0
  153. jarvis/jarvis_code_agent/test_rules/java_test.md +99 -0
  154. jarvis/jarvis_code_agent/test_rules/javascript_test.md +113 -0
  155. jarvis/jarvis_code_agent/test_rules/php_test.md +117 -0
  156. jarvis/jarvis_code_agent/test_rules/python_test.md +91 -0
  157. jarvis/jarvis_code_agent/test_rules/ruby_test.md +102 -0
  158. jarvis/jarvis_code_agent/test_rules/rust_test.md +86 -0
  159. jarvis/jarvis_code_agent/utils.py +36 -26
  160. jarvis/jarvis_code_analysis/checklists/loader.py +21 -21
  161. jarvis/jarvis_code_analysis/code_review.py +64 -33
  162. jarvis/jarvis_data/config_schema.json +285 -192
  163. jarvis/jarvis_git_squash/main.py +8 -6
  164. jarvis/jarvis_git_utils/git_commiter.py +53 -76
  165. jarvis/jarvis_mcp/__init__.py +5 -2
  166. jarvis/jarvis_mcp/sse_mcp_client.py +40 -30
  167. jarvis/jarvis_mcp/stdio_mcp_client.py +27 -19
  168. jarvis/jarvis_mcp/streamable_mcp_client.py +35 -26
  169. jarvis/jarvis_memory_organizer/memory_organizer.py +78 -55
  170. jarvis/jarvis_methodology/main.py +48 -39
  171. jarvis/jarvis_multi_agent/__init__.py +56 -23
  172. jarvis/jarvis_multi_agent/main.py +15 -18
  173. jarvis/jarvis_platform/base.py +179 -111
  174. jarvis/jarvis_platform/human.py +27 -16
  175. jarvis/jarvis_platform/kimi.py +52 -45
  176. jarvis/jarvis_platform/openai.py +101 -40
  177. jarvis/jarvis_platform/registry.py +51 -33
  178. jarvis/jarvis_platform/tongyi.py +68 -38
  179. jarvis/jarvis_platform/yuanbao.py +59 -43
  180. jarvis/jarvis_platform_manager/main.py +68 -76
  181. jarvis/jarvis_platform_manager/service.py +24 -14
  182. jarvis/jarvis_rag/README_CONFIG.md +314 -0
  183. jarvis/jarvis_rag/README_DYNAMIC_LOADING.md +311 -0
  184. jarvis/jarvis_rag/README_ONLINE_MODELS.md +230 -0
  185. jarvis/jarvis_rag/__init__.py +57 -4
  186. jarvis/jarvis_rag/cache.py +3 -1
  187. jarvis/jarvis_rag/cli.py +48 -68
  188. jarvis/jarvis_rag/embedding_interface.py +39 -0
  189. jarvis/jarvis_rag/embedding_manager.py +7 -230
  190. jarvis/jarvis_rag/embeddings/__init__.py +41 -0
  191. jarvis/jarvis_rag/embeddings/base.py +114 -0
  192. jarvis/jarvis_rag/embeddings/cohere.py +66 -0
  193. jarvis/jarvis_rag/embeddings/edgefn.py +117 -0
  194. jarvis/jarvis_rag/embeddings/local.py +260 -0
  195. jarvis/jarvis_rag/embeddings/openai.py +62 -0
  196. jarvis/jarvis_rag/embeddings/registry.py +293 -0
  197. jarvis/jarvis_rag/llm_interface.py +8 -6
  198. jarvis/jarvis_rag/query_rewriter.py +8 -9
  199. jarvis/jarvis_rag/rag_pipeline.py +61 -52
  200. jarvis/jarvis_rag/reranker.py +7 -75
  201. jarvis/jarvis_rag/reranker_interface.py +32 -0
  202. jarvis/jarvis_rag/rerankers/__init__.py +41 -0
  203. jarvis/jarvis_rag/rerankers/base.py +109 -0
  204. jarvis/jarvis_rag/rerankers/cohere.py +67 -0
  205. jarvis/jarvis_rag/rerankers/edgefn.py +140 -0
  206. jarvis/jarvis_rag/rerankers/jina.py +79 -0
  207. jarvis/jarvis_rag/rerankers/local.py +89 -0
  208. jarvis/jarvis_rag/rerankers/registry.py +293 -0
  209. jarvis/jarvis_rag/retriever.py +58 -43
  210. jarvis/jarvis_sec/__init__.py +66 -141
  211. jarvis/jarvis_sec/agents.py +21 -17
  212. jarvis/jarvis_sec/analysis.py +80 -33
  213. jarvis/jarvis_sec/checkers/__init__.py +7 -13
  214. jarvis/jarvis_sec/checkers/c_checker.py +356 -164
  215. jarvis/jarvis_sec/checkers/rust_checker.py +47 -29
  216. jarvis/jarvis_sec/cli.py +43 -21
  217. jarvis/jarvis_sec/clustering.py +430 -272
  218. jarvis/jarvis_sec/file_manager.py +99 -55
  219. jarvis/jarvis_sec/parsers.py +9 -6
  220. jarvis/jarvis_sec/prompts.py +4 -3
  221. jarvis/jarvis_sec/report.py +44 -22
  222. jarvis/jarvis_sec/review.py +180 -107
  223. jarvis/jarvis_sec/status.py +50 -41
  224. jarvis/jarvis_sec/types.py +3 -0
  225. jarvis/jarvis_sec/utils.py +160 -83
  226. jarvis/jarvis_sec/verification.py +411 -181
  227. jarvis/jarvis_sec/workflow.py +132 -21
  228. jarvis/jarvis_smart_shell/main.py +28 -41
  229. jarvis/jarvis_stats/cli.py +14 -12
  230. jarvis/jarvis_stats/stats.py +28 -19
  231. jarvis/jarvis_stats/storage.py +14 -8
  232. jarvis/jarvis_stats/visualizer.py +12 -7
  233. jarvis/jarvis_tools/base.py +5 -2
  234. jarvis/jarvis_tools/clear_memory.py +13 -9
  235. jarvis/jarvis_tools/cli/main.py +23 -18
  236. jarvis/jarvis_tools/edit_file.py +572 -873
  237. jarvis/jarvis_tools/execute_script.py +10 -7
  238. jarvis/jarvis_tools/file_analyzer.py +7 -8
  239. jarvis/jarvis_tools/meta_agent.py +287 -0
  240. jarvis/jarvis_tools/methodology.py +5 -3
  241. jarvis/jarvis_tools/read_code.py +305 -1438
  242. jarvis/jarvis_tools/read_symbols.py +50 -17
  243. jarvis/jarvis_tools/read_webpage.py +19 -18
  244. jarvis/jarvis_tools/registry.py +435 -156
  245. jarvis/jarvis_tools/retrieve_memory.py +16 -11
  246. jarvis/jarvis_tools/save_memory.py +8 -6
  247. jarvis/jarvis_tools/search_web.py +31 -31
  248. jarvis/jarvis_tools/sub_agent.py +32 -28
  249. jarvis/jarvis_tools/sub_code_agent.py +44 -60
  250. jarvis/jarvis_tools/task_list_manager.py +1811 -0
  251. jarvis/jarvis_tools/virtual_tty.py +29 -19
  252. jarvis/jarvis_utils/__init__.py +4 -0
  253. jarvis/jarvis_utils/builtin_replace_map.py +2 -1
  254. jarvis/jarvis_utils/clipboard.py +9 -8
  255. jarvis/jarvis_utils/collections.py +331 -0
  256. jarvis/jarvis_utils/config.py +699 -194
  257. jarvis/jarvis_utils/dialogue_recorder.py +294 -0
  258. jarvis/jarvis_utils/embedding.py +6 -3
  259. jarvis/jarvis_utils/file_processors.py +7 -1
  260. jarvis/jarvis_utils/fzf.py +9 -3
  261. jarvis/jarvis_utils/git_utils.py +71 -42
  262. jarvis/jarvis_utils/globals.py +116 -32
  263. jarvis/jarvis_utils/http.py +6 -2
  264. jarvis/jarvis_utils/input.py +318 -83
  265. jarvis/jarvis_utils/jsonnet_compat.py +119 -104
  266. jarvis/jarvis_utils/methodology.py +37 -28
  267. jarvis/jarvis_utils/output.py +201 -44
  268. jarvis/jarvis_utils/utils.py +986 -628
  269. {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/METADATA +49 -33
  270. jarvis_ai_assistant-1.0.2.dist-info/RECORD +304 -0
  271. jarvis/jarvis_code_agent/code_analyzer/structured_code.py +0 -556
  272. jarvis/jarvis_tools/generate_new_tool.py +0 -205
  273. jarvis/jarvis_tools/lsp_client.py +0 -1552
  274. jarvis/jarvis_tools/rewrite_file.py +0 -105
  275. jarvis_ai_assistant-0.7.8.dist-info/RECORD +0 -218
  276. {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/WHEEL +0 -0
  277. {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/entry_points.txt +0 -0
  278. {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/licenses/LICENSE +0 -0
  279. {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1196 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ 代码审查模块
4
+ """
5
+
6
+ import json
7
+ import re
8
+ from pathlib import Path
9
+ from typing import Any
10
+ from typing import Callable
11
+ from typing import Dict
12
+ from typing import List
13
+ from typing import Optional
14
+ from typing import Tuple
15
+
16
+ from jarvis.jarvis_utils.output import PrettyOutput
17
+
18
+ from jarvis.jarvis_agent import Agent
19
+ from jarvis.jarvis_agent.events import AFTER_TOOL_CALL
20
+ from jarvis.jarvis_agent.events import BEFORE_TOOL_CALL
21
+ from jarvis.jarvis_c2rust.models import FnRecord
22
+ from jarvis.jarvis_c2rust.utils import dir_tree
23
+ from jarvis.jarvis_c2rust.utils import extract_json_from_summary
24
+ from jarvis.jarvis_c2rust.utils import truncate_git_diff_with_context_limit
25
+
26
+
27
+ class ReviewManager:
28
+ """代码审查管理器"""
29
+
30
+ def __init__(
31
+ self,
32
+ crate_dir: Path,
33
+ data_dir: Path,
34
+ llm_group: Optional[str],
35
+ non_interactive: bool,
36
+ review_max_iterations: int,
37
+ disabled_libraries: List[str],
38
+ progress: Dict[str, Any],
39
+ save_progress_func: Callable[[], None],
40
+ read_source_span_func: Callable[[FnRecord], str],
41
+ collect_callees_context_func: Callable[[FnRecord], List[Dict[str, Any]]],
42
+ extract_compile_flags_func: Callable[[str], Optional[str]],
43
+ is_root_symbol_func: Callable[[FnRecord], bool],
44
+ get_crate_commit_hash_func: Callable[[], Optional[str]],
45
+ current_function_start_commit_getter: Callable[[], Optional[str]],
46
+ compose_prompt_with_context_func: Callable[[str, bool], str],
47
+ get_fix_agent_func: Callable[[Optional[str]], Any],
48
+ check_and_handle_test_deletion_func: Callable[[Optional[str], Any], bool],
49
+ append_additional_notes_func: Callable[[str], str],
50
+ cargo_build_loop_func: Callable[[], Optional[bool]],
51
+ get_build_loop_has_fixes_func: Callable[[], bool],
52
+ on_before_tool_call_func: Callable[[Any, Any], None],
53
+ on_after_tool_call_func: Callable[[Any, Any, Any, Any], None],
54
+ agent_before_commits: Dict[str, Optional[str]],
55
+ get_git_diff: Optional[Callable[[str], str]] = None,
56
+ get_git_diff_func: Optional[Callable[[Optional[str]], str]] = None,
57
+ ) -> None:
58
+ self.crate_dir = crate_dir
59
+ self.data_dir = data_dir
60
+ self.llm_group = llm_group
61
+ self.non_interactive = non_interactive
62
+ self.review_max_iterations = review_max_iterations
63
+ self.disabled_libraries = disabled_libraries
64
+ self.progress = progress
65
+ self.save_progress = save_progress_func
66
+ self.read_source_span = read_source_span_func
67
+ self.collect_callees_context = collect_callees_context_func
68
+ self.extract_compile_flags = extract_compile_flags_func
69
+ self.is_root_symbol = is_root_symbol_func
70
+ self.get_crate_commit_hash = get_crate_commit_hash_func
71
+ self.current_function_start_commit_getter = current_function_start_commit_getter
72
+ self.compose_prompt_with_context: Callable[[str, bool], str] = (
73
+ compose_prompt_with_context_func
74
+ )
75
+ self.get_fix_agent = get_fix_agent_func
76
+ self.check_and_handle_test_deletion = check_and_handle_test_deletion_func
77
+ self.append_additional_notes = append_additional_notes_func
78
+ self.cargo_build_loop = cargo_build_loop_func
79
+ self.get_build_loop_has_fixes = get_build_loop_has_fixes_func
80
+ self.on_before_tool_call = on_before_tool_call_func
81
+ self.on_after_tool_call = on_after_tool_call_func
82
+ self.agent_before_commits = agent_before_commits
83
+ self.get_git_diff = get_git_diff_func
84
+
85
+ def review_and_optimize(self, rec: FnRecord, module: str, rust_sig: str) -> None:
86
+ """
87
+ 审查生成的实现;若 summary 报告问题,则调用 CodeAgent 进行优化,直到无问题或次数用尽。
88
+ 合并了功能一致性审查和类型/边界严重问题审查,避免重复审查。
89
+ 审查只关注本次函数与相关最小上下文,避免全局重构。
90
+ """
91
+
92
+ def build_review_prompts() -> Tuple[str, str, str]:
93
+ sys_p = (
94
+ "你是Rust代码审查专家。验收标准:Rust 实现应与原始 C 实现在功能上一致,且不应包含可能导致功能错误的严重问题。\n"
95
+ "**审查优先级**:测试破坏性检查 > 测试用例完备性 > 严重问题 > 破坏性变更 > 功能一致性 > 文件结构。优先检查测试是否被破坏(#[test] 标记丢失/重复、代码插入位置错误等),然后检查测试用例是否完备,再处理可能导致程序崩溃或编译失败的问题。\n"
96
+ "**审查范围**:主要审查当前函数的实现,相关依赖函数作为辅助参考。\n"
97
+ "审查标准(合并了功能一致性和严重问题检查):\n"
98
+ "1. 功能一致性检查:\n"
99
+ " - **核心功能定义**:核心输入输出、主要功能逻辑是否与 C 实现一致。核心功能指函数的主要目的和预期行为(如'计算哈希值'、'解析字符串'、'压缩数据'等),不包括实现细节;\n"
100
+ " - **安全改进允许行为不一致**:允许 Rust 实现修复 C 代码中的安全漏洞(如缓冲区溢出、空指针解引用、未初始化内存使用、整数溢出、格式化字符串漏洞等),这些安全改进可能导致行为与 C 实现不一致,但这是允许的,不应被视为功能不一致;\n"
101
+ " - **忽略语言差异导致的行为不一致**:由于 Rust 和 C 语言的本质差异,以下行为差异是不可避免的,应被忽略:\n"
102
+ " * 整数溢出处理:Rust 在 debug 模式下会 panic,release 模式下会 wrapping,而 C 是未定义行为;\n"
103
+ " * 未定义行为:Rust 会避免或明确处理,而 C 可能产生未定义行为;\n"
104
+ " * 空指针/空引用:Rust 使用 Option<T> 或 Result<T, E> 处理,而 C 可能直接解引用导致崩溃;\n"
105
+ " * 内存安全:Rust 的借用检查器会阻止某些 C 中允许的不安全操作;\n"
106
+ " * 错误处理:Rust 使用 Result<T, E> 或 Option<T>,而 C 可能使用错误码或全局 errno;\n"
107
+ " - 允许 Rust 实现使用不同的类型设计、错误处理方式、资源管理方式等,只要功能一致即可;\n"
108
+ "2. 严重问题检查(可能导致功能错误或程序崩溃):\n"
109
+ " - 明显的空指针解引用或会导致 panic 的严重错误;\n"
110
+ " - 明显的越界访问或会导致程序崩溃的问题;\n"
111
+ " - 会导致程序无法正常运行的逻辑错误;\n"
112
+ "3. 破坏性变更检测(对现有代码的影响):\n"
113
+ " - **允许签名不一致**:允许函数签名、参数数量、参数类型、返回类型等与C实现不一致,只要功能实现了即可。这是Rust转译的正常现象,因为Rust的类型系统和设计理念与C不同;\n"
114
+ " - **仅检查实际破坏性影响**:只有当函数签名变更确实导致调用方代码无法编译或运行时,才报告为破坏性变更。如果调用方代码已经适配了新签名,或可以通过简单的适配解决,则不应视为破坏性变更;\n"
115
+ " - **⚠️⚠️ 重点:测试破坏性检查**:必须重点检查代码变更是否对当前测试造成破坏,这是最高优先级的检查项。具体包括:\n"
116
+ " * **检查 #[test] 标记丢失**:必须检查代码变更中是否错误删除了测试用例标记(#[test] 或 #[cfg(test)])。如果发现删除了测试用例标记,必须报告为破坏性变更,除非:\n"
117
+ " - 测试用例被移动到其他位置(在diff中可以看到对应的添加);\n"
118
+ " - 测试用例是重复的或过时的,确实需要删除;\n"
119
+ " - 测试用例被重构为其他形式的测试(如集成测试、文档测试等);\n"
120
+ " * **检查 #[test] 标记重复**:必须检查代码变更中是否错误添加了重复的 #[test] 标记,导致测试函数被重复定义。如果发现重复的测试标记,必须报告为破坏性变更;\n"
121
+ " * **检查代码插入位置错误**:必须检查代码插入位置是否导致 #[test] 标记丢失或测试函数结构被破坏。例如:\n"
122
+ " - 在 #[test] 标记和测试函数之间插入了代码,导致测试标记失效;\n"
123
+ " - 在测试函数内部错误位置插入代码,导致测试函数结构被破坏;\n"
124
+ " - 在 #[cfg(test)] mod tests { ... } 块外部插入测试相关代码,导致测试无法运行;\n"
125
+ " * **检查测试函数结构破坏**:必须检查测试函数的完整性,确保测试函数没有被意外截断、合并或结构被破坏;\n"
126
+ " * **验证测试仍然可运行**:如果可能,检查测试代码的语法和结构是否正确,确保测试仍然可以被 cargo test 识别和运行;\n"
127
+ " - 检查模块导出变更是否会影响其他模块的导入(如 pub 关键字缺失、模块路径变更);\n"
128
+ " - 检查类型定义变更是否会导致依赖该类型的代码失效(如结构体字段变更、枚举变体变更);\n"
129
+ " - 检查常量或静态变量变更是否会影响引用该常量的代码;\n"
130
+ " - **优先使用diff信息**:如果diff中已包含调用方代码信息,优先基于diff判断;只有在diff信息不足时,才使用 read_code 工具读取调用方代码进行验证;\n"
131
+ "4. 文件结构合理性检查:\n"
132
+ " - 检查模块文件位置是否符合 Rust 项目约定(如 src/ 目录结构、模块层次);\n"
133
+ " - 检查文件命名是否符合 Rust 命名规范(如 snake_case、模块文件命名);\n"
134
+ " - 检查模块组织是否合理(如相关功能是否放在同一模块、模块拆分是否过度或不足);\n"
135
+ " - 检查模块导出是否合理(如 lib.rs 中的 pub mod 声明是否正确、是否遗漏必要的导出);\n"
136
+ " - 检查是否存在循环依赖或过度耦合;\n"
137
+ "5. 测试用例完备性检查:\n"
138
+ " - **检查是否有测试用例**:必须检查目标函数是否有对应的测试用例。如果完全没有测试用例,必须报告为功能一致性问题,因为无法验证 Rust 实现是否与 C 实现一致;\n"
139
+ " - **检查测试用例覆盖主要功能**:测试用例应该覆盖函数的主要功能路径和预期行为。如果测试用例只覆盖了部分功能,或缺少关键功能的测试,应报告为功能一致性问题;\n"
140
+ " - **检查测试用例覆盖边界情况**:测试用例应该覆盖边界情况,如:\n"
141
+ " * 空输入(空字符串、空数组、空指针等);\n"
142
+ " * 极值输入(最大值、最小值、零值等);\n"
143
+ " * 边界值(数组边界、字符串长度边界等);\n"
144
+ " * 特殊值(负数、NaN、无穷大等,如果适用);\n"
145
+ " - **检查测试用例覆盖错误情况**:如果 C 实现有错误处理(如返回错误码、设置 errno 等),测试用例应该覆盖这些错误情况。如果 Rust 实现使用 Result<T, E> 或 Option<T> 处理错误,测试用例应该验证错误情况;\n"
146
+ " - **检查测试用例与 C 实现行为一致**:测试用例的预期结果应该与 C 实现的行为一致。如果测试用例的预期结果与 C 实现不一致,应报告为功能一致性问题;\n"
147
+ " - **检查测试用例质量**:测试用例应该:\n"
148
+ " * 有清晰的测试名称,能够说明测试的目的;\n"
149
+ " * 有适当的断言,验证函数的输出和行为;\n"
150
+ " * 测试逻辑正确,能够真正验证函数的功能;\n"
151
+ " - **注意**:如果函数是资源释放类函数(如 fclose、free 等),在 Rust 中通过 RAII 自动管理,测试用例可以非常简单(如仅验证函数可以调用而不崩溃),这是可以接受的;\n"
152
+ "不检查类型匹配、指针可变性、边界检查细节、资源释放细节、内存语义等技术细节(除非会导致功能错误)。\n"
153
+ "**重要要求:在总结阶段,对于发现的每个问题,必须提供:**\n"
154
+ "1. 详细的问题描述:明确指出问题所在的位置(文件、函数、行号等)、问题的具体表现、为什么这是一个问题\n"
155
+ "2. 具体的修复建议:提供详细的修复方案,包括需要修改的代码位置、修改方式、预期效果等\n"
156
+ "3. **修复代码示例(如果适用)**:对于可以明确修复的问题,提供具体的修复代码示例,包括修改前后的代码对比,帮助修复阶段快速定位和修复问题\n"
157
+ "4. 问题分类:使用 [function] 标记功能一致性问题(包括测试用例完备性问题),使用 [critical] 标记严重问题,使用 [breaking] 标记破坏性变更,使用 [structure] 标记文件结构问题\n"
158
+ "请在总结阶段详细指出问题、修改建议和修复代码示例,但不要尝试修复或修改任何代码,不要输出补丁。"
159
+ )
160
+ # 附加原始C函数源码片段,供审查作为只读参考
161
+ c_code = self.read_source_span(rec) or ""
162
+ # 附加被引用符号上下文与库替代上下文,以及crate目录结构,提供更完整审查背景
163
+ callees_ctx = self.collect_callees_context(rec)
164
+ librep_ctx = (
165
+ rec.lib_replacement if isinstance(rec.lib_replacement, dict) else None
166
+ )
167
+ crate_tree = dir_tree(self.crate_dir)
168
+ # 提取编译参数
169
+ compile_flags = self.extract_compile_flags(rec.file)
170
+
171
+ # 获取从初始commit到当前工作区的变更作为上下文(每次review都必须获取)
172
+ # 使用统一的 get_git_diff 方法,可以获取到当前工作区的变更(包括未提交的修改)
173
+ commit_diff = ""
174
+ diff_status = "" # 用于记录diff获取状态
175
+ if self.get_git_diff:
176
+ base_commit = self.current_function_start_commit_getter()
177
+ try:
178
+ commit_diff = self.get_git_diff(base_commit)
179
+ if commit_diff and commit_diff.strip():
180
+ # 成功获取diff,限制长度避免上下文过大
181
+ # 使用50%的比例,因为review阶段需要更多的上下文信息
182
+ commit_diff = truncate_git_diff_with_context_limit(
183
+ commit_diff,
184
+ llm_group=self.llm_group,
185
+ token_ratio=0.5,
186
+ base_commit=base_commit,
187
+ crate_dir=self.crate_dir,
188
+ )
189
+ diff_status = "success"
190
+ else:
191
+ # 没有变更或获取失败
192
+ if base_commit:
193
+ current_commit = self.get_crate_commit_hash()
194
+ if current_commit == base_commit:
195
+ commit_diff = "(无变更:当前commit与函数开始时的commit相同,且工作区无修改)"
196
+ diff_status = "no_change"
197
+ else:
198
+ commit_diff = "(无法获取git差异)"
199
+ diff_status = "error"
200
+ else:
201
+ commit_diff = "(未记录函数开始时的commit id)"
202
+ diff_status = "no_start_commit"
203
+ except Exception as e:
204
+ commit_diff = f"获取git差异时发生异常: {str(e)}"
205
+ diff_status = "error"
206
+ PrettyOutput.auto_print(
207
+ f"⚠️ [c2rust-transpiler][review] 获取git差异失败: {e}",
208
+ )
209
+ else:
210
+ # 没有提供 get_git_diff 函数
211
+ commit_diff = "(未提供git差异获取函数)"
212
+ diff_status = "no_git_diff_func"
213
+
214
+ # 获取构建修复阶段的总结信息
215
+ build_fixes_summary = ""
216
+ try:
217
+ cur = self.progress.get("current") or {}
218
+ build_fixes = cur.get("build_fixes", [])
219
+ if build_fixes:
220
+ build_fixes_summary = "\n\n【构建修复阶段总结】\n"
221
+ build_fixes_summary += (
222
+ "以下是在构建修复阶段进行的修复流程和结果总结,供审查参考:\n\n"
223
+ )
224
+ for i, fix_info in enumerate(build_fixes, 1):
225
+ fix_iter = fix_info.get("iteration", "?")
226
+ fix_stage = fix_info.get("stage", "unknown")
227
+ fix_tags = fix_info.get("tags", [])
228
+ fix_summary = fix_info.get("summary", "")
229
+ is_retry = fix_info.get("retry", False)
230
+ retry_marker = "(重试)" if is_retry else ""
231
+ build_fixes_summary += f"修复 #{i}(第 {fix_iter} 次迭代,阶段:{fix_stage}){retry_marker}:\n"
232
+ if fix_tags:
233
+ build_fixes_summary += (
234
+ f" 错误分类:{', '.join(fix_tags)}\n"
235
+ )
236
+ if fix_summary:
237
+ build_fixes_summary += f" 修复总结:\n{fix_summary}\n"
238
+ build_fixes_summary += "\n"
239
+ build_fixes_summary += "提示:请参考上述修复总结,了解在构建修复阶段遇到的问题和修复过程,这有助于更准确地审查代码。"
240
+ except Exception as e:
241
+ PrettyOutput.auto_print(
242
+ f"⚠️ [c2rust-transpiler][review] 获取构建修复总结失败: {e}",
243
+ )
244
+
245
+ usr_p_lines = [
246
+ f"待审查函数:{rec.qname or rec.name}",
247
+ f"建议签名:{rust_sig}",
248
+ f"目标模块:{module}",
249
+ f"**Crate 根目录(重要)**:{self.crate_dir.resolve()}",
250
+ " - 所有 Rust 源码文件都位于此目录下",
251
+ " - 使用 `read_code` 工具读取文件时,请使用相对于此目录的路径(如 `src/xxx.rs`)或绝对路径",
252
+ " - 使用 `edit_file_*` 工具编辑文件时,文件路径也应相对于此目录或使用绝对路径",
253
+ f"源文件位置:{rec.file}:{rec.start_line}-{rec.end_line}",
254
+ "",
255
+ "原始C函数源码片段(只读参考,不要修改C代码):",
256
+ "<C_SOURCE>",
257
+ c_code,
258
+ "</C_SOURCE>",
259
+ "",
260
+ ]
261
+ # 如果有构建修复总结,添加到 prompt 中
262
+ if build_fixes_summary:
263
+ usr_p_lines.append(build_fixes_summary)
264
+ usr_p_lines.append("")
265
+
266
+ usr_p_lines.extend(
267
+ [
268
+ "审查说明(合并审查):",
269
+ "**审查优先级**:测试破坏性检查 > 严重问题 > 破坏性变更 > 功能一致性 > 文件结构。优先检查测试是否被破坏(#[test] 标记丢失/重复、代码插入位置错误等),然后处理可能导致程序崩溃或编译失败的问题。",
270
+ "",
271
+ "1. 功能一致性:",
272
+ " - **核心功能定义**:核心输入输出、主要功能逻辑是否与 C 实现一致。核心功能指函数的主要目的和预期行为(如'计算哈希值'、'解析字符串'、'压缩数据'等),不包括实现细节;",
273
+ " - **安全改进允许行为不一致**:允许Rust实现修复C代码中的安全漏洞(如缓冲区溢出、空指针解引用、未初始化内存使用、整数溢出、格式化字符串漏洞等),这些安全改进可能导致行为与 C 实现不一致,但这是允许的,不应被视为功能不一致;",
274
+ " - **忽略语言差异导致的行为不一致**:由于 Rust 和 C 语言的本质差异,以下行为差异是不可避免的,应被忽略:",
275
+ " * 整数溢出处理:Rust 在 debug 模式下会 panic,release 模式下会 wrapping,而 C 是未定义行为;",
276
+ " * 未定义行为:Rust 会避免或明确处理,而 C 可能产生未定义行为;",
277
+ " * 空指针/空引用:Rust 使用 Option<T> 或 Result<T, E> 处理,而 C 可能直接解引用导致崩溃;",
278
+ " * 内存安全:Rust 的借用检查器会阻止某些 C 中允许的不安全操作;",
279
+ " * 错误处理:Rust 使用 Result<T, E> 或 Option<T>,而 C 可能使用错误码或全局 errno;",
280
+ " - 允许Rust实现使用不同的类型设计、错误处理方式、资源管理方式等,只要功能一致即可;",
281
+ "2. 严重问题(可能导致功能错误):",
282
+ " - 明显的空指针解引用或会导致 panic 的严重错误;",
283
+ " - 明显的越界访问或会导致程序崩溃的问题;",
284
+ "3. 破坏性变更检测(对现有代码的影响):",
285
+ " - **允许签名不一致**:允许函数签名、参数数量、参数类型、返回类型等与C实现不一致,只要功能实现了即可。这是Rust转译的正常现象,因为Rust的类型系统和设计理念与C不同;",
286
+ " - **仅检查实际破坏性影响**:只有当函数签名变更确实导致调用方代码无法编译或运行时,才报告为破坏性变更。如果调用方代码已经适配了新签名,或可以通过简单的适配解决,则不应视为破坏性变更;",
287
+ " - **⚠️⚠️ 重点:测试破坏性检查**:必须重点检查代码变更是否对当前测试造成破坏,这是最高优先级的检查项。具体包括:",
288
+ " * **检查 #[test] 标记丢失**:必须检查代码变更中是否错误删除了测试用例标记(#[test] 或 #[cfg(test)])。如果发现删除了测试用例标记,必须报告为破坏性变更,除非:",
289
+ " - 测试用例被移动到其他位置(在diff中可以看到对应的添加);",
290
+ " - 测试用例是重复的或过时的,确实需要删除;",
291
+ " - 测试用例被重构为其他形式的测试(如集成测试、文档测试等);",
292
+ " * **检查 #[test] 标记重复**:必须检查代码变更中是否错误添加了重复的 #[test] 标记,导致测试函数被重复定义。如果发现重复的测试标记,必须报告为破坏性变更;",
293
+ " * **检查代码插入位置错误**:必须检查代码插入位置是否导致 #[test] 标记丢失或测试函数结构被破坏。例如:",
294
+ " - 在 #[test] 标记和测试函数之间插入了代码,导致测试标记失效;",
295
+ " - 在测试函数内部错误位置插入代码,导致测试函数结构被破坏;",
296
+ " - 在 #[cfg(test)] mod tests { ... } 块外部插入测试相关代码,导致测试无法运行;",
297
+ " * **检查测试函数结构破坏**:必须检查测试函数的完整性,确保测试函数没有被意外截断、合并或结构被破坏;",
298
+ " * **验证测试仍然可运行**:如果可能,检查测试代码的语法和结构是否正确,确保测试仍然可以被 cargo test 识别和运行;",
299
+ " - 检查模块导出变更是否会影响其他模块的导入(如 pub 关键字缺失、模块路径变更);",
300
+ " - 检查类型定义变更是否会导致依赖该类型的代码失效(如结构体字段变更、枚举变体变更);",
301
+ " - 检查常量或静态变量变更是否会影响引用该常量的代码;",
302
+ " - **优先使用diff信息**:如果diff中已包含调用方代码信息,优先基于diff判断;只有在diff信息不足时,才使用 read_code 工具读取调用方代码进行验证;",
303
+ " - 如果该函数是根符号或被其他已转译函数调用,检查调用方代码是否仍能正常编译和使用;如果调用方已经适配了新签名,则不应视为破坏性变更;",
304
+ "4. 文件结构合理性检查:",
305
+ " - 检查模块文件位置是否符合 Rust 项目约定(如 src/ 目录结构、模块层次);",
306
+ " - 检查文件命名是否符合 Rust 命名规范(如 snake_case、模块文件命名);",
307
+ " - 检查模块组织是否合理(如相关功能是否放在同一模块、模块拆分是否过度或不足);",
308
+ " - 检查模块导出是否合理(如 lib.rs 中的 pub mod 声明是否正确、是否遗漏必要的导出);",
309
+ " - 检查是否存在循环依赖或过度耦合;",
310
+ " - 检查文件大小是否合理(如单个文件是否过大需要拆分,或是否过度拆分导致文件过多);",
311
+ "5. 测试用例完备性检查:",
312
+ " - **检查是否有测试用例**:必须检查目标函数是否有对应的测试用例。如果完全没有测试用例,必须报告为功能一致性问题,因为无法验证 Rust 实现是否与 C 实现一致;",
313
+ " - **检查测试用例覆盖主要功能**:测试用例应该覆盖函数的主要功能路径和预期行为。如果测试用例只覆盖了部分功能,或缺少关键功能的测试,应报告为功能一致性问题;",
314
+ " - **检查测试用例覆盖边界情况**:测试用例应该覆盖边界情况,如空输入(空字符串、空数组、空指针等)、极值输入(最大值、最小值、零值等)、边界值(数组边界、字符串长度边界等)、特殊值(负数、NaN、无穷大等,如果适用);",
315
+ " - **检查测试用例覆盖错误情况**:如果 C 实现有错误处理(如返回错误码、设置 errno 等),测试用例应该覆盖这些错误情况。如果 Rust 实现使用 Result<T, E> 或 Option<T> 处理错误,测试用例应该验证错误情况;",
316
+ " - **检查测试用例与 C 实现行为一致**:测试用例的预期结果应该与 C 实现的行为一致。如果测试用例的预期结果与 C 实现不一致,应报告为功能一致性问题;",
317
+ " - **检查测试用例质量**:测试用例应该有清晰的测试名称、适当的断言、正确的测试逻辑,能够真正验证函数的功能;",
318
+ " - **注意**:如果函数是资源释放类函数(如 fclose、free 等),在 Rust 中通过 RAII 自动管理,测试用例可以非常简单(如仅验证函数可以调用而不崩溃),这是可以接受的;",
319
+ "不检查类型匹配、指针可变性、边界检查细节等技术细节(除非会导致功能错误)。",
320
+ "",
321
+ "**重要:问题报告要求**",
322
+ "对于发现的每个问题,必须在总结中提供:",
323
+ "1. 详细的问题描述:明确指出问题所在的位置(文件、函数、行号等)、问题的具体表现、为什么这是一个问题",
324
+ "2. 具体的修复建议:提供详细的修复方案,包括需要修改的代码位置、修改方式、预期效果等",
325
+ "3. **修复代码示例(如果适用)**:对于可以明确修复的问题,必须提供具体的修复代码示例,包括修改前后的代码对比。格式要求:",
326
+ " - 修改前:显示有问题的代码片段(包含足够的上下文,如函数签名、前后几行代码)",
327
+ " - 修改后:显示修复后的代码片段",
328
+ " - 使用代码块格式(```rust ... ```)展示代码",
329
+ " - 如果问题涉及多个位置,提供所有相关位置的修复代码示例",
330
+ "4. 问题分类:使用 [function] 标记功能一致性问题,使用 [critical] 标记严重问题,使用 [breaking] 标记破坏性变更,使用 [structure] 标记文件结构问题",
331
+ "示例(必须包含修复代码示例):",
332
+ ' "[function] 返回值处理缺失:在函数 foo 的第 42 行,当输入参数为负数时,函数没有返回错误码,但 C 实现中会返回 -1。修复建议:在函数开始处添加参数验证,当参数为负数时返回 Result::Err(Error::InvalidInput)。修复代码示例:\n'
333
+ " 修改前:\n"
334
+ " ```rust\n"
335
+ " pub fn foo(x: i32) -> i32 {\n"
336
+ " // 缺少参数验证\n"
337
+ " x * 2\n"
338
+ " }\n"
339
+ " ```\n"
340
+ " 修改后:\n"
341
+ " ```rust\n"
342
+ " pub fn foo(x: i32) -> Result<i32, Error> {\n"
343
+ " if x < 0 {\n"
344
+ " return Err(Error::InvalidInput);\n"
345
+ " }\n"
346
+ " Ok(x * 2)\n"
347
+ " }\n"
348
+ ' ```"',
349
+ ' "[critical] 空指针解引用风险:在函数 bar 的第 58 行,直接解引用指针 ptr 而没有检查其是否为 null,可能导致 panic。修复建议:使用 if let Some(value) = ptr.as_ref() 进行空指针检查,或使用 Option<&T> 类型。修复代码示例:\n'
350
+ " 修改前:\n"
351
+ " ```rust\n"
352
+ " pub fn bar(ptr: *const i32) -> i32 {\n"
353
+ " unsafe { *ptr }\n"
354
+ " }\n"
355
+ " ```\n"
356
+ " 修改后:\n"
357
+ " ```rust\n"
358
+ " pub fn bar(ptr: Option<&i32>) -> Option<i32> {\n"
359
+ " ptr.map(|p| *p)\n"
360
+ " }\n"
361
+ ' ```"',
362
+ ' "[breaking] 测试函数重复定义:在 src/common/buffer.rs 第 111-112 行处,新增的测试函数 test_write_buf_1_byte 被同时标记了两次 #[test],导致重复定义。修复建议:删除其中一个 #[test] 标记,确保每个测试函数只有一个 #[test] 属性。修复代码示例:\n'
363
+ " 修改前:\n"
364
+ " ```rust\n"
365
+ " #[test]\n"
366
+ " #[test] // 重复的标记\n"
367
+ " fn test_write_buf_1_byte() {\n"
368
+ " // ...\n"
369
+ " }\n"
370
+ " ```\n"
371
+ " 修改后:\n"
372
+ " ```rust\n"
373
+ " #[test]\n"
374
+ " fn test_write_buf_1_byte() {\n"
375
+ " // ...\n"
376
+ " }\n"
377
+ ' ```"',
378
+ ' "[function] 测试用例缺失:函数 foo 没有对应的测试用例,无法验证 Rust 实现是否与 C 实现一致。修复建议:在目标模块的 #[cfg(test)] mod tests { ... } 块中添加测试用例,覆盖函数的主要功能、边界情况和错误情况。修复代码示例:\n'
379
+ " 修改前(src/foo.rs):\n"
380
+ " ```rust\n"
381
+ " pub fn foo(x: i32) -> i32 {\n"
382
+ " x * 2\n"
383
+ " }\n"
384
+ " // 缺少测试用例\n"
385
+ " ```\n"
386
+ " 修改后(src/foo.rs):\n"
387
+ " ```rust\n"
388
+ " pub fn foo(x: i32) -> i32 {\n"
389
+ " x * 2\n"
390
+ " }\n"
391
+ " \n"
392
+ " #[cfg(test)]\n"
393
+ " mod tests {\n"
394
+ " use super::*;\n"
395
+ " \n"
396
+ " #[test]\n"
397
+ " fn test_foo_normal() {\n"
398
+ " assert_eq!(foo(5), 10);\n"
399
+ " }\n"
400
+ " \n"
401
+ " #[test]\n"
402
+ " fn test_foo_zero() {\n"
403
+ " assert_eq!(foo(0), 0);\n"
404
+ " }\n"
405
+ " }\n"
406
+ ' ```"',
407
+ ' "[structure] 模块导出缺失:函数 qux 所在的模块 utils 未在 src/lib.rs 中导出,导致无法从 crate 外部访问。修复建议:在 src/lib.rs 中添加 `pub mod utils;` 声明。修复代码示例:\n'
408
+ " 修改前(src/lib.rs):\n"
409
+ " ```rust\n"
410
+ " // 缺少 pub mod utils;\n"
411
+ " ```\n"
412
+ " 修改后(src/lib.rs):\n"
413
+ " ```rust\n"
414
+ " pub mod utils;\n"
415
+ ' ```"',
416
+ "",
417
+ "被引用符号上下文(如已转译则包含Rust模块信息):",
418
+ json.dumps(callees_ctx, ensure_ascii=False, indent=2),
419
+ "",
420
+ "库替代上下文(若存在):",
421
+ json.dumps(librep_ctx, ensure_ascii=False, indent=2),
422
+ "",
423
+ ]
424
+ )
425
+ # 添加禁用库列表(如果存在)
426
+ if self.disabled_libraries:
427
+ usr_p_lines.append(
428
+ f"禁用库列表(禁止在实现中使用这些库):{', '.join(self.disabled_libraries)}"
429
+ )
430
+ # 添加根符号要求(如果是根符号)
431
+ if self.is_root_symbol(rec):
432
+ usr_p_lines.append(
433
+ f"根符号要求:此函数是根符号({rec.qname or rec.name}),必须使用 `pub` 关键字对外暴露,确保可以从 crate 外部访问。同时,该函数所在的模块必须在 src/lib.rs 中被导出(使用 `pub mod <模块名>;`)。"
434
+ )
435
+ # 添加编译参数(如果存在)
436
+ if compile_flags:
437
+ usr_p_lines.extend(
438
+ [
439
+ "",
440
+ "C文件编译参数(来自 compile_commands.json):",
441
+ compile_flags,
442
+ ]
443
+ )
444
+ usr_p_lines.extend(
445
+ [
446
+ "",
447
+ "当前crate目录结构(部分):",
448
+ "<CRATE_TREE>",
449
+ crate_tree,
450
+ "</CRATE_TREE>",
451
+ ]
452
+ )
453
+
454
+ # 添加git变更上下文(每次review都必须包含)
455
+ usr_p_lines.extend(
456
+ [
457
+ "",
458
+ "从函数开始到当前的git变更(用于了解代码变更历史和上下文,包括未提交的修改):",
459
+ "<GIT_DIFF>",
460
+ commit_diff,
461
+ "</GIT_DIFF>",
462
+ "",
463
+ ]
464
+ )
465
+
466
+ # 根据diff状态添加不同的说明
467
+ if diff_status == "success":
468
+ usr_p_lines.extend(
469
+ [
470
+ "**重要:git变更上下文说明**",
471
+ "- 上述diff显示了从函数开始处理时的commit到当前工作区的所有变更(包括已提交和未提交的修改)",
472
+ "- 这些变更可能包括:当前函数的实现、依赖函数的实现、模块结构的调整等",
473
+ "- **优先使用diff信息进行审查判断**:如果diff中已经包含了足够的信息(如函数实现、签名变更、模块结构等),可以直接基于diff进行审查,无需读取原始文件",
474
+ "- 只有在diff信息不足或需要查看完整上下文时,才使用 read_code 工具读取原始文件",
475
+ "- 在审查破坏性变更时,请特别关注这些变更对现有代码的影响",
476
+ "- 如果发现变更中存在问题(如破坏性变更、文件结构不合理等),请在审查报告中指出",
477
+ ]
478
+ )
479
+ elif diff_status == "no_change":
480
+ usr_p_lines.extend(
481
+ [
482
+ "**注意**:当前commit与函数开始时的commit相同,且工作区无修改,说明没有代码变更。请使用 read_code 工具读取目标模块文件的最新内容进行审查。",
483
+ ]
484
+ )
485
+ else:
486
+ # diff_status 为 "error"、"no_current_commit"、"no_start_commit" 或 "no_git_diff_func"
487
+ usr_p_lines.extend(
488
+ [
489
+ "**注意**:由于无法获取git差异信息,请使用 read_code 工具读取目标模块文件的最新内容进行审查。",
490
+ ]
491
+ )
492
+
493
+ usr_p_lines.extend(
494
+ [
495
+ "",
496
+ "如需定位或交叉验证 C 符号位置,请使用符号表检索工具:",
497
+ "- 工具: read_symbols",
498
+ "- 参数示例(JSON):",
499
+ f' {{"symbols_file": "{(self.data_dir / "symbols.jsonl").resolve()}", "symbols": ["{rec.qname or rec.name}"]}}',
500
+ "",
501
+ "**代码读取工具使用说明**:",
502
+ "- 工具: read_code",
503
+ "- 用途: 读取 C 源码实现或 Rust 模块文件",
504
+ "- **重要**:读取 Rust 源码文件时,必须使用绝对路径或相对于 crate 根目录的路径",
505
+ f"- **Crate 根目录**:{self.crate_dir.resolve()}",
506
+ " - 示例:",
507
+ f" * 读取 Rust 文件:`read_code` 工具,文件路径使用 `{self.crate_dir.resolve()}/src/xxx.rs` 或 `src/xxx.rs`(相对于 crate 根目录)",
508
+ " * 读取 C 文件:`read_code` 工具,文件路径使用 C 源文件的完整路径",
509
+ "",
510
+ "**重要:审查要求**",
511
+ "- **优先使用diff信息**:如果提供了git差异(GIT_DIFF),优先基于diff信息进行审查判断,只有在diff信息不足时才使用 read_code 工具读取原始文件",
512
+ "- 必须基于最新的代码进行审查,如果使用 read_code 工具,请读取目标模块文件的最新内容",
513
+ f"- **使用 read_code 工具时,文件路径应相对于 crate 根目录({self.crate_dir.resolve()})或使用绝对路径**",
514
+ "- 禁止依赖任何历史记忆、之前的审查结论或对话历史进行判断",
515
+ "- 每次审查都必须基于最新的代码状态(通过diff或read_code获取),确保审查结果反映当前代码的真实状态",
516
+ "- 结合git变更上下文(如果提供),全面评估代码变更的影响和合理性",
517
+ "",
518
+ "请基于提供的diff信息(如果可用)或读取crate中该函数的当前实现进行审查,并准备总结。",
519
+ ]
520
+ )
521
+ usr_p = "\n".join(usr_p_lines)
522
+ sum_p = (
523
+ "请仅输出一个 <SUMMARY> 块,块内直接包含 JSON 对象(不需要额外的标签),字段:\n"
524
+ '"ok": bool // 若满足功能一致且无严重问题、无破坏性变更、文件结构合理,则为 true\n'
525
+ '"function_issues": [string, ...] // 功能一致性问题,每项以 [function] 开头,必须包含详细的问题描述和修复建议\n'
526
+ '"critical_issues": [string, ...] // 严重问题(可能导致功能错误),每项以 [critical] 开头,必须包含详细的问题描述和修复建议\n'
527
+ '"breaking_issues": [string, ...] // 破坏性变更问题(对现有代码的影响),每项以 [breaking] 开头,必须包含详细的问题描述和修复建议\n'
528
+ '"structure_issues": [string, ...] // 文件结构问题,每项以 [structure] 开头,必须包含详细的问题描述和修复建议\n'
529
+ "注意:\n"
530
+ "- 前置条件:必须在crate中找到该函数的实现(匹配函数名或签名)。若未找到,ok 必须为 false,function_issues 应包含 [function] function not found: 详细描述问题位置和如何查找函数实现\n"
531
+ "- **安全改进允许行为不一致**:若Rust实现修复了C代码中的安全漏洞(如缓冲区溢出、空指针解引用、未初始化内存使用等),即使导致行为与 C 实现不一致,这也是允许的,不应被视为功能不一致;\n"
532
+ "- **忽略语言差异导致的行为不一致**:由于 Rust 和 C 语言的本质差异(如内存安全、类型系统、错误处理、未定义行为处理等),某些行为差异是不可避免的,这些差异应被忽略,不应被视为功能不一致;\n"
533
+ "- **允许签名不一致**:允许函数签名、参数数量、参数类型、返回类型等与C实现不一致,只要功能实现了即可。这是Rust转译的正常现象,不应被视为破坏性变更;\n"
534
+ "- **破坏性变更判断标准**:只有当函数签名变更确实导致调用方代码无法编译或运行时,才报告为破坏性变更。如果调用方代码已经适配了新签名,或可以通过简单的适配解决,则不应视为破坏性变更;\n"
535
+ "- **⚠️⚠️ 重点:测试破坏性检查**:必须重点检查代码变更是否对当前测试造成破坏,这是最高优先级的检查项。具体包括:\n"
536
+ " 1. **检查 #[test] 标记丢失**:必须检查代码变更中是否错误删除了测试用例标记(#[test] 或 #[cfg(test)])。如果发现删除了测试用例标记且没有合理的理由(如移动到其他位置、重复测试等),必须报告为破坏性变更;\n"
537
+ " 2. **检查 #[test] 标记重复**:必须检查代码变更中是否错误添加了重复的 #[test] 标记,导致测试函数被重复定义。如果发现重复的测试标记,必须报告为破坏性变更;\n"
538
+ " 3. **检查代码插入位置错误**:必须检查代码插入位置是否导致 #[test] 标记丢失或测试函数结构被破坏。例如:在 #[test] 标记和测试函数之间插入了代码、在测试函数内部错误位置插入代码、在 #[cfg(test)] mod tests { ... } 块外部插入测试相关代码等;\n"
539
+ " 4. **检查测试函数结构破坏**:必须检查测试函数的完整性,确保测试函数没有被意外截断、合并或结构被破坏;\n"
540
+ " 5. **验证测试仍然可运行**:如果可能,检查测试代码的语法和结构是否正确,确保测试仍然可以被 cargo test 识别和运行;\n"
541
+ "- **⚠️⚠️ 重点:测试用例完备性检查**:必须检查测试用例是否完备,这是第二优先级的检查项。具体包括:\n"
542
+ " 1. **检查是否有测试用例**:必须检查目标函数是否有对应的测试用例。如果完全没有测试用例,必须报告为功能一致性问题([function]),因为无法验证 Rust 实现是否与 C 实现一致;\n"
543
+ " 2. **检查测试用例覆盖主要功能**:测试用例应该覆盖函数的主要功能路径和预期行为。如果测试用例只覆盖了部分功能,或缺少关键功能的测试,应报告为功能一致性问题;\n"
544
+ " 3. **检查测试用例覆盖边界情况**:测试用例应该覆盖边界情况,如空输入(空字符串、空数组、空指针等)、极值输入(最大值、最小值、零值等)、边界值(数组边界、字符串长度边界等)、特殊值(负数、NaN、无穷大等,如果适用);\n"
545
+ " 4. **检查测试用例覆盖错误情况**:如果 C 实现有错误处理(如返回错误码、设置 errno 等),测试用例应该覆盖这些错误情况。如果 Rust 实现使用 Result<T, E> 或 Option<T> 处理错误,测试用例应该验证错误情况;\n"
546
+ " 5. **检查测试用例与 C 实现行为一致**:测试用例的预期结果应该与 C 实现的行为一致。如果测试用例的预期结果与 C 实现不一致,应报告为功能一致性问题;\n"
547
+ " 6. **检查测试用例质量**:测试用例应该有清晰的测试名称、适当的断言、正确的测试逻辑,能够真正验证函数的功能;\n"
548
+ " 7. **注意**:如果函数是资源释放类函数(如 fclose、free 等),在 Rust 中通过 RAII 自动管理,测试用例可以非常简单(如仅验证函数可以调用而不崩溃),这是可以接受的;\n"
549
+ "- 若Rust实现使用了不同的实现方式但保持了功能一致,且无严重问题、无破坏性变更、文件结构合理,ok 应为 true\n"
550
+ "- 仅报告功能不一致、严重问题、破坏性变更和文件结构问题,不报告类型匹配、指针可变性、边界检查细节等技术细节(除非会导致功能错误)\n"
551
+ "- **重要:每个问题描述必须包含以下内容:**\n"
552
+ " 1. 问题的详细描述:明确指出问题所在的位置(文件、函数、行号等)、问题的具体表现、为什么这是一个问题\n"
553
+ " 2. 修复建议:提供具体的修复方案,包括需要修改的代码位置、修改方式、预期效果等\n"
554
+ " 3. **修复代码示例(如果适用)**:对于可以明确修复的问题,提供具体的修复代码示例,包括修改前后的代码对比。格式:\n"
555
+ " - 修改前:显示有问题的代码片段(包含足够的上下文,如函数签名、前后几行代码)\n"
556
+ " - 修改后:显示修复后的代码片段\n"
557
+ " - 如果问题涉及多个位置,提供所有相关位置的修复代码示例\n"
558
+ " 4. 问题格式:[function]、[critical]、[breaking] 或 [structure] 开头,后跟详细的问题描述、修复建议和修复代码示例\n"
559
+ " 示例格式:\n"
560
+ ' "[function] 返回值处理缺失:在函数 foo 的第 42 行,当输入参数为负数时,函数没有返回错误码,但 C 实现中会返回 -1。修复建议:在函数开始处添加参数验证,当参数为负数时返回 Result::Err(Error::InvalidInput)。修复代码示例:\n'
561
+ " 修改前:\n"
562
+ " ```rust\n"
563
+ " pub fn foo(x: i32) -> i32 {\n"
564
+ " // 缺少参数验证\n"
565
+ " x * 2\n"
566
+ " }\n"
567
+ " ```\n"
568
+ " 修改后:\n"
569
+ " ```rust\n"
570
+ " pub fn foo(x: i32) -> Result<i32, Error> {\n"
571
+ " if x < 0 {\n"
572
+ " return Err(Error::InvalidInput);\n"
573
+ " }\n"
574
+ " Ok(x * 2)\n"
575
+ " }\n"
576
+ ' ```"\n'
577
+ ' "[critical] 空指针解引用风险:在函数 bar 的第 58 行,直接解引用指针 ptr 而没有检查其是否为 null,可能导致 panic。修复建议:使用 if let Some(value) = ptr.as_ref() 进行空指针检查,或使用 Option<&T> 类型。修复代码示例:\n'
578
+ " 修改前:\n"
579
+ " ```rust\n"
580
+ " pub fn bar(ptr: *const i32) -> i32 {\n"
581
+ " unsafe { *ptr }\n"
582
+ " }\n"
583
+ " ```\n"
584
+ " 修改后:\n"
585
+ " ```rust\n"
586
+ " pub fn bar(ptr: Option<&i32>) -> Option<i32> {\n"
587
+ " ptr.map(|p| *p)\n"
588
+ " }\n"
589
+ ' ```"\n'
590
+ ' "[breaking] 测试函数重复定义:在 src/common/buffer.rs 第 111-112 行处,新增的测试函数 test_write_buf_1_byte 被同时标记了两次 #[test],导致重复定义。修复建议:删除其中一个 #[test] 标记,确保每个测试函数只有一个 #[test] 属性。修复代码示例:\n'
591
+ " 修改前:\n"
592
+ " ```rust\n"
593
+ " #[test]\n"
594
+ " #[test] // 重复的标记\n"
595
+ " fn test_write_buf_1_byte() {\n"
596
+ " // ...\n"
597
+ " }\n"
598
+ " ```\n"
599
+ " 修改后:\n"
600
+ " ```rust\n"
601
+ " #[test]\n"
602
+ " fn test_write_buf_1_byte() {\n"
603
+ " // ...\n"
604
+ " }\n"
605
+ ' ```"\n'
606
+ ' "[structure] 模块导出缺失:函数 qux 所在的模块 utils 未在 src/lib.rs 中导出,导致无法从 crate 外部访问。修复建议:在 src/lib.rs 中添加 `pub mod utils;` 声明。修复代码示例:\n'
607
+ " 修改前(src/lib.rs):\n"
608
+ " ```rust\n"
609
+ " // 缺少 pub mod utils;\n"
610
+ " ```\n"
611
+ " 修改后(src/lib.rs):\n"
612
+ " ```rust\n"
613
+ " pub mod utils;\n"
614
+ ' ```"\n'
615
+ "请严格按以下格式输出(JSON格式,支持jsonnet语法如尾随逗号、注释、|||分隔符多行字符串等):\n"
616
+ "**示例1:审查通过(无问题)**\n"
617
+ '<SUMMARY>\n{\n "ok": true,\n "function_issues": [],\n "critical_issues": [],\n "breaking_issues": [],\n "structure_issues": []\n}\n</SUMMARY>\n'
618
+ "**示例2:发现问题(必须报告问题)**\n"
619
+ '<SUMMARY>\n{\n "ok": false,\n "function_issues": [],\n "critical_issues": [],\n "breaking_issues": [\n "[breaking] 测试函数重复定义:在 src/common/buffer.rs 第 111-112 行处,新增的测试函数 test_write_buf_1_byte 被同时标记了两次 #[test],导致重复定义。修复建议:删除其中一个 #[test] 标记,确保每个测试函数只有一个 #[test] 属性。"\n ],\n "structure_issues": []\n}\n</SUMMARY>\n'
620
+ "**重要**:如果发现问题,ok 必须为 false,并在相应的问题数组中报告问题。不要因为只有小问题就设置 ok 为 true。"
621
+ )
622
+ # 在 usr_p 和 sum_p 中追加附加说明(sys_p 通常不需要)
623
+ usr_p = self.append_additional_notes(usr_p)
624
+ sum_p = self.append_additional_notes(sum_p)
625
+ return sys_p, usr_p, sum_p
626
+
627
+ i = 0
628
+ max_iterations = self.review_max_iterations
629
+ # 注意:Agent 必须在 crate 根目录下创建,以确保工具(如 read_symbols)能正确获取符号表
630
+ # 由于 transpile() 开始时已切换到 crate 目录,此处无需再次切换
631
+
632
+ # 获取函数信息用于 Agent name
633
+ fn_name = rec.qname or rec.name or f"fn_{rec.id}"
634
+ agent_name = f"C2Rust-Review-Agent({fn_name})"
635
+
636
+ # 0表示无限重试,否则限制迭代次数
637
+ use_direct_model_review = False # 标记是否使用直接模型调用
638
+ parse_failed = False # 标记上一次解析是否失败
639
+ parse_error_msg: Optional[str] = None # 保存上一次的YAML解析错误信息
640
+ previous_issues: Dict[
641
+ str, List[str]
642
+ ] = {} # 保存上一次审查发现的问题,用于验证是否已修复
643
+ while max_iterations == 0 or i < max_iterations:
644
+ # 每次迭代都创建新的 Agent,避免历史记忆干扰
645
+ # 但会在提示词中包含 previous_issues
646
+ sys_p_init, _, sum_p_init = build_review_prompts()
647
+ review_agent = Agent(
648
+ system_prompt=sys_p_init,
649
+ name=agent_name,
650
+ model_group=self.llm_group,
651
+ summary_prompt=sum_p_init,
652
+ need_summary=True,
653
+ auto_complete=True,
654
+ use_tools=["execute_script", "read_code", "read_symbols"],
655
+ non_interactive=self.non_interactive,
656
+ use_methodology=False,
657
+ use_analysis=False,
658
+ )
659
+ # 订阅 BEFORE_TOOL_CALL 和 AFTER_TOOL_CALL 事件,用于细粒度检测测试代码删除
660
+ review_agent.event_bus.subscribe(BEFORE_TOOL_CALL, self.on_before_tool_call)
661
+ review_agent.event_bus.subscribe(AFTER_TOOL_CALL, self.on_after_tool_call)
662
+ # 记录 Agent 创建时的 commit id(作为初始值)
663
+ agent_id = id(review_agent)
664
+ agent_key = f"agent_{agent_id}"
665
+ initial_commit = self.get_crate_commit_hash()
666
+ if initial_commit:
667
+ self.agent_before_commits[agent_key] = initial_commit
668
+
669
+ agent = review_agent
670
+ # 由于 transpile() 开始时已切换到 crate 目录,此处无需再次切换
671
+
672
+ # 每次迭代都重新获取最新的 diff(从保存的 commit 到当前的 HEAD)
673
+ # 重新构建 user prompt,包含最新的 diff
674
+ _, usr_p_current, _ = build_review_prompts() # 重新构建,获取最新的 diff
675
+
676
+ if i > 0:
677
+ # 修复后的审查,添加代码已更新的提示和之前问题的验证要求
678
+ previous_issues_text = ""
679
+ if previous_issues:
680
+ previous_issues_lines = []
681
+ if previous_issues.get("function_issues"):
682
+ previous_issues_lines.append("功能一致性问题:")
683
+ previous_issues_lines.extend(
684
+ [
685
+ f" - {issue}"
686
+ for issue in previous_issues["function_issues"]
687
+ ]
688
+ )
689
+ if previous_issues.get("critical_issues"):
690
+ previous_issues_lines.append("严重问题:")
691
+ previous_issues_lines.extend(
692
+ [
693
+ f" - {issue}"
694
+ for issue in previous_issues["critical_issues"]
695
+ ]
696
+ )
697
+ if previous_issues.get("breaking_issues"):
698
+ previous_issues_lines.append("破坏性变更问题:")
699
+ previous_issues_lines.extend(
700
+ [
701
+ f" - {issue}"
702
+ for issue in previous_issues["breaking_issues"]
703
+ ]
704
+ )
705
+ if previous_issues.get("structure_issues"):
706
+ previous_issues_lines.append("文件结构问题:")
707
+ previous_issues_lines.extend(
708
+ [
709
+ f" - {issue}"
710
+ for issue in previous_issues["structure_issues"]
711
+ ]
712
+ )
713
+ if previous_issues_lines:
714
+ previous_issues_text = "\n".join(
715
+ [
716
+ "",
717
+ "【重要:需要验证之前发现的问题是否已修复】",
718
+ f"在第 {i} 次审查中,发现了以下问题:",
719
+ "<PREVIOUS_ISSUES>",
720
+ "\n".join(previous_issues_lines),
721
+ "</PREVIOUS_ISSUES>",
722
+ "",
723
+ "**必须验证:**",
724
+ "- 请仔细检查上述每个问题是否已经在代码修复中得到解决",
725
+ "- 如果问题仍然存在,必须再次报告为相应类型的问题",
726
+ "- 如果问题已经修复,则不应再报告",
727
+ "- **必须使用 read_code 工具或 git diff 来验证问题是否已修复,不能仅凭假设**",
728
+ "",
729
+ ]
730
+ )
731
+
732
+ code_changed_notice = "\n".join(
733
+ [
734
+ "",
735
+ "【重要:代码已更新】",
736
+ f"在本次审查之前(第 {i} 次迭代),已根据审查意见对代码进行了修复和优化。",
737
+ "目标函数的实现已经发生变化,包括但不限于:",
738
+ "- 函数实现逻辑的修改",
739
+ "- 类型和签名的调整",
740
+ "- 依赖关系的更新",
741
+ "- 错误处理的改进",
742
+ "",
743
+ "**审查要求:**",
744
+ "- **优先使用diff信息**:如果提供了最新的git差异(GIT_DIFF),优先基于diff信息进行审查判断,只有在diff信息不足时才使用 read_code 工具读取原始文件",
745
+ "- 如果必须使用 read_code 工具,请读取目标模块文件的最新内容",
746
+ "- **禁止基于之前的审查结果、对话历史或任何缓存信息进行判断**",
747
+ "- 必须基于最新的代码状态(通过diff或read_code获取)进行审查评估",
748
+ "",
749
+ "如果diff信息充足,可以直接基于diff进行审查;如果diff信息不足,请使用 read_code 工具读取最新代码。",
750
+ "",
751
+ ]
752
+ )
753
+ usr_p_with_notice = (
754
+ usr_p_current + previous_issues_text + code_changed_notice
755
+ )
756
+ composed_prompt = self.compose_prompt_with_context(
757
+ usr_p_with_notice, False
758
+ )
759
+ # 修复后必须使用 Agent.run(),不能使用直接模型调用(因为需要工具调用)
760
+ use_direct_model_review = False
761
+ else:
762
+ composed_prompt = self.compose_prompt_with_context(usr_p_current, False)
763
+
764
+ if use_direct_model_review:
765
+ # 格式解析失败后,直接调用模型接口
766
+ # 构造包含摘要提示词和具体错误信息的完整提示
767
+ error_guidance = ""
768
+ # 检查上一次的解析结果
769
+ if parse_error_msg:
770
+ # 如果有JSON解析错误,优先反馈
771
+ error_guidance = (
772
+ f"\n\n**格式错误详情(请根据以下错误修复输出格式):**\n"
773
+ f"- JSON解析失败: {parse_error_msg}\n\n"
774
+ f"请确保输出的JSON格式正确,包括正确的引号、逗号、大括号等。JSON 对象必须包含字段:ok(布尔值)、function_issues(字符串数组)、critical_issues(字符串数组)、breaking_issues(字符串数组)、structure_issues(字符串数组)。支持jsonnet语法(如尾随逗号、注释、||| 或 ``` 分隔符多行字符串等)。"
775
+ )
776
+ elif parse_failed:
777
+ error_guidance = (
778
+ "\n\n**格式错误详情(请根据以下错误修复输出格式):**\n"
779
+ "- 无法从摘要中解析出有效的 JSON 对象\n\n"
780
+ "请确保输出格式正确:仅输出一个 <SUMMARY> 块,块内直接包含 JSON 对象(不需要额外的标签),字段:ok(布尔值)、function_issues(字符串数组)、critical_issues(字符串数组)、breaking_issues(字符串数组)、structure_issues(字符串数组)。支持jsonnet语法(如尾随逗号、注释、||| 或 ``` 分隔符多行字符串等)。"
781
+ )
782
+
783
+ full_prompt = f"{composed_prompt}{error_guidance}\n\n{sum_p_init}"
784
+ PrettyOutput.auto_print(
785
+ f"⚠️ [c2rust-transpiler][review] 直接调用模型接口修复格式错误(第 {i + 1} 次重试)",
786
+ )
787
+ try:
788
+ response = agent.model.chat_until_success(full_prompt)
789
+ summary = str(response or "")
790
+ except Exception as e:
791
+ PrettyOutput.auto_print(
792
+ f"⚠️ [c2rust-transpiler][review] 直接模型调用失败: {e},回退到 run()",
793
+ )
794
+ summary = str(agent.run(composed_prompt) or "")
795
+ else:
796
+ # 第一次使用 run(),让 Agent 完整运行(可能使用工具)
797
+ summary = str(agent.run(composed_prompt) or "")
798
+
799
+ # 解析 JSON 格式的审查结果
800
+ verdict, parse_error_review = extract_json_from_summary(summary)
801
+ parse_failed = False
802
+ parse_error_msg = None
803
+ if parse_error_review:
804
+ # JSON解析失败
805
+ parse_failed = True
806
+ parse_error_msg = parse_error_review
807
+ PrettyOutput.auto_print(
808
+ f"[c2rust-transpiler][review] JSON解析失败: {parse_error_review}",
809
+ )
810
+ # 兼容旧格式:尝试解析纯文本 OK
811
+ m = re.search(
812
+ r"<SUMMARY>([\s\S]*?)</SUMMARY>", summary, flags=re.IGNORECASE
813
+ )
814
+ content = (m.group(1).strip() if m else summary.strip()).upper()
815
+ if content == "OK":
816
+ verdict = {
817
+ "ok": True,
818
+ "function_issues": [],
819
+ "critical_issues": [],
820
+ "breaking_issues": [],
821
+ "structure_issues": [],
822
+ }
823
+ parse_failed = False # 兼容格式成功,不算解析失败
824
+ parse_error_msg = None
825
+ else:
826
+ # 无法解析,立即重试:设置标志并继续循环
827
+ use_direct_model_review = True
828
+ # 继续循环,立即重试
829
+ continue
830
+ elif not isinstance(verdict, dict):
831
+ parse_failed = True
832
+ # 兼容旧格式:尝试解析纯文本 OK
833
+ m = re.search(
834
+ r"<SUMMARY>([\s\S]*?)</SUMMARY>", summary, flags=re.IGNORECASE
835
+ )
836
+ content = (m.group(1).strip() if m else summary.strip()).upper()
837
+ if content == "OK":
838
+ verdict = {
839
+ "ok": True,
840
+ "function_issues": [],
841
+ "critical_issues": [],
842
+ "breaking_issues": [],
843
+ "structure_issues": [],
844
+ }
845
+ parse_failed = False # 兼容格式成功,不算解析失败
846
+ else:
847
+ # 无法解析,立即重试:设置标志并继续循环
848
+ use_direct_model_review = True
849
+ parse_error_msg = f"无法从摘要中解析出有效的 JSON 对象,得到的内容类型为: {type(verdict).__name__}"
850
+ # 继续循环,立即重试
851
+ continue
852
+
853
+ ok = bool(verdict.get("ok") is True)
854
+ raw_function_issues = verdict.get("function_issues")
855
+ function_issues: list[str] = (
856
+ [str(item) for item in raw_function_issues]
857
+ if isinstance(raw_function_issues, list)
858
+ else []
859
+ )
860
+ raw_critical_issues = verdict.get("critical_issues")
861
+ critical_issues: list[str] = (
862
+ [str(item) for item in raw_critical_issues]
863
+ if isinstance(raw_critical_issues, list)
864
+ else []
865
+ )
866
+ raw_breaking_issues = verdict.get("breaking_issues")
867
+ breaking_issues: list[str] = (
868
+ [str(item) for item in raw_breaking_issues]
869
+ if isinstance(raw_breaking_issues, list)
870
+ else []
871
+ )
872
+ raw_structure_issues = verdict.get("structure_issues")
873
+ structure_issues: list[str] = (
874
+ [str(item) for item in raw_structure_issues]
875
+ if isinstance(raw_structure_issues, list)
876
+ else []
877
+ )
878
+
879
+ PrettyOutput.auto_print(
880
+ f"📊 [c2rust-transpiler][review][iter={i + 1}] verdict ok={ok}, function_issues={len(function_issues)}, critical_issues={len(critical_issues)}, breaking_issues={len(breaking_issues)}, structure_issues={len(structure_issues)}",
881
+ )
882
+
883
+ # 保存本次审查发现的问题,供下次审查时验证(仅在发现问题时保存)
884
+ if (
885
+ not ok
886
+ or function_issues
887
+ or critical_issues
888
+ or breaking_issues
889
+ or structure_issues
890
+ ):
891
+ previous_issues = {
892
+ "function_issues": list(function_issues),
893
+ "critical_issues": list(critical_issues),
894
+ "breaking_issues": list(breaking_issues),
895
+ "structure_issues": list(structure_issues),
896
+ }
897
+
898
+ # 如果 ok 为 true,表示审查通过(功能一致且无严重问题、无破坏性变更、文件结构合理),直接返回,不触发修复
899
+ if ok:
900
+ limit_info = (
901
+ f" (上限: {max_iterations if max_iterations > 0 else '无限'})"
902
+ )
903
+ PrettyOutput.auto_print(
904
+ f"✅ [c2rust-transpiler][review] 代码审查通过{limit_info} (共 {i + 1} 次迭代)。",
905
+ )
906
+ # 清理当前 agent 的事件订阅和记录
907
+ try:
908
+ review_agent.event_bus.unsubscribe(
909
+ BEFORE_TOOL_CALL, self.on_before_tool_call
910
+ )
911
+ review_agent.event_bus.unsubscribe(
912
+ AFTER_TOOL_CALL, self.on_after_tool_call
913
+ )
914
+ # 清理 agent_before_commits 中的记录
915
+ if agent_key in self.agent_before_commits:
916
+ del self.agent_before_commits[agent_key]
917
+ except Exception:
918
+ pass
919
+ # 记录审查结果到进度
920
+ try:
921
+ cur = self.progress.get("current") or {}
922
+ cur["review"] = {
923
+ "ok": True,
924
+ "function_issues": list(function_issues),
925
+ "critical_issues": list(critical_issues),
926
+ "breaking_issues": list(breaking_issues),
927
+ "structure_issues": list(structure_issues),
928
+ "iterations": i + 1,
929
+ }
930
+ metrics = cur.get("metrics") or {}
931
+ metrics["review_iterations"] = i + 1
932
+ metrics["function_issues"] = len(function_issues)
933
+ metrics["type_issues"] = len(critical_issues)
934
+ metrics["breaking_issues"] = len(breaking_issues)
935
+ metrics["structure_issues"] = len(structure_issues)
936
+ cur["metrics"] = metrics
937
+ self.progress["current"] = cur
938
+ self.save_progress()
939
+ except Exception:
940
+ pass
941
+ return
942
+
943
+ # 需要优化:提供详细上下文背景,并明确审查意见仅针对 Rust crate,不修改 C 源码
944
+ crate_tree = dir_tree(self.crate_dir)
945
+ issues_text = "\n".join(
946
+ [
947
+ "功能一致性问题:" if function_issues else "",
948
+ *[f" - {issue}" for issue in function_issues],
949
+ "严重问题(可能导致功能错误):" if critical_issues else "",
950
+ *[f" - {issue}" for issue in critical_issues],
951
+ "破坏性变更问题(对现有代码的影响):" if breaking_issues else "",
952
+ *[f" - {issue}" for issue in breaking_issues],
953
+ "文件结构问题:" if structure_issues else "",
954
+ *[f" - {issue}" for issue in structure_issues],
955
+ ]
956
+ )
957
+ # 获取 C 源文件位置信息
958
+ c_file_location = ""
959
+ if hasattr(rec, "file") and rec.file:
960
+ if (
961
+ hasattr(rec, "start_line")
962
+ and hasattr(rec, "end_line")
963
+ and rec.start_line
964
+ and rec.end_line
965
+ ):
966
+ c_file_location = f"{rec.file}:{rec.start_line}-{rec.end_line}"
967
+ else:
968
+ c_file_location = str(rec.file)
969
+
970
+ fix_prompt = "\n".join(
971
+ [
972
+ "请根据以下审查结论对目标函数进行最小优化(保留结构与意图,不进行大范围重构):",
973
+ "<REVIEW>",
974
+ issues_text
975
+ if issues_text.strip()
976
+ else "审查发现问题,但未提供具体问题描述",
977
+ "</REVIEW>",
978
+ "",
979
+ "上下文背景信息:",
980
+ f"- **Crate 根目录(重要)**: {self.crate_dir.resolve()}",
981
+ " * 所有 Rust 源码文件都位于此目录下",
982
+ " * 使用 `read_code` 工具读取 Rust 文件时,文件路径应相对于此目录(如 `src/xxx.rs`)或使用绝对路径",
983
+ " * 使用 `edit_file_*` 工具编辑文件时,文件路径也应相对于此目录或使用绝对路径",
984
+ " * 当前工作目录应切换到此目录,或使用绝对路径访问文件",
985
+ f"- 目标模块文件: {module}",
986
+ f"- 建议/当前 Rust 签名: {rust_sig}",
987
+ *(
988
+ [f"- C 源文件位置:{c_file_location}"]
989
+ if c_file_location
990
+ else []
991
+ ),
992
+ "crate 目录结构(部分):",
993
+ crate_tree,
994
+ "",
995
+ "约束与范围:",
996
+ "- 本次审查意见仅针对 Rust crate 的代码与配置;不要修改任何 C/C++ 源文件(*.c、*.h 等)。",
997
+ "- 仅允许在 crate_dir 下进行最小必要修改(Cargo.toml、src/**/*.rs);不要改动其他目录。",
998
+ "- 保持最小改动,避免与问题无关的重构或格式化。",
999
+ "- 优先修复严重问题(可能导致功能错误),然后修复功能一致性问题;",
1000
+ "- ⚠️ **重要:修复范围要求** - 不仅要修复当前审查发现的问题,如果修复过程中导致其他测试用例失败,也必须一并修复:",
1001
+ " * 修复后必须运行 `cargo test -- --nocapture` 验证所有测试用例都能通过",
1002
+ " * 如果发现修复后某些原本通过的测试用例现在失败了,说明修复引入了回归问题,必须一并修复",
1003
+ " * 必须确保修复后所有测试用例(包括目标函数的测试和其他函数的测试)都能通过",
1004
+ " * 如果修复影响了其他函数或模块,需要检查并修复所有受影响的部分",
1005
+ "- 如审查问题涉及缺失/未实现的被调函数或依赖,请阅读其 C 源码并在本次一并补齐等价的 Rust 实现;必要时在合理模块新增函数或引入精确 use;",
1006
+ "- 禁止使用 todo!/unimplemented! 作为占位;",
1007
+ "- 可使用工具 read_symbols/read_code 获取依赖符号的 C 源码与位置以辅助实现;仅精确导入所需符号(禁止通配);",
1008
+ "- **使用工具时的路径说明**:",
1009
+ f" * `read_code` 工具:读取 Rust 文件时,使用相对于 crate 根目录({self.crate_dir.resolve()})的路径(如 `src/xxx.rs`)或绝对路径",
1010
+ " * `edit_file_*` 工具:编辑文件时,文件路径也应相对于 crate 根目录或使用绝对路径",
1011
+ "- 注释规范:所有代码注释(包括文档注释、行内注释、块注释等)必须使用中文;",
1012
+ *(
1013
+ [
1014
+ f"- **禁用库约束**:禁止在优化中使用以下库:{', '.join(self.disabled_libraries)}。如果这些库在 Cargo.toml 中已存在,请移除相关依赖;如果优化需要使用这些库的功能,请使用标准库或其他允许的库替代。"
1015
+ ]
1016
+ if self.disabled_libraries
1017
+ else []
1018
+ ),
1019
+ *(
1020
+ [
1021
+ f"- **根符号要求**:此函数是根符号({rec.qname or rec.name}),必须使用 `pub` 关键字对外暴露,确保可以从 crate 外部访问。同时,该函数所在的模块必须在 src/lib.rs 中被导出(使用 `pub mod <模块名>;`)。"
1022
+ ]
1023
+ if self.is_root_symbol(rec)
1024
+ else []
1025
+ ),
1026
+ "",
1027
+ "【重要:依赖检查与实现要求】",
1028
+ "在优化函数之前,请务必检查以下内容:",
1029
+ "1. 检查当前函数是否已完整实现:",
1030
+ f" - 在目标模块 {module} 中查找函数 {rec.qname or rec.name} 的实现",
1031
+ " - 如果已存在实现,检查其是否完整且正确",
1032
+ "2. 检查所有依赖函数是否已实现:",
1033
+ " - 遍历当前函数调用的所有被调函数(包括直接调用和间接调用)",
1034
+ " - 对于每个被调函数,检查其在 Rust crate 中是否已有完整实现",
1035
+ " - 可以使用 read_code 工具读取相关模块文件进行检查",
1036
+ "3. 对于未实现的依赖函数:",
1037
+ " - 使用 read_symbols 工具获取其 C 源码和符号信息",
1038
+ " - 使用 read_code 工具读取其 C 源码实现",
1039
+ " - 在本次优化中一并补齐这些依赖函数的 Rust 实现",
1040
+ " - 根据依赖关系选择合适的模块位置(可在同一模块或合理的新模块中)",
1041
+ " - 确保所有依赖函数都有完整实现,禁止使用 todo!/unimplemented! 占位",
1042
+ "4. 实现顺序:",
1043
+ " - 优先实现最底层的依赖函数(不依赖其他未实现函数的函数)",
1044
+ " - 然后实现依赖这些底层函数的函数",
1045
+ " - 最后优化当前目标函数",
1046
+ "5. 验证:",
1047
+ " - 确保当前函数及其所有依赖函数都已完整实现",
1048
+ " - 确保没有遗留的 todo!/unimplemented! 占位",
1049
+ " - 确保所有函数调用都能正确解析",
1050
+ "",
1051
+ ]
1052
+ )
1053
+
1054
+ # 获取 C 代码用于修复 Agent
1055
+ c_code = self.read_source_span(rec)
1056
+
1057
+ # 先创建修复 Agent(后续会复用)
1058
+ # 由于 transpile() 开始时已切换到 crate 目录,此处无需再次切换
1059
+ # 记录运行前的 commit
1060
+ before_commit = self.get_crate_commit_hash()
1061
+ # 使用修复 Agent,每次重新创建,并传入 C 代码
1062
+ ca = self.get_fix_agent(c_code)
1063
+
1064
+ # 添加 git 变更信息作为上下文
1065
+ if self.get_git_diff:
1066
+ try:
1067
+ base_commit = self.current_function_start_commit_getter()
1068
+ git_diff = self.get_git_diff(base_commit)
1069
+ if git_diff and git_diff.strip():
1070
+ # 限制 git diff 长度,避免上下文过大
1071
+ # 使用较小的比例(30%)因为修复提示词本身已经很长
1072
+ # 使用已创建的修复 Agent 获取更准确的剩余 token
1073
+ git_diff = truncate_git_diff_with_context_limit(
1074
+ git_diff,
1075
+ agent=ca,
1076
+ llm_group=self.llm_group,
1077
+ token_ratio=0.3,
1078
+ base_commit=base_commit,
1079
+ crate_dir=self.crate_dir,
1080
+ )
1081
+
1082
+ fix_prompt += "\n\n"
1083
+ fix_prompt += "【Git 变更信息】\n"
1084
+ fix_prompt += "以下是从函数开始处理到当前的 git 变更,可以帮助你了解已经做了哪些修改:\n"
1085
+ fix_prompt += "<GIT_DIFF>\n"
1086
+ fix_prompt += git_diff
1087
+ fix_prompt += "\n</GIT_DIFF>\n"
1088
+ fix_prompt += "\n"
1089
+ fix_prompt += "提示:\n"
1090
+ fix_prompt += "- 请仔细查看上述 git diff,了解当前代码的状态和已做的修改\n"
1091
+ fix_prompt += (
1092
+ "- 如果看到之前的修改引入了问题,可以在修复时一并处理\n"
1093
+ )
1094
+ fix_prompt += (
1095
+ "- 如果看到某些文件被意外修改,需要确认这些修改是否必要\n"
1096
+ )
1097
+ fix_prompt += "- 结合审查问题,有针对性地修复代码\n"
1098
+ except Exception:
1099
+ # 如果获取 git diff 失败,不影响主流程
1100
+ pass
1101
+
1102
+ fix_prompt += "\n请仅以补丁形式输出修改,避免冗余解释。"
1103
+ limit_info = f"/{max_iterations}" if max_iterations > 0 else "/∞"
1104
+ fix_prompt_with_notes = self.append_additional_notes(fix_prompt)
1105
+ ca.run(
1106
+ self.compose_prompt_with_context(fix_prompt_with_notes, True),
1107
+ prefix=f"⚠️ [c2rust-transpiler][review-fix iter={i + 1}{limit_info}]",
1108
+ suffix="",
1109
+ )
1110
+
1111
+ # 检测并处理测试代码删除
1112
+ if self.check_and_handle_test_deletion(before_commit, ca):
1113
+ # 如果回退了,需要重新运行 agent
1114
+ PrettyOutput.auto_print(
1115
+ f"⚠️ [c2rust-transpiler][review-fix] 检测到测试代码删除问题,已回退,重新运行 agent (iter={i + 1})",
1116
+ )
1117
+ before_commit = self.get_crate_commit_hash()
1118
+ # 重新创建修复 Agent
1119
+ ca = self.get_fix_agent(c_code)
1120
+ ca.run(
1121
+ self.compose_prompt_with_context(fix_prompt_with_notes, True),
1122
+ prefix=f"⚠️ [c2rust-transpiler][review-fix iter={i + 1}{limit_info}][retry]",
1123
+ suffix="",
1124
+ )
1125
+ # 再次检测
1126
+ if self.check_and_handle_test_deletion(before_commit, ca):
1127
+ PrettyOutput.auto_print(
1128
+ f"❌ [c2rust-transpiler][review-fix] 再次检测到测试代码删除问题,已回退 (iter={i + 1})",
1129
+ )
1130
+
1131
+ # 优化后进行一次构建验证;若未通过则进入构建修复循环,直到通过为止
1132
+ self.cargo_build_loop()
1133
+
1134
+ # 检查构建修复过程中是否进行了修复
1135
+ build_has_fixes = (
1136
+ self.get_build_loop_has_fixes()
1137
+ if hasattr(self, "get_build_loop_has_fixes")
1138
+ else False
1139
+ )
1140
+ if build_has_fixes:
1141
+ PrettyOutput.auto_print(
1142
+ "[c2rust-transpiler][review-fix] 构建修复过程中进行了修复,将在下一轮审查中重新检查",
1143
+ )
1144
+
1145
+ # 记录本次审查结果
1146
+ try:
1147
+ cur = self.progress.get("current") or {}
1148
+ cur["review"] = {
1149
+ "ok": False,
1150
+ "function_issues": list(function_issues),
1151
+ "critical_issues": list(critical_issues),
1152
+ "breaking_issues": list(breaking_issues),
1153
+ "structure_issues": list(structure_issues),
1154
+ "iterations": i + 1,
1155
+ }
1156
+ metrics = cur.get("metrics") or {}
1157
+ metrics["function_issues"] = len(function_issues)
1158
+ metrics["type_issues"] = len(critical_issues)
1159
+ metrics["breaking_issues"] = len(breaking_issues)
1160
+ metrics["structure_issues"] = len(structure_issues)
1161
+ cur["metrics"] = metrics
1162
+ self.progress["current"] = cur
1163
+ self.save_progress()
1164
+ except Exception:
1165
+ pass
1166
+
1167
+ # 清理当前迭代的 agent 事件订阅和记录(为下一次迭代做准备)
1168
+ # 取消事件订阅,避免事件总线持有对 agent 的引用,导致 agent 无法被垃圾回收
1169
+ try:
1170
+ review_agent.event_bus.unsubscribe(
1171
+ BEFORE_TOOL_CALL, self.on_before_tool_call
1172
+ )
1173
+ review_agent.event_bus.unsubscribe(
1174
+ AFTER_TOOL_CALL, self.on_after_tool_call
1175
+ )
1176
+ # 清理 agent_before_commits 中的记录
1177
+ if agent_key in self.agent_before_commits:
1178
+ del self.agent_before_commits[agent_key]
1179
+ except Exception:
1180
+ pass
1181
+
1182
+ i += 1
1183
+
1184
+ # 达到迭代上限(仅当设置了上限时)
1185
+ # 注意:最后一次迭代的 agent 已经在循环内部清理过了,这里不需要额外清理
1186
+ PrettyOutput.auto_print(
1187
+ f"⚠️ [c2rust-transpiler][review] 已达到最大迭代次数上限({max_iterations}),停止审查优化。",
1188
+ )
1189
+ try:
1190
+ cur = self.progress.get("current") or {}
1191
+ cur["review_max_iterations_reached"] = True
1192
+ cur["review_iterations"] = i
1193
+ self.progress["current"] = cur
1194
+ self.save_progress()
1195
+ except Exception:
1196
+ pass