1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

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]
This commit is contained in:
yui-knk 2022-10-01 17:19:34 +09:00 committed by Yuichiro Kaneko
parent 342d4c16d9
commit 4f24f3ea94
Notes: git 2022-10-08 17:59:34 +09:00
2 changed files with 87 additions and 1 deletions

37
parse.y
View file

@ -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. */

View file

@ -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