patch 9.1.0831: 'findexpr' can't be used as lambad or Funcref

Problem:  'findexpr' can't be used for lambads
          (Justin Keyes)
Solution: Replace the findexpr option with the findfunc option
          (Yegappan Lakshmanan)

related: #15905
closes: #15976

Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/testdir/test_findfile.vim b/src/testdir/test_findfile.vim
index 25cd9da..4db5850 100644
--- a/src/testdir/test_findfile.vim
+++ b/src/testdir/test_findfile.vim
@@ -1,6 +1,7 @@
 " Test findfile() and finddir()
 
 source check.vim
+import './vim9.vim' as v9
 
 let s:files = [ 'Xfinddir1/foo',
       \         'Xfinddir1/bar',
@@ -283,223 +284,491 @@
   let &path = save_path
 endfunc
 
-" Test for 'findexpr'
-func Test_findexpr()
+" Test for 'findfunc'
+func Test_findfunc()
   CheckUnix
-  call assert_equal('', &findexpr)
-  call writefile(['aFile'], 'Xfindexpr1.c', 'D')
-  call writefile(['bFile'], 'Xfindexpr2.c', 'D')
-  call writefile(['cFile'], 'Xfindexpr3.c', 'D')
+  call assert_equal('', &findfunc)
+  call writefile(['aFile'], 'Xfindfunc1.c', 'D')
+  call writefile(['bFile'], 'Xfindfunc2.c', 'D')
+  call writefile(['cFile'], 'Xfindfunc3.c', 'D')
 
   " basic tests
-  func FindExpr1()
-    let fnames = ['Xfindexpr1.c', 'Xfindexpr2.c', 'Xfindexpr3.c']
-    return fnames->copy()->filter('v:val =~? v:fname')
+  func FindFuncBasic(pat, cmdcomplete)
+    let fnames = ['Xfindfunc1.c', 'Xfindfunc2.c', 'Xfindfunc3.c']
+    return fnames->copy()->filter('v:val =~? a:pat')
   endfunc
 
-  set findexpr=FindExpr1()
-  find Xfindexpr3
-  call assert_match('Xfindexpr3.c', @%)
+  set findfunc=FindFuncBasic
+  find Xfindfunc3
+  call assert_match('Xfindfunc3.c', @%)
   bw!
   2find Xfind
-  call assert_match('Xfindexpr2.c', @%)
+  call assert_match('Xfindfunc2.c', @%)
   bw!
   call assert_fails('4find Xfind', 'E347: No more file "Xfind" found in path')
   call assert_fails('find foobar', 'E345: Can''t find file "foobar" in path')
 
-  sfind Xfindexpr2.c
-  call assert_match('Xfindexpr2.c', @%)
+  sfind Xfindfunc2.c
+  call assert_match('Xfindfunc2.c', @%)
   call assert_equal(2, winnr('$'))
   %bw!
   call assert_fails('sfind foobar', 'E345: Can''t find file "foobar" in path')
 
-  tabfind Xfindexpr3.c
-  call assert_match('Xfindexpr3.c', @%)
+  tabfind Xfindfunc3.c
+  call assert_match('Xfindfunc3.c', @%)
   call assert_equal(2, tabpagenr())
   %bw!
   call assert_fails('tabfind foobar', 'E345: Can''t find file "foobar" in path')
 
+  " Test garbage collection
+  call test_garbagecollect_now()
+  find Xfindfunc2
+  call assert_match('Xfindfunc2.c', @%)
+  bw!
+  delfunc FindFuncBasic
+  call test_garbagecollect_now()
+  call assert_fails('find Xfindfunc2', 'E117: Unknown function: FindFuncBasic')
+
   " Buffer-local option
-  set findexpr=['abc']
+  func GlobalFindFunc(pat, cmdcomplete)
+    return ['global']
+  endfunc
+  func LocalFindFunc(pat, cmdcomplete)
+    return ['local']
+  endfunc
+  set findfunc=GlobalFindFunc
   new
-  setlocal findexpr=['def']
+  setlocal findfunc=LocalFindFunc
   find xxxx
-  call assert_equal('def', @%)
+  call assert_equal('local', @%)
   wincmd w
   find xxxx
-  call assert_equal('abc', @%)
+  call assert_equal('global', @%)
   aboveleft new
-  call assert_equal("['abc']", &findexpr)
+  call assert_equal("GlobalFindFunc", &findfunc)
   wincmd k
   aboveleft new
-  call assert_equal("['abc']", &findexpr)
+  call assert_equal("GlobalFindFunc", &findfunc)
   %bw!
+  delfunc GlobalFindFunc
+  delfunc LocalFindFunc
 
-  " Empty list
-  set findexpr=[]
-  call assert_fails('find xxxx', 'E345: Can''t find file "xxxx" in path')
+  " Assign an expression
+  set findfunc=[]
+  call assert_fails('find xxxx', 'E117: Unknown function: []')
 
   " Error cases
 
-  " Syntax error in the expression
-  set findexpr=FindExpr1{}
-  call assert_fails('find Xfindexpr1.c', 'E15: Invalid expression')
+  " Function that doesn't any argument
+  func FindFuncNoArg()
+  endfunc
+  set findfunc=FindFuncNoArg
+  call assert_fails('find Xfindfunc1.c', 'E118: Too many arguments for function: FindFuncNoArg')
+  delfunc FindFuncNoArg
 
-  " Find expression throws an error
-  func FindExpr2()
+  " Syntax error in the function
+  func FindFuncSyntaxError(pat, cmdcomplete)
+    return l
+  endfunc
+  set findfunc=FindFuncSyntaxError
+  call assert_fails('find Xfindfunc1.c', 'E121: Undefined variable: l')
+  delfunc FindFuncSyntaxError
+
+  " Find function throws an error
+  func FindFuncWithThrow(pat, cmdcomplete)
     throw 'find error'
   endfunc
-  set findexpr=FindExpr2()
-  call assert_fails('find Xfindexpr1.c', 'find error')
+  set findfunc=FindFuncWithThrow
+  call assert_fails('find Xfindfunc1.c', 'find error')
+  delfunc FindFuncWithThrow
 
-  " Try using a null List as the expression
-  set findexpr=test_null_list()
-  call assert_fails('find Xfindexpr1.c', 'E345: Can''t find file "Xfindexpr1.c" in path')
+  " Try using a null function
+  call assert_fails('let &findfunc = test_null_function()', 'E129: Function name required')
 
-  " Try to create a new window from the find expression
-  func FindExpr3()
+  " Try to create a new window from the find function
+  func FindFuncNewWindow(pat, cmdexpand)
     new
     return ["foo"]
   endfunc
-  set findexpr=FindExpr3()
-  call assert_fails('find Xfindexpr1.c', 'E565: Not allowed to change text or change window')
+  set findfunc=FindFuncNewWindow
+  call assert_fails('find Xfindfunc1.c', 'E565: Not allowed to change text or change window')
+  delfunc FindFuncNewWindow
 
-  " Try to modify the current buffer from the find expression
-  func FindExpr4()
+  " Try to modify the current buffer from the find function
+  func FindFuncModifyBuf(pat, cmdexpand)
     call setline(1, ['abc'])
     return ["foo"]
   endfunc
-  set findexpr=FindExpr4()
-  call assert_fails('find Xfindexpr1.c', 'E565: Not allowed to change text or change window')
+  set findfunc=FindFuncModifyBuf
+  call assert_fails('find Xfindfunc1.c', 'E565: Not allowed to change text or change window')
+  delfunc FindFuncModifyBuf
 
-  " Expression returning a string
-  set findexpr='abc'
-  call assert_fails('find Xfindexpr1.c', "E1514: 'findexpr' did not return a List type")
+  " Return the wrong type from the function
+  func FindFuncWrongRet(pat, cmdexpand)
+    return 'foo'
+  endfunc
+  set findfunc=FindFuncWrongRet
+  call assert_fails('find Xfindfunc1.c', "E1514: 'findfunc' did not return a List type")
+  delfunc FindFuncWrongRet
 
-  set findexpr&
-  delfunc! FindExpr1
-  delfunc! FindExpr2
-  delfunc! FindExpr3
-  delfunc! FindExpr4
+  set findfunc&
 endfunc
 
-" Test for using a script-local function for 'findexpr'
-func Test_findexpr_scriptlocal_func()
-  func! s:FindExprScript()
-    let g:FindExprArg = v:fname
+" Test for using a script-local function for 'findfunc'
+func Test_findfunc_scriptlocal_func()
+  func! s:FindFuncScript(pat, cmdexpand)
+    let g:FindFuncArg = a:pat
     return ['xxx']
   endfunc
 
-  set findexpr=s:FindExprScript()
-  call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr)
-  call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr)
+  set findfunc=s:FindFuncScript
+  call assert_equal(expand('<SID>') .. 'FindFuncScript', &findfunc)
+  call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc)
   new | only
