Esempio n. 1
0
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());
    }
}
Esempio n. 2
0
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;
}
Esempio n. 3
0
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);
}