fix(discover): split dep-rule targets on every D whitespace

is_dep_rule_line split targets only on space, tab and CR, so valid D such as
"import\fstd.stdio : writeln;" kept the keyword and the dotted module name in
one path-like target and was dropped as dep-info. Vertical tab, form feed
and the UTF-8 separators U+2028/U+2029 now split targets too.

Over 12,062 real D files and 4,494 real files under deps/ dirs, no
classification changes.

Signed-off-by: Nelson Spence <nelson@projectnavi.ai>
This commit is contained in:
Nelson Spence committed 2026-10-01 14:40:06 -05:00
1 parent ceb729d8a3
commit 006f2a991c
2 files changed
+27 -1

No files matched your search

+17 -1
View File
@@ -1338,6 +1338,20 @@ static bool is_rule_colon(const char *p, bool whole_file) {
(next == '\0' && whole_file));
}
/* Bytes of D whitespace at p, or 0: space, tab, vertical tab, form feed, CR
* and the UTF-8 line and paragraph separators U+2028/U+2029, all of which split
* D tokens. '\n' ends the line and is the caller's. */
static size_t d_space_len(const char *p) {
if (*p == ' ' || *p == '\t' || *p == '\v' || *p == '\f' || *p == '\r') {
return SKIP_ONE;
}
if (strncmp(p, "\xE2\x80\xA8", SLEN("\xE2\x80\xA8")) == 0 ||
strncmp(p, "\xE2\x80\xA9", SLEN("\xE2\x80\xA9")) == 0) {
return SLEN("\xE2\x80\xA8");
}
return 0;
}
/* True if the line at p is a make rule "target...: prereq..." whose targets all
* look like paths (contain '/', '\' or '.'); a backslash-escaped space stays
* inside its target. D source fails this: "public:", "@safe:", "extern(C):"
@@ -1353,12 +1367,14 @@ static bool is_dep_rule_line(const char *p, bool whole_file) {
if (is_rule_colon(p, whole_file)) {
return in_target ? path_like : any_target;
}
if (*p == ' ' || *p == '\t' || *p == '\r') {
size_t space = d_space_len(p);
if (space) {
if (in_target && !path_like) {
return false;
}
in_target = false;
path_like = false;
p += space - SKIP_ONE;
continue;
}
if (strchr("(){};=\"',@", *p)) {
+10
View File
@@ -886,6 +886,16 @@ TEST(lang_d_source_stays_dlang) {
ASSERT_EQ(disambiguate_d_content("public/**/:\nvoid f() {}\n"), CBM_LANG_DLANG);
ASSERT_EQ(disambiguate_d_content("public/+ +/:\nvoid f() {}\n"), CBM_LANG_DLANG);
ASSERT_EQ(disambiguate_d_content("public//note: x\nvoid f() {}\n"), CBM_LANG_DLANG);
/* Every D whitespace splits tokens, not only space, tab and CR: form feed,
* vertical tab, U+2028 and U+2029. */
ASSERT_EQ(disambiguate_d_content("import\fstd.stdio : writeln;\n"), CBM_LANG_DLANG);
ASSERT_EQ(disambiguate_d_content("import\vstd.stdio : writeln;\n"), CBM_LANG_DLANG);
ASSERT_EQ(disambiguate_d_content("import\xE2\x80\xA8"
"std.stdio : writeln;\n"),
CBM_LANG_DLANG);
ASSERT_EQ(disambiguate_d_content("import\xE2\x80\xA9"
"std.stdio : writeln;\n"),
CBM_LANG_DLANG);
/* Default on doubt: empty or unreadable. */
ASSERT_EQ(disambiguate_d_content(""), CBM_LANG_DLANG);
ASSERT_EQ(cbm_disambiguate_d("/tmp/nonexistent_file_12345.d"), CBM_LANG_DLANG);