-  let g:FindExprArg = ''
+  let g:FindFuncArg = ''
   find abc
-  call assert_equal('abc', g:FindExprArg)
+  call assert_equal('abc', g:FindFuncArg)
   bw!
 
-  set findexpr=<SID>FindExprScript()
-  call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr)
-  call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr)
+  set findfunc=<SID>FindFuncScript
+  call assert_equal(expand('<SID>') .. 'FindFuncScript', &findfunc)
+  call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc)
   new | only
-  let g:FindExprArg = ''
+  let g:FindFuncArg = ''
   find abc
-  call assert_equal('abc', g:FindExprArg)
+  call assert_equal('abc', g:FindFuncArg)
   bw!
 
-  let &findexpr = 's:FindExprScript()'
-  call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr)
+  let &findfunc = 's:FindFuncScript'
+  call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc)
   new | only
-  let g:FindExprArg = ''
+  let g:FindFuncArg = ''
   find abc
-  call assert_equal('abc', g:FindExprArg)
+  call assert_equal('abc', g:FindFuncArg)
   bw!
 
-  let &findexpr = '<SID>FindExprScript()'
-  call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr)
+  let &findfunc = '<SID>FindFuncScript'
+  call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc)
   new | only
-  let g:FindExprArg = ''
+  let g:FindFuncArg = ''
   find abc
