aboutsummaryrefslogtreecommitdiffstats
path: root/include/ruby/ruby.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/ruby/ruby.h')
-rw-r--r--include/ruby/ruby.h106
1 files changed, 86 insertions, 20 deletions
diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h
index 71a1d2e83d..6f7708009a 100644
--- a/include/ruby/ruby.h
+++ b/include/ruby/ruby.h
@@ -1899,6 +1899,11 @@ VALUE rb_funcall_passing_block(VALUE, ID, int, const VALUE*);
VALUE rb_funcall_with_block(VALUE, ID, int, const VALUE*, VALUE);
VALUE rb_funcall_with_block_kw(VALUE, ID, int, const VALUE*, VALUE, int);
int rb_scan_args(int, const VALUE*, const char*, ...);
+#define RB_SCAN_ARGS_PASS_CALLED_KEYWORDS 0
+#define RB_SCAN_ARGS_KEYWORDS 1
+#define RB_SCAN_ARGS_EMPTY_KEYWORDS 2
+#define RB_SCAN_ARGS_LAST_HASH_KEYWORDS 3
+int rb_scan_args_kw(int, int, const VALUE*, const char*, ...);
VALUE rb_call_super(int, const VALUE*);
VALUE rb_call_super_kw(int, const VALUE*, int);
VALUE rb_current_receiver(void);
@@ -2324,6 +2329,9 @@ unsigned long ruby_strtoul(const char *str, char **endptr, int base);
PRINTF_ARGS(int ruby_snprintf(char *str, size_t n, char const *fmt, ...), 3, 4);
int ruby_vsnprintf(char *str, size_t n, char const *fmt, va_list ap);
+/* -- Remove In 3.0, Only public for rb_scan_args optimized version -- */
+int rb_empty_keyword_given_p(void);
+
#if defined(HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P) && defined(HAVE_VA_ARGS_MACRO) && defined(__OPTIMIZE__)
# define rb_scan_args(argc,argvp,fmt,...) \
__builtin_choose_expr(__builtin_constant_p(fmt), \
@@ -2519,30 +2527,81 @@ rb_scan_args_set(int argc, const VALUE *argv,
int i, argi = 0, vari = 0, last_idx = -1;
VALUE *var, hash = Qnil, last_hash = 0;
const int n_mand = n_lead + n_trail;
+ int keyword_given = rb_keyword_given_p();
+ int empty_keyword_given = 0;
+ VALUE tmp_buffer = 0;
+
+ if (!keyword_given) {
+ empty_keyword_given = rb_empty_keyword_given_p();
+ }
/* capture an option hash - phase 1: pop */
- if (f_hash && n_mand < argc) {
- VALUE last = argv[argc - 1];
-
- if (RB_NIL_P(last)) {
- /* nil is taken as an empty option hash only if it is not
- ambiguous; i.e. '*' is not specified and arguments are
- given more than sufficient */
- if (!f_var && n_mand + n_opt < argc)
- argc--;
- }
- else {
- hash = rb_check_hash_type(last);
- if (!RB_NIL_P(hash)) {
- VALUE opts = rb_extract_keywords(&hash);
- if (!(last_hash = hash)) argc--;
- else last_idx = argc - 1;
- hash = opts ? opts : Qnil;
- }
- }
+ /* Ignore final positional hash if empty keywords given */
+ if (argc > 0 && !(f_hash && empty_keyword_given)) {
+ VALUE last = argv[argc - 1];
+
+ if (f_hash && n_mand < argc) {
+ if (keyword_given) {
+ if (!RB_TYPE_P(last, T_HASH)) {
+ rb_warn("Keyword flag set when calling rb_scan_args, but last entry is not a hash");
+ }
+ else {
+ hash = last;
+ }
+ }
+ else if (NIL_P(last)) {
+ /* For backwards compatibility, nil is taken as an empty
+ option hash only if it is not ambiguous; i.e. '*' is
+ not specified and arguments are given more than sufficient.
+ This will be removed in Ruby 3. */
+ if (!f_var && n_mand + n_opt < argc) {
+ rb_warn("The last argument is nil, treating as empty keywords");
+ argc--;
+ }
+ }
+ else {
+ hash = rb_check_hash_type(last);
+ }
+
+ /* Ruby 3: Remove if branch, as it will not attempt to split hashes */
+ if (!NIL_P(hash)) {
+ VALUE opts = rb_extract_keywords(&hash);
+
+ if (!(last_hash = hash)) {
+ if (!keyword_given) {
+ /* Warn if treating positional as keyword, as in Ruby 3,
+ this will be an error */
+ rb_warn("The last argument is used as the keyword parameter");
+ }
+ argc--;
+ }
+ else {
+ /* Warn if splitting either positional hash to keywords or keywords
+ to positional hash, as in Ruby 3, no splitting will be done */
+ rb_warn("The last argument is split into positional and keyword parameters");
+ last_idx = argc - 1;
+ }
+ hash = opts ? opts : Qnil;
+ }
+ }
+ else if (f_hash && keyword_given && n_mand == argc) {
+ /* Warn if treating keywords as positional, as in Ruby 3, this will be an error */
+ rb_warn("The keyword argument is passed as the last hash parameter");
+ }
+ }
+ if (f_hash && n_mand == argc+1 && empty_keyword_given) {
+ VALUE *ptr = (VALUE *)rb_alloc_tmp_buffer2(&tmp_buffer, argc+1, sizeof(VALUE));
+ memcpy(ptr, argv, sizeof(VALUE)*argc);
+ ptr[argc] = rb_hash_new();
+ argc++;
+ *(&argv) = ptr;
+ rb_warn("The keyword argument is passed as the last hash parameter");
}
- rb_check_arity(argc, n_mand, f_var ? UNLIMITED_ARGUMENTS : n_mand + n_opt);
+
+ if (argc < n_mand) {
+ goto argc_error;
+ }
/* capture leading mandatory arguments */
for (i = n_lead; i-- > 0; ) {
@@ -2600,6 +2659,13 @@ rb_scan_args_set(int argc, const VALUE *argv,
}
}
+ if (argi < argc) {
+ argc_error:
+ if(tmp_buffer) rb_free_tmp_buffer(&tmp_buffer);
+ rb_error_arity(argc, n_mand, f_var ? UNLIMITED_ARGUMENTS : n_mand + n_opt);
+ }
+
+ if(tmp_buffer) rb_free_tmp_buffer(&tmp_buffer);
return argc;
}
#endif