@@ -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.
409459fn 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