-  call assert_equal('abc', g:FindExprArg)
+  call assert_equal('abc', g:FindFuncArg)
   bw!
 
-  set findexpr=
-  setglobal findexpr=s:FindExprScript()
-  setlocal findexpr=
-  call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr)
-  call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr)
-  call assert_equal('', &l:findexpr)
+  set findfunc=
+  setglobal findfunc=s:FindFuncScript
+  setlocal findfunc=
+  call assert_equal(expand('<SID>') .. 'FindFuncScript', &findfunc)
+  call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc)
+  call assert_equal('', &l:findfunc)
   new | only
-  let g:FindExprArg = ''
+  let g:FindFuncArg = ''
   find abc
-  call assert_equal('abc', g:FindExprArg)
+  call assert_equal('abc', g:FindFuncArg)
   bw!
 
   new | only
-  set findexpr=
-  setglobal findexpr=
-  setlocal findexpr=s:FindExprScript()
-  call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr)
-  call assert_equal(expand('<SID>') .. 'FindExprScript()', &l:findexpr)
-  call assert_equal('', &g:findexpr)
-  let g:FindExprArg = ''
+  set findfunc=
+  setglobal findfunc=
+  setlocal findfunc=s:FindFuncScript
+  call assert_equal(expand('<SID>') .. 'FindFuncScript', &findfunc)
+  call assert_equal(expand('<SID>') .. 'FindFuncScript', &l:findfunc)
+  call assert_equal('', &g:findfunc)
+  let g:FindFuncArg = ''
   find abc
-  call assert_equal('abc', g:FindExprArg)
+  call assert_equal('abc', g:FindFuncArg)
   bw!
 
-  set findexpr=
-  delfunc s:FindExprScript
+  set findfunc=
+  delfunc s:FindFuncScript
 endfunc
 
-" Test for expanding the argument to the :find command using 'findexpr'
-func Test_findexpr_expand_arg()
-  let s:fnames = ['Xfindexpr1.c', 'Xfindexpr2.c', 'Xfindexpr3.c']
+" Test for expanding the argument to the :find command using 'findfunc'
+func Test_findfunc_expand_arg()
+  let s:fnames = ['Xfindfunc1.c', 'Xfindfunc2.c', 'Xfindfunc3.c']
 
-  " 'findexpr' that accepts a regular expression
-  func FindExprRegexp()
-    return s:fnames->copy()->filter('v:val =~? v:fname')
+  " 'findfunc' that accepts a regular expression
+  func FindFuncRegexp(pat, cmdcomplete)
+    return s:fnames->copy()->filter('v:val =~? a:pat')
   endfunc
 
