void parser_t::get_backtrace(const wcstring &src, const parse_error_list_t &errors, wcstring *output) const { assert(output != NULL); if (! errors.empty()) { const parse_error_t &err = errors.at(0); const bool is_interactive = get_is_interactive(); // Determine if we want to try to print a caret to point at the source error // The err.source_start <= src.size() check is due to the nasty way that slices work, // which is by rewriting the source (!) size_t which_line = 0; bool skip_caret = true; if (err.source_start != SOURCE_LOCATION_UNKNOWN && err.source_start <= src.size()) { // Determine which line we're on which_line = 1 + std::count(src.begin(), src.begin() + err.source_start, L'\n'); // Don't include the caret if we're interactive, this is the first line of text, and our source is at its beginning, because then it's obvious skip_caret = (is_interactive && which_line == 1 && err.source_start == 0); } wcstring prefix; const wchar_t *filename = this->current_filename(); if (filename) { if (which_line > 0) { prefix = format_string(_(L"%ls (line %lu): "), user_presentable_path(filename).c_str(), which_line); } else { prefix = format_string(_(L"%ls: "), user_presentable_path(filename).c_str()); } } else { prefix = L"fish: "; } const wcstring description = err.describe_with_prefix(src, prefix, is_interactive, skip_caret); if (! description.empty()) { output->append(description); output->push_back(L'\n'); } output->append(this->stack_trace()); } }
wcstring parser_t::current_line() { if (execution_contexts.empty()) { return wcstring(); } const parse_execution_context_t *context = execution_contexts.back(); assert(context != NULL); int source_offset = context->get_current_source_offset(); if (source_offset < 0) { return wcstring(); } const int lineno = this->get_lineno(); const wchar_t *file = this->current_filename(); wcstring prefix; /* If we are not going to print a stack trace, at least print the line number and filename */ if (!get_is_interactive() || is_function()) { if (file) { append_format(prefix, _(L"%ls (line %d): "), user_presentable_path(file).c_str(), lineno); } else if (is_within_fish_initialization) { append_format(prefix, L"%ls: ", _(L"Startup"), lineno); } else { append_format(prefix, L"%ls: ", _(L"Standard input"), lineno); } } bool is_interactive = get_is_interactive(); bool skip_caret = is_interactive && ! is_function(); /* Use an error with empty text */ assert(source_offset >= 0); parse_error_t empty_error = {}; empty_error.source_start = source_offset; wcstring line_info = empty_error.describe_with_prefix(context->get_source(), prefix, is_interactive, skip_caret); if (! line_info.empty()) { line_info.push_back(L'\n'); } line_info.append(this->stack_trace()); return line_info; }
void parser_t::stack_trace_internal(size_t block_idx, wcstring *buff) const { // Check if we should end the recursion. if (block_idx >= this->block_count()) return; const block_t *b = this->block_at_index(block_idx); if (b->type() == EVENT) { // This is an event handler. const event_block_t *eb = static_cast<const event_block_t *>(b); wcstring description = event_get_desc(eb->event); append_format(*buff, _(L"in event handler: %ls\n"), description.c_str()); buff->append(L"\n"); // Stop recursing at event handler. No reason to believe that any other code is relevant. // // It might make sense in the future to continue printing the stack trace of the code that // invoked the event, if this is a programmatic event, but we can't currently detect that. return; } if (b->type() == FUNCTION_CALL || b->type() == FUNCTION_CALL_NO_SHADOW || b->type() == SOURCE || b->type() == SUBST) { // These types of blocks should be printed. int i; switch (b->type()) { case SOURCE: { const source_block_t *sb = static_cast<const source_block_t *>(b); const wchar_t *source_dest = sb->source_file; append_format(*buff, _(L"from sourcing file %ls\n"), user_presentable_path(source_dest).c_str()); break; } case FUNCTION_CALL: case FUNCTION_CALL_NO_SHADOW: { const function_block_t *fb = static_cast<const function_block_t *>(b); append_format(*buff, _(L"in function '%ls'\n"), fb->name.c_str()); break; } case SUBST: { append_format(*buff, _(L"in command substitution\n")); break; } default: { break; // can't get here } } const wchar_t *file = b->src_filename; if (file) { append_format(*buff, _(L"\tcalled on line %d of file %ls\n"), b->src_lineno, user_presentable_path(file).c_str()); } else if (is_within_fish_initialization) { append_format(*buff, _(L"\tcalled during startup\n")); } else { append_format(*buff, _(L"\tcalled on standard input\n")); } if (b->type() == FUNCTION_CALL) { const function_block_t *fb = static_cast<const function_block_t *>(b); const process_t *const process = fb->process; if (process->argv(1)) { wcstring tmp; for (i = 1; process->argv(i); i++) { if (i > 1) tmp.push_back(L' '); tmp.append(process->argv(i)); } append_format(*buff, _(L"\twith parameter list '%ls'\n"), tmp.c_str()); } } append_format(*buff, L"\n"); } // Recursively print the next block. parser_t::stack_trace_internal(block_idx + 1, buff); }