Skip to content

Commit 72ea9e3

Browse files
olwangclaude
andcommitted
feat(selfhost): checker step-2 milestone 2e — RS0028 invalid self parameter
Adds RS0028 (INVALID_SELF_PARAMETER): a parameter named `self` that is not the first parameter of a qualified (dotted-name) method — mirrors check_params' `self && (!is_qualified_method || index != 0)`. CHECKER_TARGET_CODES now 11 codes; checker_parity_corpus byte-exact over 619 files, 0 mismatches, 0 run-failures. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 67e5a34 commit 72ea9e3

2 files changed

Lines changed: 57 additions & 2 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] = &["RS0002","RS0003","RS0004","RS0005","RS0006","RS0010","RS0011","RS0012","RS0016","RS0017"];
506+
const CHECKER_TARGET_CODES: &[&str] = &["RS0002","RS0003","RS0004","RS0005","RS0006","RS0010","RS0011","RS0012","RS0016","RS0017","RS0028"];
507507

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

selfhost/check.rss

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,56 @@ fn fn_has_share_param(toks: read List<Tok>, start: read Int) -> Bool {
405405
return hit
406406
}
407407

408+
// RS0028: a parameter named `self` that is not the first parameter of a qualified
409+
// method (check_params: `self` && (!is_qualified_method || index != 0)). A method
410+
// is qualified iff its name is dotted (`Type.method`).
411+
fn fn_has_invalid_self(toks: read List<Tok>, start: read Int) -> Bool {
412+
let ns = fn_name_start(toks: read toks, i: read start)
413+
let qualified = at_symbol(toks: read toks, i: read (ns + 1), code: read SYM_DOT)
414+
let sigEnd = function_signature_end(toks: read toks, start: read (ns - 1))
415+
let open = find_param_open(toks: read toks, from: read ns, end: read sigEnd)
416+
if open < 0 { return false }
417+
let close = find_matching(toks: read toks, open: read open, openSym: read SYM_LPAREN, closeSym: read SYM_RPAREN)
418+
if close < 0 { return false }
419+
let mut k = open + 1
420+
let mut depth = 0
421+
let mut angle = 0
422+
let mut segStart = open + 1
423+
let mut idx = 0
424+
let mut hit = false
425+
while k < close {
426+
let opener = at_symbol(toks: read toks, i: read k, code: read SYM_LPAREN) || at_symbol(toks: read toks, i: read k, code: read SYM_LBRACKET) || at_symbol(toks: read toks, i: read k, code: read SYM_LBRACE)
427+
let closer = at_symbol(toks: read toks, i: read k, code: read SYM_RPAREN) || at_symbol(toks: read toks, i: read k, code: read SYM_RBRACKET) || at_symbol(toks: read toks, i: read k, code: read SYM_RBRACE)
428+
if opener {
429+
depth = depth + 1
430+
} else if closer {
431+
if depth > 0 { depth = depth - 1 }
432+
} else if at_symbol(toks: read toks, i: read k, code: read SYM_LANGLE) {
433+
angle = angle + 1
434+
} else if at_symbol(toks: read toks, i: read k, code: read SYM_RANGLE) {
435+
if angle > 0 { angle = angle - 1 }
436+
} else if depth == 0 && angle == 0 && at_symbol(toks: read toks, i: read k, code: read SYM_COMMA) {
437+
if seg_is_bad_self(toks: read toks, segStart: read segStart, segEnd: read k, idx: read idx, qualified: read qualified) { hit = true }
438+
segStart = k + 1
439+
idx = idx + 1
440+
}
441+
k = k + 1
442+
}
443+
if seg_is_bad_self(toks: read toks, segStart: read segStart, segEnd: read close, idx: read idx, qualified: read qualified) { hit = true }
444+
return hit
445+
}
446+
447+
// A param segment is an invalid `self` iff it is named `self` and either the
448+
// enclosing function is not a qualified method or this is not the first parameter.
449+
fn seg_is_bad_self(toks: read List<Tok>, segStart: read Int, segEnd: read Int, idx: read Int, qualified: read Bool) -> Bool {
450+
if segStart >= segEnd { return false }
451+
if is_ident_tok(toks: read toks, i: read segStart) == false { return false }
452+
if tk_text(toks: read toks, i: read segStart) == "self" {
453+
return qualified == false || idx != 0
454+
}
455+
return false
456+
}
457+
408458
// `name : share` — ident, `:`, then `share` as the type's first token.
409459
fn seg_is_share(toks: read List<Tok>, segStart: read Int, segEnd: read Int) -> Bool {
410460
if (segStart + 2) >= segEnd { return false }
@@ -434,6 +484,7 @@ fn main() -> Unit {
434484
let mut shareParam = false
435485
let mut unknownEffect = false
436486
let mut removedRuntime = false
487+
let mut invalidSelf = false
437488
let mut i = 0
438489
let mut running = true
439490
while running {
@@ -581,6 +632,7 @@ fn main() -> Unit {
581632
if fn_has_share_param(toks: read toks, start: read i) { shareParam = true }
582633
if effects_name_probe(toks: read toks, start: read i, mode: read 0) { unknownEffect = true }
583634
if effects_name_probe(toks: read toks, start: read i, mode: read 1) { removedRuntime = true }
635+
if fn_has_invalid_self(toks: read toks, start: read i) { invalidSelf = true }
584636
i = r
585637
}
586638
} else {
@@ -613,6 +665,9 @@ fn main() -> Unit {
613665
if removedRuntime {
614666
Log.write(message: read "RS0012")
615667
}
668+
if invalidSelf {
669+
Log.write(message: read "RS0028")
670+
}
616671
if hasUnknownFeat {
617672
Log.write(message: read "RS0016")
618673
}
@@ -622,7 +677,7 @@ fn main() -> Unit {
622677
if headerCount >= 2 {
623678
Log.write(message: read "RS0006")
624679
}
625-
let anyDiag = found || hasUnknownFeat || hasDupFeat || headerCount >= 2 || missingReturn || untypedParam || hasProfile || shareParam || unknownEffect || removedRuntime
680+
let anyDiag = found || hasUnknownFeat || hasDupFeat || headerCount >= 2 || missingReturn || untypedParam || hasProfile || shareParam || unknownEffect || removedRuntime || invalidSelf
626681
if anyDiag == false {
627682
Log.write(message: read "CLEAN")
628683
}

0 commit comments

Comments
 (0)