-  " 'findexpr' that accepts a glob
-  func FindExprGlob()
-    let pat = glob2regpat(v:cmdcomplete ? $'*{v:fname}*' : v:fname)
+  " 'findfunc' that accepts a glob
+  func FindFuncGlob(pat_arg, cmdcomplete)
+    let pat = glob2regpat(a:cmdcomplete ? $'*{a:pat_arg}*' : a:pat_arg)
     return s:fnames->copy()->filter('v:val =~? pat')
   endfunc
 
   for regexp in [v:true, v:false]
-    let &findexpr = regexp ? 'FindExprRegexp()' : 'FindExprGlob()'
+    let &findfunc = regexp ? 'FindFuncRegexp' : 'FindFuncGlob'
 
     call feedkeys(":find \<Tab>\<C-B>\"\<CR>", "xt")
-    call assert_equal('"find Xfindexpr1.c', @:)
+    call assert_equal('"find Xfindfunc1.c', @:)
 
     call feedkeys(":find Xfind\<Tab>\<Tab>\<C-B>\"\<CR>", "xt")
-    call assert_equal('"find Xfindexpr2.c', @:)
+    call assert_equal('"find Xfindfunc2.c', @:)
 
     call assert_equal(s:fnames, getcompletion('find ', 'cmdline'))
     call assert_equal(s:fnames, getcompletion('find Xfind', 'cmdline'))
 
     let pat = regexp ? 'X.*1\.c' : 'X*1.c'
     call feedkeys($":find {pat}\<Tab>\<C-B>\"\<CR>", "xt")
-    call assert_equal('"find Xfindexpr1.c', @:)
-    call assert_equal(['Xfindexpr1.c'], getcompletion($'find {pat}', 'cmdline'))
+    call assert_equal('"find Xfindfunc1.c', @:)
+    call assert_equal(['Xfindfunc1.c'], getcompletion($'find {pat}', 'cmdline'))
 
     call feedkeys(":find 3\<Tab>\<C-B>\"\<CR>", "xt")
-    call assert_equal('"find Xfindexpr3.c', @:)
-    call assert_equal(['Xfindexpr3.c'], getcompletion($'find 3', 'cmdline'))
+    call assert_equal('"find Xfindfunc3.c', @:)
+    call assert_equal(['Xfindfunc3.c'], getcompletion($'find 3', 'cmdline'))
 
     call feedkeys(":find Xfind\<C-A>\<C-B>\"\<CR>", "xt")
-    call assert_equal('"find Xfindexpr1.c Xfindexpr2.c Xfindexpr3.c', @:)
+    call assert_equal('"find Xfindfunc1.c Xfindfunc2.c Xfindfunc3.c', @:)
 
     call feedkeys(":find abc\<Tab>\<C-B>\"\<CR>", "xt")
     call assert_equal('"find abc', @:)
     call assert_equal([], getcompletion('find abc', 'cmdline'))
   endfor
 
-  set findexpr&
-  delfunc! FindExprRegexp
-  delfunc! FindExprGlob
+  set findfunc&
+  delfunc! FindFuncRegexp
+  delfunc! FindFuncGlob
   unlet s:fnames
 endfunc
 
