Skip to content

Commit e9f1620

Browse files
committed
补齐 Clippy 修复事件的当前诊断与批次后 lint 指引
1 parent d6850bd commit e9f1620

23 files changed

Lines changed: 947 additions & 4 deletions

‎README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,3 +810,5 @@ The Rust-owned developer replay now supports explicitly selected Rustfmt1.9.0-st
810810
Rust selected-file parser preparation now resolves Cargo edition from bounded package/workspace declarations and rechecks declaration/source continuity between native calls. The scoped Unix library service has actual2015/2021/2024 counterexample evidence; selected-file edit Hook, stable confirmation tasks and original-tool rechecks are now connected; complete project and real-host acceptance remain open. See [project edition contract](docs/Rust-Project-Edition-Syntax.md).
811811

812812
Rust edit feedback now reports safe lines, stable tasks and original Rustfmt recheck guidance while retaining Clippy/type/build obligations. See [scoped chain acceptance](tests/acceptance/rust-native-hook.md).
813+
814+
Rust edit dialogue now provides an executable post-batch Clippy command and explicitly states that project lint did not run during editing. Original-task repair-ready retains current rule/line feedback and withdraws changed-input guidance; this is not a background queue or authoritative closure. See [project lint follow-up](docs/Rust-Project-Lint-Followup.md).

‎README.zh-CN.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,3 +793,5 @@ Rust 开发重放现支持显式选择 Rustfmt1.9.0-stable:冻结 stdin、私
793793
Rust选中文件解析前置现从有界包/工作区声明确定Cargo edition,并在原生调用之间复核声明与源码连续性。Unix库服务已保留真实2015/2021/2024反例;选中文件编辑Hook、稳定确认任务与原工具复检已连接;完整项目与实际宿主验收仍开放。见[项目edition契约](docs/Rust-Project-Edition-Syntax.zh_CN.md)。
794794

795795
Rust编辑反馈现保留安全行号、稳定任务与原Rustfmt复检指引,Clippy/类型/构建义务继续保留。见[局部链路验收](tests/acceptance/rust-native-hook.md)。
796+
797+
Rust编辑后现提供可执行的批次后Clippy指令,明确编辑阶段未运行项目lint。原任务repair_ready保留当前规则/行号并撤回输入变化后的指引;这不是后台队列或可信关闭。见[项目lint后续流程](docs/Rust-Project-Lint-Followup.zh_CN.md)。

