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
35 changes: 19 additions & 16 deletions src/prism.c
Original file line number Diff line number Diff line change
Expand Up @@ -6766,6 +6766,21 @@ pm_symbol_node_create_current_string(pm_parser_t *parser, const pm_token_t *open
return node;
}

/**
* Allocate and initialize a new SymbolNode node from a bare name. This covers
* every symbol whose contents are shared straight from the source: method
* names, keywords, constants, identifiers, instance/class/global variables, and
* numbered or back references. None of them hold escapes, so the encoding is
* determined entirely by those contents.
*/
static pm_symbol_node_t *
pm_symbol_node_bare_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *value) {
pm_string_t unescaped;
pm_string_shared_init(&unescaped, value->start, value->end);

return pm_symbol_node_create_unescaped(parser, opening, value, NULL, &unescaped, parse_symbol_encoding(parser, NULL, value, &unescaped, false));
}

/**
* Allocate and initialize a new SymbolNode node from a label.
*/
Expand All @@ -6779,7 +6794,7 @@ pm_symbol_node_label_create(pm_parser_t *parser, const pm_token_t *token) {

assert((label.end - label.start) >= 0);
pm_string_shared_init(&node->unescaped, label.start, label.end);
pm_node_flag_set(UP(node), parse_symbol_encoding(parser, parser->explicit_encoding, &label, &node->unescaped, false));
pm_node_flag_set(UP(node), parse_symbol_encoding(parser, NULL, &label, &node->unescaped, false));

return node;
}
Expand Down Expand Up @@ -16240,11 +16255,7 @@ parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_s
break;
}

pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, &parser->previous, NULL);
pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end);
pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, parser->explicit_encoding, &parser->previous, &symbol->unescaped, false));

return UP(symbol);
return UP(pm_symbol_node_bare_create(parser, &opening, &parser->previous));
}

if (lex_mode->as.string.interpolation) {
Expand Down Expand Up @@ -16361,11 +16372,7 @@ parse_undef_argument(pm_parser_t *parser, uint16_t depth) {
case PM_TOKEN_METHOD_NAME: {
parser_lex(parser);

pm_symbol_node_t *symbol = pm_symbol_node_create(parser, NULL, &parser->previous, NULL);
pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end);
pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, parser->explicit_encoding, &parser->previous, &symbol->unescaped, false));

return UP(symbol);
return UP(pm_symbol_node_bare_create(parser, NULL, &parser->previous));
}
case PM_TOKEN_SYMBOL_BEGIN: {
pm_lex_mode_t lex_mode = *parser->lex_modes.current;
Expand Down Expand Up @@ -16397,11 +16404,7 @@ parse_alias_argument(pm_parser_t *parser, bool first, uint16_t depth) {
if (first) lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM);
parser_lex(parser);

pm_symbol_node_t *symbol = pm_symbol_node_create(parser, NULL, &parser->previous, NULL);
pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end);
pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, parser->explicit_encoding, &parser->previous, &symbol->unescaped, false));

return UP(symbol);
return UP(pm_symbol_node_bare_create(parser, NULL, &parser->previous));
}
case PM_TOKEN_SYMBOL_BEGIN: {
pm_lex_mode_t lex_mode = *parser->lex_modes.current;
Expand Down
21 changes: 21 additions & 0 deletions test/prism/encoding/string_encoding_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,27 @@ def test_encodings_are_not_leaked_across_hash_elements
end
end

def test_encodings_are_not_leaked_into_undef_and_alias
{
["ascii-8bit", "undef :\"\\u00E9\", foo"] => [Encoding::UTF_8, Encoding::US_ASCII],
["ascii-8bit", "undef foo, :\"\\u00E9\""] => [Encoding::US_ASCII, Encoding::UTF_8],
["ascii-8bit", "alias :\"\\u00E9\" foo"] => [Encoding::UTF_8, Encoding::US_ASCII],
["ascii-8bit", "alias foo :\"\\u00E9\""] => [Encoding::US_ASCII, Encoding::UTF_8],
# A US-ASCII source is the only one that forces a symbol to binary, so
# it is the only place the binary branch can leak into the bare name.
["us-ascii", "undef :\"\\xE9\", foo"] => [Encoding::ASCII_8BIT, Encoding::US_ASCII],
["us-ascii", "alias :\"\\xE9\" foo"] => [Encoding::ASCII_8BIT, Encoding::US_ASCII]
}.each do |(encoding, expression), expected|
result = Prism.parse("# encoding: #{encoding}\n#{expression}")
assert_predicate result, :success?, "failed to parse: #{expression}"

node = result.statement
names = node.is_a?(UndefNode) ? node.names : [node.new_name, node.old_name]

assert_equal expected, names.map { |name| binary_node_encoding(name) }, "#{encoding} #{expression}"
end
end

private

def binary_node_encoding(node)
Expand Down
Loading