+" Test for different ways of setting the 'findfunc' option
+func Test_findfunc_callback()
+  new
+  func FindFunc1(pat, cmdexpand)
+    let g:FindFunc1Args = [a:pat, a:cmdexpand]
+    return ['findfunc1']
+  endfunc
+
+  let lines =<< trim END
+    #" Test for using a function name
+    LET &findfunc = 'g:FindFunc1'
+    LET g:FindFunc1Args = []
+    find abc1
+    call assert_equal(['abc1', v:false], g:FindFunc1Args)
+
+    #" Test for using a function()
+    set findfunc=function('g:FindFunc1')
+    LET g:FindFunc1Args = []
+    find abc2
+    call assert_equal(['abc2', v:false], g:FindFunc1Args)
+
+    #" Using a funcref variable to set 'findfunc'
+    VAR Fn = function('g:FindFunc1')
+    LET &findfunc = Fn
+    LET g:FindFunc1Args = []
+    find abc3
+    call assert_equal(['abc3', v:false], g:FindFunc1Args)
+
+    #" Using a string(funcref_variable) to set 'findfunc'
+    LET Fn = function('g:FindFunc1')
+    LET &findfunc = string(Fn)
+    LET g:FindFunc1Args = []
+    find abc4
+    call assert_equal(['abc4', v:false], g:FindFunc1Args)
+
+    #" Test for using a funcref()
+    set findfunc=funcref('g:FindFunc1')
+    LET g:FindFunc1Args = []
+    find abc5
+    call assert_equal(['abc5', v:false], g:FindFunc1Args)
+
+    #" Using a funcref variable to set 'findfunc'
+    LET Fn = funcref('g:FindFunc1')
+    LET &findfunc = Fn
+    LET g:FindFunc1Args = []
+    find abc6
+    call assert_equal(['abc6', v:false], g:FindFunc1Args)
+
+    #" Using a string(funcref_variable) to set 'findfunc'
+    LET Fn = funcref('g:FindFunc1')
+    LET &findfunc = string(Fn)
+    LET g:FindFunc1Args = []
+    find abc7
+    call assert_equal(['abc7', v:false], g:FindFunc1Args)
+
+    #" Test for using a lambda function using set
+    VAR optval = "LSTART pat, cmdexpand LMIDDLE FindFunc1(pat, cmdexpand) LEND"
+    LET optval = substitute(optval, ' ', '\\ ', 'g')
+    exe "set findfunc=" .. optval
+    LET g:FindFunc1Args = []
+    find abc8
+    call assert_equal(['abc8', v:false], g:FindFunc1Args)
+
+    #" Test for using a lambda function using LET
+    LET &findfunc = LSTART pat, _ LMIDDLE FindFunc1(pat, v:false) LEND
+    LET g:FindFunc1Args = []
+    find abc9
+    call assert_equal(['abc9', v:false], g:FindFunc1Args)
+
+    #" Set 'findfunc' to a string(lambda expression)
+    LET &findfunc = 'LSTART pat, _ LMIDDLE FindFunc1(pat, v:false) LEND'
+    LET g:FindFunc1Args = []
+    find abc10
+    call assert_equal(['abc10', v:false], g:FindFunc1Args)
+
+    #" Set 'findfunc' to a variable with a lambda expression
+    VAR Lambda = LSTART pat, _ LMIDDLE FindFunc1(pat, v:false) LEND
+    LET &findfunc = Lambda
+    LET g:FindFunc1Args = []
+    find abc11
+    call assert_equal(['abc11', v:false], g:FindFunc1Args)
+
+    #" Set 'findfunc' to a string(variable with a lambda expression)
+    LET Lambda = LSTART pat, _ LMIDDLE FindFunc1(pat, v:false) LEND
+    LET &findfunc = string(Lambda)
+    LET g:FindFunc1Args = []
+    find abc12
+    call assert_equal(['abc12', v:false], g:FindFunc1Args)
+
+    #" Try to use 'findfunc' after the function is deleted
+    func g:TmpFindFunc(pat, cmdexpand)
+      let g:TmpFindFunc1Args = [a:pat, a:cmdexpand]
+    endfunc
+    LET &findfunc = function('g:TmpFindFunc')
+    delfunc g:TmpFindFunc
+    call test_garbagecollect_now()
+    LET g:TmpFindFunc1Args = []
+    call assert_fails('find abc13', 'E117:')
+    call assert_equal([], g:TmpFindFunc1Args)
+
+    #" Try to use a function with three arguments for 'findfunc'
+    func g:TmpFindFunc2(x, y, z)
+      let g:TmpFindFunc2Args = [a:x, a:y, a:z]
+    endfunc
+    set findfunc=TmpFindFunc2
+    LET g:TmpFindFunc2Args = []
+    call assert_fails('find abc14', 'E119:')
+    call assert_equal([], g:TmpFindFunc2Args)
+    delfunc TmpFindFunc2
+
+    #" Try to use a function with zero arguments for 'findfunc'
+    func g:TmpFindFunc3()
+      let g:TmpFindFunc3Called = v:true
+    endfunc
+    set findfunc=TmpFindFunc3
+    LET g:TmpFindFunc3Called = v:false
+    call assert_fails('find abc15', 'E118:')
+    call assert_equal(v:false, g:TmpFindFunc3Called)
+    delfunc TmpFindFunc3
+
+    #" Try to use a lambda function with three arguments for 'findfunc'
+    LET &findfunc = LSTART a, b, c LMIDDLE FindFunc1(a, v:false) LEND
+    LET g:FindFunc1Args = []
+    call assert_fails('find abc16', 'E119:')
+    call assert_equal([], g:FindFunc1Args)
+
+    #" Test for clearing the 'findfunc' option
+    set findfunc=''
+    set findfunc&
+    call assert_fails("set findfunc=function('abc')", "E700:")
+    call assert_fails("set findfunc=funcref('abc')", "E700:")
+
+    #" set 'findfunc' to a non-existing function
+    LET &findfunc = function('g:FindFunc1')
+    call assert_fails("set findfunc=function('NonExistingFunc')", 'E700:')
+    call assert_fails("LET &findfunc = function('NonExistingFunc')", 'E700:')
+    LET g:FindFunc1Args = []
+    find abc17
+    call assert_equal(['abc17', v:false], g:FindFunc1Args)
+  END
+  call v9.CheckTransLegacySuccess(lines)
+
+  " Test for using a script-local function name
+  func s:FindFunc2(pat, cmdexpand)
+    let g:FindFunc2Args = [a:pat, a:cmdexpand]
+    return ['findfunc2']
+  endfunc
+  set findfunc=s:FindFunc2
+  let g:FindFunc2Args = []
+  find abc18
+  call assert_equal(['abc18', v:false], g:FindFunc2Args)
+
+  let &findfunc = 's:FindFunc2'
+  let g:FindFunc2Args = []
+  find abc19
+  call assert_equal(['abc19', v:false], g:FindFunc2Args)
+  delfunc s:FindFunc2
+
+  " Using Vim9 lambda expression in legacy context should fail
+  set findfunc=(pat,\ cmdexpand)\ =>\ FindFunc1(pat,\ v:false)
+  let g:FindFunc1Args = []
+  call assert_fails('find abc20', 'E117:')
+  call assert_equal([], g:FindFunc1Args)
+
+  " set 'findfunc' to a partial with dict.
+  func SetFindFunc()
+    let operator = {'execute': function('FindFuncExecute')}
+    let &findfunc = operator.execute
+  endfunc
+  func FindFuncExecute(pat, cmdexpand) dict
+    return ['findfuncexecute']
+  endfunc
+  call SetFindFunc()
+  call test_garbagecollect_now()
+  set findfunc=
+  delfunc SetFindFunc
+  delfunc FindFuncExecute
+
+  func FindFunc2(pat, cmdexpand)
+    let g:FindFunc2Args = [a:pat, a:cmdexpand]
+    return ['findfunc2']
+  endfunc
+
+  " Vim9 tests
+  let lines =<< trim END
+    vim9script
+
+    def g:Vim9findFunc(pat: string, cmdexpand: bool): list<string>
+      g:FindFunc1Args = [pat, cmdexpand]
+      return ['vim9findfunc']
+    enddef
+
+    # Test for using a def function with findfunc
+    set findfunc=function('g:Vim9findFunc')
+    g:FindFunc1Args = []
+    find abc21
+    assert_equal(['abc21', false], g:FindFunc1Args)
+
+    # Test for using a global function name
+    &findfunc = g:FindFunc2
+    g:FindFunc2Args = []
+    find abc22
+    assert_equal(['abc22', false], g:FindFunc2Args)
+    bw!
+
+    # Test for using a script-local function name
+    def LocalFindFunc(pat: string, cmdexpand: bool): list<string>
+      g:LocalFindFuncArgs = [pat, cmdexpand]
+      return ['localfindfunc']
+    enddef
+    &findfunc = LocalFindFunc
+    g:LocalFindFuncArgs = []
+    find abc23
+    assert_equal(['abc23', false], g:LocalFindFuncArgs)
+    bw!
+  END
+  call v9.CheckScriptSuccess(lines)
+
+  " setting 'findfunc' to a script local function outside of a script context
+  " should fail
+  let cleanup =<< trim END
+    call writefile([execute('messages')], 'Xtest.out')
+    qall
+  END
+  call writefile(cleanup, 'Xverify.vim', 'D')
+  call RunVim([], [], "-c \"set findfunc=s:abc\" -S Xverify.vim")
+  call assert_match('E81: Using <SID> not in a', readfile('Xtest.out')[0])
+  call delete('Xtest.out')
+
+  " cleanup
+  set findfunc&
+  delfunc FindFunc1
+  delfunc FindFunc2
+  unlet g:FindFunc1Args g:FindFunc2Args
+  %bw!
+endfunc
+
+
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_modeline.vim b/src/testdir/test_modeline.vim
index bb5bc6b..0697fbb 100644
--- a/src/testdir/test_modeline.vim
+++ b/src/testdir/test_modeline.vim
@@ -208,7 +208,7 @@
   call s:modeline_fails('equalprg', 'equalprg=Something()', 'E520:')
   call s:modeline_fails('errorfile', 'errorfile=Something()', 'E520:')
   call s:modeline_fails('exrc', 'exrc=Something()', 'E520:')
