From 4f24f3ea94e43d1021fdd8548480f130f5112b99 Mon Sep 17 00:00:00 2001 From: yui-knk Date: Sat, 1 Oct 2022 17:19:34 +0900 Subject: [PATCH] Treat "end" as reserved word with consideration of indent "end" after "." or "::" is treated as local variable or method, see `EXPR_DOT_bit` for detail. However this "changes" where `bar` method is defined. In the example below it is not module Z but class Foo. ``` module Z class Foo foo. end def bar end end ``` [Feature #19013] --- parse.y | 37 ++++++++++++++++++++++++++++++- test/ruby/test_ast.rb | 51 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/parse.y b/parse.y index a1b1e2ee46..c0959a5e81 100644 --- a/parse.y +++ b/parse.y @@ -438,6 +438,13 @@ pop_end_expect_token_localtions(struct parser_params *p) rb_ary_pop(p->end_expect_token_localtions); debug_end_expect_token_localtions(p, "pop_end_expect_token_localtions"); } + +static VALUE +peek_end_expect_token_localtions(struct parser_params *p) +{ + if(NIL_P(p->end_expect_token_localtions)) return Qnil; + return rb_ary_last(0, 0, p->end_expect_token_localtions); +} #endif RBIMPL_ATTR_NONNULL((1, 2, 3)) @@ -9285,6 +9292,7 @@ parse_ident(struct parser_params *p, int c, int cmd_state) int mb = ENC_CODERANGE_7BIT; const enum lex_state_e last_state = p->lex.state; ID ident; + int enforce_keyword_end = 0; do { if (!ISASCII(c)) mb = ENC_CODERANGE_UNKNOWN; @@ -9314,7 +9322,34 @@ parse_ident(struct parser_params *p, int c, int cmd_state) return tLABEL; } } - if (mb == ENC_CODERANGE_7BIT && !IS_lex_state(EXPR_DOT)) { + +#ifndef RIPPER + if (!NIL_P(peek_end_expect_token_localtions(p))) { + VALUE end_loc; + int lineno, column; + int beg_pos = (int)(p->lex.ptok - p->lex.pbeg); + + end_loc = peek_end_expect_token_localtions(p); + lineno = NUM2INT(rb_ary_entry(end_loc, 0)); + column = NUM2INT(rb_ary_entry(end_loc, 1)); + + if (p->debug) { + rb_parser_printf(p, "enforce_keyword_end check. current: (%d, %d), peek: (%d, %d)\n", + p->ruby_sourceline, beg_pos, lineno, column); + } + + if ((p->ruby_sourceline > lineno) && (beg_pos <= column)) { + const struct kwtable *kw; + + if ((IS_lex_state(EXPR_DOT)) && (kw = rb_reserved_word(tok(p), toklen(p))) && (kw && kw->id[0] == keyword_end)) { + if (p->debug) rb_parser_printf(p, "enforce_keyword_end is enabled\n"); + enforce_keyword_end = 1; + } + } + } +#endif + + if (mb == ENC_CODERANGE_7BIT && (!IS_lex_state(EXPR_DOT) || enforce_keyword_end)) { const struct kwtable *kw; /* See if it is a reserved word. */ diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb index 2f05bf97a1..033a09b4c0 100644 --- a/test/ruby/test_ast.rb +++ b/test/ruby/test_ast.rb @@ -956,4 +956,55 @@ dummy body: (VCALL@2:2-2:3 :a)))) EXP end + + def test_error_tolerant_treat_end_as_keyword_based_on_indent + node = RubyVM::AbstractSyntaxTree.parse(<<~STR, error_tolerant: true) + module Z + class Foo + foo. + end + + def bar + end + end + STR + + str = "" + PP.pp(node, str) + assert_equal(<<~EXP, str) + (SCOPE@1:0-8:3 + tbl: [] + args: nil + body: + (MODULE@1:0-8:3 (COLON2@1:7-1:8 nil :Z) + (SCOPE@1:0-8:3 + tbl: [] + args: nil + body: + (BLOCK@1:8-8:3 (BEGIN@1:8-1:8 nil) + (CLASS@2:2-8:3 (COLON2@2:8-2:11 nil :Foo) nil + (SCOPE@2:2-8:3 + tbl: [] + args: nil + body: + (DEFN@6:2-7:5 + mid: :bar + body: + (SCOPE@6:2-7:5 + tbl: [] + args: + (ARGS@6:9-6:9 + pre_num: 0 + pre_init: nil + opt: nil + first_post: nil + post_num: 0 + post_init: nil + rest: nil + kw: nil + kwrest: nil + block: nil) + body: nil)))))))) + EXP + end end