Skip to content

Commit c5c0d42

Browse files
committed
保留 Rust 聚合文档检查的双原生义务
1 parent b2bcb4d commit c5c0d42

17 files changed

Lines changed: 8442 additions & 5 deletions

‎README.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,3 +1096,6 @@ The standalone C/C++ Clang entry now distinguishes literal/comment hashes and ra
10961096

10971097

10981098
Native Clippy documentation findings now receive specific Errors/Panics/Safety repair guidance through stable tasks and original-tool suppression rechecks. The installed Clippy accepts bare headings: zero diagnostics do not qualify detailed documentation and facts remain open. Ordinary checks do not enable pedantic implicitly. `comments rust` remains a local Rustdoc probe; complete documentation contracts are not unified or qualified. See [acceptance and gaps](tests/acceptance/clippy-documentation-contract.md).
1099+
1100+
1101+
`check rust/all` now retains a separate comments observation for the three native Clippy documentation rules alongside Rustdoc, preserving each tool obligation without another Clippy execution. Only exact known diagnostics participate; zero findings do not establish enabled or complete documentation contracts. See [aggregate acceptance](tests/acceptance/clippy-documentation-aggregate.md).

‎README.zh-CN.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,3 +1073,6 @@ C/C++ 独立 Clang 入口已修复字符串、注释和原始字符串中的井
10731073

10741074

