From 2522c1ed611b0237b51a7e7c3fa1bc009f7d7fdd Mon Sep 17 00:00:00 2001 From: Oleh Prypin Date: Fri, 12 Nov 2021 15:12:39 +0100 Subject: [PATCH] Show proper syntax errors in some edge cases in the parser * A backslash in a regex at the end of the source code was a crash. * A regex capture `$123` with too big of an integer was a crash. --- spec/compiler/lexer/lexer_spec.cr | 1 + spec/compiler/parser/parser_spec.cr | 3 +++ src/compiler/crystal/syntax/lexer.cr | 1 + src/compiler/crystal/syntax/parser.cr | 4 +++- 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/spec/compiler/lexer/lexer_spec.cr b/spec/compiler/lexer/lexer_spec.cr index 30910c406354..d0db0cf68c89 100644 --- a/spec/compiler/lexer/lexer_spec.cr +++ b/spec/compiler/lexer/lexer_spec.cr @@ -402,6 +402,7 @@ describe "Lexer" do end assert_syntax_error "/foo", "Unterminated regular expression" + assert_syntax_error "/\\", "Unterminated regular expression" assert_syntax_error ":\"foo", "unterminated quoted symbol" it "lexes utf-8 char" do diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 8e4216e52ccb..219d08d4e111 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -1121,6 +1121,9 @@ module Crystal it_parses "foo $1", Call.new(nil, "foo", Call.new(Global.new("$~"), "[]", 1.int32)) it_parses "$~ = 1", Assign.new("$~".var, 1.int32) + assert_syntax_error "$2147483648" + assert_syntax_error "$99999999999999999999999?", "Regex capture index 99999999999999999999999 doesn't fit in an Int32" + it_parses "foo /a/", Call.new(nil, "foo", regex("a")) it_parses "foo(/a/)", Call.new(nil, "foo", regex("a")) it_parses "foo(//)", Call.new(nil, "foo", regex("")) diff --git a/src/compiler/crystal/syntax/lexer.cr b/src/compiler/crystal/syntax/lexer.cr index 5efa3490cca8..93551535850c 100644 --- a/src/compiler/crystal/syntax/lexer.cr +++ b/src/compiler/crystal/syntax/lexer.cr @@ -2019,6 +2019,7 @@ module Crystal if delimiter_state.allow_escapes if delimiter_state.kind == :regex char = next_char + raise_unterminated_quoted delimiter_state if char == '\0' next_char @token.type = :STRING if char == '/' || char.ascii_whitespace? diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index f63e324f4383..b0e590b51ba4 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -997,7 +997,9 @@ module Crystal method = "[]" end location = @token.location - node_and_next_token Call.new(Global.new("$~").at(location), method, NumberLiteral.new(value.to_i)) + index = value.to_i? + raise "Regex capture index #{value} doesn't fit in an Int32" unless index + node_and_next_token Call.new(Global.new("$~").at(location), method, NumberLiteral.new(index)) when :__LINE__ node_and_next_token MagicConstant.expand_line_node(@token.location) when :__END_LINE__