-  call s:modeline_fails('findexpr', 'findexpr=Something()', 'E520:')
+  call s:modeline_fails('findfunc', 'findfunc=Something', 'E520:')
   call s:modeline_fails('formatprg', 'formatprg=Something()', 'E520:')
   call s:modeline_fails('fsync', 'fsync=Something()', 'E520:')
   call s:modeline_fails('grepprg', 'grepprg=Something()', 'E520:')
diff --git a/src/testdir/test_options.vim b/src/testdir/test_options.vim
index 35b7d48..bd9da73 100644
--- a/src/testdir/test_options.vim
+++ b/src/testdir/test_options.vim
@@ -1570,7 +1570,7 @@
 
 " Test for changing options in a sandbox
 func Test_opt_sandbox()
-  for opt in ['backupdir', 'cdpath', 'exrc', 'findexpr']
+  for opt in ['backupdir', 'cdpath', 'exrc', 'findfunc']
     call assert_fails('sandbox set ' .. opt .. '?', 'E48:')
     call assert_fails('sandbox let &' .. opt .. ' = 1', 'E48:')
   endfor
diff --git a/src/testdir/test_vim9_import.vim b/src/testdir/test_vim9_import.vim
index ad0248c..4a3e239 100644
--- a/src/testdir/test_vim9_import.vim
+++ b/src/testdir/test_vim9_import.vim
@@ -1560,27 +1560,47 @@
   set printexpr=
 enddef
 