10751075
Rust 的详细文档任务现对原生 Clippy `missing_errors_doc`、`missing_panics_doc`、`missing_safety_doc` 给出具体 Errors/Panics/Safety 修复指引,复用稳定任务及原工具抑制对照。已有 Clippy 实测接受空章节标题,因此零诊断不代表详细说明合格,事实继续 open;不会隐式启用 pedantic。`comments rust` 仍为 Rustdoc 局部探针,尚未统一完整文档契约。参见 [局部验收与缺口](tests/acceptance/clippy-documentation-contract.md)。
1076+
1077+
1078+
`check rust/all` 现在把原生 Clippy 的三类文档诊断作为独立 comments 观察,与原 Rustdoc 行并存,不覆盖原工具阻塞或重复执行 Clippy;仅精确已知规则参与,零发现不证明详细契约启用。参见 [聚合验收](tests/acceptance/clippy-documentation-aggregate.md)。

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1801,6 +1801,40 @@ fn run_scoped(args: &[String], lint_only: bool) -> ExitCode {
18011801
"ruff_d100_native_observation_incomplete"
18021802
});
18031803
}
1804+
// 文档类原生发现来自两个独立服务;保留Rustdoc义务,不能用Clippy结果覆盖。
1805+
// 只分类精确已适配的原规则,空结果不证明这些规则启用或详细内容合格。
1806+
if rust_comments_selected
1807+
&& rust_lint["findings"].as_array().is_some_and(|findings| {
1808+
findings.iter().any(|finding| {
1809+
matches!(
1810+
finding["rule_id"].as_str(),
1811+
Some(
1812+
"clippy::missing_errors_doc"
1813+
| "clippy::missing_panics_doc"
1814+
| "clippy::missing_safety_doc"
1815+
)
1816+
)
1817+
})
1818+
})
1819+
{
1820+
candidates.push(candidate.clone());
1821+
candidate["checker_id"] = json!("rust.cargo_clippy");
1822+
let complete = rust_task_outcome == Some(TaskOutcome::Succeeded)
1823+
&& rust_lint["local_scan_complete"] == true;
1824+
candidate["status"] = json!(if complete {
1825+
"observed_unverified"
1826+
} else {
1827+
"native_incomplete"
1828+
});
1829+
candidate["reason"] = json!(if complete {
1830+
"trusted_policy_and_coverage_unavailable"
1831+
} else {
1832+
"native_scan_incomplete"
1833+
});
1834+
candidate["next_action"] = json!(
1835+
"读取 native_results.rust_lint 的原生 Errors/Panics/Safety 规则与稳定任务,说明实际契约并用原 Cargo 对原任务 task verify;空章节不能代替详细说明,Rustdoc 与完整文档覆盖仍须分别核验"
1836+
);
1837+
}
18041838
if language == "python" && category == "cve" {
18051839
candidate["checker_id"] = json!("python.pip_audit");
18061840
candidate["next_action"] = json!(

‎crates/codeguard-cli/tests/check_all_rust_native.rs‎

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,82 @@ impl Drop for Fixture {
7474
}
7575
}
7676

77+
#[test]
78+
fn clippy_documentation_keeps_both_native_comment_obligations() {
79+
for (rule, exit, doc_failed) in [
80+
("missing_errors_doc", 0, false),
81+
("missing_panics_doc", 0, false),
82+
("missing_safety_doc", 0, false),
83+
("missing_errors_doc", 101, false),
84+
("missing_errors_doc", 0, true),
85+
] {
86+
let f = Fixture::new();
87+
let warning = serde_json::json!({"reason":"compiler-message","message":{"level":"warning","code":{"code":format!("clippy::{rule}")},"spans":[{"file_name":"src/lib.rs","line_start":1,"column_start":1,"is_primary":true}]}});
88+
let tool = f.tool(&format!(
89+
"printf '%s\\n' '{}' '{{\"reason\":\"build-finished\",\"success\":{}}}'; exit {exit}",
90+
warning,
91+
exit == 0
92+
));
93+
if doc_failed {
94+
let script =
95+
fs::read_to_string(&tool)
96+
.unwrap()
97+
.replacen("exit 0 ;; esac", "exit 1 ;; esac", 1);
98+
fs::write(&tool, script).unwrap();
99+
}
100+
let (_, r) = f.check(&["--cargo-tool", tool.to_str().unwrap()]);
101+
let rows: Vec<&Value> = r["category_candidates"]
102+
.as_array()
103+
.unwrap()
104+
.iter()
105+
.filter(|c| c["language"] == "rust" && c["category"] == "comments")
106+
.collect();
107+
assert!(
108+
rows.iter().any(|c| c["checker_id"] == "rust.cargo_rustdoc"),
109+
"{r}"
110+
);
111+
let clippy = rows
112+
.iter()
113+
.find(|c| c["checker_id"] == "rust.cargo_clippy")
114+
.expect("Clippy comments obligation");
115+
assert_eq!(
116+
clippy["status"],
117+
if exit == 0 {
118+
"observed_unverified"
119+
} else {
120+
"native_incomplete"
121+
}
122+
);
123+
if doc_failed {
124+
let rustdoc = rows
125+
.iter()
126+
.find(|c| c["checker_id"] == "rust.cargo_rustdoc")
127+
.unwrap();
128+
assert_eq!(rustdoc["status"], "native_incomplete");
129+
}
130+
assert!(clippy["next_action"].as_str().unwrap().contains("空章节"));
131+
assert_eq!(r["delivery_decision"], "incomplete");
132+
}
133+
for rule in ["missing_errors_doc_unknown", "needless_return"] {
134+
let f = Fixture::new();
135+
let warning = serde_json::json!({"reason":"compiler-message","message":{"level":"warning","code":{"code":format!("clippy::{rule}")},"spans":[{"file_name":"src/lib.rs","line_start":1,"column_start":1,"is_primary":true}]}});
136+
let tool = f.tool(&format!(
137+
"printf '%s\\n' '{}' '{{\"reason\":\"build-finished\",\"success\":true}}'",
138+
warning
139+
));
140+
let (_, r) = f.check(&["--cargo-tool", tool.to_str().unwrap()]);
141+
assert!(
142+
!r["category_candidates"]
143+
.as_array()
144+
.unwrap()
145+
.iter()
146+
.any(|c| c["language"] == "rust"
147+
&& c["category"] == "comments"
148+
&& c["checker_id"] == "rust.cargo_clippy")
149+
);
150+
}
151+
}
152+
77153
#[test]
78154
fn check_all_schedules_native_cargo_type_check_and_keeps_compiler_error_separate() {
79155
let fixture = Fixture::new();

‎crates/codeguard-cli/tests/rust_clippy_documentation.rs‎

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,22 @@ fn native_documentation_contracts_reuse_tasks_and_preserve_semantic_limits() {
125125
let verify = || p.invoke(&["task", "verify", id, "--cargo-tool", &tool, "--format=json"]);
126126
let present = verify();
127127
assert_eq!(present["observation"], "still_present", "{present}");
128+
let aggregate = p.invoke(&["check", "rust", "--cargo-tool", &tool, "--format=json"]);
129+
let rows = aggregate["category_candidates"].as_array().unwrap();
130+
for checker in ["rust.cargo_clippy", "rust.cargo_rustdoc"] {
131+
assert!(
132+
rows.iter()
133+
.any(|r| r["category"] == "comments" && r["checker_id"] == checker),
134+
"{aggregate}"
135+
);
136+
}
137+
let observed = aggregate["native_results"]["rust_lint"]["findings"]
138+
.as_array()
139+
.unwrap()
140+
.iter()
141+
.find(|f| f["rule_id"] == native_rule)
142+
.unwrap();
143+
assert_eq!(observed["finding_id"], id);
128144
fs::write(
129145
p.0.join("src/lib.rs"),
130146
source("", &format!("#[allow(clippy::{rule})]")),
@@ -166,7 +182,7 @@ fn native_documentation_contracts_reuse_tasks_and_preserve_semantic_limits() {
166182
)
167183
.unwrap();
168184
assert_eq!(fact["state"], "open");
169-
evidence.push(json!({"rule":native_rule,"first":first,"repeat":repeat,"present":present,"suppressed":suppressed,"bare_heading":bare,"repaired":repaired,"state":fact["state"]}));
185+
evidence.push(json!({"rule":native_rule,"first":first,"repeat":repeat,"present":present,"aggregate":aggregate,"suppressed":suppressed,"bare_heading":bare,"repaired":repaired,"state":fact["state"]}));
170186
}
171187
let p = Project::new(
172188
3,

‎docs/Codeguard-Architecture.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1507,3 +1507,6 @@ The standalone C/C++ Clang entry now distinguishes literal/comment hashes and ra
15071507

15081508

15091509
Native Clippy documentation findings now receive specific Errors/Panics/Safety repair guidance through stable tasks and original-tool suppression rechecks. The installed Clippy accepts bare headings: zero diagnostics do not qualify detailed documentation and facts remain open. Ordinary checks do not enable pedantic implicitly. `comments rust` remains a local Rustdoc probe; complete documentation contracts are not unified or qualified. See [acceptance and gaps](../tests/acceptance/clippy-documentation-contract.md).
1510+
1511+
1512+
`check rust/all` now retains a separate comments observation for the three native Clippy documentation rules alongside Rustdoc, preserving each tool obligation without another Clippy execution. Only exact known diagnostics participate; zero findings do not establish enabled or complete documentation contracts. See [aggregate acceptance](../tests/acceptance/clippy-documentation-aggregate.md).

‎docs/Codeguard-Architecture.zh_CN.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1507,3 +1507,6 @@ C/C++ 独立 Clang 入口已修复字符串、注释和原始字符串中的井
15071507

15081508

15091509
Rust 的详细文档任务现对原生 Clippy `missing_errors_doc`、`missing_panics_doc`、`missing_safety_doc` 给出具体 Errors/Panics/Safety 修复指引,复用稳定任务及原工具抑制对照。已有 Clippy 实测接受空章节标题,因此零诊断不代表详细说明合格,事实继续 open;不会隐式启用 pedantic。`comments rust` 仍为 Rustdoc 局部探针,尚未统一完整文档契约。参见 [局部验收与缺口](../tests/acceptance/clippy-documentation-contract.md)。
1510+
1511+
1512+
`check rust/all` 现在把原生 Clippy 的三类文档诊断作为独立 comments 观察,与原 Rustdoc 行并存,不覆盖原工具阻塞或重复执行 Clippy;仅精确已知规则参与,零发现不证明详细契约启用。参见 [聚合验收](../tests/acceptance/clippy-documentation-aggregate.md)。

‎docs/Codeguard-Technical-Design.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1980,3 +1980,6 @@ The standalone C/C++ Clang entry now distinguishes literal/comment hashes and ra
19801980

19811981

19821982
Native Clippy documentation findings now receive specific Errors/Panics/Safety repair guidance through stable tasks and original-tool suppression rechecks. The installed Clippy accepts bare headings: zero diagnostics do not qualify detailed documentation and facts remain open. Ordinary checks do not enable pedantic implicitly. `comments rust` remains a local Rustdoc probe; complete documentation contracts are not unified or qualified. See [acceptance and gaps](../tests/acceptance/clippy-documentation-contract.md).
1983+
1984+
1985+
`check rust/all` now retains a separate comments observation for the three native Clippy documentation rules alongside Rustdoc, preserving each tool obligation without another Clippy execution. Only exact known diagnostics participate; zero findings do not establish enabled or complete documentation contracts. See [aggregate acceptance](../tests/acceptance/clippy-documentation-aggregate.md).

‎docs/Codeguard-Technical-Design.zh_CN.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1981,3 +1981,6 @@ C/C++ 独立 Clang 入口已修复字符串、注释和原始字符串中的井
19811981

19821982

19831983
Rust 的详细文档任务现对原生 Clippy `missing_errors_doc`、`missing_panics_doc`、`missing_safety_doc` 给出具体 Errors/Panics/Safety 修复指引,复用稳定任务及原工具抑制对照。已有 Clippy 实测接受空章节标题,因此零诊断不代表详细说明合格,事实继续 open;不会隐式启用 pedantic。`comments rust` 仍为 Rustdoc 局部探针,尚未统一完整文档契约。参见 [局部验收与缺口](../tests/acceptance/clippy-documentation-contract.md)。
1984+
1985+
1986+
`check rust/all` 现在把原生 Clippy 的三类文档诊断作为独立 comments 观察,与原 Rustdoc 行并存,不覆盖原工具阻塞或重复执行 Clippy;仅精确已知规则参与,零发现不证明详细契约启用。参见 [聚合验收](../tests/acceptance/clippy-documentation-aggregate.md)。

‎openspec/changes/introduce-rust-codeguard-cli/specs/native-tool-adapters/spec.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ Rustdoc逐问题反馈 MUST 提供问题证据、规则依据、允许修改的
125125
- **THEN** 保留原生规则和同一稳定任务,分别要求说明实际错误及条件、panic 条件或 unsafe 调用前置条件;不得改变 API 行为或关闭规则迎合检查,使用原工具 task verify 复检
126126
- **AND** 不为普通检查强制启用 pedantic 规则;抑制对照仍保持任务开放,空章节标题的原生零诊断不代表详细说明合格或完整文档验收
127127

128+
#### Scenario: Aggregate Rust documentation preserves both native sources
129+
- **WHEN** check rust/all 收到三个已适配 Clippy 文档规则之一的原生发现
130+
- **THEN** comments 类别保留独立 Clippy 观察和原 Rustdoc 观察,不覆盖或丢失任一原工具义务;两者只提供局部未核验结果,任务仍复用原检查器身份
131+
- **AND** 原生失败后保留已有发现并标未完成;未知相似规则或零发现不能据此前缀声称详细文档规则完整启用
132+
128133
#### Scenario: Standalone Rust lint has no native Cargo
129134
- **WHEN** 未显式选择Cargo且绝对PATH没有可用Cargo
130135
- **THEN** WASM构建对有界Rust源码提供候选初检并同步确认任务;发现候选或范围/运行未完成时必须准备原生工具,完整有界范围零候选时推荐准备;不安装工具、不把候选认定为源码违规;无WASM构建明确初检不可用

0 commit comments

Comments
 (0)