vim-patch:9.2.0825: regexp: submatch in a look-behind is empty with the NFA engine (#40899)

Problem:  With the NFA engine a sub-expression inside a variable width
          look-behind, e.g. "\v(.)@<=", is empty for the first match on
          every line except the first one.  The old engine is correct
          (Mukundan)
Solution: The look-behind is retried from the previous line, because the
          width of "." is over-estimated.  While scanning that line the
          start state is added at the end of the line, where it gets the
          position of the line break as its start position, even though
          the match actually starts on the next line.  Use the position
          of the start of the next line in that case (Hirohito Higashi).

fixes:  vim/vim#20802
closes: vim/vim#20805

ade577e4dc

Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
zeertzjq
2026-07-22 17:32:24 +08:00
committed by GitHub
parent a277c08d98
commit c9326e6df8
2 changed files with 49 additions and 1 deletions

View File

@@ -15525,7 +15525,30 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
}
}
} else {
if (addstate(nextlist, start, m, NULL, clen) == NULL) {
uint8_t *save_line = rex.line;
uint8_t *save_input = rex.input;
linenr_T save_lnum = rex.lnum;
// At the end of a line the match can only start on the next
// line, use that position instead of the line break.
if (REG_MULTI && clen == 0 && nfa_endp != NULL
&& rex.lnum < nfa_endp->se_u.pos.lnum) {
char *next_line = reg_getline(rex.lnum + 1);
if (next_line != NULL) {
rex.line = (uint8_t *)next_line;
rex.input = rex.line;
rex.lnum++;
}
}
r = addstate(nextlist, start, m, NULL, clen);
rex.line = save_line;
rex.input = save_input;
rex.lnum = save_lnum;
if (r == NULL) {
nfa_match = NFA_TOO_EXPENSIVE;
goto theend;
}

View File

@@ -656,4 +656,29 @@ func Test_regex_collection_range_with_composing_crash()
bwipe!
endfunc
" A submatch in a look-behind must not start on the previous line. See
" issue #20802.
func Test_lookbehind_submatch_on_second_line()
new
for re in range(0, 2)
exe "set re=" .. re
call setline(1, ['testing', 'testing'])
%s/\v(.)@<=/[\1]/g
call assert_equal(['t[t]e[e]s[s]t[t]i[i]n[n]g',
\ 't[t]e[e]s[s]t[t]i[i]n[n]g'], getline(1, '$'), 're=' .. re)
%d _
" With "\_." the look-behind can match the line break, then the submatch
" does start on the previous line.
call setline(1, ['abc', 'def'])
%s/\v(\_.)@<=/[\1]/g
call assert_equal(['a[a]b[b]c[c]', '[]d[d]e[e]f[f]'],
\ getline(1, '$'), 're=' .. re)
%d _
endfor
set re&
bwipe!
endfunc
" vim: shiftwidth=2 sts=2 expandtab