‎crates/codeguard-cli/src/claude_hook_command.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,7 @@ fn summarize(path: &str, report: &Value) -> String {
551551
if scan["task_status"] == "not_connected" || scan["task_status"] == "incomplete" {
552552
repair.push_str("Rust 任务工作台未连接或同步未完成;保留当前观察,不假定已有任务。");
553553
}
554-
repair.push_str("Rustfmt 仅提供语法解析观察;继续 Clippy、类型和完整构建检查,零诊断不自动关闭任务或允许交付。");
554+
repair.push_str("Rustfmt 仅提供语法解析观察,本次未运行项目级 Clippy;完成编辑批次后执行 codeguard lint rust . --format=json,按返回任务使用原 Cargo 复检;类型和完整构建检查继续保留,零诊断不自动关闭任务或允许交付。");
555555
}
556556
if feedback["go_syntax"].is_object() {
557557
let scan = &feedback["go_syntax"];
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
//! Clippy 修复事件的当前输入核对与脱敏投影;不在编辑事件中运行项目编译。
2+
use crate::rust_lint_inputs::RustLintInputs;
3+
use serde_json::{Value, json};
4+
use sha2::{Digest, Sha256};
5+
use std::{path::Path, time::Instant};
6+
7+
/// 复检启动前冻结已观察源码、清单、锁及配置;失败保持未完成而不制造源码违规。
8+
pub(crate) fn capture(root: &Path) -> Option<RustLintInputs> {
9+
let inventory = crate::rust_input_inventory::RustInputInventory::capture(root).ok()?;
10+
RustLintInputs::capture(root, &inventory.source_files).ok()
11+
}
12+
13+
/// 从当前同任务原生复检报告投影规则与行号;参数为根、原任务、报告、父请求快照与截止时间。
14+
/// 只提供局部 lint 观察,不猜测列单位,不关闭任务或签发覆盖与交付许可。
15+
pub(crate) fn project(
16+
root: &Path,
17+
brief: &Value,
18+
scan: &Value,
19+
inputs: Option<&RustLintInputs>,
20+
selected_tool: Option<&Path>,
21+
deadline: Instant,
22+
) -> Result<Value, &'static str> {
23+
if !matches!(scan["schema_version"].as_str(), Some("0.2.0" | "0.3.0"))
24+
|| scan["report_type"] != "rust_clippy_local_observation"
25+
|| scan["checker_id"] != "rust.cargo_clippy"
26+
|| scan["language"] != "rust"
27+
|| scan["authority"] != "local_unverified"
28+
|| scan["coverage_proven"] != false
29+
|| scan["delivery_decision"] != "not_evaluated"
30+
|| !scan["local_scan_complete"].is_boolean()
31+
|| !scan["findings"].is_array()
32+
{
33+
return Err("verification_report_invalid");
34+
}
35+
let tool_current = (scan["local_scan_complete"] != true && scan["tool_sha256"].is_null())
36+
|| selected_tool
37+
.and_then(|tool| tool.canonicalize().ok())
38+
.and_then(|path| {
39+
codeguard_runtime::read_bounded_regular_file(&path, 128 * 1024 * 1024).ok()
40+
})
41+
.is_some_and(|bytes| scan["tool_sha256"] == format!("{:x}", Sha256::digest(bytes)));
42+
let mut current = tool_current
43+
&& Instant::now() < deadline
44+
&& !codeguard_runtime::sigint_cancellation_requested()
45+
&& inputs.is_some_and(|snapshot| snapshot.unchanged(root));
46+
let mut positions = Vec::new();
47+
if current && scan["local_scan_complete"] == true && brief["kind"] == "finding" {
48+
for finding in scan["findings"]
49+
.as_array()
50+
.ok_or("verification_report_invalid")?
51+
{
52+
if finding["finding_id"] != brief["task_id"]
53+
|| finding["path"] != brief["scope"]
54+
|| finding["rule_id"] != brief["native_rule_id"]
55+
{
56+
continue;
57+
}
58+
let path = finding["path"]
59+
.as_str()
60+
.ok_or("verification_report_invalid")?;
61+
if !Path::new(path)
62+
.components()
63+
.all(|component| matches!(component, std::path::Component::Normal(_)))
64+
{
65+
return Err("verification_report_invalid");
66+
}
67+
let Ok(source) = crate::plain_syntax_source::read_plain_source(&root.join(path)) else {
68+
current = false;
69+
break;
70+
};
71+
let Ok(source_text) = std::str::from_utf8(&source) else {
72+
current = false;
73+
break;
74+
};
75+
let line = finding["line"]
76+
.as_u64()
77+
.filter(|line| *line > 0 && *line as usize <= source_text.split('\n').count())
78+
.ok_or("verification_report_invalid")?;
79+
let rule = finding["rule_id"]
80+
.as_str()
81+
.filter(|rule| {
82+
rule.strip_prefix("clippy::").is_some_and(|name| {
83+
!name.is_empty()
84+
&& name.len() <= 72
85+
&& name
86+
.bytes()
87+
.all(|b| b.is_ascii_lowercase() || b.is_ascii_digit() || b == b'_')
88+
})
89+
})
90+
.ok_or("verification_report_invalid")?;
91+
if finding["source_sha256"] != format!("{:x}", Sha256::digest(&source)) {
92+
current = false;
93+
break;
94+
}
95+
positions.push(json!({"line":line,"rule_id":rule}));
96+
if positions.len() > 32 {
97+
return Err("verification_output_exceeded");
98+
}
99+
}
100+
}
101+
current = current
102+
&& Instant::now() < deadline
103+
&& !codeguard_runtime::sigint_cancellation_requested()
104+
&& inputs.is_some_and(|snapshot| snapshot.unchanged(root));
105+
if !current {
106+
positions.clear();
107+
}
108+
let status = if !current {
109+
"stale"
110+
} else if scan["local_scan_complete"] != true {
111+
"incomplete"
112+
} else if positions.is_empty() {
113+
"completed"
114+
} else {
115+
"diagnostics_observed"
116+
};
117+
Ok(
118+
json!({"schema_version":"0.8.0","native_confirmation_status":status,
119+
"native_confirmation_reason":if !current {"clippy_confirmation_inputs_changed"} else if status=="incomplete" {"clippy_confirmation_incomplete"} else if positions.is_empty(){"clippy_confirmation_no_task_diagnostics"}else{"clippy_confirmation_task_diagnostics"},
120+
"native_column_unit":"unavailable","native_diagnostic_positions":positions}),
121+
)
122+
}

