зеркало из https://github.com/github/ruby.git
Родитель
d42d19059d
Коммит
9afc6a981d
|
@ -31,6 +31,7 @@ ID rb_id_option_encoding;
|
||||||
ID rb_id_option_filepath;
|
ID rb_id_option_filepath;
|
||||||
ID rb_id_option_frozen_string_literal;
|
ID rb_id_option_frozen_string_literal;
|
||||||
ID rb_id_option_line;
|
ID rb_id_option_line;
|
||||||
|
ID rb_id_option_main_script;
|
||||||
ID rb_id_option_scopes;
|
ID rb_id_option_scopes;
|
||||||
ID rb_id_option_version;
|
ID rb_id_option_version;
|
||||||
ID rb_id_source_for;
|
ID rb_id_source_for;
|
||||||
|
@ -179,6 +180,8 @@ build_options_i(VALUE key, VALUE value, VALUE argument) {
|
||||||
|
|
||||||
pm_options_command_line_set(options, command_line);
|
pm_options_command_line_set(options, command_line);
|
||||||
}
|
}
|
||||||
|
} else if (key_id == rb_id_option_main_script) {
|
||||||
|
if (!NIL_P(value)) pm_options_main_script_set(options, RTEST(value));
|
||||||
} else {
|
} else {
|
||||||
rb_raise(rb_eArgError, "unknown keyword: %" PRIsVALUE, key);
|
rb_raise(rb_eArgError, "unknown keyword: %" PRIsVALUE, key);
|
||||||
}
|
}
|
||||||
|
@ -1165,6 +1168,7 @@ Init_prism(void) {
|
||||||
rb_id_option_filepath = rb_intern_const("filepath");
|
rb_id_option_filepath = rb_intern_const("filepath");
|
||||||
rb_id_option_frozen_string_literal = rb_intern_const("frozen_string_literal");
|
rb_id_option_frozen_string_literal = rb_intern_const("frozen_string_literal");
|
||||||
rb_id_option_line = rb_intern_const("line");
|
rb_id_option_line = rb_intern_const("line");
|
||||||
|
rb_id_option_main_script = rb_intern_const("main_script");
|
||||||
rb_id_option_scopes = rb_intern_const("scopes");
|
rb_id_option_scopes = rb_intern_const("scopes");
|
||||||
rb_id_option_version = rb_intern_const("version");
|
rb_id_option_version = rb_intern_const("version");
|
||||||
rb_id_source_for = rb_intern("for");
|
rb_id_source_for = rb_intern("for");
|
||||||
|
|
|
@ -100,6 +100,14 @@ pm_options_version_set(pm_options_t *options, const char *version, size_t length
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the main script option on the given options struct.
|
||||||
|
*/
|
||||||
|
PRISM_EXPORTED_FUNCTION void
|
||||||
|
pm_options_main_script_set(pm_options_t *options, bool main_script) {
|
||||||
|
options->main_script = main_script;
|
||||||
|
}
|
||||||
|
|
||||||
// For some reason, GCC analyzer thinks we're leaking allocated scopes and
|
// For some reason, GCC analyzer thinks we're leaking allocated scopes and
|
||||||
// locals here, even though we definitely aren't. This is a false positive.
|
// locals here, even though we definitely aren't. This is a false positive.
|
||||||
// Ideally we wouldn't need to suppress this.
|
// Ideally we wouldn't need to suppress this.
|
||||||
|
|
|
@ -139,6 +139,13 @@ typedef struct pm_options {
|
||||||
* but ignore any encoding magic comments at the top of the file.
|
* but ignore any encoding magic comments at the top of the file.
|
||||||
*/
|
*/
|
||||||
bool encoding_locked;
|
bool encoding_locked;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the file being parsed is the main script, the shebang will be
|
||||||
|
* considered for command-line flags (or for implicit -x). The caller needs
|
||||||
|
* to pass this information to the parser so that it can behave correctly.
|
||||||
|
*/
|
||||||
|
bool main_script;
|
||||||
} pm_options_t;
|
} pm_options_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -248,6 +255,14 @@ PRISM_EXPORTED_FUNCTION void pm_options_command_line_set(pm_options_t *options,
|
||||||
*/
|
*/
|
||||||
PRISM_EXPORTED_FUNCTION bool pm_options_version_set(pm_options_t *options, const char *version, size_t length);
|
PRISM_EXPORTED_FUNCTION bool pm_options_version_set(pm_options_t *options, const char *version, size_t length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the main script option on the given options struct.
|
||||||
|
*
|
||||||
|
* @param options The options struct to set the main script value on.
|
||||||
|
* @param main_script The main script value to set.
|
||||||
|
*/
|
||||||
|
PRISM_EXPORTED_FUNCTION void pm_options_main_script_set(pm_options_t *options, bool main_script);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocate and zero out the scopes array on the given options struct.
|
* Allocate and zero out the scopes array on the given options struct.
|
||||||
*
|
*
|
||||||
|
|
|
@ -21973,25 +21973,42 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm
|
||||||
// "ruby" and start parsing from there.
|
// "ruby" and start parsing from there.
|
||||||
bool search_shebang = PM_PARSER_COMMAND_LINE_OPTION_X(parser);
|
bool search_shebang = PM_PARSER_COMMAND_LINE_OPTION_X(parser);
|
||||||
|
|
||||||
// If the first two bytes of the source are a shebang, then we'll indicate
|
// If the first two bytes of the source are a shebang, then we will do a bit
|
||||||
// that the encoding comment is at the end of the shebang.
|
// of extra processing.
|
||||||
|
//
|
||||||
|
// First, we'll indicate that the encoding comment is at the end of the
|
||||||
|
// shebang. This means that when a shebang is present the encoding comment
|
||||||
|
// can begin on the second line.
|
||||||
|
//
|
||||||
|
// Second, we will check if the shebang includes "ruby". If it does, then we
|
||||||
|
// we will start parsing from there. We will also potentially warning the
|
||||||
|
// user if there is a carriage return at the end of the shebang. We will
|
||||||
|
// also potentially call the shebang callback if this is the main script to
|
||||||
|
// allow the caller to parse the shebang and find any command-line options.
|
||||||
|
// If the shebang does not include "ruby" and this is the main script being
|
||||||
|
// parsed, then we will start searching the file for a shebang that does
|
||||||
|
// contain "ruby" as if -x were passed on the command line.
|
||||||
const uint8_t *newline = next_newline(parser->start, parser->end - parser->start);
|
const uint8_t *newline = next_newline(parser->start, parser->end - parser->start);
|
||||||
size_t length = (size_t) ((newline != NULL ? newline : parser->end) - parser->start);
|
size_t length = (size_t) ((newline != NULL ? newline : parser->end) - parser->start);
|
||||||
|
|
||||||
if (length > 2 && parser->current.end[0] == '#' && parser->current.end[1] == '!') {
|
if (length > 2 && parser->current.end[0] == '#' && parser->current.end[1] == '!') {
|
||||||
const char *engine;
|
const char *engine;
|
||||||
|
|
||||||
if ((engine = pm_strnstr((const char *) parser->start, "ruby", length)) != NULL) {
|
if ((engine = pm_strnstr((const char *) parser->start, "ruby", length)) != NULL) {
|
||||||
if (newline != NULL) {
|
if (newline != NULL) {
|
||||||
pm_parser_warn_shebang_carriage_return(parser, parser->start, length + 1);
|
|
||||||
parser->encoding_comment_start = newline + 1;
|
parser->encoding_comment_start = newline + 1;
|
||||||
|
|
||||||
|
if (options == NULL || options->main_script) {
|
||||||
|
pm_parser_warn_shebang_carriage_return(parser, parser->start, length + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options != NULL && options->shebang_callback != NULL) {
|
if (options != NULL && options->main_script && options->shebang_callback != NULL) {
|
||||||
pm_parser_init_shebang(parser, options, engine, length - ((size_t) (engine - (const char *) parser->start)));
|
pm_parser_init_shebang(parser, options, engine, length - ((size_t) (engine - (const char *) parser->start)));
|
||||||
}
|
}
|
||||||
|
|
||||||
search_shebang = false;
|
search_shebang = false;
|
||||||
} else if (!parser->parsing_eval) {
|
} else if (options->main_script && !parser->parsing_eval) {
|
||||||
search_shebang = true;
|
search_shebang = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
2
ruby.c
2
ruby.c
|
@ -2155,6 +2155,8 @@ prism_script(ruby_cmdline_options_t *opt, pm_parse_result_t *result)
|
||||||
|
|
||||||
pm_options_t *options = &result->options;
|
pm_options_t *options = &result->options;
|
||||||
pm_options_line_set(options, 1);
|
pm_options_line_set(options, 1);
|
||||||
|
pm_options_main_script_set(options, true);
|
||||||
|
|
||||||
const bool read_stdin = (strcmp(opt->script, "-") == 0);
|
const bool read_stdin = (strcmp(opt->script, "-") == 0);
|
||||||
|
|
||||||
if (read_stdin) {
|
if (read_stdin) {
|
||||||
|
|
|
@ -67,7 +67,7 @@ module Prism
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_command_line_x_implicit
|
def test_command_line_x_implicit
|
||||||
result = Prism.parse_statement(<<~RUBY)
|
result = Prism.parse_statement(<<~RUBY, main_script: true)
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ module Prism
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_command_line_x_implicit_fail
|
def test_command_line_x_implicit_fail
|
||||||
result = Prism.parse(<<~RUBY)
|
result = Prism.parse(<<~RUBY, main_script: true)
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
exit 1
|
exit 1
|
||||||
RUBY
|
RUBY
|
||||||
|
|
|
@ -336,23 +336,24 @@ module Prism
|
||||||
def test_shebang_ending_with_carriage_return
|
def test_shebang_ending_with_carriage_return
|
||||||
msg = "shebang line ending with \\r may cause problems"
|
msg = "shebang line ending with \\r may cause problems"
|
||||||
|
|
||||||
assert_warning(<<~RUBY, msg, compare: false)
|
assert_warning(<<~RUBY, msg, compare: false, main_script: true)
|
||||||
#!ruby\r
|
#!ruby\r
|
||||||
p(123)
|
p(123)
|
||||||
RUBY
|
RUBY
|
||||||
|
|
||||||
assert_warning(<<~RUBY, msg, compare: false)
|
assert_warning(<<~RUBY, msg, compare: false, main_script: true)
|
||||||
#!ruby \r
|
#!ruby \r
|
||||||
p(123)
|
p(123)
|
||||||
RUBY
|
RUBY
|
||||||
|
|
||||||
assert_warning(<<~RUBY, msg, compare: false)
|
assert_warning(<<~RUBY, msg, compare: false, main_script: true)
|
||||||
#!ruby -Eutf-8\r
|
#!ruby -Eutf-8\r
|
||||||
p(123)
|
p(123)
|
||||||
RUBY
|
RUBY
|
||||||
|
|
||||||
# Used with the `-x` object, to ignore the script up until the first shebang that mentioned "ruby".
|
# Used with the `-x` object, to ignore the script up until the first
|
||||||
assert_warning(<<~SCRIPT, msg, compare: false)
|
# shebang that mentioned "ruby".
|
||||||
|
assert_warning(<<~SCRIPT, msg, compare: false, main_script: true)
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Some initial shell script or other content
|
# Some initial shell script or other content
|
||||||
# that Ruby should ignore
|
# that Ruby should ignore
|
||||||
|
@ -364,11 +365,11 @@ module Prism
|
||||||
puts "Hello from Ruby!"
|
puts "Hello from Ruby!"
|
||||||
SCRIPT
|
SCRIPT
|
||||||
|
|
||||||
refute_warning("#ruby not_a_shebang\r\n", compare: false)
|
refute_warning("#ruby not_a_shebang\r\n", compare: false, main_script: true)
|
||||||
|
|
||||||
# CRuby doesn't emit the warning if a malformed file only has `\r` and not `\n`.
|
# CRuby doesn't emit the warning if a malformed file only has `\r` and
|
||||||
# https://bugs.ruby-lang.org/issues/20700
|
# not `\n`. https://bugs.ruby-lang.org/issues/20700.
|
||||||
refute_warning("#!ruby\r", compare: false)
|
refute_warning("#!ruby\r", compare: false, main_script: true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -384,8 +385,8 @@ module Prism
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def assert_warning(source, *messages, compare: true)
|
def assert_warning(source, *messages, compare: true, **options)
|
||||||
warnings = Prism.parse(source).warnings
|
warnings = Prism.parse(source, **options).warnings
|
||||||
assert_equal messages.length, warnings.length, "Expected #{messages.length} warning(s) in #{source.inspect}, got #{warnings.map(&:message).inspect}"
|
assert_equal messages.length, warnings.length, "Expected #{messages.length} warning(s) in #{source.inspect}, got #{warnings.map(&:message).inspect}"
|
||||||
|
|
||||||
warnings.zip(messages).each do |warning, message|
|
warnings.zip(messages).each do |warning, message|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче