Skip to content

Commit e8141f9

Browse files
olwangclaude
andcommitted
feat(selfhost): Step 2 diagnostics — self-hosted feature codes RS0006/RS0016/RS0017
First expansion of the self-hosted checker beyond RS0005, the "functions-first" analog for diagnostics parity (per the step-2 scoping). check.rss now reproduces, from the same top-level token scan: - RS0016 UNKNOWN_FILE_FEATURE — a `features:` entry outside the known set (local/native/unsafe/async/device/ffi/reflection). - RS0017 DUPLICATE_FILE_FEATURE — a feature repeated WITHIN one header (per-header seen-set, matching parse_features). - RS0006 DUPLICATE_FEATURE_DECLARATION — a second top-level `features:` header. The existing top-level driver already isolates headers at declaration granularity, so a `features` struct field (features_field_name.rss) can't misfire. CHECKER_TARGET_CODES extended to the 4 codes; checker_parity_corpus is byte-exact over all 576 files (code-mismatches: 0), and the three codes + CLEAN verified firing on crafted inputs and the unknown-file-feature fixture. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 97b4e44 commit e8141f9

2 files changed

Lines changed: 50 additions & 3 deletions

File tree

crates/rsscript/src/selfhost_parity.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,7 @@ fn parser_parity_corpus() {
503503
// ---------------------------------------------------------------------------
504504

505505
/// Diagnostic codes the rss checker is expected to reproduce.
506-
const CHECKER_TARGET_CODES: &[&str] = &["RS0005"];
506+
const CHECKER_TARGET_CODES: &[&str] = &["RS0005", "RS0006", "RS0016", "RS0017"];
507507

508508
fn is_target_code(code: &str) -> bool {
509509
CHECKER_TARGET_CODES.contains(&code)

selfhost/check.rss

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,12 @@ fn body_has_duplicate_field(toks: read List<Tok>, ob: read Int, close: read Int)
165165
return dup
166166
}
167167

168+
// The file features recognized by the compiler (parse_file_feature). Anything
169+
// else in a `features:` header is an unknown feature (RS0016).
170+
fn is_known_feature(name: read String) -> Bool {
171+
return name == "local" || name == "native" || name == "unsafe" || name == "async" || name == "device" || name == "ffi" || name == "reflection"
172+
}
173+
168174
// ---------------------------------------------------------------------------
169175
// Driver.
170176
// ---------------------------------------------------------------------------
@@ -176,6 +182,11 @@ fn main() -> Unit {
176182
let mut callable = Set<String>.new()
177183
let mut types = Set<String>.new()
178184
let mut found = false
185+
// Feature-header diagnostics: RS0016 (unknown feature), RS0017 (duplicate
186+
// feature within a header), RS0006 (a second `features:` header).
187+
let mut headerCount = 0
188+
let mut hasUnknownFeat = false
189+
let mut hasDupFeat = false
179190
let mut i = 0
180191
let mut running = true
181192
while running {
@@ -193,7 +204,33 @@ fn main() -> Unit {
193204
let isTypeAlias = cls.isTypeAlias
194205
let isConst = cls.isConst
195206
if isFeatures {
196-
i = declaration_line_end(toks: read toks, start: read i)
207+
headerCount = headerCount + 1
208+
let lineEnd = declaration_line_end(toks: read toks, start: read i)
209+
// Feature list is [i+2, lineEnd) (skip the `features :` prefix).
210+
// Duplicate detection is per-header (seen is reset each header),
211+
// matching parse_features.
212+
let mut seen = Set<String>.new()
213+
let mut j = i + 2
214+
while j < lineEnd {
215+
if at_symbol(toks: read toks, i: read j, code: read SYM_COMMA) {
216+
j = j + 1
217+
} else if tk_kind(toks: read toks, i: read j) == TOK_EOF {
218+
j = j + 1
219+
} else {
220+
let fname = tk_text(toks: read toks, i: read j)
221+
if is_known_feature(name: read fname) {
222+
if Set.contains<String>(set: read seen, value: read fname) {
223+
hasDupFeat = true
224+
} else {
225+
Set.insert<String>(set: mut seen, value: read fname)
226+
}
227+
} else {
228+
hasUnknownFeat = true
229+
}
230+
j = j + 1
231+
}
232+
}
233+
i = lineEnd
197234
} else if isProfile {
198235
i = declaration_line_end(toks: read toks, start: read i)
199236
} else if starts_type_decl(toks: read toks, i: read i, isPub: read isPub) {
@@ -304,7 +341,17 @@ fn main() -> Unit {
304341
}
305342
if found {
306343
Log.write(message: read "RS0005")
307-
} else {
344+
}
345+
if hasUnknownFeat {
346+
Log.write(message: read "RS0016")
347+
}
348+
if hasDupFeat {
349+
Log.write(message: read "RS0017")
350+
}
351+
if headerCount >= 2 {
352+
Log.write(message: read "RS0006")
353+
}
354+
if found == false && hasUnknownFeat == false && hasDupFeat == false && headerCount < 2 {
308355
Log.write(message: read "CLEAN")
309356
}
310357
return Unit

0 commit comments

Comments
 (0)