summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakashi Kokubun <takashikkbn@gmail.com>2026-03-16 11:55:53 -0700
committerTakashi Kokubun <takashikkbn@gmail.com>2026-03-16 11:55:53 -0700
commit1d3581e18a689f0d392686283e340058ac468ffd (patch)
tree25f8f785a329cec271543dccef31dfbf8ced3e72
parent0d845e4a071f67de40e1151cafb419c7aba87050 (diff)
merge revision(s) 55694ad7efc3f8dc6d5c7aefa60ded4c303ed6cf: [Backport #21945]
[Bug #21945] Correctly handle `and?` and similar
-rw-r--r--parse.y3
-rw-r--r--test/ripper/test_lexer.rb52
2 files changed, 55 insertions, 0 deletions
diff --git a/parse.y b/parse.y
index 067f45e6d6..9303ed8c32 100644
--- a/parse.y
+++ b/parse.y
@@ -7010,6 +7010,9 @@ peek_word_at(struct parser_params *p, const char *str, size_t len, int at)
if (lex_eol_ptr_n_p(p, ptr, len-1)) return false;
if (memcmp(ptr, str, len)) return false;
if (lex_eol_ptr_n_p(p, ptr, len)) return true;
+ switch (ptr[len]) {
+ case '!': case '?': return false;
+ }
return !is_identchar(p, ptr+len, p->lex.pend, p->enc);
}
diff --git a/test/ripper/test_lexer.rb b/test/ripper/test_lexer.rb
index 7a2c22ff2d..4bc6fd7ced 100644
--- a/test/ripper/test_lexer.rb
+++ b/test/ripper/test_lexer.rb
@@ -586,6 +586,58 @@ world"
assert_lexer(expected, code)
end
+ def test_fluent_and
+ code = "foo\n" "and"
+ expected = [
+ [[1, 0], :on_ident, "foo", state(:EXPR_CMDARG)],
+ [[1, 3], :on_ignored_nl, "\n", state(:EXPR_CMDARG)],
+ [[2, 0], :on_kw, "and", state(:EXPR_BEG)],
+ ]
+ assert_lexer(expected, code)
+
+ code = "foo\n" "and?"
+ expected = [
+ [[1, 0], :on_ident, "foo", state(:EXPR_CMDARG)],
+ [[1, 3], :on_nl, "\n", state(:EXPR_BEG)],
+ [[2, 0], :on_ident, "and?", state(:EXPR_CMDARG)],
+ ]
+ assert_lexer(expected, code)
+
+ code = "foo\n" "and!"
+ expected = [
+ [[1, 0], :on_ident, "foo", state(:EXPR_CMDARG)],
+ [[1, 3], :on_nl, "\n", state(:EXPR_BEG)],
+ [[2, 0], :on_ident, "and!", state(:EXPR_CMDARG)],
+ ]
+ assert_lexer(expected, code)
+ end
+
+ def test_fluent_or
+ code = "foo\n" "or"
+ expected = [
+ [[1, 0], :on_ident, "foo", state(:EXPR_CMDARG)],
+ [[1, 3], :on_ignored_nl, "\n", state(:EXPR_CMDARG)],
+ [[2, 0], :on_kw, "or", state(:EXPR_BEG)],
+ ]
+ assert_lexer(expected, code)
+
+ code = "foo\n" "or?"
+ expected = [
+ [[1, 0], :on_ident, "foo", state(:EXPR_CMDARG)],
+ [[1, 3], :on_nl, "\n", state(:EXPR_BEG)],
+ [[2, 0], :on_ident, "or?", state(:EXPR_CMDARG)],
+ ]
+ assert_lexer(expected, code)
+
+ code = "foo\n" "or!"
+ expected = [
+ [[1, 0], :on_ident, "foo", state(:EXPR_CMDARG)],
+ [[1, 3], :on_nl, "\n", state(:EXPR_BEG)],
+ [[2, 0], :on_ident, "or!", state(:EXPR_CMDARG)],
+ ]
+ assert_lexer(expected, code)
+ end
+
def assert_lexer(expected, code)
assert_equal(code, Ripper.tokenize(code).join(""))
assert_equal(expected, result = Ripper.lex(code),