complete: Don't load completions if command isn't in $PATH

This stops us from loading the completions for e.g. `./foo` if there
is no `foo` in path.

This is because the completion scripts will call an unqualified `foo`,
and then error out.

This of course means if the script would work because it never calls
the command, we still don't load it.

Pathed completions via `complete --path` should be unaffected because
they aren't autoloaded anyway.

Workaround for #3117
Fixes #9133
This commit is contained in:
Fabian Boehm 2022-08-11 17:05:32 +02:00
parent 2191faf17e
commit b2eea4b46f
2 changed files with 34 additions and 3 deletions

View file

@ -821,9 +821,11 @@ bool completer_t::complete_param_for_command(const wcstring &cmd_orig, const wcs
wcstring cmd, path;
parse_cmd_string(cmd_orig, &path, &cmd, ctx.vars);
// Use cmd_orig here for paths, as it is potentially pathed.
// Don't use cmd_orig here for paths. It's potentially pathed,
// so that command might exist, but the completion script
// won't be using it.
bool cmd_exists = builtin_exists(cmd) || function_exists_no_autoload(cmd) ||
path_get_path(cmd_orig, ctx.vars).has_value();
path_get_path(cmd, ctx.vars).has_value();
if (!cmd_exists) {
// Do not load custom completions if the command does not exist
// This prevents errors caused during the execution of completion providers for

View file

@ -1,4 +1,4 @@
# RUN: %fish %s
#RUN: %fish -C 'set -l fish %fish' %s
function complete_test_alpha1
echo $argv
end
@ -499,3 +499,32 @@ complete -C'oooops '
# CHECK: oops
echo $oops
# CHECK: 1
# See that we load completions only if the command exists in $PATH,
# as a workaround for #3117.
# We need a completion script that runs the command,
# and prints something simple, and isn't already used above.
#
# Currently, tr fits the bill (it does `tr --version` to check for GNUisms)
begin
$fish -c "complete -C'tr -'" | string match -- '-d*'
# CHECK: -d{{\t.*}}
end
set -l tmpdir (mktemp -d)
cd $tmpdir
begin
touch tr
chmod +x tr
set -l PATH
complete -C'./tr -' | string match -- -d
or echo No match for relative
# CHECK: No match for relative
complete -c tr | string length -q
or echo Empty completions
# CHECK: Empty completions
end
rm -r $tmpdir