From f388a795ce2b7c6486e772a085d206d71783a294 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Wed, 11 Nov 2026 22:49:43 -0800 Subject: [PATCH] Script checker: a private name that starts with a directive's name is read to its end Inside braces, a line such as `#include$size = 1` was taken for an `#include` line, because only the ASCII letters, digits and `_` after the `#` were compared with the directive names. The name now runs over every character a name may hold, written out or as an escape, before the comparison. --- crates/scriptcheck/src/lex.rs | 14 ++++++++++++-- crates/scriptcheck/src/tests.rs | 15 +++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/crates/scriptcheck/src/lex.rs b/crates/scriptcheck/src/lex.rs index 02d8c2de0..a9a805d87 100644 --- a/crates/scriptcheck/src/lex.rs +++ b/crates/scriptcheck/src/lex.rs @@ -457,10 +457,20 @@ impl<'s> Lexer<'s> { /// A line that starts with `#` and then `rest` is a private name here, not a preprocessor line: /// the line is inside braces or in the code of an XML literal, and the name `rest` starts with - /// is not a directive's. + /// is not a directive's. The name runs over every character a name may hold, written out or as + /// an escape, so `#include$size` is not `#include`. pub fn private_line(&self, rest: &str) -> bool { let in_code = !self.braces.is_empty() || self.deeper_braces > 0 || !self.xml.is_empty(); - let end = rest.find(|c: char| !c.is_ascii_alphanumeric() && c != '_').unwrap_or(rest.len()); + let mut end = 0; + while let Some(more) = rest.get(end..) { + let len = match more.chars().next() { + Some('\\') => escape(more).filter(|&(c, _)| ident_part(c)).map(|(_, len)| len), + Some(c) if ident_part(c) => Some(c.len_utf8()), + _ => None, + }; + let Some(len) = len else { break }; + end += len; + } let name = rest.get(..end).unwrap_or_default(); in_code && !catalog::DIRECTIVES.iter().any(|d| d.eq_ignore_ascii_case(name)) } diff --git a/crates/scriptcheck/src/tests.rs b/crates/scriptcheck/src/tests.rs index 19cee6160..853f212c1 100644 --- a/crates/scriptcheck/src/tests.rs +++ b/crates/scriptcheck/src/tests.rs @@ -491,6 +491,21 @@ fn private_name_at_line_start_is_not_a_directive() { ); } +#[test] +fn private_name_that_starts_with_a_directive_name_is_not_a_directive() { + let r = report(concat!( + "class Box {\n", + " #include$size = 1;\n", + " #include\u{e9} = 2;\n", + " #include\\u0041 = 3;\n", + " #include\\u{41} = 4;\n", + " #INCLUDE 'a.jsxinc'\n", + "}\n", + )); + let directives: Vec<_> = r.directives.iter().map(|d| (d.name, d.line)).collect(); + assert_eq!(directives, [("include", 6)]); +} + #[test] fn xml_code_uses_escaped_names() { let r = report("var x = {\\u{61}pp.activeDocument};\nvar y = \\u0061pp.z \\u{61};\n");