From 9ce54a8119debd45616bd20e0469c67b484f0823 Mon Sep 17 00:00:00 2001 From: AskaEth Date: Wed, 11 Nov 2026 19:37:28 +0800 Subject: [PATCH] =?UTF-8?q?test(app-iced):=20accent=20=E5=9B=9E=E8=BD=A6?= =?UTF-8?q?=E5=AE=88=E5=8D=AB=E6=94=B9=E5=88=A4=E4=BB=A3=E7=A0=81=E8=A1=8C?= =?UTF-8?q?=E5=B9=B6=E6=95=B4=E5=8F=96=E5=AE=9E=E5=8F=82=EF=BC=88M252=20r2?= =?UTF-8?q?=20P2-1=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 上一轮(e2a9692)声称三处守卫都改了,实际只有 field.rs 与 rows.rs 进了提交: `section/accent.rs` 的改写留在工作树里,被我自己重放 C4 变异时的 `git checkout --` 还原掉了 —— 提交面因此看不到它,那条守卫仍是 `body.contains(..)`,注释掉接线后它照样绿 (只有白名单顺手红)。先修过程、再修判据: - `the_accent_field_wires_enter_to_commit` 改成 `code_line(body, ".on_submit(")` 取**代码行**, 再用 `call_arguments` **整取**实参并断言等于 `["accent_commit"]` —— 顺带堵上 「`accent_commit_none` 这类同前缀产地静默通过」。 - `code_lines()` 的边界文档换成实测口径(`app-iced/src` 9 处 `r#"`、8 处内容含引号; 全仓 `crates/**.rs` 291 处),并写清两个方向:内嵌引号 ⇒ 尾部注释漏剥 ⇒ **假绿**; JSON 原始字符串同行有 `//` ⇒ 多剥 ⇒ **假红**。两种都由 `code/tests.rs` 钉住。 过程纪律(写进报告):**变异重放只在已提交的树上做**,否则 `git checkout --` 会把未提交的 修复一起扔掉,而「声称改了、diff 没动」正是这次被复审抓到的形状。 --- .../src/pages/appearance/section/accent.rs | 34 +++++++++++++------ .../app-iced/src/pages/layout/source/code.rs | 24 +++++++++---- .../src/pages/layout/source/code/tests.rs | 27 +++++++++++---- 3 files changed, 62 insertions(+), 23 deletions(-) diff --git a/crates/app-iced/src/pages/appearance/section/accent.rs b/crates/app-iced/src/pages/appearance/section/accent.rs index 8d5363e..da81fff 100644 --- a/crates/app-iced/src/pages/appearance/section/accent.rs +++ b/crates/app-iced/src/pages/appearance/section/accent.rs @@ -232,26 +232,40 @@ mod tests { /// 本文件源码(源码结构断言用,与 `pages/layout/source.rs` 同一条纪律)。 const SOURCE: &str = include_str!("accent.rs"); - /// **M252 / P18 回车接线的两条断言**:输入框接了 `on_submit`,且接的是「提交」那条消息。 + /// **M252 / P18 回车接线的判据**:`push` 里有一**行代码**接了 `on_submit`,实参整取等于 + /// `accent_commit`,且那个产地发的确实是 `AccentCommit`(提交)而不是 `Accent(原文)`(敲字)。 /// /// iced 的 `Element` 没有内省接口(见 `pages::layout::source` 的模块文档),所以「回车真的 - /// 按下去」在这里量不到——能钉住的是**它接的是哪条消息**: + /// 按下去」在这里量不到——能钉住的是**它接的是哪条消息**。 /// - /// - 抓得住:`.on_submit(..)` 被删掉(T27 反例);接成 `Change::Accent(原文)`(那是**逐键** - /// 路径,`retry(false)` 会把已经亮起来的错吞回去,等于回车把错误擦掉);接成别的控件消息。 + /// - 抓得住:`.on_submit(..)` 被删掉 **或注释掉**(`source::code_line` 先剥注释,判的是**代码行**); + /// 实参被换成同前缀的别的产地(`accent_commit_none`——`call_arguments` 是**整取**,不是子串); + /// 接成 `Change::Accent(原文)`(那是**逐键**路径,`retry(false)` 会把已经亮起来的错吞回去)。 /// - 抓不住:按键真的到达控件(iced 的 `Widget::update`)、以及红框/错行画没画(出图负责)。 #[test] fn the_accent_field_wires_enter_to_commit() { let body = crate::pages::layout::source::function_body(SOURCE, "fn push"); - assert!( - body.contains(".on_submit("), - "强调色输入框没接回车(去掉接线这条必须红),实际函数体:\n{body}" + let line = + crate::pages::layout::source::code_line(body, ".on_submit(").unwrap_or_else(|| { + panic!("`push` 里没有一行**代码**接 `on_submit`(注释掉也算没有):\n{body}") + }); + + // 实参**整取**(不是子串):`accent_commit_none` 这种同前缀产地不许静默通过。 + assert_eq!( + crate::pages::layout::source::call_arguments(&line, ".on_submit("), + vec!["accent_commit"], + "回车必须接 `accent_commit`(唯一产地),实际接线:{line}" ); + + // 产地本身发什么:先看**代码行**(注释里的字不算),再跑一次拿 `Debug` 文本核形态 + // (`Message` 没有 `PartialEq`)。 + let factory = crate::pages::layout::source::function_body(SOURCE, "fn accent_commit"); assert!( - body.contains("accent_commit"), - "回车必须接 `accent_commit`(唯一产地),实际函数体:\n{body}" + crate::pages::layout::source::code_lines(factory) + .iter() + .any(|line| line.contains("Change::AccentCommit")), + "产地必须发 `Change::AccentCommit`(提交),实际函数体:\n{factory}" ); - // 那条产地发的到底是什么:`Message` 没有 `PartialEq`,比 `Debug` 文本(变体名 + 字段)。 assert_eq!( format!("{:?}", super::accent_commit()), format!( diff --git a/crates/app-iced/src/pages/layout/source/code.rs b/crates/app-iced/src/pages/layout/source/code.rs index b55979c..e3f1c6a 100644 --- a/crates/app-iced/src/pages/layout/source/code.rs +++ b/crates/app-iced/src/pages/layout/source/code.rs @@ -21,17 +21,29 @@ //! //! ## 剥不了什么(边界,改守卫前先读这一节) //! -//! 1. **原始字符串**没有被特判,只是**偶然正确**:`r"…"` 与 `r#"…"#` 里的那个 `"` 会被当普通 -//! 字符串起点,于是内容(含 `//`)原样保留。真正会出错的是**原始字符串里出现 `"` 或 `\"`** -//! (`r#"a " b // c"#`):扫描器会提前收尾,后面那段按代码处理 ⇒ 若那段正好是注释就会**漏剥** -//! (方向变成「假绿」)。这是本模块唯一能朝松方向错的形态,登记在此。本仓 `src/**` 现在没有 -//! 这种写法(8 处 `r#"` 全是 JSON 字面量,无内嵌引号/反斜杠)。 +//! 1. **原始字符串没有被特判**:`r"…"` / `r#"…"#` 里那个 `"` 被当成普通字符串起点,于是 +//! 内容(含 `//`)原样保留——对**不含**引号的内容(`r"a // b"`)恰好是对的。含引号时会错位, +//! 而**含引号是常态不是例外**(JSON 字面量必然含 `"`)。实测(`git ls-files` + 逐字面量扫 +//! `r(#+)"`):`crates/app-iced/src` 下 **9 处** `r#"`、其中 **8 处内容含 `"`**; +//! 全仓 `crates/**.rs` **291 处**。 +//! +//! 两个方向都要知道(都由 `code/tests.rs` 的用例钉住): +//! +//! | 写法 | 偏差 | 方向 | +//! |---|---|---| +//! | `r#"a " b"#; // .on_submit(fake())` | 提前收尾后又开了一个「字符串」⇒ **尾部注释不被剥** | **假绿**(`code_text(..).contains(..)` 形式的断言会被骗) | +//! | `r#"{"url":"http://x"}"#; // …` | 截断到 `http:` 之后 ⇒ **多剥** | **假红** | +//! +//! **对本仓的三处接线守卫无影响**:它们判「行首就是那个调用」([`code_line`]),而行首是 +//! `r#"…` 的行永远匹配不上;受影响的是 `code_text(..).contains(..)` 形式的旧断言 +//! (T29 清单里还有约 90 处)。三处被守卫的文件(`field.rs` / `saves/rows.rs` / +//! `section/accent.rs`)`r#"` 计数都是 0。 //! 2. **跨行普通字符串**(行尾 `\` 续行)**不识别**:字符串状态每行复位,续行的下一行会当代码。 //! 后果是**假红**(把字符串里的一段当代码看见,反而更啰嗦)。 //! 3. 它**不判语义**:注释剥掉之后,「`.on_submit(..)` 在不在」仍然只是文本事实。等价重构 //! (先赋值再调用)可能绕过——所以接线守卫的 needle 与写法要一起维护(见各守卫的 docstring)。 //! -//! 除第 1 条那个狭窄形态外,其余偏差都是**假红**:宁可假红,也不要「注释掉仍绿」。 +//! 除第 1 条里「内嵌引号」那个形态外,其余偏差都是**假红**:宁可假红,也不要「注释掉仍绿」。 /// 逐行的代码文本(注释已剥)。**行数与输入一一对应**:只被注释占满的行变成空串。 /// diff --git a/crates/app-iced/src/pages/layout/source/code/tests.rs b/crates/app-iced/src/pages/layout/source/code/tests.rs index 23291a7..aebafb4 100644 --- a/crates/app-iced/src/pages/layout/source/code/tests.rs +++ b/crates/app-iced/src/pages/layout/source/code/tests.rs @@ -69,17 +69,30 @@ let raw = r#"a // b"#; let tail2 = 3; assert!(lines[3].contains("let tail2 = 3;"), "{:?}", lines[3]); } -/// 已知缺口(模块文档边界第 1 条):原始字符串里出现 `"` 时扫描器提前收尾,后面那段按代码看。 +/// 已知缺口(模块文档边界第 1 条):原始字符串含内嵌引号时扫描器会错位,**两个方向都钉住**。 /// -/// 这条用例把那个**朝松方向**的形态钉住:不是「希望这样」,是「哪天修了要知道」。 +/// 不是「希望这样」,是「哪天修了要知道」;也是 `code_text(..).contains(..)` 形式旧断言的已知上限。 #[test] fn raw_strings_with_inner_quotes_are_a_known_gap() { - let source = "let s = r#\"a \" b // c\"#; let after = 1;\n"; - let lines = code_lines(source); + // ① 多剥 ⇒ 假红:`r#"{"url":"http://x"}"#` 那一行会被截断(字符串里的 `//` 当成注释)。 + let json = "let s = r#\"{\"url\":\"http://x\"}\"#; let after = 1;\n"; assert!( - !lines[0].contains("let after = 1;"), - "已知缺口:内嵌引号的原始字符串会让后半行被当注释剥掉(这里只钉现状):{:?}", - lines[0] + !code_lines(json)[0].contains("let after = 1;"), + "已知缺口(假红方向):JSON 原始字符串那行的后半行被剥掉了", + ); + + // ② 漏剥 ⇒ 假绿:内嵌引号之后扫描器又开了一个「字符串」,**尾部注释不被剥**, + // 于是注释里的 `.on_submit(fake())` 会留在「代码」里 —— `code_text(..).contains(..)` + // 形式的断言会被它骗过(三处接线守卫按行首判,不受影响)。 + let sneaky = "let s = r#\"a \" b\"#; // .on_submit(fake())\n"; + let line = &code_lines(sneaky)[0]; + assert!( + line.contains(".on_submit(fake())"), + "已知缺口(假绿方向):内嵌引号的原始字符串会让尾部注释留下,实际:{line:?}" + ); + assert!( + !has_code_line(sneaky, ".on_submit("), + "行首判不受这条影响:那一行的行首是 `let s = r#\"`,永远匹配不上" ); }