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,1294 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ 构建和修复模块
4
+ """
5
+
6
+ import re
7
+ import subprocess
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
+ from typing import cast
16
+
17
+
18
+ from jarvis.jarvis_utils.output import PrettyOutput
19
+ from jarvis.jarvis_c2rust.constants import CONSECUTIVE_FIX_FAILURE_THRESHOLD
20
+ from jarvis.jarvis_c2rust.constants import ERROR_SUMMARY_MAX_LENGTH
21
+ from jarvis.jarvis_c2rust.utils import truncate_git_diff_with_context_limit
22
+
23
+
24
+ class BuildManager:
25
+ """构建和修复管理器"""
26
+
27
+ def __init__(
28
+ self,
29
+ crate_dir: Path,
30
+ project_root: Path,
31
+ data_dir: Path,
32
+ test_max_retries: int,
33
+ disabled_libraries: List[str],
34
+ root_symbols: List[str],
35
+ progress: Dict[str, Any],
36
+ save_progress_func,
37
+ extract_compile_flags_func,
38
+ get_current_function_context_func,
39
+ get_fix_agent_func,
40
+ compose_prompt_with_context_func,
41
+ check_and_handle_test_deletion_func,
42
+ get_crate_commit_hash_func,
43
+ reset_to_commit_func,
44
+ append_additional_notes_func,
45
+ consecutive_fix_failures_getter: Callable[[], int],
46
+ consecutive_fix_failures_setter: Callable[[int], None],
47
+ current_function_start_commit_getter: Callable[[], Optional[str]],
48
+ get_git_diff_func: Optional[Callable[[Optional[str]], str]] = None,
49
+ ) -> None:
50
+ self.crate_dir = crate_dir
51
+ self.project_root = project_root
52
+ self.data_dir = data_dir
53
+ self.test_max_retries = test_max_retries
54
+ self.disabled_libraries = disabled_libraries
55
+ self.root_symbols = root_symbols
56
+ self.progress = progress
57
+ self.save_progress = save_progress_func
58
+ self.extract_compile_flags = extract_compile_flags_func
59
+ self.get_current_function_context = get_current_function_context_func
60
+ self.get_fix_agent = get_fix_agent_func
61
+ self.compose_prompt_with_context = compose_prompt_with_context_func
62
+ self.check_and_handle_test_deletion = check_and_handle_test_deletion_func
63
+ self.get_crate_commit_hash = get_crate_commit_hash_func
64
+ self.reset_to_commit = reset_to_commit_func
65
+ self.append_additional_notes = append_additional_notes_func
66
+ self._consecutive_fix_failures_getter = consecutive_fix_failures_getter
67
+ self._consecutive_fix_failures_setter = consecutive_fix_failures_setter
68
+ self._current_function_start_commit_getter = (
69
+ current_function_start_commit_getter
70
+ )
71
+ self.get_git_diff = get_git_diff_func
72
+ self._build_loop_has_fixes = False # 标记构建循环中是否进行了修复
73
+
74
+ def classify_rust_error(self, text: str) -> List[str]:
75
+ """
76
+ 朴素错误分类,用于提示 CodeAgent 聚焦修复:
77
+ - missing_import: unresolved import / not found in this scope / cannot find ...
78
+ - type_mismatch: mismatched types / expected ... found ...
79
+ - visibility: private module/field/function
80
+ - borrow_checker: does not live long enough / borrowed data escapes / cannot borrow as mutable
81
+ - dependency_missing: failed to select a version / could not find crate
82
+ - module_not_found: file not found for module / unresolved module
83
+ """
84
+ tags: List[str] = []
85
+ t = (text or "").lower()
86
+
87
+ def has(s: str) -> bool:
88
+ return s in t
89
+
90
+ if (
91
+ ("unresolved import" in t)
92
+ or ("not found in this scope" in t)
93
+ or ("cannot find" in t)
94
+ or ("use of undeclared crate or module" in t)
95
+ ):
96
+ tags.append("missing_import")
97
+ if ("mismatched types" in t) or ("expected" in t and "found" in t):
98
+ tags.append("type_mismatch")
99
+ if (
100
+ ("private" in t and "module" in t)
101
+ or ("private" in t and "field" in t)
102
+ or ("private" in t and "function" in t)
103
+ ):
104
+ tags.append("visibility")
105
+ if (
106
+ ("does not live long enough" in t)
107
+ or ("borrowed data escapes" in t)
108
+ or ("cannot borrow" in t)
109
+ ):
110
+ tags.append("borrow_checker")
111
+ if (
112
+ ("failed to select a version" in t)
113
+ or ("could not find crate" in t)
114
+ or ("no matching package named" in t)
115
+ ):
116
+ tags.append("dependency_missing")
117
+ if ("file not found for module" in t) or ("unresolved module" in t):
118
+ tags.append("module_not_found")
119
+ # 去重
120
+ try:
121
+ tags = list(dict.fromkeys(tags))
122
+ except Exception:
123
+ tags = list(set(tags))
124
+ return tags
125
+
126
+ def detect_crate_kind(self) -> str:
127
+ """
128
+ 检测 crate 类型:lib、bin 或 mixed。
129
+ 判定规则(尽量保守,避免误判):
130
+ - 若存在 src/lib.rs 或 Cargo.toml 中包含 [lib],视为包含 lib
131
+ - 若存在 src/main.rs 或 Cargo.toml 中包含 [[bin]](或 [bin] 兼容),视为包含 bin
132
+ - 同时存在则返回 mixed
133
+ - 两者都不明确时,默认返回 lib(与默认模版一致)
134
+ """
135
+ try:
136
+ cargo_path = (self.crate_dir / "Cargo.toml").resolve()
137
+ txt = ""
138
+ if cargo_path.exists():
139
+ try:
140
+ txt = cargo_path.read_text(encoding="utf-8", errors="ignore")
141
+ except Exception:
142
+ txt = ""
143
+ txt_lower = txt.lower()
144
+ has_lib = (self.crate_dir / "src" / "lib.rs").exists() or bool(
145
+ re.search(r"(?m)^\s*\[lib\]\s*$", txt_lower)
146
+ )
147
+ # 兼容:[[bin]] 为数组表,极少数项目也会写成 [bin]
148
+ has_bin = (self.crate_dir / "src" / "main.rs").exists() or bool(
149
+ re.search(r"(?m)^\s*\[\[bin\]\]\s*$", txt_lower)
150
+ or re.search(r"(?m)^\s*\[bin\]\s*$", txt_lower)
151
+ )
152
+ if has_lib and has_bin:
153
+ return "mixed"
154
+ if has_bin:
155
+ return "bin"
156
+ if has_lib:
157
+ return "lib"
158
+ except Exception:
159
+ pass
160
+ # 默认假设为 lib
161
+ return "lib"
162
+
163
+ def run_cargo_fmt(self, workspace_root: str) -> None:
164
+ """执行 cargo fmt 格式化代码"""
165
+ try:
166
+ res = subprocess.run(
167
+ ["cargo", "fmt"],
168
+ capture_output=True,
169
+ text=True,
170
+ check=False,
171
+ cwd=workspace_root,
172
+ )
173
+ if res.returncode == 0:
174
+ PrettyOutput.auto_print("🔍 [c2rust-transpiler][fmt] 代码格式化完成")
175
+ else:
176
+ # fmt 失败不影响主流程,只记录警告
177
+ PrettyOutput.auto_print(
178
+ f"⚠️ [c2rust-transpiler][fmt] 代码格式化失败(非致命): {res.stderr or res.stdout}"
179
+ )
180
+ except Exception as e:
181
+ # fmt 失败不影响主流程,只记录警告
182
+ PrettyOutput.auto_print(
183
+ f"⚠️ [c2rust-transpiler][fmt] 代码格式化异常(非致命): {e}"
184
+ )
185
+
186
+ def build_repair_prompt_base(
187
+ self,
188
+ stage: str,
189
+ tags: List[str],
190
+ sym_name: str,
191
+ src_loc: str,
192
+ c_code: str,
193
+ curr: Dict[str, Any],
194
+ symbols_path: str,
195
+ include_output_patch_hint: bool = False,
196
+ agent: Optional[Any] = None,
197
+ ) -> List[str]:
198
+ """
199
+ 构建修复提示词的基础部分。
200
+
201
+ 返回基础行列表。
202
+ """
203
+ # 检查是否为根符号
204
+ is_root = sym_name in (self.root_symbols or [])
205
+ # 获取 C 源文件位置信息(如果 src_loc 包含文件路径和行号)
206
+ c_file_location = ""
207
+ if src_loc:
208
+ # src_loc 格式可能是 "file:start-end" 或 "file"
209
+ if ":" in src_loc and "-" in src_loc:
210
+ c_file_location = src_loc
211
+ elif src_loc:
212
+ # 如果只有文件路径,尝试从 curr 获取行号信息
213
+ if curr.get("file"):
214
+ file_path = curr.get("file", "")
215
+ start_line = curr.get("start_line")
216
+ end_line = curr.get("end_line")
217
+ if start_line and end_line:
218
+ c_file_location = f"{file_path}:{start_line}-{end_line}"
219
+ else:
220
+ c_file_location = file_path
221
+ else:
222
+ c_file_location = src_loc
223
+
224
+ base_lines = [
225
+ f"目标:以最小的改动修复问题,使 `{stage}` 命令可以通过。",
226
+ f"阶段:{stage}",
227
+ f"错误分类标签: {tags}",
228
+ *([f"C 源文件位置:{c_file_location}"] if c_file_location else []),
229
+ f"**Crate 根目录(重要)**:{self.crate_dir.resolve()}",
230
+ " - 所有 Rust 源码文件都位于此目录下",
231
+ " - 使用 `read_code` 工具读取文件时,请使用相对于此目录的路径(如 `src/xxx.rs`)或绝对路径",
232
+ " - 使用 `edit_file_*` 工具编辑文件时,文件路径也应相对于此目录或使用绝对路径",
233
+ "",
234
+ "允许的修复:修正入口/模块声明/依赖;对入口文件与必要mod.rs进行轻微调整;在缺失/未实现的被调函数导致错误时,一并补齐这些依赖的Rust实现(可新增合理模块/函数);避免大范围改动。",
235
+ "- 保持最小改动,避免与错误无关的重构或格式化;",
236
+ "- 如构建失败源于缺失或未实现的被调函数/依赖,请阅读其 C 源码并在本次一并补齐等价的 Rust 实现;必要时可在合理的模块中新建函数;",
237
+ "- 禁止使用 todo!/unimplemented! 作为占位;",
238
+ "- 可使用工具 read_symbols/read_code 获取依赖符号的 C 源码与位置以辅助实现;仅精确导入所需符号,避免通配;",
239
+ "- **编译警告消除**:修复时必须同时消除所有编译警告(compiler warnings)。修复后应运行 `cargo check --message-format=short` 验证没有警告;",
240
+ "- **Clippy 警告消除**:修复时必须同时消除所有 clippy 警告。修复后应运行 `cargo clippy -- -D warnings` 验证没有警告;",
241
+ "- **🔍 调试辅助:如果遇到难以定位的问题,可以使用以下方法辅助调试**:",
242
+ " * 添加临时调试输出:使用 `println!()` 或 `dbg!()` 宏输出关键变量的值、函数调用路径、中间状态等",
243
+ " * 检查函数调用链:使用 `read_code` 工具读取相关函数的实现,确认调用关系是否正确",
244
+ " * 验证数据流:在关键位置添加调试输出,检查数据在函数间的传递是否正确",
245
+ " * 对比 C 实现:使用 `read_symbols` 和 `read_code` 工具读取 C 源码,对比 Rust 实现与 C 实现的差异",
246
+ " * 检查类型转换:确认 Rust 类型与 C 类型的对应关系是否正确,特别是指针、数组、结构体等",
247
+ " * 验证边界条件:检查数组边界、空值处理、溢出处理等边界情况",
248
+ " * 运行单个测试:如果测试套件很大,可以使用 `cargo test -- --nocapture <test_name>` 运行特定测试,加快调试速度",
249
+ " * 查看完整错误信息:确保阅读完整的错误输出,包括堆栈跟踪、类型信息、位置信息等",
250
+ " * 注意:调试输出可以在修复后移除,但建议保留关键位置的调试信息直到问题完全解决",
251
+ "- ⚠️ **重要:修复范围要求** - 不仅要修复当前问题,如果修复过程中导致其他测试用例失败,也必须一并修复:",
252
+ " * 修复后必须运行 `cargo test -- --nocapture` 验证所有测试用例都能通过",
253
+ " * 如果发现修复后某些原本通过的测试用例现在失败了,说明修复引入了回归问题,必须一并修复",
254
+ " * 必须确保修复后所有测试用例(包括目标函数的测试和其他函数的测试)都能通过",
255
+ " * 如果修复影响了其他函数或模块,需要检查并修复所有受影响的部分",
256
+ " * 不要只修复当前问题,而忽略其他测试的失败",
257
+ "- **⚠️ 重要:修复后必须验证** - 修复完成后,必须使用 `execute_script` 工具执行验证命令:",
258
+ " * 执行 `cargo test -- --nocapture` 验证编译和测试是否通过",
259
+ " * 命令必须成功(返回码为 0),才说明修复成功",
260
+ " * **不要假设修复成功,必须实际执行命令验证**",
261
+ " * **cargo test 会自动编译,无需单独执行 cargo check**",
262
+ "- 注释规范:所有代码注释(包括文档注释、行内注释、块注释等)必须使用中文;",
263
+ f"- 依赖管理:如修复中引入新的外部 crate 或需要启用 feature,请同步更新 Cargo.toml 的 [dependencies]/[dev-dependencies]/[features]{(',避免未声明依赖导致构建失败;版本号可使用兼容范围(如 ^x.y)或默认值' if stage == 'cargo test' else '')};",
264
+ *(
265
+ [
266
+ f"- **禁用库约束**:禁止在修复中使用以下库:{', '.join(self.disabled_libraries)}。如果这些库在 Cargo.toml 中已存在,请移除相关依赖;如果修复需要使用这些库的功能,请使用标准库或其他允许的库替代。"
267
+ ]
268
+ if self.disabled_libraries
269
+ else []
270
+ ),
271
+ *(
272
+ [
273
+ f"- **根符号要求**:此函数是根符号({sym_name}),必须使用 `pub` 关键字对外暴露,确保可以从 crate 外部访问。同时,该函数所在的模块必须在 src/lib.rs 中被导出(使用 `pub mod <模块名>;`)。"
274
+ ]
275
+ if is_root
276
+ else []
277
+ ),
278
+ "",
279
+ "【重要:依赖检查与实现要求】",
280
+ "在修复问题之前,请务必检查以下内容:",
281
+ "1. 检查当前函数是否已完整实现:",
282
+ f" - 在目标模块中查找函数 {sym_name} 的实现",
283
+ " - 如果已存在实现,检查其是否完整且正确",
284
+ "2. 检查所有依赖函数是否已实现:",
285
+ " - 分析构建错误,识别所有缺失或未实现的被调函数",
286
+ " - 遍历当前函数调用的所有被调函数(包括直接调用和间接调用)",
287
+ " - 对于每个被调函数,检查其在 Rust crate 中是否已有完整实现",
288
+ " - 可以使用 read_code 工具读取相关模块文件进行检查",
289
+ "3. 对于未实现的依赖函数:",
290
+ " - 使用 read_symbols 工具获取其 C 源码和符号信息",
291
+ " - 使用 read_code 工具读取其 C 源码实现",
292
+ " - 在本次修复中一并补齐这些依赖函数的 Rust 实现",
293
+ " - 根据依赖关系选择合适的模块位置(可在同一模块或合理的新模块中)",
294
+ " - 确保所有依赖函数都有完整实现,禁止使用 todo!/unimplemented! 占位",
295
+ "4. 实现顺序:",
296
+ " - 优先实现最底层的依赖函数(不依赖其他未实现函数的函数)",
297
+ " - 然后实现依赖这些底层函数的函数",
298
+ " - 最后修复当前目标函数",
299
+ "5. 验证:",
300
+ " - 确保当前函数及其所有依赖函数都已完整实现",
301
+ " - 确保没有遗留的 todo!/unimplemented! 占位",
302
+ " - 确保所有函数调用都能正确解析",
303
+ ]
304
+ if include_output_patch_hint:
305
+ base_lines.append("- 请仅输出补丁,不要输出解释或多余文本。")
306
+ base_lines.extend(
307
+ [
308
+ "",
309
+ "最近处理的函数上下文(供参考,优先修复构建错误):",
310
+ f"- 函数:{sym_name}",
311
+ f"- 源位置:{src_loc}",
312
+ f"- 目标模块(progress):{curr.get('module') or ''}",
313
+ f"- 建议签名(progress):{curr.get('rust_signature') or ''}",
314
+ "",
315
+ "原始C函数源码片段(只读参考):",
316
+ "<C_SOURCE>",
317
+ c_code,
318
+ "</C_SOURCE>",
319
+ ]
320
+ )
321
+ # 添加编译参数(如果存在)
322
+ c_file_path = curr.get("file") or ""
323
+ if c_file_path:
324
+ compile_flags = self.extract_compile_flags(c_file_path)
325
+ if compile_flags:
326
+ base_lines.extend(
327
+ [
328
+ "",
329
+ "C文件编译参数(来自 compile_commands.json):",
330
+ compile_flags,
331
+ ]
332
+ )
333
+ base_lines.extend(
334
+ [
335
+ "",
336
+ "【工具使用建议】",
337
+ "1. 符号表检索:",
338
+ " - 工具: read_symbols",
339
+ " - 用途: 定位或交叉验证 C 符号位置",
340
+ " - 参数示例(JSON):",
341
+ f' {{"symbols_file": "{symbols_path}", "symbols": ["{sym_name}"]}}',
342
+ "",
343
+ "2. 代码读取:",
344
+ " - 工具: read_code",
345
+ " - 用途: 读取 C 源码实现或 Rust 模块文件",
346
+ " - 调试用途: 当遇到问题时,可以读取相关文件检查实现是否正确",
347
+ " - **重要**:读取 Rust 源码文件时,必须使用绝对路径或相对于 crate 根目录的路径",
348
+ f" - **Crate 根目录**:{self.crate_dir.resolve()}",
349
+ " - 示例:",
350
+ f" * 读取 Rust 文件:`read_code` 工具,文件路径使用 `{self.crate_dir.resolve()}/src/xxx.rs` 或 `src/xxx.rs`(相对于 crate 根目录)",
351
+ " * 读取 C 文件:`read_code` 工具,文件路径使用 C 源文件的完整路径",
352
+ "",
353
+ "3. 脚本执行(调试辅助):",
354
+ " - 工具: execute_script",
355
+ " - 调试用途:",
356
+ " * 执行 `cargo test -- --nocapture <test_name>` 运行特定测试,加快调试速度",
357
+ " * 执行 `cargo test --message-format=short --no-run` 只检查编译,不运行测试",
358
+ " * 执行 `cargo check` 快速检查编译错误(如果测试太慢)",
359
+ " * 执行 `cargo test --lib` 只运行库测试,跳过集成测试",
360
+ " * 执行 `cargo test --test <test_file>` 运行特定的测试文件",
361
+ "",
362
+ "上下文:",
363
+ f"- **Crate 根目录路径(重要)**: {self.crate_dir.resolve()}",
364
+ " * 所有 Rust 源码文件都位于此目录下",
365
+ " * 使用 `read_code` 工具读取 Rust 文件时,文件路径应相对于此目录(如 `src/xxx.rs`)或使用绝对路径",
366
+ " * 当前工作目录应切换到此目录,或使用绝对路径访问文件",
367
+ f"- 包名称(用于 cargo -p): {self.crate_dir.name}",
368
+ ]
369
+ )
370
+
371
+ # 添加 git 变更信息作为上下文
372
+ if self.get_git_diff:
373
+ try:
374
+ base_commit = self._current_function_start_commit_getter()
375
+ git_diff = self.get_git_diff(base_commit)
376
+ if git_diff and git_diff.strip():
377
+ # 限制 git diff 长度,避免上下文过大
378
+ # 使用较小的比例(30%)因为修复提示词本身已经很长
379
+ # 如果提供了 agent 则使用它获取更准确的剩余 token,否则使用回退方案
380
+ git_diff = truncate_git_diff_with_context_limit(
381
+ git_diff, agent=agent, token_ratio=0.3
382
+ )
383
+
384
+ base_lines.extend(
385
+ [
386
+ "",
387
+ "【Git 变更信息】",
388
+ "以下是从函数开始处理到当前的 git 变更,可以帮助你了解已经做了哪些修改:",
389
+ "<GIT_DIFF>",
390
+ git_diff,
391
+ "</GIT_DIFF>",
392
+ "",
393
+ "提示:",
394
+ "- 请仔细查看上述 git diff,了解当前代码的状态和已做的修改",
395
+ "- 如果看到之前的修改引入了问题,可以在修复时一并处理",
396
+ "- 如果看到某些文件被意外修改,需要确认这些修改是否必要",
397
+ ]
398
+ )
399
+ except Exception:
400
+ # 如果获取 git diff 失败,不影响主流程,只记录警告
401
+ pass
402
+
403
+ return base_lines
404
+
405
+ def build_repair_prompt_stage_section(
406
+ self, stage: str, output: str, command: Optional[str] = None
407
+ ) -> List[str]:
408
+ """
409
+ 构建修复提示词的阶段特定部分(测试或检查)。
410
+
411
+ 返回阶段特定的行列表。
412
+ """
413
+ section_lines: List[str] = []
414
+ if stage == "cargo_test_warning":
415
+ section_lines.extend(
416
+ [
417
+ "",
418
+ "【⚠️ 重要:Cargo Test 编译警告 - 必须消除】",
419
+ "以下输出来自 `cargo test -- --nocapture` 命令,包含编译和测试过程中的警告详情:",
420
+ "- **Cargo Test 警告当前状态:有警告** - 必须消除所有警告才能继续",
421
+ "- 这些警告是在 `cargo test` 编译阶段产生的(如 `unused_mut`、`unused_variables`、`dead_code` 等)",
422
+ "- Cargo Test 警告通常表示代码存在潜在问题或可以改进的地方",
423
+ "- **请仔细阅读警告信息**,包括:",
424
+ " * 警告类型(如 `unused_mut`、`unused_variables`、`dead_code`、`unused_import` 等)",
425
+ " * 警告位置(文件路径和行号)",
426
+ " * 警告说明和建议的修复方法",
427
+ "",
428
+ "**关键要求:**",
429
+ "- 必须分析每个警告的根本原因,并按照编译器的建议进行修复",
430
+ "- 必须实际修复导致警告的代码,而不是忽略警告",
431
+ "- 修复后必须确保 `cargo test -- --nocapture` 能够通过(返回码为 0 且无警告输出)",
432
+ "- 注意:`cargo test` 会自动编译代码,编译阶段的警告会显示在输出中",
433
+ "",
434
+ ]
435
+ )
436
+ if command:
437
+ section_lines.append(f"执行的命令:{command}")
438
+ section_lines.append(
439
+ "提示:如果不相信上述命令执行结果,可以使用 execute_script 工具自己执行一次该命令进行验证。"
440
+ )
441
+ section_lines.extend(
442
+ [
443
+ "",
444
+ "【Cargo Test 警告详细信息 - 必须仔细阅读并修复】",
445
+ "以下是从 `cargo test -- --nocapture` 命令获取的完整输出,包含所有警告的具体信息:",
446
+ "<CARGO_TEST_WARNINGS>",
447
+ output,
448
+ "</CARGO_TEST_WARNINGS>",
449
+ "",
450
+ "**修复要求:**",
451
+ "1. 仔细分析上述警告信息,找出每个警告的根本原因",
452
+ "2. 定位到具体的代码位置(文件路径和行号)",
453
+ "3. 按照编译器的建议进行修复:",
454
+ " - 如果警告建议移除 `mut`,请移除不必要的 `mut` 关键字",
455
+ " - 如果警告建议使用下划线前缀,请将未使用的变量改为 `_变量名`",
456
+ " - 如果警告是 `dead_code`(未使用的函数/变量),请移除未使用的代码或使用 `#[allow(dead_code)]` 注解(仅在必要时)",
457
+ " - 如果警告建议移除未使用的导入,请移除或使用 `#[allow(unused_imports)]` 注解(仅在必要时)",
458
+ " - 如果警告建议使用更安全的 API,请使用建议的 API",
459
+ " - 如果警告建议改进代码结构,请按照建议优化代码",
460
+ "4. 修复所有警告,确保 `cargo test -- --nocapture` 能够通过(返回码为 0 且无警告输出)",
461
+ "5. 如果某些警告确实无法修复或需要特殊处理,可以使用 `#[allow(warning_name)]` 注解,但必须添加注释说明原因",
462
+ "",
463
+ "**⚠️ 重要:修复后必须验证**",
464
+ "- 修复完成后,**必须使用 `execute_script` 工具执行以下命令验证修复效果**:",
465
+ f" - 命令:`{command or 'cargo test -- --nocapture'}`",
466
+ "- 验证要求:",
467
+ " * 如果命令执行成功(返回码为 0)且无警告输出,说明修复成功",
468
+ " * 如果命令执行失败(返回码非 0)或有警告输出,说明仍有警告,需要继续修复",
469
+ " * **不要假设修复成功,必须实际执行命令验证**",
470
+ "- 如果验证失败,请分析失败原因并继续修复,直到验证通过",
471
+ "",
472
+ "修复后请再次执行 `cargo test -- --nocapture` 进行验证。",
473
+ ]
474
+ )
475
+ elif stage == "compiler_warning":
476
+ section_lines.extend(
477
+ [
478
+ "",
479
+ "【⚠️ 重要:编译警告 - 必须消除】",
480
+ "以下输出来自 `cargo check --message-format=short` 命令,包含编译警告详情:",
481
+ "- **编译警告当前状态:有警告** - 必须消除所有警告才能继续",
482
+ "- 编译警告通常表示代码存在潜在问题或可以改进的地方",
483
+ "- **请仔细阅读警告信息**,包括:",
484
+ " * 警告类型(如 `unused_variable`、`dead_code`、`unused_import` 等)",
485
+ " * 警告位置(文件路径和行号)",
486
+ " * 警告说明和建议的修复方法",
487
+ "",
488
+ "**关键要求:**",
489
+ "- 必须分析每个警告的根本原因,并按照编译器的建议进行修复",
490
+ "- 必须实际修复导致警告的代码,而不是忽略警告",
491
+ "- 修复后必须确保 `cargo check --message-format=short` 能够通过(返回码为 0 且无警告输出)",
492
+ "",
493
+ ]
494
+ )
495
+ if command:
496
+ section_lines.append(f"执行的命令:{command}")
497
+ section_lines.append(
498
+ "提示:如果不相信上述命令执行结果,可以使用 execute_script 工具自己执行一次该命令进行验证。"
499
+ )
500
+ section_lines.extend(
501
+ [
502
+ "",
503
+ "【编译警告详细信息 - 必须仔细阅读并修复】",
504
+ "以下是从 `cargo check --message-format=short` 命令获取的完整输出,包含所有警告的具体信息:",
505
+ "<COMPILER_WARNINGS>",
506
+ output,
507
+ "</COMPILER_WARNINGS>",
508
+ "",
509
+ "**修复要求:**",
510
+ "1. **优先尝试自动修复**:先使用 `cargo fix` 命令自动修复可以自动修复的警告:",
511
+ " - 执行 `cargo fix --allow-dirty --allow-staged` 尝试自动修复编译警告",
512
+ " - 如果自动修复成功,验证修复效果:执行 `cargo check --message-format=short` 检查是否还有警告",
513
+ " - 如果自动修复后仍有警告,继续手动修复剩余的警告",
514
+ "2. 仔细分析上述编译警告信息,找出每个警告的根本原因",
515
+ "3. 定位到具体的代码位置(文件路径和行号)",
516
+ "4. 按照编译器的建议进行修复:",
517
+ " - 如果警告建议移除未使用的代码,请移除或使用 `#[allow(...)]` 注解(仅在必要时)",
518
+ " - 如果警告建议使用更安全的 API,请使用建议的 API",
519
+ " - 如果警告建议改进代码结构,请按照建议优化代码",
520
+ "5. 修复所有警告,确保 `cargo check --message-format=short` 能够通过(无警告输出)",
521
+ "6. 如果某些警告确实无法修复或需要特殊处理,可以使用 `#[allow(warning_name)]` 注解,但必须添加注释说明原因",
522
+ "",
523
+ "**⚠️ 重要:修复后必须验证**",
524
+ "- 修复完成后,**必须使用 `execute_script` 工具执行以下命令验证修复效果**:",
525
+ f" - 命令:`{command or 'cargo check --message-format=short'}`",
526
+ "- 验证要求:",
527
+ " * 如果命令执行成功(返回码为 0)且无警告输出,说明修复成功",
528
+ " * 如果命令执行失败(返回码非 0)或有警告输出,说明仍有警告,需要继续修复",
529
+ " * **不要假设修复成功,必须实际执行命令验证**",
530
+ "- 如果验证失败,请分析失败原因并继续修复,直到验证通过",
531
+ "",
532
+ "修复后请再次执行 `cargo check --message-format=short` 进行验证。",
533
+ ]
534
+ )
535
+ elif stage == "clippy":
536
+ section_lines.extend(
537
+ [
538
+ "",
539
+ "【⚠️ 重要:Clippy 警告 - 必须消除】",
540
+ "以下输出来自 `cargo clippy -- -D warnings` 命令,包含 clippy 警告详情:",
541
+ "- **Clippy 当前状态:有警告** - 必须消除所有警告才能继续",
542
+ "- Clippy 是 Rust 的代码质量检查工具,警告通常表示代码可以改进",
543
+ "- **请仔细阅读警告信息**,包括:",
544
+ " * 警告类型(如 `unused_variable`、`needless_borrow`、`clippy::unwrap_used` 等)",
545
+ " * 警告位置(文件路径和行号)",
546
+ " * 警告说明和建议的修复方法",
547
+ "",
548
+ "**关键要求:**",
549
+ "- 必须分析每个警告的根本原因,并按照 clippy 的建议进行修复",
550
+ "- 必须实际修复导致警告的代码,而不是忽略警告",
551
+ "- 修复后必须确保 `cargo clippy -- -D warnings` 能够通过(返回码为 0)",
552
+ "",
553
+ ]
554
+ )
555
+ if command:
556
+ section_lines.append(f"执行的命令:{command}")
557
+ section_lines.append(
558
+ "提示:如果不相信上述命令执行结果,可以使用 execute_script 工具自己执行一次该命令进行验证。"
559
+ )
560
+ section_lines.extend(
561
+ [
562
+ "",
563
+ "【Clippy 警告详细信息 - 必须仔细阅读并修复】",
564
+ "以下是从 `cargo clippy -- -D warnings` 命令获取的完整输出,包含所有警告的具体信息:",
565
+ "<CLIPPY_WARNINGS>",
566
+ output,
567
+ "</CLIPPY_WARNINGS>",
568
+ "",
569
+ "**修复要求:**",
570
+ "1. **优先尝试自动修复**:先使用 `cargo clippy --fix` 命令自动修复可以自动修复的警告:",
571
+ " - 执行 `cargo clippy --fix --allow-dirty --allow-staged -- -D warnings` 尝试自动修复 clippy 警告",
572
+ " - 如果自动修复成功,验证修复效果:执行 `cargo clippy -- -D warnings` 检查是否还有警告",
573
+ " - 如果自动修复后仍有警告,继续手动修复剩余的警告",
574
+ "2. 仔细分析上述 clippy 警告信息,找出每个警告的根本原因",
575
+ "3. 定位到具体的代码位置(文件路径和行号)",
576
+ "4. 按照 clippy 的建议进行修复:",
577
+ " - 如果警告建议使用更简洁的写法,请采用建议的写法",
578
+ " - 如果警告建议移除未使用的代码,请移除或使用 `#[allow(...)]` 注解(仅在必要时)",
579
+ " - 如果警告建议使用更安全的 API,请使用建议的 API",
580
+ " - 如果警告建议改进性能,请按照建议优化代码",
581
+ "5. 修复所有警告,确保 `cargo clippy -- -D warnings` 能够通过",
582
+ "6. 如果某些警告确实无法修复或需要特殊处理,可以使用 `#[allow(clippy::warning_name)]` 注解,但必须添加注释说明原因",
583
+ "",
584
+ "**⚠️ 重要:修复后必须验证**",
585
+ "- 修复完成后,**必须使用 `execute_script` 工具执行以下命令验证修复效果**:",
586
+ f" - 命令:`{command or 'cargo clippy -- -D warnings'}`",
587
+ "- 验证要求:",
588
+ " * 如果命令执行成功(返回码为 0),说明修复成功",
589
+ " * 如果命令执行失败(返回码非 0),说明仍有警告,需要继续修复",
590
+ " * **不要假设修复成功,必须实际执行命令验证**",
591
+ "- 如果验证失败,请分析失败原因并继续修复,直到验证通过",
592
+ "",
593
+ "修复后请再次执行 `cargo clippy -- -D warnings` 进行验证。",
594
+ ]
595
+ )
596
+ elif stage == "cargo test":
597
+ section_lines.extend(
598
+ [
599
+ "",
600
+ "【⚠️ 重要:测试失败 - 必须修复】",
601
+ "以下输出来自 `cargo test` 命令,包含测试执行结果和失败详情:",
602
+ "- **测试当前状态:失败** - 必须修复才能继续",
603
+ "- 如果看到测试用例名称和断言失败,说明测试逻辑或实现有问题",
604
+ "- 如果看到编译错误,说明代码存在语法或类型错误",
605
+ "- **请仔细阅读失败信息**,包括:",
606
+ " * 测试用例名称(如 `test_bz_read_get_unused`)",
607
+ " * 失败位置(文件路径和行号,如 `src/ffi/decompress.rs:76:47`)",
608
+ " * 错误类型(如 `SequenceError`、`Result::unwrap()` 失败等)",
609
+ " * 期望值与实际值的差异",
610
+ " * 完整的堆栈跟踪信息",
611
+ "",
612
+ "**关键要求:**",
613
+ "- 必须分析测试失败的根本原因,而不是假设问题已解决",
614
+ "- 必须实际修复导致测试失败的代码,而不是只修改测试用例",
615
+ "- 修复后必须确保测试能够通过,而不是只修复编译错误",
616
+ "",
617
+ ]
618
+ )
619
+ if command:
620
+ section_lines.append(f"执行的命令:{command}")
621
+ section_lines.append(
622
+ "提示:如果不相信上述命令执行结果,可以使用 execute_script 工具自己执行一次该命令进行验证。"
623
+ )
624
+ section_lines.extend(
625
+ [
626
+ "",
627
+ "【测试失败详细信息 - 必须仔细阅读并修复】",
628
+ "以下是从 `cargo test` 命令获取的完整输出,包含测试失败的具体信息:",
629
+ "<TEST_FAILURE>",
630
+ output,
631
+ "</TEST_FAILURE>",
632
+ "",
633
+ "**修复要求:**",
634
+ "1. 仔细分析上述测试失败信息,找出失败的根本原因",
635
+ "2. 定位到具体的代码位置(文件路径和行号)",
636
+ "3. **如果问题难以定位,添加调试信息辅助定位**:",
637
+ " - 在关键位置添加 `println!()` 或 `dbg!()` 输出变量值、函数调用路径、中间状态",
638
+ " - 检查函数参数和返回值是否正确传递",
639
+ " - 验证数据结构和类型转换是否正确",
640
+ " - 对比 C 实现与 Rust 实现的差异,找出可能导致问题的点",
641
+ " - 使用 `read_code` 工具读取相关函数的实现,确认逻辑是否正确",
642
+ " - 如果测试输出信息不足,可以添加更详细的调试输出来定位问题",
643
+ "4. 修复导致测试失败的代码逻辑",
644
+ "5. ⚠️ **重要:修复范围要求** - 不仅要修复当前失败的测试用例,如果修复过程中导致其他测试用例失败,也必须一并修复:",
645
+ " - 修复后必须运行 `cargo test -- --nocapture` 验证所有测试用例都能通过",
646
+ " - 如果发现修复后某些原本通过的测试用例现在失败了,说明修复引入了回归问题,必须一并修复",
647
+ " - 必须确保修复后所有测试用例(包括目标函数的测试和其他函数的测试)都能通过",
648
+ " - 如果修复影响了其他函数或模块,需要检查并修复所有受影响的部分",
649
+ " - 不要只修复当前失败的测试,而忽略其他测试的失败",
650
+ "6. 确保修复后所有测试能够通过(不要只修复编译错误)",
651
+ "7. 如果测试用例本身有问题,可以修改测试用例,但必须确保测试能够正确验证函数行为",
652
+ "",
653
+ "**⚠️ 重要:修复后必须验证**",
654
+ "- 修复完成后,**必须使用 `execute_script` 工具执行以下命令验证修复效果**:",
655
+ f" - 命令:`{command or 'cargo test -- --nocapture'}`",
656
+ "- 验证要求:",
657
+ " * 如果命令执行成功(返回码为 0),说明修复成功",
658
+ " * 如果命令执行失败(返回码非 0),说明修复未成功,需要继续修复",
659
+ " * **不要假设修复成功,必须实际执行命令验证**",
660
+ "- 如果验证失败,请分析失败原因并继续修复,直到验证通过",
661
+ "",
662
+ "**⚠️ 并发问题提示**:",
663
+ "- 如果测试运行失败,但在修复时没有修改任何代码,重新运行测试却成功了,这可能是并发问题导致的:",
664
+ " * 可能是测试之间存在竞态条件(race condition)",
665
+ " * 可能是共享资源(文件、网络、数据库等)的并发访问问题",
666
+ " * 可能是测试执行顺序依赖问题",
667
+ "- 如果遇到这种情况,建议:",
668
+ " * 多次运行测试确认问题是否稳定复现",
669
+ " * 检查测试之间是否存在共享状态或资源",
670
+ " * 检查测试是否依赖特定的执行顺序",
671
+ " * 考虑添加同步机制或隔离测试环境",
672
+ " * 如果问题不稳定,可能需要添加重试机制或调整测试策略",
673
+ "",
674
+ "修复后请再次执行 `cargo test -q` 进行验证。",
675
+ ]
676
+ )
677
+ else:
678
+ section_lines.extend(
679
+ [
680
+ "",
681
+ "请阅读以下构建错误并进行必要修复:",
682
+ ]
683
+ )
684
+ if command:
685
+ section_lines.append(f"执行的命令:{command}")
686
+ section_lines.append(
687
+ "提示:如果不相信上述命令执行结果,可以使用 execute_script 工具自己执行一次该命令进行验证。"
688
+ )
689
+ section_lines.extend(
690
+ [
691
+ "",
692
+ "<BUILD_ERROR>",
693
+ output,
694
+ "</BUILD_ERROR>",
695
+ "",
696
+ "**修复要求:**",
697
+ "1. 仔细分析上述构建错误信息,找出错误的根本原因",
698
+ "2. 定位到具体的代码位置(文件路径和行号)",
699
+ "3. **如果问题难以定位,添加调试信息辅助定位**:",
700
+ " - 使用 `read_code` 工具读取相关文件,检查代码实现是否正确",
701
+ " - 检查类型定义、函数签名、模块导入等是否正确",
702
+ " - 验证依赖关系是否正确,所有被调用的函数/类型是否已定义",
703
+ " - 如果错误信息不够清晰,可以尝试编译单个文件或模块来缩小问题范围",
704
+ " - 对比 C 实现与 Rust 实现的差异,确认类型映射是否正确",
705
+ "4. 修复导致构建错误的代码",
706
+ "5. ⚠️ **重要:修复范围要求** - 不仅要修复当前构建错误,如果修复过程中导致其他测试用例失败,也必须一并修复:",
707
+ " - 修复后必须运行 `cargo test -- --nocapture` 验证所有测试用例都能通过",
708
+ " - 如果发现修复后某些原本通过的测试用例现在失败了,说明修复引入了回归问题,必须一并修复",
709
+ " - 必须确保修复后所有测试用例(包括目标函数的测试和其他函数的测试)都能通过",
710
+ " - 如果修复影响了其他函数或模块,需要检查并修复所有受影响的部分",
711
+ " - 不要只修复当前构建错误,而忽略其他测试的失败",
712
+ "6. 确保修复后代码能够编译通过,且所有测试用例都能通过",
713
+ "",
714
+ "**⚠️ 重要:修复后必须验证**",
715
+ "- 修复完成后,**必须使用 `execute_script` 工具执行以下命令验证修复效果**:",
716
+ " - 命令:`cargo test -- --nocapture`",
717
+ "- 验证要求:",
718
+ " * 命令必须执行成功(返回码为 0),才说明修复成功",
719
+ " * 如果命令执行失败(返回码非 0),说明修复未成功,需要继续修复",
720
+ " * **不要假设修复成功,必须实际执行命令验证**",
721
+ "- 如果验证失败,请分析失败原因并继续修复,直到验证通过",
722
+ "",
723
+ "修复后请执行 `cargo test -- --nocapture` 进行验证。",
724
+ ]
725
+ )
726
+ return section_lines
727
+
728
+ def build_repair_prompt(
729
+ self,
730
+ stage: str,
731
+ output: str,
732
+ tags: List[str],
733
+ sym_name: str,
734
+ src_loc: str,
735
+ c_code: str,
736
+ curr: Dict[str, Any],
737
+ symbols_path: str,
738
+ include_output_patch_hint: bool = False,
739
+ command: Optional[str] = None,
740
+ agent: Optional[Any] = None,
741
+ ) -> str:
742
+ """
743
+ 构建修复提示词。
744
+
745
+ Args:
746
+ stage: 阶段名称("cargo test")
747
+ output: 构建错误输出
748
+ tags: 错误分类标签
749
+ sym_name: 符号名称
750
+ src_loc: 源文件位置
751
+ c_code: C 源码片段
752
+ curr: 当前进度信息
753
+ symbols_path: 符号表文件路径
754
+ include_output_patch_hint: 是否包含"仅输出补丁"提示(test阶段需要)
755
+ command: 执行的命令(可选)
756
+ """
757
+ base_lines = self.build_repair_prompt_base(
758
+ stage,
759
+ tags,
760
+ sym_name,
761
+ src_loc,
762
+ c_code,
763
+ curr,
764
+ symbols_path,
765
+ include_output_patch_hint,
766
+ agent=agent,
767
+ )
768
+ stage_lines = self.build_repair_prompt_stage_section(stage, output, command)
769
+ prompt = "\n".join(base_lines + stage_lines)
770
+ return cast(str, self.append_additional_notes(prompt))
771
+
772
+ def run_cargo_test_and_fix(
773
+ self, workspace_root: str, test_iter: int
774
+ ) -> Tuple[bool, Optional[bool]]:
775
+ """
776
+ 运行 cargo test 并在失败时修复。
777
+
778
+ Returns:
779
+ (是否成功, 是否需要回退重新开始,None表示需要回退)
780
+ """
781
+ # 测试失败时需要详细输出,移除 -q 参数以获取完整的测试失败信息(包括堆栈跟踪、断言详情等)
782
+ try:
783
+ res_test = subprocess.run(
784
+ ["cargo", "test", "--", "--nocapture"],
785
+ capture_output=True,
786
+ text=True,
787
+ timeout=30,
788
+ check=False,
789
+ cwd=workspace_root,
790
+ )
791
+ returncode = res_test.returncode
792
+ stdout = res_test.stdout or ""
793
+ stderr = res_test.stderr or ""
794
+ except subprocess.TimeoutExpired as e:
795
+ # 超时视为测试失败,继续修复流程
796
+ returncode = -1
797
+ stdout = e.stdout.decode("utf-8", errors="replace") if e.stdout else ""
798
+ stderr = "命令执行超时(30秒)\n" + (
799
+ e.stderr.decode("utf-8", errors="replace") if e.stderr else ""
800
+ )
801
+ PrettyOutput.auto_print(
802
+ "⚠️ [c2rust-transpiler][build] Cargo 测试超时(30秒),视为失败并继续修复流程"
803
+ )
804
+ except Exception as e:
805
+ # 其他异常也视为测试失败
806
+ returncode = -1
807
+ stdout = ""
808
+ stderr = f"执行 cargo test 时发生异常: {str(e)}"
809
+ PrettyOutput.auto_print(
810
+ f"⚠️ [c2rust-transpiler][build] Cargo 测试执行异常: {e},视为失败并继续修复流程"
811
+ )
812
+
813
+ # 检查 cargo test 输出中是否包含警告(即使测试通过也可能有警告)
814
+ test_output_combined = stdout + "\n" + stderr
815
+ test_has_warnings = "warning:" in test_output_combined.lower()
816
+
817
+ warning_type: Optional[str] = None
818
+ output: str = ""
819
+ if returncode == 0:
820
+ # 测试通过,先检查 cargo test 输出中的警告,再检查编译警告,最后检查 clippy 警告
821
+ if test_has_warnings:
822
+ # cargo test 输出中有警告,提取并修复
823
+ PrettyOutput.auto_print(
824
+ "⚠️ [c2rust-transpiler][build] Cargo 测试通过,但输出中存在警告,需要修复。"
825
+ )
826
+ PrettyOutput.auto_print(test_output_combined)
827
+ # 将 cargo test 输出中的警告作为修复目标
828
+ warning_type = "cargo_test_warning"
829
+ output = test_output_combined
830
+ else:
831
+ # cargo test 输出中无警告,检查编译警告
832
+ compiler_has_warnings = False
833
+ compiler_output = ""
834
+ try:
835
+ res_compiler = subprocess.run(
836
+ ["cargo", "check", "--message-format=short"],
837
+ capture_output=True,
838
+ text=True,
839
+ timeout=30,
840
+ check=False,
841
+ cwd=workspace_root,
842
+ )
843
+ # 检查是否有警告输出(即使返回码为0,也可能有警告)
844
+ combined_output = (
845
+ (res_compiler.stdout or "") + "\n" + (res_compiler.stderr or "")
846
+ )
847
+ # 检查输出中是否包含警告(warning: 关键字)
848
+ if (
849
+ "warning:" in combined_output.lower()
850
+ or res_compiler.returncode != 0
851
+ ):
852
+ compiler_has_warnings = True
853
+ compiler_output = combined_output
854
+ except subprocess.TimeoutExpired:
855
+ # 编译检查超时,视为有警告
856
+ compiler_has_warnings = True
857
+ compiler_output = "编译检查超时(30秒)"
858
+ except Exception as e:
859
+ # 编译检查执行异常,视为有警告
860
+ compiler_has_warnings = True
861
+ compiler_output = f"执行编译检查时发生异常: {str(e)}"
862
+
863
+ if compiler_has_warnings:
864
+ PrettyOutput.auto_print(
865
+ "⚠️ [c2rust-transpiler][build] Cargo 测试通过,无 cargo test 警告,但存在编译警告,需要修复。"
866
+ )
867
+ PrettyOutput.auto_print(compiler_output)
868
+ # 将编译警告作为修复目标,继续修复流程
869
+ warning_type = "compiler"
870
+ output = compiler_output
871
+ else:
872
+ # 无编译警告,检查 clippy 警告
873
+ clippy_has_warnings = False
874
+ clippy_output = ""
875
+ try:
876
+ res_clippy = subprocess.run(
877
+ ["cargo", "clippy", "--", "-D", "warnings"],
878
+ capture_output=True,
879
+ text=True,
880
+ timeout=30,
881
+ check=False,
882
+ cwd=workspace_root,
883
+ )
884
+ if res_clippy.returncode != 0:
885
+ clippy_has_warnings = True
886
+ clippy_output = (
887
+ (res_clippy.stdout or "")
888
+ + "\n"
889
+ + (res_clippy.stderr or "")
890
+ )
891
+ except subprocess.TimeoutExpired:
892
+ # clippy 超时,视为有警告
893
+ clippy_has_warnings = True
894
+ clippy_output = "Clippy 检查超时(30秒)"
895
+ except Exception as e:
896
+ # clippy 执行异常,视为有警告
897
+ clippy_has_warnings = True
898
+ clippy_output = f"执行 clippy 时发生异常: {str(e)}"
899
+
900
+ if clippy_has_warnings:
901
+ PrettyOutput.auto_print(
902
+ "⚠️ [c2rust-transpiler][build] Cargo 测试通过,无编译警告,但存在 clippy 警告,需要修复。"
903
+ )
904
+ PrettyOutput.auto_print(clippy_output)
905
+ # 将 clippy 警告作为修复目标,继续修复流程
906
+ warning_type = "clippy"
907
+ output = clippy_output
908
+ else:
909
+ PrettyOutput.auto_print(
910
+ "✅ [c2rust-transpiler][build] Cargo 测试通过,无 cargo test 警告,无编译警告,clippy 无警告。"
911
+ )
912
+ # 测试通过且无编译警告和 clippy 警告,重置连续失败计数
913
+ self._consecutive_fix_failures_setter(0)
914
+ try:
915
+ cur = self.progress.get("current") or {}
916
+ metrics = cur.get("metrics") or {}
917
+ metrics["test_attempts"] = int(test_iter)
918
+ cur["metrics"] = metrics
919
+ cur["impl_verified"] = True
920
+ cur["failed_stage"] = None
921
+ self.progress["current"] = cur
922
+ self.save_progress()
923
+ except Exception:
924
+ pass
925
+ return (True, False)
926
+ else:
927
+ # 测试失败
928
+ # 检查测试失败输出中是否也包含警告(可能需要一并修复)
929
+ if test_has_warnings:
930
+ # 测试失败且输出中有警告,优先修复警告(因为警告可能导致测试失败)
931
+ PrettyOutput.auto_print(
932
+ "⚠️ [c2rust-transpiler][build] Cargo 测试失败,且输出中存在警告,将优先修复警告。"
933
+ )
934
+ warning_type = "cargo_test_warning"
935
+ output = test_output_combined
936
+ else:
937
+ # 测试失败但无警告,按测试失败处理
938
+ warning_type = None
939
+ output = test_output_combined
940
+ limit_info = (
941
+ f" (上限: {self.test_max_retries if self.test_max_retries > 0 else '无限'})"
942
+ if test_iter % 10 == 0 or test_iter == 1
943
+ else ""
944
+ )
945
+ PrettyOutput.auto_print(
946
+ f"❌ [c2rust-transpiler][build] Cargo 测试失败 (第 {test_iter} 次尝试{limit_info})。"
947
+ )
948
+ PrettyOutput.auto_print(output)
949
+ maxr = self.test_max_retries
950
+ if maxr > 0 and test_iter >= maxr:
951
+ PrettyOutput.auto_print(
952
+ f"❌ [c2rust-transpiler][build] 已达到最大重试次数上限({maxr}),停止构建修复循环。"
953
+ )
954
+ try:
955
+ cur = self.progress.get("current") or {}
956
+ metrics = cur.get("metrics") or {}
957
+ metrics["test_attempts"] = int(test_iter)
958
+ cur["metrics"] = metrics
959
+ cur["impl_verified"] = False
960
+ cur["failed_stage"] = "test"
961
+ err_summary = (output or "").strip()
962
+ if len(err_summary) > ERROR_SUMMARY_MAX_LENGTH:
963
+ err_summary = (
964
+ err_summary[:ERROR_SUMMARY_MAX_LENGTH] + "...(truncated)"
965
+ )
966
+ cur["last_build_error"] = err_summary
967
+ self.progress["current"] = cur
968
+ self.save_progress()
969
+ except Exception:
970
+ pass
971
+ return (False, False)
972
+
973
+ # 构建失败(测试阶段)修复、编译警告修复、clippy 警告修复或 cargo test 警告修复
974
+ if warning_type == "cargo_test_warning":
975
+ # cargo test 输出中的警告修复
976
+ tags = ["cargo_test_warning"]
977
+ stage_name = "cargo_test_warning"
978
+ command_str = "cargo test -- --nocapture"
979
+ elif warning_type == "compiler":
980
+ # 编译警告修复
981
+ tags = ["compiler_warning"]
982
+ stage_name = "compiler_warning"
983
+ command_str = "cargo check --message-format=short"
984
+ elif warning_type == "clippy":
985
+ # clippy 警告修复
986
+ tags = ["clippy_warning"]
987
+ stage_name = "clippy"
988
+ command_str = "cargo clippy -- -D warnings"
989
+ else:
990
+ # 测试失败修复
991
+ tags = self.classify_rust_error(output)
992
+ stage_name = "cargo test"
993
+ command_str = "cargo test -- --nocapture"
994
+
995
+ symbols_path = str((self.data_dir / "symbols.jsonl").resolve())
996
+ curr, sym_name, src_loc, c_code = self.get_current_function_context()
997
+
998
+ # 调试输出:确认错误信息是否正确传递
999
+ if warning_type is None:
1000
+ PrettyOutput.auto_print(
1001
+ f"🔍 [c2rust-transpiler][debug] 测试失败信息长度: {len(output)} 字符"
1002
+ )
1003
+ if output:
1004
+ # 提取关键错误信息用于调试
1005
+ error_lines = output.split("\n")
1006
+ key_errors = [
1007
+ line
1008
+ for line in error_lines
1009
+ if any(
1010
+ keyword in line.lower()
1011
+ for keyword in [
1012
+ "failed",
1013
+ "error",
1014
+ "panic",
1015
+ "unwrap",
1016
+ "sequence",
1017
+ ]
1018
+ )
1019
+ ]
1020
+ if key_errors:
1021
+ PrettyOutput.auto_print(
1022
+ "🔍 [c2rust-transpiler][debug] 关键错误信息(前5行):"
1023
+ )
1024
+ for i, line in enumerate(key_errors[:5], 1):
1025
+ PrettyOutput.auto_print(f"🔍 {i}. {line[:100]}")
1026
+
1027
+ # 由于 transpile() 开始时已切换到 crate 目录,此处无需再次切换
1028
+ # 记录运行前的 commit
1029
+ before_commit = self.get_crate_commit_hash()
1030
+ # 先创建修复 Agent(后续会复用)
1031
+ # 使用修复 Agent,每次重新创建,并传入 C 代码
1032
+ agent = self.get_fix_agent(c_code)
1033
+
1034
+ repair_prompt = self.build_repair_prompt(
1035
+ stage=stage_name,
1036
+ output=output,
1037
+ tags=tags,
1038
+ sym_name=sym_name,
1039
+ src_loc=src_loc,
1040
+ c_code=c_code,
1041
+ curr=curr,
1042
+ symbols_path=symbols_path,
1043
+ include_output_patch_hint=True,
1044
+ command=command_str,
1045
+ agent=agent,
1046
+ )
1047
+ agent.run(
1048
+ self.compose_prompt_with_context(repair_prompt, for_fix=True),
1049
+ prefix=f"[c2rust-transpiler][build-fix iter={test_iter}][test]",
1050
+ suffix="",
1051
+ )
1052
+
1053
+ # 检测并处理测试代码删除
1054
+ if self.check_and_handle_test_deletion(before_commit, agent):
1055
+ # 如果回退了,需要重新运行 agent
1056
+ PrettyOutput.auto_print(
1057
+ f"⚠️ [c2rust-transpiler][build-fix] 检测到测试代码删除问题,已回退,重新运行 agent (iter={test_iter})"
1058
+ )
1059
+ before_commit = self.get_crate_commit_hash()
1060
+ # 重新创建修复 Agent
1061
+ agent = self.get_fix_agent(c_code)
1062
+ agent.run(
1063
+ self.compose_prompt_with_context(repair_prompt, for_fix=True),
1064
+ prefix=f"[c2rust-transpiler][build-fix iter={test_iter}][test][retry]",
1065
+ suffix="",
1066
+ )
1067
+ # 再次检测
1068
+ if self.check_and_handle_test_deletion(before_commit, agent):
1069
+ PrettyOutput.auto_print(
1070
+ f"❌ [c2rust-transpiler][build-fix] 再次检测到测试代码删除问题,已回退 (iter={test_iter})"
1071
+ )
1072
+
1073
+ # 修复后验证:先检查编译,再实际运行测试
1074
+ # 第一步:检查编译是否通过
1075
+ res_compile = subprocess.run(
1076
+ ["cargo", "test", "--message-format=short", "-q", "--no-run"],
1077
+ capture_output=True,
1078
+ text=True,
1079
+ check=False,
1080
+ cwd=workspace_root,
1081
+ )
1082
+ if res_compile.returncode != 0:
1083
+ PrettyOutput.auto_print(
1084
+ "⚠️ [c2rust-transpiler][build] 修复后编译仍有错误,将在下一轮循环中处理"
1085
+ )
1086
+ # 编译失败,增加连续失败计数
1087
+ current_failures = self._consecutive_fix_failures_getter()
1088
+ self._consecutive_fix_failures_setter(current_failures + 1)
1089
+ # 检查是否需要回退
1090
+ current_start_commit = self._current_function_start_commit_getter()
1091
+ if (
1092
+ current_failures >= CONSECUTIVE_FIX_FAILURE_THRESHOLD
1093
+ and current_start_commit
1094
+ ):
1095
+ PrettyOutput.auto_print(
1096
+ f"❌ [c2rust-transpiler][build] 连续修复失败 {current_failures} 次,回退到函数开始时的 commit: {current_start_commit}"
1097
+ )
1098
+ if self.reset_to_commit(current_start_commit):
1099
+ PrettyOutput.auto_print(
1100
+ "⚠️ [c2rust-transpiler][build] 已回退到函数开始时的 commit,将重新开始处理该函数"
1101
+ )
1102
+ # 返回特殊值,表示需要重新开始
1103
+ return (False, None)
1104
+ else:
1105
+ PrettyOutput.auto_print(
1106
+ "⚠️ [c2rust-transpiler][build] 回退失败,继续尝试修复"
1107
+ )
1108
+ return (False, False) # 需要继续循环
1109
+
1110
+ # 第二步:编译通过,实际运行测试验证
1111
+ try:
1112
+ res_test_verify = subprocess.run(
1113
+ ["cargo", "test", "--", "--nocapture"],
1114
+ capture_output=True,
1115
+ text=True,
1116
+ timeout=30,
1117
+ check=False,
1118
+ cwd=workspace_root,
1119
+ )
1120
+ verify_returncode = res_test_verify.returncode
1121
+ verify_stdout = res_test_verify.stdout or ""
1122
+ verify_stderr = res_test_verify.stderr or ""
1123
+ verify_output_combined = verify_stdout + "\n" + verify_stderr
1124
+ verify_has_warnings = "warning:" in verify_output_combined.lower()
1125
+ except subprocess.TimeoutExpired:
1126
+ # 超时视为测试失败
1127
+ verify_returncode = -1
1128
+ verify_has_warnings = False
1129
+ verify_output_combined = ""
1130
+ PrettyOutput.auto_print(
1131
+ "⚠️ [c2rust-transpiler][build] 修复后验证测试超时(30秒),视为失败"
1132
+ )
1133
+ except Exception as e:
1134
+ # 其他异常也视为测试失败
1135
+ verify_returncode = -1
1136
+ verify_has_warnings = False
1137
+ verify_output_combined = ""
1138
+ PrettyOutput.auto_print(
1139
+ f"⚠️ [c2rust-transpiler][build] 修复后验证测试执行异常: {e},视为失败"
1140
+ )
1141
+
1142
+ if verify_returncode == 0:
1143
+ # 测试通过,检查是否有警告
1144
+ if verify_has_warnings:
1145
+ PrettyOutput.auto_print(
1146
+ "⚠️ [c2rust-transpiler][build] 修复后测试通过,但输出中存在警告,将在下一轮循环中处理"
1147
+ )
1148
+ PrettyOutput.auto_print(verify_output_combined)
1149
+ # 有警告,继续循环修复
1150
+ return (False, False)
1151
+ # 测试通过,先检查编译警告,再检查 clippy 警告
1152
+ compiler_has_warnings_after_fix = False
1153
+ compiler_output_after_fix = ""
1154
+ try:
1155
+ res_compiler_verify = subprocess.run(
1156
+ ["cargo", "check", "--message-format=short"],
1157
+ capture_output=True,
1158
+ text=True,
1159
+ timeout=30,
1160
+ check=False,
1161
+ cwd=workspace_root,
1162
+ )
1163
+ # 检查是否有警告输出
1164
+ combined_output = (
1165
+ (res_compiler_verify.stdout or "")
1166
+ + "\n"
1167
+ + (res_compiler_verify.stderr or "")
1168
+ )
1169
+ if (
1170
+ "warning:" in combined_output.lower()
1171
+ or res_compiler_verify.returncode != 0
1172
+ ):
1173
+ compiler_has_warnings_after_fix = True
1174
+ compiler_output_after_fix = combined_output
1175
+ except subprocess.TimeoutExpired:
1176
+ compiler_has_warnings_after_fix = True
1177
+ compiler_output_after_fix = "编译检查超时(30秒)"
1178
+ except Exception as e:
1179
+ compiler_has_warnings_after_fix = True
1180
+ compiler_output_after_fix = f"执行编译检查时发生异常: {str(e)}"
1181
+
1182
+ if compiler_has_warnings_after_fix:
1183
+ PrettyOutput.auto_print(
1184
+ "⚠️ [c2rust-transpiler][build] 修复后测试通过,但存在编译警告,将在下一轮循环中处理"
1185
+ )
1186
+ PrettyOutput.auto_print(compiler_output_after_fix)
1187
+ # 有编译警告,继续循环修复
1188
+ return (False, False)
1189
+ else:
1190
+ # 无编译警告,检查 clippy 警告
1191
+ clippy_has_warnings_after_fix = False
1192
+ clippy_output_after_fix = ""
1193
+ try:
1194
+ res_clippy_verify = subprocess.run(
1195
+ ["cargo", "clippy", "--", "-D", "warnings"],
1196
+ capture_output=True,
1197
+ text=True,
1198
+ timeout=30,
1199
+ check=False,
1200
+ cwd=workspace_root,
1201
+ )
1202
+ if res_clippy_verify.returncode != 0:
1203
+ clippy_has_warnings_after_fix = True
1204
+ clippy_output_after_fix = (
1205
+ (res_clippy_verify.stdout or "")
1206
+ + "\n"
1207
+ + (res_clippy_verify.stderr or "")
1208
+ )
1209
+ except subprocess.TimeoutExpired:
1210
+ clippy_has_warnings_after_fix = True
1211
+ clippy_output_after_fix = "Clippy 检查超时(30秒)"
1212
+ except Exception as e:
1213
+ clippy_has_warnings_after_fix = True
1214
+ clippy_output_after_fix = f"执行 clippy 时发生异常: {str(e)}"
1215
+
1216
+ if clippy_has_warnings_after_fix:
1217
+ PrettyOutput.auto_print(
1218
+ "⚠️ [c2rust-transpiler][build] 修复后测试通过,无编译警告,但存在 clippy 警告,将在下一轮循环中处理"
1219
+ )
1220
+ PrettyOutput.auto_print(clippy_output_after_fix)
1221
+ # 有 clippy 警告,继续循环修复
1222
+ return (False, False)
1223
+ else:
1224
+ PrettyOutput.auto_print(
1225
+ "✅ [c2rust-transpiler][build] 修复后测试通过,无 cargo test 警告,无编译警告,clippy 无警告,继续构建循环"
1226
+ )
1227
+ # 测试真正通过且无编译警告和 clippy 警告,重置连续失败计数
1228
+ self._consecutive_fix_failures_setter(0)
1229
+ return (False, False) # 需要继续循环(但下次应该会通过)
1230
+ else:
1231
+ # 编译通过但测试仍然失败,说明修复没有解决测试逻辑问题
1232
+ PrettyOutput.auto_print(
1233
+ "⚠️ [c2rust-transpiler][build] 修复后编译通过,但测试仍然失败,将在下一轮循环中处理"
1234
+ )
1235
+ # 测试失败,增加连续失败计数(即使编译通过)
1236
+ current_failures = self._consecutive_fix_failures_getter()
1237
+ self._consecutive_fix_failures_setter(current_failures + 1)
1238
+ current_start_commit = self._current_function_start_commit_getter()
1239
+ # 检查是否需要回退
1240
+ if (
1241
+ current_failures >= CONSECUTIVE_FIX_FAILURE_THRESHOLD
1242
+ and current_start_commit
1243
+ ):
1244
+ PrettyOutput.auto_print(
1245
+ f"❌ [c2rust-transpiler][build] 连续修复失败 {current_failures} 次(编译通过但测试失败),回退到函数开始时的 commit: {current_start_commit}"
1246
+ )
1247
+ if self.reset_to_commit(current_start_commit):
1248
+ PrettyOutput.auto_print(
1249
+ "⚠️ [c2rust-transpiler][build] 已回退到函数开始时的 commit,将重新开始处理该函数"
1250
+ )
1251
+ # 返回特殊值,表示需要重新开始
1252
+ return (False, None)
1253
+ else:
1254
+ PrettyOutput.auto_print(
1255
+ "⚠️ [c2rust-transpiler][build] 回退失败,继续尝试修复"
1256
+ )
1257
+ return (False, False) # 需要继续循环
1258
+
1259
+ def cargo_build_loop(self) -> Optional[bool]:
1260
+ """在 crate 目录执行构建与测试:直接运行 cargo test(运行所有测试,不区分项目结构)。失败则最小化修复直到通过或达到上限。
1261
+
1262
+ Returns:
1263
+ Optional[bool]:
1264
+ - True: 测试通过(可能进行了修复)
1265
+ - False: 测试失败(达到重试上限)
1266
+ - None: 需要回退重新开始
1267
+ """
1268
+ workspace_root = str(self.crate_dir)
1269
+ test_limit = f"最大重试: {self.test_max_retries if self.test_max_retries > 0 else '无限'}"
1270
+ PrettyOutput.auto_print(
1271
+ f"🔍 [c2rust-transpiler][build] 工作区={workspace_root},开始构建循环(test,{test_limit})"
1272
+ )
1273
+ test_iter = 0
1274
+ has_fixes = False # 标记是否进行了修复
1275
+ while True:
1276
+ # 运行所有测试(不区分项目结构)
1277
+ # cargo test 会自动编译并运行所有类型的测试:lib tests、bin tests、integration tests、doc tests 等
1278
+ test_iter += 1
1279
+ test_success, need_restart = self.run_cargo_test_and_fix(
1280
+ workspace_root, test_iter
1281
+ )
1282
+ if need_restart is None:
1283
+ self._build_loop_has_fixes = False # 回退时重置标记
1284
+ return None # 需要回退重新开始
1285
+ if test_success:
1286
+ # 如果进行了修复(test_iter > 1),标记需要重新 review
1287
+ if test_iter > 1:
1288
+ has_fixes = True
1289
+ # 将修复标记保存到实例变量,供调用方检查
1290
+ self._build_loop_has_fixes = has_fixes
1291
+ return True # 测试通过
1292
+ # 如果测试失败,说明进行了修复尝试
1293
+ if test_iter > 1:
1294
+ has_fixes = True