1 // The fish parser. Contains functions for parsing and evaluating code.
2 #include "config.h"  // IWYU pragma: keep
3 
4 #include "parser.h"
5 
6 #include <fcntl.h>
7 #include <stdio.h>
8 
9 #include <algorithm>
10 #include <cwchar>
11 #include <memory>
12 #include <utility>
13 
14 #include "ast.h"
15 #include "common.h"
16 #include "env.h"
17 #include "event.h"
18 #include "expand.h"
19 #include "fallback.h"  // IWYU pragma: keep
20 #include "flog.h"
21 #include "function.h"
22 #include "intern.h"
23 #include "job_group.h"
24 #include "parse_constants.h"
25 #include "parse_execution.h"
26 #include "parse_util.h"
27 #include "proc.h"
28 #include "reader.h"
29 #include "sanity.h"
30 #include "signal.h"
31 #include "wutil.h"  // IWYU pragma: keep
32 
33 class io_chain_t;
34 
35 /// Error for evaluating in illegal scope.
36 #define INVALID_SCOPE_ERR_MSG _(L"Tried to evaluate commands using invalid block type '%ls'")
37 
38 /// While block description.
39 #define WHILE_BLOCK N_(L"'while' block")
40 
41 /// For block description.
42 #define FOR_BLOCK N_(L"'for' block")
43 
44 /// Breakpoint block.
45 #define BREAKPOINT_BLOCK N_(L"block created by breakpoint")
46 
47 /// Variable assignment block.
48 #define VARIABLE_ASSIGNMENT_BLOCK N_(L"block created by variable assignment prefixing a command")
49 
50 /// If block description.
51 #define IF_BLOCK N_(L"'if' conditional block")
52 
53 /// Function invocation block description.
54 #define FUNCTION_CALL_BLOCK N_(L"function invocation block")
55 
56 /// Function invocation block description.
57 #define FUNCTION_CALL_NO_SHADOW_BLOCK N_(L"function invocation block with no variable shadowing")
58 
59 /// Switch block description.
60 #define SWITCH_BLOCK N_(L"'switch' block")
61 
62 /// Top block description.
63 #define TOP_BLOCK N_(L"global root block")
64 
65 /// Command substitution block description.
66 #define SUBST_BLOCK N_(L"command substitution block")
67 
68 /// Begin block description.
69 #define BEGIN_BLOCK N_(L"'begin' unconditional block")
70 
71 /// Source block description.
72 #define SOURCE_BLOCK N_(L"block created by the . builtin")
73 
74 /// Source block description.
75 #define EVENT_BLOCK N_(L"event handler block")
76 
77 /// Unknown block description.
78 #define UNKNOWN_BLOCK N_(L"unknown/invalid block")
79 
80 // Given a file path, return something nicer. Currently we just "unexpand" tildes.
user_presentable_path(const wcstring & path,const environment_t & vars)81 static wcstring user_presentable_path(const wcstring &path, const environment_t &vars) {
82     return replace_home_directory_with_tilde(path, vars);
83 }
84 
parser_t(std::shared_ptr<env_stack_t> vars)85 parser_t::parser_t(std::shared_ptr<env_stack_t> vars) : variables(std::move(vars)) {
86     assert(variables.get() && "Null variables in parser initializer");
87     int cwd = open_cloexec(".", O_RDONLY);
88     if (cwd < 0) {
89         perror("Unable to open the current working directory");
90         return;
91     }
92     libdata().cwd_fd = std::make_shared<const autoclose_fd_t>(cwd);
93 }
94 
parser_t()95 parser_t::parser_t() : parser_t(env_stack_t::principal_ref()) {}
96 
97 // Out of line destructor to enable forward declaration of parse_execution_context_t
98 parser_t::~parser_t() = default;
99 
100 std::shared_ptr<parser_t> parser_t::principal{new parser_t()};
101 
principal_parser()102 parser_t &parser_t::principal_parser() {
103     ASSERT_IS_MAIN_THREAD();
104     return *principal;
105 }
106 
set_var_and_fire(const wcstring & key,env_mode_flags_t mode,wcstring_list_t vals)107 int parser_t::set_var_and_fire(const wcstring &key, env_mode_flags_t mode, wcstring_list_t vals) {
108     std::vector<event_t> events;
109     int res = vars().set(key, mode, std::move(vals), &events);
110     for (const auto &evt : events) {
111         event_fire(*this, evt);
112     }
113     return res;
114 }
115 
set_var_and_fire(const wcstring & key,env_mode_flags_t mode,wcstring val)116 int parser_t::set_var_and_fire(const wcstring &key, env_mode_flags_t mode, wcstring val) {
117     wcstring_list_t vals;
118     vals.push_back(std::move(val));
119     return set_var_and_fire(key, mode, std::move(vals));
120 }
121 
set_empty_var_and_fire(const wcstring & key,env_mode_flags_t mode)122 int parser_t::set_empty_var_and_fire(const wcstring &key, env_mode_flags_t mode) {
123     return set_var_and_fire(key, mode, wcstring_list_t{});
124 }
125 
126 // Given a new-allocated block, push it onto our block list, acquiring ownership.
push_block(block_t && block)127 block_t *parser_t::push_block(block_t &&block) {
128     block_t new_current{block};
129     const enum block_type_t type = new_current.type();
130     new_current.src_lineno = parser_t::get_lineno();
131 
132     wcstring func = new_current.function_name;
133 
134     const wchar_t *filename = parser_t::current_filename();
135     if (filename != nullptr) {
136         new_current.src_filename = intern(filename);
137     }
138 
139     // Types top and subst are not considered blocks for the purposes of `status is-block`.
140     if (type != block_type_t::top && type != block_type_t::subst) {
141         libdata().is_block = true;
142     }
143 
144     if (type == block_type_t::breakpoint) {
145         libdata().is_breakpoint = true;
146     }
147 
148     if (new_current.type() != block_type_t::top) {
149         bool shadow = (type == block_type_t::function_call);
150         vars().push(shadow);
151         new_current.wants_pop_env = true;
152     }
153 
154     // Push it onto our list and return a pointer to it.
155     // Note that deques do not move their contents so this is safe.
156     this->block_list.push_front(std::move(new_current));
157     return &this->block_list.front();
158 }
159 
pop_block(const block_t * expected)160 void parser_t::pop_block(const block_t *expected) {
161     assert(expected == this->current_block());
162     assert(!block_list.empty() && "empty block list");
163 
164     // Acquire ownership out of the block list.
165     block_t old = block_list.front();
166     block_list.pop_front();
167 
168     if (old.wants_pop_env) vars().pop();
169 
170     // Figure out if `status is-block` should consider us to be in a block now.
171     bool new_is_block = false;
172     for (const auto &b : block_list) {
173         if (b.type() != block_type_t::top && b.type() != block_type_t::subst) {
174             new_is_block = true;
175             break;
176         }
177     }
178     libdata().is_block = new_is_block;
179 
180     // Are we still in a breakpoint?
181     bool new_is_breakpoint = false;
182     for (const auto &b : block_list) {
183         if (b.type() == block_type_t::breakpoint) {
184             new_is_breakpoint = true;
185             break;
186         }
187     }
188     libdata().is_breakpoint = new_is_breakpoint;
189 }
190 
get_block_desc(block_type_t block)191 const wchar_t *parser_t::get_block_desc(block_type_t block) {
192     switch (block) {
193         case block_type_t::while_block:
194             return WHILE_BLOCK;
195         case block_type_t::for_block:
196             return FOR_BLOCK;
197         case block_type_t::if_block:
198             return IF_BLOCK;
199         case block_type_t::function_call:
200             return FUNCTION_CALL_BLOCK;
201 
202         case block_type_t::function_call_no_shadow:
203             return FUNCTION_CALL_NO_SHADOW_BLOCK;
204         case block_type_t::switch_block:
205             return SWITCH_BLOCK;
206         case block_type_t::subst:
207             return SUBST_BLOCK;
208         case block_type_t::top:
209             return TOP_BLOCK;
210         case block_type_t::begin:
211             return BEGIN_BLOCK;
212         case block_type_t::source:
213             return SOURCE_BLOCK;
214         case block_type_t::event:
215             return EVENT_BLOCK;
216         case block_type_t::breakpoint:
217             return BREAKPOINT_BLOCK;
218         case block_type_t::variable_assignment:
219             return VARIABLE_ASSIGNMENT_BLOCK;
220     }
221     return _(UNKNOWN_BLOCK);
222 }
223 
block_at_index(size_t idx) const224 const block_t *parser_t::block_at_index(size_t idx) const {
225     return idx < block_list.size() ? &block_list[idx] : nullptr;
226 }
227 
block_at_index(size_t idx)228 block_t *parser_t::block_at_index(size_t idx) {
229     return idx < block_list.size() ? &block_list[idx] : nullptr;
230 }
231 
current_block()232 block_t *parser_t::current_block() { return block_at_index(0); }
233 
234 /// Print profiling information to the specified stream.
print_profile(const std::deque<profile_item_t> & items,FILE * out)235 static void print_profile(const std::deque<profile_item_t> &items, FILE *out) {
236     for (size_t idx = 0; idx < items.size(); idx++) {
237         const profile_item_t &item = items.at(idx);
238         if (item.skipped || item.cmd.empty()) continue;
239 
240         long long total_time = item.duration;
241 
242         // Compute the self time as the total time, minus the total time consumed by subsequent
243         // items exactly one eval level deeper.
244         long long self_time = item.duration;
245         for (size_t i = idx + 1; i < items.size(); i++) {
246             const profile_item_t &nested_item = items.at(i);
247             if (nested_item.skipped) continue;
248 
249             // If the eval level is not larger, then we have exhausted nested items.
250             if (nested_item.level <= item.level) break;
251 
252             // If the eval level is exactly one more than our level, it is a directly nested item.
253             if (nested_item.level == item.level + 1) self_time -= nested_item.duration;
254         }
255 
256         if (std::fwprintf(out, L"%lld\t%lld\t", self_time, total_time) < 0) {
257             wperror(L"fwprintf");
258             return;
259         }
260 
261         for (size_t i = 0; i < item.level; i++) {
262             if (std::fwprintf(out, L"-") < 0) {
263                 wperror(L"fwprintf");
264                 return;
265             }
266         }
267 
268         if (std::fwprintf(out, L"> %ls\n", item.cmd.c_str()) < 0) {
269             wperror(L"fwprintf");
270             return;
271         }
272     }
273 }
274 
clear_profiling()275 void parser_t::clear_profiling() { profile_items.clear(); }
276 
emit_profiling(const char * path) const277 void parser_t::emit_profiling(const char *path) const {
278     // Save profiling information. OK to not use CLO_EXEC here because this is called while fish is
279     // exiting (and hence will not fork).
280     FILE *f = fopen(path, "w");
281     if (!f) {
282         FLOGF(warning, _(L"Could not write profiling information to file '%s'"), path);
283     } else {
284         if (std::fwprintf(f, _(L"Time\tSum\tCommand\n"), profile_items.size()) < 0) {
285             wperror(L"fwprintf");
286         } else {
287             print_profile(profile_items, f);
288         }
289 
290         if (fclose(f)) {
291             wperror(L"fclose");
292         }
293     }
294 }
295 
expand_argument_list(const wcstring & arg_list_src,expand_flags_t eflags,const operation_context_t & ctx)296 completion_list_t parser_t::expand_argument_list(const wcstring &arg_list_src,
297                                                  expand_flags_t eflags,
298                                                  const operation_context_t &ctx) {
299     // Parse the string as an argument list.
300     auto ast = ast::ast_t::parse_argument_list(arg_list_src);
301     if (ast.errored()) {
302         // Failed to parse. Here we expect to have reported any errors in test_args.
303         return {};
304     }
305 
306     // Get the root argument list and extract arguments from it.
307     completion_list_t result;
308     const ast::freestanding_argument_list_t *list =
309         ast.top()->as<ast::freestanding_argument_list_t>();
310     for (const ast::argument_t &arg : list->arguments) {
311         wcstring arg_src = arg.source(arg_list_src);
312         if (expand_string(arg_src, &result, eflags, ctx) == expand_result_t::error) {
313             break;  // failed to expand a string
314         }
315     }
316     return result;
317 }
318 
shared()319 std::shared_ptr<parser_t> parser_t::shared() { return shared_from_this(); }
320 
cancel_checker() const321 cancel_checker_t parser_t::cancel_checker() const {
322     return [] { return signal_check_cancel() != 0; };
323 }
324 
context()325 operation_context_t parser_t::context() {
326     return operation_context_t{this->shared(), this->vars(), this->cancel_checker()};
327 }
328 
329 /// Append stack trace info for the block \p b to \p trace.
append_block_description_to_stack_trace(const parser_t & parser,const block_t & b,wcstring & trace)330 static void append_block_description_to_stack_trace(const parser_t &parser, const block_t &b,
331                                                     wcstring &trace) {
332     bool print_call_site = false;
333     switch (b.type()) {
334         case block_type_t::function_call:
335         case block_type_t::function_call_no_shadow: {
336             append_format(trace, _(L"in function '%ls'"), b.function_name.c_str());
337             // Print arguments on the same line.
338             wcstring args_str;
339             for (const wcstring &arg : b.function_args) {
340                 if (!args_str.empty()) args_str.push_back(L' ');
341                 // We can't quote the arguments because we print this in quotes.
342                 // As a special-case, add the empty argument as "".
343                 if (!arg.empty()) {
344                     args_str.append(escape_string(arg, ESCAPE_ALL | ESCAPE_NO_QUOTED));
345                 } else {
346                     args_str.append(L"\"\"");
347                 }
348             }
349             if (!args_str.empty()) {
350                 // TODO: Escape these.
351                 append_format(trace, _(L" with arguments '%ls'"), args_str.c_str());
352             }
353             trace.push_back('\n');
354             print_call_site = true;
355             break;
356         }
357         case block_type_t::subst: {
358             append_format(trace, _(L"in command substitution\n"));
359             print_call_site = true;
360             break;
361         }
362         case block_type_t::source: {
363             const wchar_t *source_dest = b.sourced_file;
364             append_format(trace, _(L"from sourcing file %ls\n"),
365                           user_presentable_path(source_dest, parser.vars()).c_str());
366             print_call_site = true;
367             break;
368         }
369         case block_type_t::event: {
370             assert(b.event && "Should have an event");
371             wcstring description = event_get_desc(parser, *b.event);
372             append_format(trace, _(L"in event handler: %ls\n"), description.c_str());
373             print_call_site = true;
374             break;
375         }
376 
377         case block_type_t::top:
378         case block_type_t::begin:
379         case block_type_t::switch_block:
380         case block_type_t::while_block:
381         case block_type_t::for_block:
382         case block_type_t::if_block:
383         case block_type_t::breakpoint:
384         case block_type_t::variable_assignment:
385             break;
386     }
387 
388     if (print_call_site) {
389         // Print where the function is called.
390         const wchar_t *file = b.src_filename;
391         if (file) {
392             append_format(trace, _(L"\tcalled on line %d of file %ls\n"), b.src_lineno,
393                           user_presentable_path(file, parser.vars()).c_str());
394         } else if (is_within_fish_initialization()) {
395             append_format(trace, _(L"\tcalled during startup\n"));
396         }
397     }
398 }
399 
stack_trace() const400 wcstring parser_t::stack_trace() const {
401     wcstring trace;
402     for (const auto &b : blocks()) {
403         append_block_description_to_stack_trace(*this, b, trace);
404 
405         // Stop at event handler. No reason to believe that any other code is relevant.
406         //
407         // It might make sense in the future to continue printing the stack trace of the code
408         // that invoked the event, if this is a programmatic event, but we can't currently
409         // detect that.
410         if (b.type() == block_type_t::event) break;
411     }
412     return trace;
413 }
414 
415 /// Returns the name of the currently evaluated function if we are currently evaluating a function,
416 /// NULL otherwise. This is tested by moving down the block-scope-stack, checking every block if it
417 /// is of type FUNCTION_CALL. If the caller doesn't specify a starting position in the stack we
418 /// begin with the current block.
is_function(size_t idx) const419 const wchar_t *parser_t::is_function(size_t idx) const {
420     // PCA: Have to make this a string somehow.
421     ASSERT_IS_MAIN_THREAD();
422 
423     for (size_t block_idx = idx; block_idx < block_list.size(); block_idx++) {
424         const block_t &b = block_list[block_idx];
425         if (b.is_function_call()) {
426             return b.function_name.c_str();
427         } else if (b.type() == block_type_t::source) {
428             // If a function sources a file, obviously that function's offset doesn't
429             // contribute.
430             break;
431         }
432     }
433     return nullptr;
434 }
435 
436 /// Return the function name for the specified stack frame. Default is zero (current frame).
437 /// The special value zero means the function frame immediately above the closest breakpoint frame.
get_function_name(int level)438 const wchar_t *parser_t::get_function_name(int level) {
439     if (level == 0) {
440         // Return the function name for the level preceding the most recent breakpoint. If there
441         // isn't one return the function name for the current level.
442         // Walk until we find a breakpoint, then take the next function.
443         bool found_breakpoint = false;
444         for (const auto &b : block_list) {
445             if (b.type() == block_type_t::breakpoint) {
446                 found_breakpoint = true;
447             } else if (found_breakpoint && b.is_function_call()) {
448                 return b.function_name.c_str();
449             }
450         }
451         return nullptr;  // couldn't find a breakpoint frame
452     } else if (level == 1) {
453         // Return the function name for the current level.
454         return this->is_function();
455     }
456 
457     // Level 1 is the topmost function call. Level 2 is its caller. Etc.
458     int funcs_seen = 0;
459     for (const auto &b : block_list) {
460         if (b.is_function_call()) {
461             funcs_seen++;
462             if (funcs_seen == level) {
463                 return b.function_name.c_str();
464             }
465         }
466     }
467     return nullptr;  // couldn't find that function level
468 }
469 
get_lineno() const470 int parser_t::get_lineno() const {
471     int lineno = -1;
472     if (execution_context) {
473         lineno = execution_context->get_current_line_number();
474     }
475     return lineno;
476 }
477 
current_filename() const478 const wchar_t *parser_t::current_filename() const {
479     ASSERT_IS_MAIN_THREAD();
480 
481     for (const auto &b : block_list) {
482         if (b.is_function_call()) {
483             return function_get_definition_file(b.function_name);
484         } else if (b.type() == block_type_t::source) {
485             return b.sourced_file;
486         }
487     }
488     // Fall back to the file being sourced.
489     return libdata().current_filename;
490 }
491 
function_stack_is_overflowing() const492 bool parser_t::function_stack_is_overflowing() const {
493     // We are interested in whether the count of functions on the stack exceeds
494     // FISH_MAX_STACK_DEPTH. We don't separately track the number of functions, but we can have a
495     // fast path through the eval_level. If the eval_level is in bounds, so must be the stack depth.
496     if (eval_level <= FISH_MAX_STACK_DEPTH) {
497         return false;
498     }
499     // Count the functions.
500     int depth = 0;
501     for (const auto &b : block_list) {
502         depth += b.is_function_call();
503     }
504     return depth > FISH_MAX_STACK_DEPTH;
505 }
506 
current_line()507 wcstring parser_t::current_line() {
508     if (!execution_context) {
509         return wcstring();
510     }
511     int source_offset = execution_context->get_current_source_offset();
512     if (source_offset < 0) {
513         return wcstring();
514     }
515 
516     const int lineno = this->get_lineno();
517     const wchar_t *file = this->current_filename();
518 
519     wcstring prefix;
520 
521     // If we are not going to print a stack trace, at least print the line number and filename.
522     if (!is_interactive() || is_function()) {
523         if (file) {
524             append_format(prefix, _(L"%ls (line %d): "),
525                           user_presentable_path(file, vars()).c_str(), lineno);
526         } else if (is_within_fish_initialization()) {
527             append_format(prefix, L"%ls (line %d): ", _(L"Startup"), lineno);
528         } else {
529             append_format(prefix, L"%ls (line %d): ", _(L"Standard input"), lineno);
530         }
531     }
532 
533     bool skip_caret = is_interactive() && !is_function();
534 
535     // Use an error with empty text.
536     assert(source_offset >= 0);
537     parse_error_t empty_error = {};
538     empty_error.source_start = source_offset;
539 
540     wcstring line_info = empty_error.describe_with_prefix(execution_context->get_source(), prefix,
541                                                           is_interactive(), skip_caret);
542     if (!line_info.empty()) {
543         line_info.push_back(L'\n');
544     }
545 
546     line_info.append(this->stack_trace());
547     return line_info;
548 }
549 
job_add(shared_ptr<job_t> job)550 void parser_t::job_add(shared_ptr<job_t> job) {
551     assert(job != nullptr);
552     assert(!job->processes.empty());
553     job_list.push_front(std::move(job));
554 }
555 
job_promote(job_t * job)556 void parser_t::job_promote(job_t *job) {
557     job_list_t::iterator loc;
558     for (loc = job_list.begin(); loc != job_list.end(); ++loc) {
559         if (loc->get() == job) {
560             break;
561         }
562     }
563     assert(loc != job_list.end());
564 
565     // Move the job to the beginning.
566     std::rotate(job_list.begin(), loc, std::next(loc));
567 }
568 
job_with_id(job_id_t id) const569 const job_t *parser_t::job_with_id(job_id_t id) const {
570     for (const auto &job : job_list) {
571         if (id <= 0 || job->job_id() == id) return job.get();
572     }
573     return nullptr;
574 }
575 
job_with_internal_id(internal_job_id_t id) const576 const job_t *parser_t::job_with_internal_id(internal_job_id_t id) const {
577     for (const auto &job : job_list) {
578         if (job->internal_job_id == id) return job.get();
579     }
580     return nullptr;
581 }
582 
job_get_from_pid(pid_t pid) const583 job_t *parser_t::job_get_from_pid(pid_t pid) const {
584     for (const auto &job : jobs()) {
585         for (const process_ptr_t &p : job->processes) {
586             if (p->pid == pid) {
587                 return job.get();
588             }
589         }
590     }
591     return nullptr;
592 }
593 
create_profile_item()594 profile_item_t *parser_t::create_profile_item() {
595     if (g_profiling_active) {
596         profile_items.emplace_back();
597         return &profile_items.back();
598     }
599     return nullptr;
600 }
601 
eval(const wcstring & cmd,const io_chain_t & io,const job_group_ref_t & job_group,enum block_type_t block_type)602 eval_res_t parser_t::eval(const wcstring &cmd, const io_chain_t &io,
603                           const job_group_ref_t &job_group, enum block_type_t block_type) {
604     // Parse the source into a tree, if we can.
605     parse_error_list_t error_list;
606     if (parsed_source_ref_t ps = parse_source(wcstring{cmd}, parse_flag_none, &error_list)) {
607         return this->eval(ps, io, job_group, block_type);
608     } else {
609         // Get a backtrace. This includes the message.
610         wcstring backtrace_and_desc;
611         this->get_backtrace(cmd, error_list, backtrace_and_desc);
612 
613         // Print it.
614         std::fwprintf(stderr, L"%ls\n", backtrace_and_desc.c_str());
615 
616         // Set a valid status.
617         this->set_last_statuses(statuses_t::just(STATUS_ILLEGAL_CMD));
618         bool break_expand = true;
619         return eval_res_t{proc_status_t::from_exit_code(STATUS_ILLEGAL_CMD), break_expand};
620     }
621 }
622 
eval(const parsed_source_ref_t & ps,const io_chain_t & io,const job_group_ref_t & job_group,enum block_type_t block_type)623 eval_res_t parser_t::eval(const parsed_source_ref_t &ps, const io_chain_t &io,
624                           const job_group_ref_t &job_group, enum block_type_t block_type) {
625     assert(block_type == block_type_t::top || block_type == block_type_t::subst);
626     const auto *job_list = ps->ast.top()->as<ast::job_list_t>();
627     if (!job_list->empty()) {
628         // Execute the top job list.
629         return this->eval_node(ps, *job_list, io, job_group, block_type);
630     } else {
631         auto status = proc_status_t::from_exit_code(get_last_status());
632         bool break_expand = false;
633         bool was_empty = true;
634         bool no_status = true;
635         return eval_res_t{status, break_expand, was_empty, no_status};
636     }
637 }
638 
639 template <typename T>
eval_node(const parsed_source_ref_t & ps,const T & node,const io_chain_t & block_io,const job_group_ref_t & job_group,block_type_t block_type)640 eval_res_t parser_t::eval_node(const parsed_source_ref_t &ps, const T &node,
641                                const io_chain_t &block_io, const job_group_ref_t &job_group,
642                                block_type_t block_type) {
643     static_assert(
644         std::is_same<T, ast::statement_t>::value || std::is_same<T, ast::job_list_t>::value,
645         "Unexpected node type");
646 
647     // Only certain blocks are allowed.
648     assert((block_type == block_type_t::top || block_type == block_type_t::subst) &&
649            "Invalid block type");
650 
651     // If fish itself got a cancel signal, then we want to unwind back to the principal parser.
652     // If we are the principal parser and our block stack is empty, then we want to clear the
653     // signal.
654     // Note this only happens in interactive sessions. In non-interactive sessions, SIGINT will
655     // cause fish to exit.
656     if (int sig = signal_check_cancel()) {
657         if (this == principal.get() && block_list.empty()) {
658             signal_clear_cancel();
659         } else {
660             return proc_status_t::from_signal(sig);
661         }
662     }
663 
664     // If we are provided a cancellation group, use it; otherwise create one.
665     cancellation_group_ref_t cancel_group =
666         job_group ? job_group->cancel_group : cancellation_group_t::create();
667 
668     // A helper to detect if we got a signal.
669     // This includes both signals sent to fish (user hit control-C while fish is foreground) and
670     // signals from the job group (e.g. some external job terminated with SIGQUIT).
671     auto check_cancel_signal = [=] {
672         // Did fish itself get a signal?
673         int sig = signal_check_cancel();
674         // Has this job group been cancelled?
675         if (!sig) sig = cancel_group->get_cancel_signal();
676         return sig;
677     };
678 
679     // If we have a job group which is cancelled, then do nothing.
680     if (int sig = check_cancel_signal()) {
681         return proc_status_t::from_signal(sig);
682     }
683 
684     job_reap(*this, false);  // not sure why we reap jobs here
685 
686     // Start it up
687     operation_context_t op_ctx = this->context();
688     block_t *scope_block = this->push_block(block_t::scope_block(block_type));
689 
690     // Propogate our job group.
691     op_ctx.job_group = job_group;
692 
693     // Replace the context's cancel checker with one that checks the job group's signal.
694     op_ctx.cancel_checker = [=] { return check_cancel_signal() != 0; };
695 
696     // Create and set a new execution context.
697     using exc_ctx_ref_t = std::unique_ptr<parse_execution_context_t>;
698     scoped_push<exc_ctx_ref_t> exc(&execution_context, make_unique<parse_execution_context_t>(
699                                                            ps, op_ctx, cancel_group, block_io));
700 
701     // Check the exec count so we know if anything got executed.
702     const size_t prev_exec_count = libdata().exec_count;
703     const size_t prev_status_count = libdata().status_count;
704     end_execution_reason_t reason = execution_context->eval_node(node, scope_block);
705     const size_t new_exec_count = libdata().exec_count;
706     const size_t new_status_count = libdata().status_count;
707 
708     exc.restore();
709     this->pop_block(scope_block);
710 
711     job_reap(*this, false);  // reap again
712 
713     if (int sig = check_cancel_signal()) {
714         return proc_status_t::from_signal(sig);
715     } else {
716         auto status = proc_status_t::from_exit_code(this->get_last_status());
717         bool break_expand = (reason == end_execution_reason_t::error);
718         bool was_empty = !break_expand && prev_exec_count == new_exec_count;
719         bool no_status = prev_status_count == new_status_count;
720         return eval_res_t{status, break_expand, was_empty, no_status};
721     }
722 }
723 
724 // Explicit instantiations. TODO: use overloads instead?
725 template eval_res_t parser_t::eval_node(const parsed_source_ref_t &, const ast::statement_t &,
726                                         const io_chain_t &, const job_group_ref_t &, block_type_t);
727 template eval_res_t parser_t::eval_node(const parsed_source_ref_t &, const ast::job_list_t &,
728                                         const io_chain_t &, const job_group_ref_t &, block_type_t);
729 
get_backtrace(const wcstring & src,const parse_error_list_t & errors,wcstring & output) const730 void parser_t::get_backtrace(const wcstring &src, const parse_error_list_t &errors,
731                              wcstring &output) const {
732     if (!errors.empty()) {
733         const parse_error_t &err = errors.at(0);
734 
735         // Determine if we want to try to print a caret to point at the source error. The
736         // err.source_start <= src.size() check is due to the nasty way that slices work, which is
737         // by rewriting the source.
738         size_t which_line = 0;
739         bool skip_caret = true;
740         if (err.source_start != SOURCE_LOCATION_UNKNOWN && err.source_start <= src.size()) {
741             // Determine which line we're on.
742             which_line = 1 + std::count(src.begin(), src.begin() + err.source_start, L'\n');
743 
744             // Don't include the caret if we're interactive, this is the first line of text, and our
745             // source is at its beginning, because then it's obvious.
746             skip_caret = (is_interactive() && which_line == 1 && err.source_start == 0);
747         }
748 
749         wcstring prefix;
750         const wchar_t *filename = this->current_filename();
751         if (filename) {
752             if (which_line > 0) {
753                 prefix = format_string(_(L"%ls (line %lu): "),
754                                        user_presentable_path(filename, vars()).c_str(), which_line);
755             } else {
756                 prefix =
757                     format_string(_(L"%ls: "), user_presentable_path(filename, vars()).c_str());
758             }
759         } else {
760             prefix = L"fish: ";
761         }
762 
763         const wcstring description =
764             err.describe_with_prefix(src, prefix, is_interactive(), skip_caret);
765         if (!description.empty()) {
766             output.append(description);
767             output.push_back(L'\n');
768         }
769         output.append(this->stack_trace());
770     }
771 }
772 
block_t(block_type_t t)773 block_t::block_t(block_type_t t) : block_type(t) {}
774 
775 block_t::~block_t() = default;
776 
description() const777 wcstring block_t::description() const {
778     wcstring result;
779     switch (this->type()) {
780         case block_type_t::while_block: {
781             result.append(L"while");
782             break;
783         }
784         case block_type_t::for_block: {
785             result.append(L"for");
786             break;
787         }
788         case block_type_t::if_block: {
789             result.append(L"if");
790             break;
791         }
792         case block_type_t::function_call: {
793             result.append(L"function_call");
794             break;
795         }
796         case block_type_t::function_call_no_shadow: {
797             result.append(L"function_call_no_shadow");
798             break;
799         }
800         case block_type_t::switch_block: {
801             result.append(L"switch");
802             break;
803         }
804         case block_type_t::subst: {
805             result.append(L"substitution");
806             break;
807         }
808         case block_type_t::top: {
809             result.append(L"top");
810             break;
811         }
812         case block_type_t::begin: {
813             result.append(L"begin");
814             break;
815         }
816         case block_type_t::source: {
817             result.append(L"source");
818             break;
819         }
820         case block_type_t::event: {
821             result.append(L"event");
822             break;
823         }
824         case block_type_t::breakpoint: {
825             result.append(L"breakpoint");
826             break;
827         }
828         case block_type_t::variable_assignment: {
829             result.append(L"variable_assignment");
830             break;
831         }
832     }
833 
834     if (this->src_lineno >= 0) {
835         append_format(result, L" (line %d)", this->src_lineno);
836     }
837     if (this->src_filename != nullptr) {
838         append_format(result, L" (file %ls)", this->src_filename);
839     }
840     return result;
841 }
842 
843 // Various block constructors.
844 
if_block()845 block_t block_t::if_block() { return block_t(block_type_t::if_block); }
846 
event_block(event_t evt)847 block_t block_t::event_block(event_t evt) {
848     block_t b{block_type_t::event};
849     b.event = std::move(evt);
850     return b;
851 }
852 
function_block(wcstring name,wcstring_list_t args,bool shadows)853 block_t block_t::function_block(wcstring name, wcstring_list_t args, bool shadows) {
854     block_t b{shadows ? block_type_t::function_call : block_type_t::function_call_no_shadow};
855     b.function_name = std::move(name);
856     b.function_args = std::move(args);
857     return b;
858 }
859 
source_block(const wchar_t * src)860 block_t block_t::source_block(const wchar_t *src) {
861     block_t b{block_type_t::source};
862     b.sourced_file = src;
863     return b;
864 }
865 
for_block()866 block_t block_t::for_block() { return block_t{block_type_t::for_block}; }
while_block()867 block_t block_t::while_block() { return block_t{block_type_t::while_block}; }
switch_block()868 block_t block_t::switch_block() { return block_t{block_type_t::switch_block}; }
scope_block(block_type_t type)869 block_t block_t::scope_block(block_type_t type) {
870     assert(
871         (type == block_type_t::begin || type == block_type_t::top || type == block_type_t::subst) &&
872         "Invalid scope type");
873     return block_t(type);
874 }
breakpoint_block()875 block_t block_t::breakpoint_block() { return block_t(block_type_t::breakpoint); }
variable_assignment_block()876 block_t block_t::variable_assignment_block() { return block_t(block_type_t::variable_assignment); }
877