summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoryui-knk <spiketeika@gmail.com>2022-10-01 17:19:34 +0900
committerYuichiro Kaneko <spiketeika@gmail.com>2022-10-08 17:59:11 +0900
commit4f24f3ea94e43d1021fdd8548480f130f5112b99 (patch)
tree8cfe7d2e7dd0dcdabc31aa7a781858b9364a1291
parent342d4c16d963408905fd08118d1908fe197f2364 (diff)
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]
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/6512
-rw-r--r--parse.y37
-rw-r--r--test/ruby/test_ast.rb51
2 files changed, 87 insertions, 1 deletions
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