[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
This commit is contained in:
Earlopain 2026-03-05 20:41:21 +01:00 committed by Takashi Kokubun
parent 33e5d3894f
commit 83c261f30b
5 changed files with 50 additions and 7 deletions

View File

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

View File

@ -14,8 +14,3 @@ and 3
or 2
or 3
1
andfoo
2
orfoo

View File

@ -0,0 +1,17 @@
foo
and?
foo
or?
foo
and!
foo
or!
foo
andbar
foo
orbar

View File

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

View File

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