summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEarlopain <14981592+Earlopain@users.noreply.github.com>2026-03-05 20:41:21 +0100
committerTakashi Kokubun <takashikkbn@gmail.com>2026-03-06 09:02:23 -0800
commit83c261f30bd43dc3465c0026b96e79af585fdfab (patch)
tree57b90751cb6f18eda08c99efdb00eedadd5757a6
parent33e5d3894fcddeb16518fdd0512fda097e7039fe (diff)
[ruby/prism] Correctly handle `and?` and similar on ruby 4.0
It gets confused for syntax introduced in https://bugs.ruby-lang.org/issues/20925 But it actually should be a plain method call. `!`/`?` are not valid as part of an identifier, methods however allow them as the last character. Fixes [Bug #21946] https://github.com/ruby/prism/commit/5d80bc5e1a
-rw-r--r--prism/prism.c21
-rw-r--r--test/prism/fixtures/4.0/leading_logical.txt5
-rw-r--r--test/prism/fixtures/and_or_with_suffix.txt17
-rw-r--r--test/prism/lex_test.rb7
-rw-r--r--test/prism/ruby/ripper_test.rb7
5 files changed, 50 insertions, 7 deletions
diff --git a/prism/prism.c b/prism/prism.c
index b36a6da204..1d4bd76a67 100644
--- a/prism/prism.c
+++ b/prism/prism.c
@@ -9979,8 +9979,21 @@ parser_lex(pm_parser_t *parser) {
following && (
(peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '&') ||
(peek_at(parser, following) == '|' && peek_at(parser, following + 1) == '|') ||
- (peek_at(parser, following) == 'a' && peek_at(parser, following + 1) == 'n' && peek_at(parser, following + 2) == 'd' && !char_is_identifier(parser, following + 3, parser->end - (following + 3))) ||
- (peek_at(parser, following) == 'o' && peek_at(parser, following + 1) == 'r' && !char_is_identifier(parser, following + 2, parser->end - (following + 2)))
+ (
+ peek_at(parser, following) == 'a' &&
+ peek_at(parser, following + 1) == 'n' &&
+ peek_at(parser, following + 2) == 'd' &&
+ peek_at(parser, next_content + 3) != '!' &&
+ peek_at(parser, next_content + 3) != '?' &&
+ !char_is_identifier(parser, following + 3, parser->end - (following + 3))
+ ) ||
+ (
+ peek_at(parser, following) == 'o' &&
+ peek_at(parser, following + 1) == 'r' &&
+ peek_at(parser, next_content + 2) != '!' &&
+ peek_at(parser, next_content + 2) != '?' &&
+ !char_is_identifier(parser, following + 2, parser->end - (following + 2))
+ )
)
) {
if (!lexed_comment) parser_lex_ignored_newline(parser);
@@ -10051,6 +10064,8 @@ parser_lex(pm_parser_t *parser) {
peek_at(parser, next_content) == 'a' &&
peek_at(parser, next_content + 1) == 'n' &&
peek_at(parser, next_content + 2) == 'd' &&
+ peek_at(parser, next_content + 3) != '!' &&
+ peek_at(parser, next_content + 3) != '?' &&
!char_is_identifier(parser, next_content + 3, parser->end - (next_content + 3))
) {
if (!lexed_comment) parser_lex_ignored_newline(parser);
@@ -10067,6 +10082,8 @@ parser_lex(pm_parser_t *parser) {
if (
peek_at(parser, next_content) == 'o' &&
peek_at(parser, next_content + 1) == 'r' &&
+ peek_at(parser, next_content + 2) != '!' &&
+ peek_at(parser, next_content + 2) != '?' &&
!char_is_identifier(parser, next_content + 2, parser->end - (next_content + 2))
) {
if (!lexed_comment) parser_lex_ignored_newline(parser);
diff --git a/test/prism/fixtures/4.0/leading_logical.txt b/test/prism/fixtures/4.0/leading_logical.txt
index feb5ee245c..ee87e00d4f 100644
--- a/test/prism/fixtures/4.0/leading_logical.txt
+++ b/test/prism/fixtures/4.0/leading_logical.txt
@@ -14,8 +14,3 @@ and 3
or 2
or 3
-1
-andfoo
-
-2
-orfoo
diff --git a/test/prism/fixtures/and_or_with_suffix.txt b/test/prism/fixtures/and_or_with_suffix.txt
new file mode 100644
index 0000000000..59ee4d0b88
--- /dev/null
+++ b/test/prism/fixtures/and_or_with_suffix.txt
@@ -0,0 +1,17 @@
+foo
+and?
+
+foo
+or?
+
+foo
+and!
+
+foo
+or!
+
+foo
+andbar
+
+foo
+orbar
diff --git a/test/prism/lex_test.rb b/test/prism/lex_test.rb
index 68e47a0964..d3fdb4277e 100644
--- a/test/prism/lex_test.rb
+++ b/test/prism/lex_test.rb
@@ -36,6 +36,13 @@ module Prism
except << "whitequark/ruby_bug_19281.txt"
end
+ if RUBY_VERSION.start_with?("4.")
+ except += [
+ # https://bugs.ruby-lang.org/issues/21945
+ "and_or_with_suffix.txt",
+ ]
+ end
+
# https://bugs.ruby-lang.org/issues/21168#note-5
except << "command_method_call_2.txt"
diff --git a/test/prism/ruby/ripper_test.rb b/test/prism/ruby/ripper_test.rb
index bbd85585a9..46b0970e68 100644
--- a/test/prism/ruby/ripper_test.rb
+++ b/test/prism/ruby/ripper_test.rb
@@ -37,6 +37,13 @@ module Prism
]
end
+ if RUBY_VERSION.start_with?("4.")
+ incorrect += [
+ # https://bugs.ruby-lang.org/issues/21945
+ "and_or_with_suffix.txt",
+ ]
+ end
+
# Skip these tests that we haven't implemented yet.
omitted = [
"dos_endings.txt",