Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions fish-rust/src/parse_constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,12 @@ impl ParseError {
}
}

// HACK: If we haven't added anything, remove our default "fish: " line.
// This would be easier to fix if we didn't generate the errors above.
if self.text.is_empty() && prefix == "fish: " && result == prefix {
result = L!("").to_owned();
}

let mut start = self.source_start;
let mut len = self.source_length;
if start >= src.len() {
Expand Down
17 changes: 17 additions & 0 deletions src/expand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,23 @@ static expand_result_t expand_cmdsubst(wcstring input, const operation_context_t
case STATUS_NOT_EXECUTABLE:
err = L"Command not executable";
break;
case STATUS_INVALID_ARGS:
// TODO: Also overused
// This is sent for:
// invalid redirections or pipes (like `<&foo`),
// invalid variables (invalid name or read-only) for for-loops,
// switch $foo if $foo expands to more than one argument
// time in a background job.
err = L"Invalid arguments";
break;
case STATUS_EXPAND_ERROR:
// Sent in `for $foo in ...` if $foo expands to more than one word
err = L"Expansion error";
break;
case STATUS_UNMATCHED_WILDCARD:
// Sent in `for $foo in ...` if $foo expands to more than one word
err = L"Unmatched wildcard";
break;
default:
err = L"Unknown error while evaluating command substitution";
break;
Expand Down
43 changes: 22 additions & 21 deletions src/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,6 @@ operation_context_t parser_t::context() {
/// Append stack trace info for the block \p b to \p trace.
static void append_block_description_to_stack_trace(const parser_t &parser, const block_t &b,
wcstring &trace) {
bool print_call_site = false;
switch (b.type()) {
case block_type_t::function_call:
case block_type_t::function_call_no_shadow: {
Expand All @@ -246,30 +245,31 @@ static void append_block_description_to_stack_trace(const parser_t &parser, cons
}
}
if (!args_str.empty()) {
// TODO: Escape these.
append_format(trace, _(L" with arguments '%ls'"), args_str.c_str());
// If the arguments are under 30 columns wide put them in-line,
// otherwise put them on their own line.
if (fish_wcswidth(args_str) < 30) {
append_format(trace, _(L" with arguments '%ls'"), args_str.c_str());
} else {
trace.append(_(L" with arguments:"));
append_format(trace, L"\n'%ls'\n", args_str.c_str());
}
}
trace.push_back('\n');
print_call_site = true;
break;
}
case block_type_t::subst: {
append_format(trace, _(L"in command substitution\n"));
print_call_site = true;
append_format(trace, _(L"in command substitution"));
break;
}
case block_type_t::source: {
const filename_ref_t &source_dest = b.sourced_file;
append_format(trace, _(L"from sourcing file %ls\n"),
append_format(trace, _(L"from sourcing file %ls"),
user_presentable_path(*source_dest, parser.vars()).c_str());
print_call_site = true;
break;
}
case block_type_t::event: {
assert(b.event && "Should have an event");
wcstring description = *event_get_desc(parser, **b.event);
append_format(trace, _(L"in event handler: %ls\n"), description.c_str());
print_call_site = true;
append_format(trace, _(L"in event handler: %ls"), description.c_str());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only potential downside is it could look worse if the argument strings are overlong.
I think that's an edge case so probably this is fine and overall a great change.


As a programmer, the stack traces are a bit confusing to me, if I see something like

function foo file bar.fish:3
function bar ...

I expect that line 3 in bar.fish is inside function foo. But that's a separate concern

Also I expect the ordering file,line,function,arguments instead of function,arguments,file,line (which would alleviate the concern about long arguments)


I would be entirely fine if we decided to go with just the first one, or if we decided to put the rest off for 4.0, or if the rest gets in the way of someone's porting and I need to write it again later, or if we decide to go for a cleaner solution.

I don't mind merging things like this first (it's small and I don't expect too many of them).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only potential downside is it could look worse if the argument strings are overlong.

There are a bunch of ways to solve this:

  1. Truncate/ellipsize the arguments (not great)
  2. Move the arguments onto another line if they are very long

Something like

fish: Unknown command: nonexistent
foo.fish (line 4): 
    if nonexistent
       ^~~~~~~~~~^
in function 'this-errors' with arguments:
'foo\ bar aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa baz'
 called on line 11 of file foo.fish
in function 'call-this' called on line 1 of file foo.fish

for the latter. I have something whipped up, I'll add it here.

Also I expect the ordering file,line,function,arguments instead of function,arguments,file,line

I think that would be awkward because we don't tend to use tons of files. So you would have a lot of

foo.fish:50:foo bar baz
foo.fish:42:function2 arguments
foo.fish:38:function1 arguments

which is a bunch of noise.

I've been experimenting with only showing the file when it changed

break;
}

Expand All @@ -281,19 +281,20 @@ static void append_block_description_to_stack_trace(const parser_t &parser, cons
case block_type_t::if_block:
case block_type_t::breakpoint:
case block_type_t::variable_assignment:
break;
// No message, skip
return;
}

if (print_call_site) {
// Print where the function is called.
const auto &file = b.src_filename;
if (file) {
append_format(trace, _(L"\tcalled on line %d of file %ls\n"), b.src_lineno,
user_presentable_path(*file, parser.vars()).c_str());
} else if (parser.libdata().within_fish_init) {
append_format(trace, _(L"\tcalled during startup\n"));
}
// Print where the function is called.
const auto &file = b.src_filename;
if (file) {
append_format(trace, _(L" called on line %d of file %ls"), b.src_lineno,
user_presentable_path(*file, parser.vars()).c_str());
} else if (parser.libdata().within_fish_init) {
append_format(trace, _(L" called during startup"));
}

trace.push_back(L'\n');
}

wcstring parser_t::stack_trace() const {
Expand Down
3 changes: 1 addition & 2 deletions tests/checks/broken-config.fish
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ begin
# CHECKERR: ~//fish/config.fish (line {{\d+}}):
# CHECKERR: syntax-error
# CHECKERR: ^~~~~~~~~~~^
# CHECKERR: from sourcing file ~//fish/config.fish
# CHECKERR: called during startup
# CHECKERR: from sourcing file ~//fish/config.fish called during startup

$fish -c "echo normal command" -C "echo init"
# CHECK: broken
Expand Down
21 changes: 7 additions & 14 deletions tests/checks/cd.fish
Original file line number Diff line number Diff line change
Expand Up @@ -134,17 +134,15 @@ cd nonexistent
#CHECKERR: {{.*}}/cd.fish (line {{\d+}}):
#CHECKERR: builtin cd $argv
#CHECKERR: ^
#CHECKERR: in function 'cd' with arguments 'nonexistent'
#CHECKERR: called on line {{\d+}} of file {{.*}}/cd.fish
#CHECKERR: in function 'cd' with arguments 'nonexistent' called on line {{\d+}} of file {{.*}}/cd.fish

touch file
cd file
#CHECKERR: cd: 'file' is not a directory
#CHECKERR: {{.*}}/cd.fish (line {{\d+}}):
#CHECKERR: builtin cd $argv
#CHECKERR: ^
#CHECKERR: in function 'cd' with arguments 'file'
#CHECKERR: called on line {{\d+}} of file {{.*}}/cd.fish
#CHECKERR: in function 'cd' with arguments 'file' called on line {{\d+}} of file {{.*}}/cd.fish

# a directory that isn't executable
mkdir bad-perms
Expand All @@ -154,8 +152,7 @@ cd bad-perms
#CHECKERR: {{.*}}/cd.fish (line {{\d+}}):
#CHECKERR: builtin cd $argv
#CHECKERR: ^
#CHECKERR: in function 'cd' with arguments 'bad-perms'
#CHECKERR: called on line {{\d+}} of file {{.*}}/cd.fish
#CHECKERR: in function 'cd' with arguments 'bad-perms' called on line {{\d+}} of file {{.*}}/cd.fish

cd $old_path
mkdir -p cdpath-dir/bad-perms
Expand Down Expand Up @@ -188,8 +185,7 @@ cd bad-perms
#CHECKERR: {{.*}}/cd.fish (line {{\d+}}):
#CHECKERR: builtin cd $argv
#CHECKERR: ^
#CHECKERR: in function 'cd' with arguments 'bad-perms'
#CHECKERR: called on line {{\d+}} of file {{.*}}/cd.fish
#CHECKERR: in function 'cd' with arguments 'bad-perms' called on line {{\d+}} of file {{.*}}/cd.fish
cd $old_path
cd file
cd $old_path
Expand Down Expand Up @@ -229,8 +225,7 @@ cd ""
# CHECKERR: {{.*}}/cd.fish (line {{\d+}}):
# CHECKERR: builtin cd $argv
# CHECKERR: ^
# CHECKERR: in function 'cd' with arguments '""'
# CHECKERR: called on line {{\d+}} of file {{.*}}/cd.fish
# CHECKERR: in function 'cd' with arguments '""' called on line {{\d+}} of file {{.*}}/cd.fish
echo $status
# CHECK: 1

Expand All @@ -244,8 +239,7 @@ end
# CHECKERR: {{.*}}/cd.fish (line {{\d+}}):
# CHECKERR: builtin cd $argv
# CHECKERR: ^
# CHECKERR: in function 'cd' with arguments 'broken-symbolic-link'
# CHECKERR: called on line {{\d+}} of file {{.*}}/cd.fish
# CHECKERR: in function 'cd' with arguments 'broken-symbolic-link' called on line {{\d+}} of file {{.*}}/cd.fish

# Make sure that "broken symlink" is reported over "no such file or directory".
begin
Expand All @@ -256,8 +250,7 @@ end
# CHECKERR: {{.*}}/cd.fish (line {{\d+}}):
# CHECKERR: builtin cd $argv
# CHECKERR: ^
# CHECKERR: in function 'cd' with arguments 'broken-symbolic-link'
# CHECKERR: called on line {{\d+}} of file {{.*}}/cd.fish
# CHECKERR: in function 'cd' with arguments 'broken-symbolic-link' called on line {{\d+}} of file {{.*}}/cd.fish

begin
mkdir -p foo/bar/muf
Expand Down
6 changes: 2 additions & 4 deletions tests/checks/cmdsub-limit.fish
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,8 @@ set --show c
#CHECKERR: {{.*}}: Too much data emitted by command substitution so it was discarded
#CHECKERR: set -l x (string repeat -n $argv x)
#CHECKERR: ^~~~~~~~~~~~~~~~~~~~~~~~~^
#CHECKERR: in function 'subme' with arguments '513'
#CHECKERR: called on line {{.*}}
#CHECKERR: in command substitution
#CHECKERR: called on line {{.*}}
#CHECKERR: in function 'subme' with arguments '513' called on line {{.*}}
#CHECKERR: in command substitution called on line {{.*}}

# Make sure output from builtins outside of command substitution is not affected
string repeat --max 513 a
Expand Down
3 changes: 0 additions & 3 deletions tests/checks/command-not-found.fish
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,14 @@
set -g PATH
$fish -c "nonexistent-command-1234 banana rama"
#CHECKERR: fish: Unknown command: nonexistent-command-1234
#CHECKERR: fish:
#CHECKERR: nonexistent-command-1234 banana rama
#CHECKERR: ^~~~~~~~~~~~~~~~~~~~~~~^
$fish -C 'function fish_command_not_found; echo cmd-not-found; end' -ic "nonexistent-command-1234 1 2 3 4"
#CHECKERR: cmd-not-found
#CHECKERR: fish:
#CHECKERR: nonexistent-command-1234 1 2 3 4
#CHECKERR: ^~~~~~~~~~~~~~~~~~~~~~~^
$fish -C 'function fish_command_not_found; echo command-not-found $argv; end' -c "nonexistent-command-abcd foo bar baz"
#CHECKERR: command-not-found nonexistent-command-abcd foo bar baz
#CHECKERR: fish:
#CHECKERR: nonexistent-command-abcd foo bar baz
#CHECKERR: ^~~~~~~~~~~~~~~~~~~~~~~^

Expand Down
130 changes: 0 additions & 130 deletions tests/checks/stack-overflow.fish
Original file line number Diff line number Diff line change
Expand Up @@ -147,133 +147,3 @@ left
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
#CHECKERR: {{.*}}
16 changes: 6 additions & 10 deletions tests/checks/status.fish
Original file line number Diff line number Diff line change
Expand Up @@ -114,15 +114,11 @@ function test-stack-trace-other
test-stack-trace-main
end

printf "%s\n" (test-stack-trace-other | string replace \t '<TAB>')[1..4]
# CHECK: in function 'test-stack-trace-main'
# CHECK: <TAB>called on line {{\d+}} of file {{.*}}/status.fish
# CHECK: in function 'test-stack-trace-other'
# CHECK: <TAB>called on line {{\d+}} of file {{.*}}/status.fish
printf "%s\n" (test-stack-trace-other)[1..2]
# CHECK: in function 'test-stack-trace-main' called on line {{\d+}} of file {{.*}}/status.fish
# CHECK: in function 'test-stack-trace-other' called on line {{\d+}} of file {{.*}}/status.fish

functions -c test-stack-trace-other test-stack-trace-copy
printf "%s\n" (test-stack-trace-copy | string replace \t '<TAB>')[1..4]
# CHECK: in function 'test-stack-trace-main'
# CHECK: <TAB>called on line {{\d+}} of file {{.*}}/status.fish
# CHECK: in function 'test-stack-trace-copy'
# CHECK: <TAB>called on line {{\d+}} of file {{.*}}/status.fish
printf "%s\n" (test-stack-trace-copy)[1..2]
# CHECK: in function 'test-stack-trace-main' called on line {{\d+}} of file {{.*}}/status.fish
# CHECK: in function 'test-stack-trace-copy' called on line {{\d+}} of file {{.*}}/status.fish
3 changes: 1 addition & 2 deletions tests/checks/switch.fish
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,7 @@ begin
# CHECKERR: checks/switch.fish (line {{\d+}}):
# CHECKERR: doesnotexist
# CHECKERR: ^~~~~~~~~~~^
# CHECKERR: in command substitution
# CHECKERR: {{\t}}called on line {{\d+}} of file checks/switch.fish
# CHECKERR: in command substitution called on line {{\d+}} of file checks/switch.fish
# CHECKERR: checks/switch.fish (line {{\d+}}): Unknown command
# CHECKERR: switch (doesnotexist)
# CHECKERR: ^~~~~~~~~~~~~^
Expand Down
Loading