‎crates/codeguard-cli/src/hook_execute_command.rs‎

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ fn execute_parsed(
252252
};
253253
Ok((
254254
json!({
255-
"schema_version":if feedback["schema_version"]=="0.7.0" && feedback["report_type"]=="hook_task_verification_summary" {"0.25.0"}else if feedback["schema_version"]=="0.14.0" && feedback["report_type"]=="hook_fast_feedback" {"0.24.0"} else if feedback["schema_version"]=="0.13.0" && feedback["report_type"]=="hook_fast_feedback" {"0.23.0"} else if feedback["schema_version"] == "0.12.0" && feedback["report_type"] == "hook_fast_feedback" {"0.22.0"} else if feedback["report_type"] == "hook_fast_feedback" && feedback["schema_version"] == "0.11.0" {"0.21.0"} else if feedback["report_type"] == "hook_fast_feedback" && feedback["schema_version"] == "0.10.0" {"0.19.0"} else if feedback["report_type"] == "hook_task_verification_summary" && feedback["schema_version"] == "0.6.0" {"0.20.0"} else if feedback["report_type"] == "hook_fast_feedback" && feedback["schema_version"] == "0.9.0" {"0.18.0"} else if feedback["report_type"] == "hook_fast_feedback" && feedback["schema_version"] == "0.8.0" {"0.17.0"} else if feedback["report_type"] == "hook_fast_feedback" && feedback["schema_version"] == "0.7.0" {"0.16.0"}else if feedback["report_type"] == "hook_task_verification_summary" && feedback["schema_version"] == "0.5.0" {"0.15.0"}else if feedback["report_type"] == "hook_fast_feedback" && feedback["schema_version"] == "0.6.0" {"0.14.0"} else if feedback["report_type"] == "hook_fast_feedback" && feedback["schema_version"] == "0.5.0" {"0.13.0"} else if feedback["report_type"] == "hook_fast_feedback" && feedback["schema_version"] == "0.4.0" {"0.12.0"} else if feedback["report_type"] == "hook_fast_feedback" && feedback["schema_version"] == "0.3.0" {"0.11.0"} else if feedback["report_type"] == "hook_task_verification_summary" && feedback["schema_version"] == "0.4.0" {"0.10.0"} else if feedback["report_type"] == "hook_task_verification_summary" && feedback["schema_version"] == "0.2.0" {"0.8.0"} else if feedback["report_type"] == "hook_task_verification_summary" && feedback["schema_version"] == "0.3.0" {"0.9.0"} else {"0.7.0"}, "report_type":"hook_execution_feedback",
255+
"schema_version":if feedback["schema_version"]=="0.8.0" && feedback["report_type"]=="hook_task_verification_summary" {"0.26.0"}else if feedback["schema_version"]=="0.7.0" && feedback["report_type"]=="hook_task_verification_summary" {"0.25.0"}else if feedback["schema_version"]=="0.14.0" && feedback["report_type"]=="hook_fast_feedback" {"0.24.0"} else if feedback["schema_version"]=="0.13.0" && feedback["report_type"]=="hook_fast_feedback" {"0.23.0"} else if feedback["schema_version"] == "0.12.0" && feedback["report_type"] == "hook_fast_feedback" {"0.22.0"} else if feedback["report_type"] == "hook_fast_feedback" && feedback["schema_version"] == "0.11.0" {"0.21.0"} else if feedback["report_type"] == "hook_fast_feedback" && feedback["schema_version"] == "0.10.0" {"0.19.0"} else if feedback["report_type"] == "hook_task_verification_summary" && feedback["schema_version"] == "0.6.0" {"0.20.0"} else if feedback["report_type"] == "hook_fast_feedback" && feedback["schema_version"] == "0.9.0" {"0.18.0"} else if feedback["report_type"] == "hook_fast_feedback" && feedback["schema_version"] == "0.8.0" {"0.17.0"} else if feedback["report_type"] == "hook_fast_feedback" && feedback["schema_version"] == "0.7.0" {"0.16.0"}else if feedback["report_type"] == "hook_task_verification_summary" && feedback["schema_version"] == "0.5.0" {"0.15.0"}else if feedback["report_type"] == "hook_fast_feedback" && feedback["schema_version"] == "0.6.0" {"0.14.0"} else if feedback["report_type"] == "hook_fast_feedback" && feedback["schema_version"] == "0.5.0" {"0.13.0"} else if feedback["report_type"] == "hook_fast_feedback" && feedback["schema_version"] == "0.4.0" {"0.12.0"} else if feedback["report_type"] == "hook_fast_feedback" && feedback["schema_version"] == "0.3.0" {"0.11.0"} else if feedback["report_type"] == "hook_task_verification_summary" && feedback["schema_version"] == "0.4.0" {"0.10.0"} else if feedback["report_type"] == "hook_task_verification_summary" && feedback["schema_version"] == "0.2.0" {"0.8.0"} else if feedback["report_type"] == "hook_task_verification_summary" && feedback["schema_version"] == "0.3.0" {"0.9.0"} else {"0.7.0"}, "report_type":"hook_execution_feedback",
256256
"plan":plan, "execution":execution, "reason":reason,
257257
"local_feedback":feedback, "delivery_decision":"not_evaluated",
258258
"host_blocking_verified":false, "soft_result_reused":false
@@ -281,6 +281,18 @@ fn task_verification_summary(
281281
{
282282
return Err(("verification_arguments_invalid", 3));
283283
}
284+
let clippy_tool = if checker_id == "rust.cargo_clippy" {
285+
crate::cargo_tool_selection::resolve_cargo_tool(
286+
arguments.verify_options.get("--cargo-tool").map(Path::new),
287+
)
288+
} else {
289+
None
290+
};
291+
let clippy_inputs = if checker_id == "rust.cargo_clippy" {
292+
crate::clippy_hook_feedback::capture(root)
293+
} else {
294+
None
295+
};
284296
let executable = env::current_exe().map_err(|_| ("verification_process_failed", 4))?;
285297
let remaining_ms = deadline
286298
.saturating_duration_since(Instant::now())
@@ -313,6 +325,7 @@ fn task_verification_summary(
313325
"TMPDIR",
314326
"CARGO_HOME",
315327
"RUSTUP_HOME",
328+
"RUSTUP_TOOLCHAIN",
316329
"JAVA_HOME",
317330
] {
318331
if let Some(value) = env::var_os(key) {
@@ -371,6 +384,27 @@ fn task_verification_summary(
371384
"authority":"local_unverified", "delivery_decision":"not_evaluated"
372385
});
373386
let scan = &report["native_scan"];
387+
if checker_id == "rust.cargo_clippy" {
388+
let projected = crate::clippy_hook_feedback::project(
389+
root,
390+
&brief,
391+
scan,
392+
clippy_inputs.as_ref(),
393+
clippy_tool.as_deref(),
394+
deadline,
395+
)
396+
.map_err(|reason| (reason, 3))?;
397+
if projected["native_confirmation_status"] == "stale" {
398+
summary["observation"] = json!("incomplete");
399+
summary["reason"] = projected["native_confirmation_reason"].clone();
400+
}
401+
for (key, value) in projected
402+
.as_object()
403+
.ok_or(("verification_report_invalid", 4))?
404+
{
405+
summary[key] = value.clone();
406+
}
407+
}
374408
if checker_id == "shell.shellcheck" && scan.is_object() {
375409
if report["schema_version"] != "0.24.0"
376410
|| scan["schema_version"] != "0.1.0"

‎crates/codeguard-cli/src/lib.rs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,3 +450,6 @@ mod shell_task_recheck;
450450

451451
#[cfg(unix)]
452452
mod check_shell_scan;
453+
454+
#[cfg(unix)]
455+
mod clippy_hook_feedback;

0 commit comments

Comments
 (0)