-" Test for using an imported function as 'findexpr'
-func Test_import_in_findexpr()
-  call Run_Test_import_in_findexpr()
+" Test for using an imported function as 'findfunc'
+func Test_import_in_findfunc()
+  call Run_Test_import_in_findfunc()
 endfunc
 
-def Run_Test_import_in_findexpr()
+def Run_Test_import_in_findfunc()
   var lines =<< trim END
-      vim9script
+    vim9script
 
-      export def FindExpr(): list<string>
-        var fnames = ['Xfile1.c', 'Xfile2.c', 'Xfile3.c']
-        return fnames->copy()->filter('v:val =~? v:fname')
-      enddef
+    export def FindFunc(pat: string, cmdexpand: bool): list<string>
+      var fnames = ['Xfile1.c', 'Xfile2.c', 'Xfile3.c']
+      return fnames->filter((_, v) => v =~? pat)
+    enddef
   END
-  writefile(lines, 'Xfindexpr', 'D')
+  writefile(lines, 'Xfindfunc', 'D')
 
+  # Test using the "set" command
   lines =<< trim END
-      vim9script
-      import './Xfindexpr' as find
+    vim9script
+    import './Xfindfunc' as find1
 
-      set findexpr=find.FindExpr()
+    set findfunc=find1.FindFunc
+  END
+  v9.CheckScriptSuccess(lines)
+
+  enew!
+  find Xfile2
+  assert_equal('Xfile2.c', @%)
+  bwipe!
+
+  botright vert new
+  find Xfile1
+  assert_equal('Xfile1.c', @%)
+  bw!
+
+  # Test using the option variable
+  lines =<< trim END
+    vim9script
+    import './Xfindfunc' as find2
+
+    &findfunc = find2.FindFunc
   END
   v9.CheckScriptSuccess(lines)
 
@@ -1593,7 +1613,7 @@
   find Xfile1
   assert_equal('Xfile1.c', @%)
 
-  set findexpr=
+  set findfunc=
   bwipe!
 enddef