Skip to content

Commit 9715367

Browse files
olwangclaude
andcommitted
feat(selfhost-checker): add RS0023 (Fd outside internal boundary)
Flags an `Fd` type appearing in the param/return surface of a non-native function (check_fd_surface / type_ref_contains_name(ty, "Fd")). Recovered from the batch agent's uncommitted-but-green work (crashed before committing). CHECKER_TARGET_CODES now 19 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 240ce27 commit 9715367

2 files changed

Lines changed: 93 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","RS0007","RS0008","RS0009","RS0010","RS0011","RS0012","RS0016","RS0017","RS0021","RS0024","RS0028","RS0033","RS0029"];
506+
const CHECKER_TARGET_CODES: &[&str] = &["RS0002","RS0003","RS0004","RS0005","RS0006","RS0007","RS0008","RS0009","RS0010","RS0011","RS0012","RS0016","RS0017","RS0021","RS0024","RS0028","RS0033","RS0029","RS0023"];
507507

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

selfhost/check.rss

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2093,6 +2093,84 @@ fn fn_has_bad_await(toks: read List<Tok>, start: read Int) -> Bool {
20932093
return hasAwait && suppress == false
20942094
}
20952095

2096+
// ---------------------------------------------------------------------------
2097+
// RS0023 FD_OUTSIDE_INTERNAL_BOUNDARY.
2098+
//
2099+
// `Fd` used in a param/return type of a non-native fn, or in a field type of a
2100+
// non-resource type decl (check_fd_surface, type_ref_contains_name(ty, "Fd")).
2101+
// Native fns and resource types are the trusted internal boundary and skipped.
2102+
// ---------------------------------------------------------------------------
2103+
2104+
// True if the fn decl at `start` carries the `native` modifier.
2105+
fn fn_is_native(toks: read List<Tok>, start: read Int) -> Bool {
2106+
let ns = fn_name_start(toks: read toks, i: read start)
2107+
let mut k = start
2108+
let mut hit = false
2109+
while k < (ns - 1) {
2110+
if at_ident(toks: read toks, i: read k, wid: read WORD_NATIVE) { hit = true }
2111+
k = k + 1
2112+
}
2113+
return hit
2114+
}
2115+
2116+
// RS0023 over a non-native fn: any param type or the return type names `Fd`.
2117+
fn fn_has_fd_surface(toks: read List<Tok>, start: read Int) -> Bool {
2118+
if fn_is_native(toks: read toks, start: read start) { return false }
2119+
let ns = fn_name_start(toks: read toks, i: read start)
2120+
let sigEnd = function_signature_end(toks: read toks, start: read (ns - 1))
2121+
let open = find_param_open(toks: read toks, from: read ns, end: read sigEnd)
2122+
if open < 0 { return false }
2123+
let close = find_matching(toks: read toks, open: read open, openSym: read SYM_LPAREN, closeSym: read SYM_RPAREN)
2124+
if close < 0 { return false }
2125+
let mut k = open + 1
2126+
let mut depth = 0
2127+
let mut angle = 0
2128+
let mut segStart = open + 1
2129+
let mut hit = false
2130+
while k < close {
2131+
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)
2132+
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)
2133+
if opener {
2134+
depth = depth + 1
2135+
} else if closer {
2136+
if depth > 0 { depth = depth - 1 }
2137+
} else if at_symbol(toks: read toks, i: read k, code: read SYM_LANGLE) {
2138+
angle = angle + 1
2139+
} else if at_symbol(toks: read toks, i: read k, code: read SYM_RANGLE) {
2140+
if angle > 0 { angle = angle - 1 }
2141+
} else if depth == 0 && angle == 0 && at_symbol(toks: read toks, i: read k, code: read SYM_COMMA) {
2142+
if seg_type_has_fd(toks: read toks, segStart: read segStart, segEnd: read k) { hit = true }
2143+
segStart = k + 1
2144+
}
2145+
k = k + 1
2146+
}
2147+
if seg_type_has_fd(toks: read toks, segStart: read segStart, segEnd: read close) { hit = true }
2148+
// Return type region: `-> Type` before effects/`{`/`=`.
2149+
if at_symbol(toks: read toks, i: read (close + 1), code: read SYM_ARROW) {
2150+
let rs = close + 2
2151+
let mut re = rs
2152+
let mut scanning = true
2153+
while scanning {
2154+
let stop = at_ident(toks: read toks, i: read re, wid: read WORD_EFFECTS) || at_symbol(toks: read toks, i: read re, code: read SYM_LBRACE) || at_symbol(toks: read toks, i: read re, code: read SYM_EQ)
2155+
if re < sigEnd && stop == false {
2156+
re = re + 1
2157+
} else {
2158+
scanning = false
2159+
}
2160+
}
2161+
if region_has_text(toks: read toks, s: read rs, e: read re, word: read "Fd") { hit = true }
2162+
}
2163+
return hit
2164+
}
2165+
2166+
// A param segment's type region [segStart+2, segEnd) names `Fd`.
2167+
fn seg_type_has_fd(toks: read List<Tok>, segStart: read Int, segEnd: read Int) -> Bool {
2168+
if (segStart + 2) >= segEnd { return false }
2169+
if is_ident_tok(toks: read toks, i: read segStart) == false { return false }
2170+
if at_symbol(toks: read toks, i: read (segStart + 1), code: read SYM_COLON) == false { return false }
2171+
return region_has_text(toks: read toks, s: read (segStart + 2), e: read segEnd, word: read "Fd")
2172+
}
2173+
20962174
fn main() -> Unit {
20972175
let source = Args.get_or_default(index: 0, default: read "")
20982176
let toks = tokenize(source: read source)
@@ -2121,6 +2199,8 @@ fn main() -> Unit {
21212199
let mut pureBad = false
21222200
// RS0029: any `await` in a non-async function.
21232201
let mut badAwait = false
2202+
// RS0023: `Fd` in a non-native fn signature or a non-resource type field.
2203+
let mut fdSurface = false
21242204
// RS0021: any non-exhaustive match statement or expression in the file.
21252205
let mut nonExhaustive = false
21262206
// RS0024: forward-reference-tolerant known-type set for the whole file.
@@ -2203,6 +2283,13 @@ fn main() -> Unit {
22032283
}
22042284
}
22052285
if type_decl_unknown(toks: read toks, i: read i, r: read r, declared: read declared) { unknownType = true }
2286+
// RS0023: an `Fd`-typed field on a non-resource type. Resource
2287+
// types are the trusted internal boundary and are skipped.
2288+
if at_ident(toks: read toks, i: read (ns - 1), wid: read WORD_RESOURCE) == false {
2289+
if ob >= 0 {
2290+
if region_has_text(toks: read toks, s: read (ob + 1), e: read (r - 1), word: read "Fd") { fdSurface = true }
2291+
}
2292+
}
22062293
i = r
22072294
}
22082295
} else if isSum {
@@ -2290,6 +2377,7 @@ fn main() -> Unit {
22902377
if fn_pure_bad(toks: read toks, start: read i, resources: read resources, sumVariants: read sumVariants, pureFns: read pureFns, allFns: read allFns, declared: read declared) { pureBad = true }
22912378
if fn_has_nonexhaustive_match(toks: read toks, start: read i, declared: read declared, allFns: read allFns) { nonExhaustive = true }
22922379
if fn_has_bad_await(toks: read toks, start: read i) { badAwait = true }
2380+
if fn_has_fd_surface(toks: read toks, start: read i) { fdSurface = true }
22932381
i = r
22942382
}
22952383
} else {
@@ -2340,6 +2428,9 @@ fn main() -> Unit {
23402428
if badAwait {
23412429
Log.write(message: read "RS0029")
23422430
}
2431+
if fdSurface {
2432+
Log.write(message: read "RS0023")
2433+
}
23432434
if unknownType {
23442435
Log.write(message: read "RS0024")
23452436
}
@@ -2355,7 +2446,7 @@ fn main() -> Unit {
23552446
if headerCount >= 2 {
23562447
Log.write(message: read "RS0006")
23572448
}
2358-
let anyDiag = found || hasUnknownFeat || hasDupFeat || headerCount >= 2 || missingReturn || untypedParam || hasProfile || shareParam || unknownEffect || removedRuntime || invalidSelf || outOfRangeInt || retainsBad || unknownType || missingEffect || pureBad || nonExhaustive || badAwait
2449+
let anyDiag = found || hasUnknownFeat || hasDupFeat || headerCount >= 2 || missingReturn || untypedParam || hasProfile || shareParam || unknownEffect || removedRuntime || invalidSelf || outOfRangeInt || retainsBad || unknownType || missingEffect || pureBad || nonExhaustive || badAwait || fdSurface
23592450
if anyDiag == false {
23602451
Log.write(message: read "CLEAN")
23612452
}

0 commit comments

Comments
 (0)