diff --git a/spec/ruby/language/break_spec.rb b/spec/ruby/language/break_spec.rb index 627cb4a071f..b506f5c6354 100644 --- a/spec/ruby/language/break_spec.rb +++ b/spec/ruby/language/break_spec.rb @@ -252,6 +252,25 @@ def mid(&b) end end +describe "The break statement in a method" do + it "is invalid and raises a SyntaxError" do + -> { + eval("def m; break; end") + }.should raise_error(SyntaxError) + end +end + +describe "The break statement in a module literal" do + it "is invalid and raises a SyntaxError" do + code = <<~RUBY + module BreakSpecs:ModuleWithBreak + break + end + RUBY + + -> { eval(code) }.should raise_error(SyntaxError) + end +end # TODO: Rewrite all the specs from here to the end of the file in the style # above. diff --git a/spec/ruby/language/retry_spec.rb b/spec/ruby/language/retry_spec.rb index ee5377946f7..669d5f0ff51 100644 --- a/spec/ruby/language/retry_spec.rb +++ b/spec/ruby/language/retry_spec.rb @@ -31,8 +31,11 @@ results.should == [1, 2, 3, 1, 2, 4, 5, 6, 4, 5] end - it "raises a SyntaxError when used outside of a begin statement" do + it "raises a SyntaxError when used outside of a rescue statement" do -> { eval 'retry' }.should raise_error(SyntaxError) + -> { eval 'begin; retry; end' }.should raise_error(SyntaxError) + -> { eval 'def m; retry; end' }.should raise_error(SyntaxError) + -> { eval 'module RetrySpecs; retry; end' }.should raise_error(SyntaxError) end end diff --git a/spec/ruby/language/yield_spec.rb b/spec/ruby/language/yield_spec.rb index 5283517636d..e125cf8e738 100644 --- a/spec/ruby/language/yield_spec.rb +++ b/spec/ruby/language/yield_spec.rb @@ -206,3 +206,15 @@ class << Object.new -> { eval(code) }.should raise_error(SyntaxError, /Invalid yield/) end end + +describe "Using yield in a module literal" do + it 'raises a SyntaxError' do + code = <<~RUBY + module YieldSpecs::ModuleWithYield + yield + end + RUBY + + -> { eval(code) }.should raise_error(SyntaxError, /Invalid yield/) + end +end diff --git a/spec/truffle/parsing/fixtures/TOPLEVEL_BINDING.yaml b/spec/truffle/parsing/fixtures/TOPLEVEL_BINDING.yaml index b1b0a162112..c359aaa6388 100644 --- a/spec/truffle/parsing/fixtures/TOPLEVEL_BINDING.yaml +++ b/spec/truffle/parsing/fixtures/TOPLEVEL_BINDING.yaml @@ -39,6 +39,12 @@ ast: | flags = 0 sourceCharIndex = -1 sourceLength = 0 + EmitWarningsNode + attributes: + flags = 0 + sourceCharIndex = -1 + sourceLength = 0 + warnings = RubyDeferredWarnings(WarningMessage(message = 'assigned but unused variable - a', verbosity = VERBOSE, fileName = '', lineNumber = 1)) WriteLocalVariableNode attributes: flags = 0 diff --git a/spec/truffle/parsing/fixtures/__END__.yaml b/spec/truffle/parsing/fixtures/__END__.yaml index 135d49494b0..06f43d75186 100644 --- a/spec/truffle/parsing/fixtures/__END__.yaml +++ b/spec/truffle/parsing/fixtures/__END__.yaml @@ -47,6 +47,12 @@ ast: | flags = 0 sourceCharIndex = -1 sourceLength = 0 + EmitWarningsNode + attributes: + flags = 0 + sourceCharIndex = -1 + sourceLength = 0 + warnings = RubyDeferredWarnings(WarningMessage(message = 'assigned but unused variable - a', verbosity = VERBOSE, fileName = '', lineNumber = 1)) WriteLocalVariableNode attributes: flags = 0 diff --git a/spec/truffle/parsing/fixtures/for/multi_assignment/with_multiple_variables.yaml b/spec/truffle/parsing/fixtures/for/multi_assignment/with_multiple_variables.yaml index fc17e6f2cc6..acccd229658 100644 --- a/spec/truffle/parsing/fixtures/for/multi_assignment/with_multiple_variables.yaml +++ b/spec/truffle/parsing/fixtures/for/multi_assignment/with_multiple_variables.yaml @@ -35,6 +35,12 @@ ast: | sourceLength = 51 children: body = [ + EmitWarningsNode + attributes: + flags = 0 + sourceCharIndex = -1 + sourceLength = 0 + warnings = RubyDeferredWarnings(WarningMessage(message = 'assigned but unused variable - array', verbosity = VERBOSE, fileName = '', lineNumber = 2)) WriteLocalVariableNode attributes: flags = 0 diff --git a/spec/truffle/parsing/fixtures/for/multi_assignment/with_nested_multi_assignment.yaml b/spec/truffle/parsing/fixtures/for/multi_assignment/with_nested_multi_assignment.yaml index a3e9bd4342d..473f4631439 100644 --- a/spec/truffle/parsing/fixtures/for/multi_assignment/with_nested_multi_assignment.yaml +++ b/spec/truffle/parsing/fixtures/for/multi_assignment/with_nested_multi_assignment.yaml @@ -35,6 +35,12 @@ ast: | sourceLength = 53 children: body = [ + EmitWarningsNode + attributes: + flags = 0 + sourceCharIndex = -1 + sourceLength = 0 + warnings = RubyDeferredWarnings(WarningMessage(message = 'assigned but unused variable - array', verbosity = VERBOSE, fileName = '', lineNumber = 2)) WriteLocalVariableNode attributes: flags = 0 diff --git a/spec/truffle/parsing/fixtures/for/multi_assignment/with_preceding_splat_operator_and_following_variables.yaml b/spec/truffle/parsing/fixtures/for/multi_assignment/with_preceding_splat_operator_and_following_variables.yaml index 561ace49cb3..12a9f03b70a 100644 --- a/spec/truffle/parsing/fixtures/for/multi_assignment/with_preceding_splat_operator_and_following_variables.yaml +++ b/spec/truffle/parsing/fixtures/for/multi_assignment/with_preceding_splat_operator_and_following_variables.yaml @@ -35,6 +35,12 @@ ast: | sourceLength = 52 children: body = [ + EmitWarningsNode + attributes: + flags = 0 + sourceCharIndex = -1 + sourceLength = 0 + warnings = RubyDeferredWarnings(WarningMessage(message = 'assigned but unused variable - array', verbosity = VERBOSE, fileName = '', lineNumber = 2)) WriteLocalVariableNode attributes: flags = 0 diff --git a/spec/truffle/parsing/fixtures/for/multi_assignment/with_preceding_variables_and_implicit_rest.yaml b/spec/truffle/parsing/fixtures/for/multi_assignment/with_preceding_variables_and_implicit_rest.yaml index 9ecbc487d57..5183269d248 100644 --- a/spec/truffle/parsing/fixtures/for/multi_assignment/with_preceding_variables_and_implicit_rest.yaml +++ b/spec/truffle/parsing/fixtures/for/multi_assignment/with_preceding_variables_and_implicit_rest.yaml @@ -35,6 +35,12 @@ ast: | sourceLength = 34 children: body = [ + EmitWarningsNode + attributes: + flags = 0 + sourceCharIndex = -1 + sourceLength = 0 + warnings = RubyDeferredWarnings(WarningMessage(message = 'assigned but unused variable - b', verbosity = VERBOSE, fileName = '', lineNumber = 2)) WriteLocalVariableNode attributes: flags = 0 diff --git a/spec/truffle/parsing/fixtures/for/multi_assignment/with_preceding_variables_and_splat_operator.yaml b/spec/truffle/parsing/fixtures/for/multi_assignment/with_preceding_variables_and_splat_operator.yaml index 5bbd515e4ff..27cc7b7835e 100644 --- a/spec/truffle/parsing/fixtures/for/multi_assignment/with_preceding_variables_and_splat_operator.yaml +++ b/spec/truffle/parsing/fixtures/for/multi_assignment/with_preceding_variables_and_splat_operator.yaml @@ -35,6 +35,12 @@ ast: | sourceLength = 52 children: body = [ + EmitWarningsNode + attributes: + flags = 0 + sourceCharIndex = -1 + sourceLength = 0 + warnings = RubyDeferredWarnings(WarningMessage(message = 'assigned but unused variable - array', verbosity = VERBOSE, fileName = '', lineNumber = 2)) WriteLocalVariableNode attributes: flags = 0 diff --git a/spec/truffle/parsing/fixtures/for/multi_assignment/with_splat_operator_and_following_variables.yaml b/spec/truffle/parsing/fixtures/for/multi_assignment/with_splat_operator_and_following_variables.yaml index bdb22635ca5..b7f582c4b43 100644 --- a/spec/truffle/parsing/fixtures/for/multi_assignment/with_splat_operator_and_following_variables.yaml +++ b/spec/truffle/parsing/fixtures/for/multi_assignment/with_splat_operator_and_following_variables.yaml @@ -35,6 +35,12 @@ ast: | sourceLength = 52 children: body = [ + EmitWarningsNode + attributes: + flags = 0 + sourceCharIndex = -1 + sourceLength = 0 + warnings = RubyDeferredWarnings(WarningMessage(message = 'assigned but unused variable - array', verbosity = VERBOSE, fileName = '', lineNumber = 2)) WriteLocalVariableNode attributes: flags = 0 diff --git a/spec/truffle/parsing/fixtures/for/with_attribute_assignment.yaml b/spec/truffle/parsing/fixtures/for/with_attribute_assignment.yaml index bd8dd3553de..f8b4fcbc989 100644 --- a/spec/truffle/parsing/fixtures/for/with_attribute_assignment.yaml +++ b/spec/truffle/parsing/fixtures/for/with_attribute_assignment.yaml @@ -35,6 +35,12 @@ ast: | sourceLength = 41 children: body = [ + EmitWarningsNode + attributes: + flags = 0 + sourceCharIndex = -1 + sourceLength = 0 + warnings = RubyDeferredWarnings(WarningMessage(message = 'assigned but unused variable - b', verbosity = VERBOSE, fileName = '', lineNumber = 2)) WriteLocalVariableNode attributes: flags = 0 diff --git a/spec/truffle/parsing/fixtures/for/with_class_variable.yaml b/spec/truffle/parsing/fixtures/for/with_class_variable.yaml index 8151724d295..7998dd8301c 100644 --- a/spec/truffle/parsing/fixtures/for/with_class_variable.yaml +++ b/spec/truffle/parsing/fixtures/for/with_class_variable.yaml @@ -35,6 +35,12 @@ ast: | sourceLength = 37 children: body = [ + EmitWarningsNode + attributes: + flags = 0 + sourceCharIndex = -1 + sourceLength = 0 + warnings = RubyDeferredWarnings(WarningMessage(message = 'assigned but unused variable - b', verbosity = VERBOSE, fileName = '', lineNumber = 2)) WriteLocalVariableNode attributes: flags = 0 diff --git a/spec/truffle/parsing/fixtures/for/with_constant.yaml b/spec/truffle/parsing/fixtures/for/with_constant.yaml index 5a92f84aa16..08615bd09bd 100644 --- a/spec/truffle/parsing/fixtures/for/with_constant.yaml +++ b/spec/truffle/parsing/fixtures/for/with_constant.yaml @@ -35,6 +35,12 @@ ast: | sourceLength = 33 children: body = [ + EmitWarningsNode + attributes: + flags = 0 + sourceCharIndex = -1 + sourceLength = 0 + warnings = RubyDeferredWarnings(WarningMessage(message = 'assigned but unused variable - b', verbosity = VERBOSE, fileName = '', lineNumber = 2)) WriteLocalVariableNode attributes: flags = 0 diff --git a/spec/truffle/parsing/fixtures/for/with_fully_qualified_constant.yaml b/spec/truffle/parsing/fixtures/for/with_fully_qualified_constant.yaml index 96b8d729577..c8c0dc14cf6 100644 --- a/spec/truffle/parsing/fixtures/for/with_fully_qualified_constant.yaml +++ b/spec/truffle/parsing/fixtures/for/with_fully_qualified_constant.yaml @@ -35,6 +35,12 @@ ast: | sourceLength = 39 children: body = [ + EmitWarningsNode + attributes: + flags = 0 + sourceCharIndex = -1 + sourceLength = 0 + warnings = RubyDeferredWarnings(WarningMessage(message = 'assigned but unused variable - b', verbosity = VERBOSE, fileName = '', lineNumber = 2)) WriteLocalVariableNode attributes: flags = 0 diff --git a/spec/truffle/parsing/fixtures/for/with_global_variable.yaml b/spec/truffle/parsing/fixtures/for/with_global_variable.yaml index af3d3d8f38c..976ed60b174 100644 --- a/spec/truffle/parsing/fixtures/for/with_global_variable.yaml +++ b/spec/truffle/parsing/fixtures/for/with_global_variable.yaml @@ -35,6 +35,12 @@ ast: | sourceLength = 35 children: body = [ + EmitWarningsNode + attributes: + flags = 0 + sourceCharIndex = -1 + sourceLength = 0 + warnings = RubyDeferredWarnings(WarningMessage(message = 'assigned but unused variable - b', verbosity = VERBOSE, fileName = '', lineNumber = 2)) WriteLocalVariableNode attributes: flags = 0 diff --git a/spec/truffle/parsing/fixtures/for/with_instance_variable.yaml b/spec/truffle/parsing/fixtures/for/with_instance_variable.yaml index aa467c7bd31..34186176bc1 100644 --- a/spec/truffle/parsing/fixtures/for/with_instance_variable.yaml +++ b/spec/truffle/parsing/fixtures/for/with_instance_variable.yaml @@ -35,6 +35,12 @@ ast: | sourceLength = 35 children: body = [ + EmitWarningsNode + attributes: + flags = 0 + sourceCharIndex = -1 + sourceLength = 0 + warnings = RubyDeferredWarnings(WarningMessage(message = 'assigned but unused variable - b', verbosity = VERBOSE, fileName = '', lineNumber = 2)) WriteLocalVariableNode attributes: flags = 0 diff --git a/spec/truffle/parsing/fixtures/for/with_local_variable.yaml b/spec/truffle/parsing/fixtures/for/with_local_variable.yaml index f809e7229b2..34c93eb98a2 100644 --- a/spec/truffle/parsing/fixtures/for/with_local_variable.yaml +++ b/spec/truffle/parsing/fixtures/for/with_local_variable.yaml @@ -35,6 +35,12 @@ ast: | sourceLength = 33 children: body = [ + EmitWarningsNode + attributes: + flags = 0 + sourceCharIndex = -1 + sourceLength = 0 + warnings = RubyDeferredWarnings(WarningMessage(message = 'assigned but unused variable - b', verbosity = VERBOSE, fileName = '', lineNumber = 2)) WriteLocalVariableNode attributes: flags = 0 diff --git a/spec/truffle/parsing/fixtures/for/with_reference_assignment.yaml b/spec/truffle/parsing/fixtures/for/with_reference_assignment.yaml index b9a47fd8028..4428ec6a389 100644 --- a/spec/truffle/parsing/fixtures/for/with_reference_assignment.yaml +++ b/spec/truffle/parsing/fixtures/for/with_reference_assignment.yaml @@ -35,6 +35,12 @@ ast: | sourceLength = 45 children: body = [ + EmitWarningsNode + attributes: + flags = 0 + sourceCharIndex = -1 + sourceLength = 0 + warnings = RubyDeferredWarnings(WarningMessage(message = 'assigned but unused variable - b', verbosity = VERBOSE, fileName = '', lineNumber = 2)) WriteLocalVariableNode attributes: flags = 0 diff --git a/spec/truffle/parsing/fixtures/for/with_reference_assignment_and_multiple_explicit_arguments.yaml b/spec/truffle/parsing/fixtures/for/with_reference_assignment_and_multiple_explicit_arguments.yaml index 86216fee0bc..9ea67e20d74 100644 --- a/spec/truffle/parsing/fixtures/for/with_reference_assignment_and_multiple_explicit_arguments.yaml +++ b/spec/truffle/parsing/fixtures/for/with_reference_assignment_and_multiple_explicit_arguments.yaml @@ -35,6 +35,12 @@ ast: | sourceLength = 57 children: body = [ + EmitWarningsNode + attributes: + flags = 0 + sourceCharIndex = -1 + sourceLength = 0 + warnings = RubyDeferredWarnings(WarningMessage(message = 'assigned but unused variable - b', verbosity = VERBOSE, fileName = '', lineNumber = 2)) WriteLocalVariableNode attributes: flags = 0 diff --git a/spec/truffle/parsing/fixtures/local_variables/at_top_level/writing.yaml b/spec/truffle/parsing/fixtures/local_variables/at_top_level/writing.yaml index 0c28e573f5f..339158b630c 100644 --- a/spec/truffle/parsing/fixtures/local_variables/at_top_level/writing.yaml +++ b/spec/truffle/parsing/fixtures/local_variables/at_top_level/writing.yaml @@ -34,6 +34,12 @@ ast: | sourceLength = 8 children: body = [ + EmitWarningsNode + attributes: + flags = 0 + sourceCharIndex = -1 + sourceLength = 0 + warnings = RubyDeferredWarnings(WarningMessage(message = 'assigned but unused variable - foo', verbosity = VERBOSE, fileName = '', lineNumber = 1)) WriteLocalVariableNode attributes: flags = 0 diff --git a/spec/truffle/parsing/fixtures/retry.yaml b/spec/truffle/parsing/fixtures/retry.yaml index d0a634c70ba..cd0e02d07fa 100644 --- a/spec/truffle/parsing/fixtures/retry.yaml +++ b/spec/truffle/parsing/fixtures/retry.yaml @@ -2,10 +2,13 @@ subject: "retry" description: "retry control flow operator" focused_on_node: "org.truffleruby.language.control.RetryNode" ruby: | - retry + begin + rescue + retry + end ast: | RetryNode attributes: flags = 1 - sourceCharIndex = 0 + sourceCharIndex = 15 sourceLength = 5 \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/strings/with_interpolation_%_string_expression.yaml b/spec/truffle/parsing/fixtures/strings/with_interpolation_%_string_expression.yaml index 33a2ef8b7b7..122c86b0077 100644 --- a/spec/truffle/parsing/fixtures/strings/with_interpolation_%_string_expression.yaml +++ b/spec/truffle/parsing/fixtures/strings/with_interpolation_%_string_expression.yaml @@ -11,6 +11,12 @@ ast: | sourceLength = 18 children: body = [ + EmitWarningsNode + attributes: + flags = 0 + sourceCharIndex = -1 + sourceLength = 0 + warnings = RubyDeferredWarnings(WarningMessage(message = 'possibly useless use of a literal in void context', verbosity = VERBOSE, fileName = '', lineNumber = 1)) WriteLocalVariableNode attributes: flags = 0 diff --git a/spec/truffle/parsing/fixtures/strings/with_interpolation_string_expression.yaml b/spec/truffle/parsing/fixtures/strings/with_interpolation_string_expression.yaml index feb293fbe84..6f9ea70b094 100644 --- a/spec/truffle/parsing/fixtures/strings/with_interpolation_string_expression.yaml +++ b/spec/truffle/parsing/fixtures/strings/with_interpolation_string_expression.yaml @@ -11,6 +11,12 @@ ast: | sourceLength = 16 children: body = [ + EmitWarningsNode + attributes: + flags = 0 + sourceCharIndex = -1 + sourceLength = 0 + warnings = RubyDeferredWarnings(WarningMessage(message = 'possibly useless use of a literal in void context', verbosity = VERBOSE, fileName = '', lineNumber = 1)) WriteLocalVariableNode attributes: flags = 0 diff --git a/src/main/c/yarp/Makefile b/src/main/c/yarp/Makefile index b97d1b34217..c20fdc9f9e8 100644 --- a/src/main/c/yarp/Makefile +++ b/src/main/c/yarp/Makefile @@ -6,17 +6,21 @@ Q1 = $(V:1=) Q = $(Q1:0=@) ECHO1 = $(V:1=@ :) ECHO = $(ECHO1:0=@ echo) -FUZZ_OUTPUT_DIR = $(shell pwd)/fuzz/output +FUZZ_OUTPUT_DIR = $(CURDIR)/fuzz/output -SOEXT := $(shell ruby -e 'puts RbConfig::CONFIG["SOEXT"]') +SOEXT ?= $(shell ruby -e 'puts RbConfig::CONFIG["SOEXT"]') CPPFLAGS := -Iinclude $(CPPFLAGS) CFLAGS := -g -O2 -std=c99 -Wall -Werror -Wextra -Wpedantic -Wundef -Wconversion -Wno-missing-braces -fPIC -fvisibility=hidden $(CFLAGS) -CC := cc +CC ?= cc +AR ?= ar WASI_SDK_PATH := /opt/wasi-sdk -HEADERS := $(shell find include -name '*.h') -SOURCES := $(shell find src -name '*.c') +MAKEDIRS ?= mkdir -p +RMALL ?= rm -f -r + +HEADERS := $(wildcard include/*.h include/*/*.h include/*/*/*.h') +SOURCES := $(wildcard src/*.c src/*/*.c) SHARED_OBJECTS := $(subst src/,build/shared/,$(SOURCES:.c=.o)) STATIC_OBJECTS := $(subst src/,build/static/,$(SOURCES:.c=.o)) @@ -28,11 +32,11 @@ wasm: javascript/src/prism.wasm java-wasm: java-wasm/src/test/resources/prism.wasm build/libprism.$(SOEXT): $(SHARED_OBJECTS) - $(ECHO) "linking $@" + $(ECHO) "linking $@ with $(CC)" $(Q) $(CC) $(DEBUG_FLAGS) $(CFLAGS) -shared -o $@ $(SHARED_OBJECTS) build/libprism.a: $(STATIC_OBJECTS) - $(ECHO) "building $@" + $(ECHO) "building $@ with $(AR)" $(Q) $(AR) $(ARFLAGS) $@ $(STATIC_OBJECTS) $(Q1:0=>/dev/null) javascript/src/prism.wasm: Makefile $(SOURCES) $(HEADERS) @@ -45,17 +49,17 @@ java-wasm/src/test/resources/prism.wasm: Makefile $(SOURCES) $(HEADERS) build/shared/%.o: src/%.c Makefile $(HEADERS) $(ECHO) "compiling $@" - $(Q) mkdir -p $(@D) + $(Q) $(MAKEDIRS) $(@D) $(Q) $(CC) $(DEBUG_FLAGS) -DPRISM_EXPORT_SYMBOLS $(CPPFLAGS) $(CFLAGS) -c -o $@ $< build/static/%.o: src/%.c Makefile $(HEADERS) $(ECHO) "compiling $@" - $(Q) mkdir -p $(@D) + $(Q) $(MAKEDIRS) $(@D) $(Q) $(CC) $(DEBUG_FLAGS) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< build/fuzz.%: $(SOURCES) fuzz/%.c fuzz/fuzz.c $(ECHO) "building $* fuzzer" - $(Q) mkdir -p $(@D) + $(Q) $(MAKEDIRS) $(@D) $(ECHO) "building main fuzz binary" $(Q) AFL_HARDEN=1 afl-clang-lto $(DEBUG_FLAGS) $(CPPFLAGS) $(CFLAGS) $(FUZZ_FLAGS) -O0 -fsanitize-ignorelist=fuzz/asan.ignore -fsanitize=fuzzer,address -ggdb3 -std=c99 -Iinclude -o $@ $^ $(ECHO) "building cmplog binary" @@ -66,7 +70,7 @@ build/fuzz.heisenbug.%: $(SOURCES) fuzz/%.c fuzz/heisenbug.c fuzz-debug: $(ECHO) "entering debug shell" - $(Q) docker run -it --rm -e HISTFILE=/prism/fuzz/output/.bash_history -v $(shell pwd):/prism -v $(FUZZ_OUTPUT_DIR):/fuzz_output prism/fuzz + $(Q) docker run -it --rm -e HISTFILE=/prism/fuzz/output/.bash_history -v $(CURDIR):/prism -v $(FUZZ_OUTPUT_DIR):/fuzz_output prism/fuzz fuzz-docker-build: fuzz/docker/Dockerfile $(ECHO) "building docker image" @@ -76,17 +80,17 @@ fuzz-run-%: FORCE fuzz-docker-build $(ECHO) "generating templates" $(Q) bundle exec rake templates $(ECHO) "running $* fuzzer" - $(Q) docker run --rm -v $(shell pwd):/prism prism/fuzz /bin/bash -c "FUZZ_FLAGS=\"$(FUZZ_FLAGS)\" make build/fuzz.$*" + $(Q) docker run --rm -v $(CURDIR):/prism prism/fuzz /bin/bash -c "FUZZ_FLAGS=\"$(FUZZ_FLAGS)\" make build/fuzz.$*" $(ECHO) "starting AFL++ run" - $(Q) mkdir -p $(FUZZ_OUTPUT_DIR)/$* - $(Q) docker run -it --rm -v $(shell pwd):/prism -v $(FUZZ_OUTPUT_DIR):/fuzz_output prism/fuzz /bin/bash -c "./fuzz/$*.sh /fuzz_output/$*" + $(Q) $(MAKEDIRS) $(FUZZ_OUTPUT_DIR)/$* + $(Q) docker run -it --rm -v $(CURDIR):/prism -v $(FUZZ_OUTPUT_DIR):/fuzz_output prism/fuzz /bin/bash -c "./fuzz/$*.sh /fuzz_output/$*" FORCE: fuzz-clean: - $(Q) rm -f -r fuzz/output + $(Q) $(RMALL) fuzz/output clean: - $(Q) rm -f -r build + $(Q) $(RMALL) build .PHONY: clean fuzz-clean diff --git a/src/main/c/yarp/include/prism/ast.h b/src/main/c/yarp/include/prism/ast.h index d124fff720d..26693fe80ae 100644 --- a/src/main/c/yarp/include/prism/ast.h +++ b/src/main/c/yarp/include/prism/ast.h @@ -1099,16 +1099,31 @@ typedef struct pm_alias_global_variable_node { /** * AliasGlobalVariableNode#new_name + * + * Represents the new name of the global variable that can be used after aliasing. This can be either a global variable, a back reference, or a numbered reference. + * + * alias $foo $bar + * ^^^^ */ struct pm_node *new_name; /** * AliasGlobalVariableNode#old_name + * + * Represents the old name of the global variable that could be used before aliasing. This can be either a global variable, a back reference, or a numbered reference. + * + * alias $foo $bar + * ^^^^ */ struct pm_node *old_name; /** * AliasGlobalVariableNode#keyword_loc + * + * The location of the `alias` keyword. + * + * alias $foo $bar + * ^^^^^ */ pm_location_t keyword_loc; } pm_alias_global_variable_node_t; @@ -1249,16 +1264,32 @@ typedef struct pm_array_node { /** * ArrayNode#elements + * + * Represent the list of zero or more [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression) within the array. */ struct pm_node_list elements; /** * ArrayNode#opening_loc + * + * Represents the optional source location for the opening token. + * + * [1,2,3] # "[" + * %w[foo bar baz] # "%w[" + * %I(apple orange banana) # "%I(" + * foo = 1, 2, 3 # nil */ pm_location_t opening_loc; /** * ArrayNode#closing_loc + * + * Represents the optional source location for the closing token. + * + * [1,2,3] # "]" + * %w[foo bar baz] # "]" + * %I(apple orange banana) # ")" + * foo = 1, 2, 3 # nil */ pm_location_t closing_loc; } pm_array_node_t; @@ -1605,11 +1636,21 @@ typedef struct pm_break_node { /** * BreakNode#arguments + * + * The arguments to the break statement, if present. These can be any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * break foo + * ^^^ */ struct pm_arguments_node *arguments; /** * BreakNode#keyword_loc + * + * The location of the `break` keyword. + * + * break foo + * ^^^^^ */ pm_location_t keyword_loc; } pm_break_node_t; @@ -2209,8 +2250,7 @@ typedef struct pm_class_variable_write_node { /** * ClassVariableWriteNode#value * - * The value to assign to the class variable. Can be any node that - * represents a non-void expression. + * The value to write to the class variable. This can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). * * @@foo = :bar * ^^^^ @@ -2372,16 +2412,47 @@ typedef struct pm_constant_path_node { /** * ConstantPathNode#parent + * + * The left-hand node of the path, if present. It can be `nil` or any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). It will be `nil` when the constant lookup is at the root of the module tree. + * + * Foo::Bar + * ^^^ + * + * self::Test + * ^^^^ + * + * a.b::C + * ^^^ */ struct pm_node *parent; /** * ConstantPathNode#child + * + * The right-hand node of the path. Always a `ConstantReadNode` in a + * valid Ruby syntax tree. + * + * ::Foo + * ^^^ + * + * self::Test + * ^^^^ + * + * a.b::C + * ^ */ struct pm_node *child; /** * ConstantPathNode#delimiter_loc + * + * The location of the `::` delimiter. + * + * ::Foo + * ^^ + * + * One::Two + * ^^ */ pm_location_t delimiter_loc; } pm_constant_path_node_t; @@ -2485,16 +2556,34 @@ typedef struct pm_constant_path_write_node { /** * ConstantPathWriteNode#target + * + * A node representing the constant path being written to. + * + * Foo::Bar = 1 + * ^^^^^^^^ + * + * ::Foo = :abc + * ^^^^^ */ struct pm_constant_path_node *target; /** * ConstantPathWriteNode#operator_loc + * + * The location of the `=` operator. + * + * ::ABC = 123 + * ^ */ pm_location_t operator_loc; /** * ConstantPathWriteNode#value + * + * The value to write to the constant path. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * FOO::BAR = :abc + * ^^^^ */ struct pm_node *value; } pm_constant_path_write_node_t; @@ -2552,21 +2641,45 @@ typedef struct pm_constant_write_node { /** * ConstantWriteNode#name + * + * The name of the [constant](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#constants). + * + * Foo = :bar # name `:Foo` + * + * XYZ = 1 # name `:XYZ` */ pm_constant_id_t name; /** * ConstantWriteNode#name_loc + * + * The location of the constant name. + * + * FOO = 1 + * ^^^ */ pm_location_t name_loc; /** * ConstantWriteNode#value + * + * The value to write to the constant. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * FOO = :bar + * ^^^^ + * + * MyClass = Class.new + * ^^^^^^^^^ */ struct pm_node *value; /** * ConstantWriteNode#operator_loc + * + * The location of the `=` operator. + * + * FOO = :bar + * ^ */ pm_location_t operator_loc; } pm_constant_write_node_t; @@ -3122,21 +3235,45 @@ typedef struct pm_global_variable_write_node { /** * GlobalVariableWriteNode#name + * + * The name of the global variable, which is a `$` followed by an [identifier](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#identifier). Alternatively, it can be one of the special global variables designated by a symbol. + * + * $foo = :bar # name `:$foo` + * + * $_Test = 123 # name `:$_Test` */ pm_constant_id_t name; /** * GlobalVariableWriteNode#name_loc + * + * The location of the global variable's name. + * + * $foo = :bar + * ^^^^ */ pm_location_t name_loc; /** * GlobalVariableWriteNode#value + * + * The value to write to the global variable. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * $foo = :bar + * ^^^^ + * + * $-xyz = 123 + * ^^^ */ struct pm_node *value; /** * GlobalVariableWriteNode#operator_loc + * + * The location of the `=` operator. + * + * $foo = :bar + * ^ */ pm_location_t operator_loc; } pm_global_variable_write_node_t; @@ -3236,31 +3373,89 @@ typedef struct pm_if_node { /** * IfNode#if_keyword_loc + * + * The location of the `if` keyword if present. + * + * bar if foo + * ^^ + * + * The `if_keyword_loc` field will be `nil` when the `IfNode` represents a ternary expression. */ pm_location_t if_keyword_loc; /** * IfNode#predicate + * + * The node for the condition the `IfNode` is testing. + * + * if foo + * ^^^ + * bar + * end + * + * bar if foo + * ^^^ + * + * foo ? bar : baz + * ^^^ */ struct pm_node *predicate; /** * IfNode#then_keyword_loc + * + * The location of the `then` keyword (if present) or the `?` in a ternary expression, `nil` otherwise. + * + * if foo then bar end + * ^^^^ + * + * a ? b : c + * ^ */ pm_location_t then_keyword_loc; /** * IfNode#statements + * + * Represents the body of statements that will be executed when the predicate is evaluated as truthy. Will be `nil` when no body is provided. + * + * if foo + * bar + * ^^^ + * baz + * ^^^ + * end */ struct pm_statements_node *statements; /** * IfNode#consequent + * + * Represents an `ElseNode` or an `IfNode` when there is an `else` or an `elsif` in the `if` statement. + * + * if foo + * bar + * elsif baz + * ^^^^^^^^^ + * qux + * ^^^ + * end + * ^^^ + * + * if foo then bar else baz end + * ^^^^^^^^^^^^ */ struct pm_node *consequent; /** * IfNode#end_keyword_loc + * + * The location of the `end` keyword if present, `nil` otherwise. + * + * if foo + * bar + * end + * ^^^ */ pm_location_t end_keyword_loc; } pm_if_node_t; @@ -3737,8 +3932,7 @@ typedef struct pm_instance_variable_write_node { /** * InstanceVariableWriteNode#value * - * The value to assign to the instance variable. Can be any node that - * represents a non-void expression. + * The value to write to the instance variable. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). * * @foo = :bar * ^^^^ @@ -4242,26 +4436,62 @@ typedef struct pm_local_variable_write_node { /** * LocalVariableWriteNode#name + * + * The name of the local variable, which is an [identifier](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#identifiers). + * + * foo = :bar # name `:foo` + * + * abc = 123 # name `:abc` */ pm_constant_id_t name; /** * LocalVariableWriteNode#depth + * + * The number of semantic scopes we have to traverse to find the declaration of this variable. + * + * foo = 1 # depth 0 + * + * tap { foo = 1 } # depth 1 + * + * The specific rules for calculating the depth may differ from individual Ruby implementations, as they are not specified by the language. For more information, see [the Prism documentation](https://github.com/ruby/prism/blob/main/docs/local_variable_depth.md). */ uint32_t depth; /** * LocalVariableWriteNode#name_loc + * + * The location of the variable name. + * + * foo = :bar + * ^^^ */ pm_location_t name_loc; /** * LocalVariableWriteNode#value + * + * The value to write to the local variable. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * foo = :bar + * ^^^^ + * + * abc = 1234 + * ^^^^ + * + * Note that since the name of a local variable is known before the value is parsed, it is valid for a local variable to appear within the value of its own write. + * + * foo = foo */ struct pm_node *value; /** * LocalVariableWriteNode#operator_loc + * + * The location of the `=` operator. + * + * x = :y + * ^ */ pm_location_t operator_loc; } pm_local_variable_write_node_t; @@ -5350,6 +5580,8 @@ typedef struct pm_source_file_node { /** * SourceFileNode#filepath + * + * Represents the file path being parsed. This corresponds directly to the `filepath` option given to the various `Prism::parse*` APIs. */ pm_string_t filepath; } pm_source_file_node_t; @@ -5562,31 +5794,66 @@ typedef struct pm_unless_node { /** * UnlessNode#keyword_loc + * + * The location of the `unless` keyword. + * + * unless cond then bar end + * ^^^^^^ + * + * bar unless cond + * ^^^^^^ */ pm_location_t keyword_loc; /** * UnlessNode#predicate + * + * The condition to be evaluated for the unless expression. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). + * + * unless cond then bar end + * ^^^^ + * + * bar unless cond + * ^^^^ */ struct pm_node *predicate; /** * UnlessNode#then_keyword_loc + * + * The location of the `then` keyword, if present. + * unless cond then bar end ^^^^ */ pm_location_t then_keyword_loc; /** * UnlessNode#statements + * + * The body of statements that will executed if the unless condition is + * falsey. Will be `nil` if no body is provided. + * + * unless cond then bar end + * ^^^ */ struct pm_statements_node *statements; /** * UnlessNode#consequent + * + * The else clause of the unless expression, if present. + * + * unless cond then bar else baz end + * ^^^^^^^^ */ struct pm_else_node *consequent; /** * UnlessNode#end_keyword_loc + * + * The location of the `end` keyword, if present. + * + * unless cond then bar end + * ^^^ */ pm_location_t end_keyword_loc; } pm_unless_node_t; diff --git a/src/main/c/yarp/include/prism/diagnostic.h b/src/main/c/yarp/include/prism/diagnostic.h index f81a0754330..dff797d8d85 100644 --- a/src/main/c/yarp/include/prism/diagnostic.h +++ b/src/main/c/yarp/include/prism/diagnostic.h @@ -89,7 +89,6 @@ typedef enum { PM_ERR_DEF_ENDLESS, PM_ERR_DEF_ENDLESS_SETTER, PM_ERR_DEF_NAME, - PM_ERR_DEF_NAME_AFTER_RECEIVER, PM_ERR_DEF_PARAMS_TERM, PM_ERR_DEF_PARAMS_TERM_PAREN, PM_ERR_DEF_RECEIVER, @@ -126,6 +125,7 @@ typedef enum { PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, PM_ERR_EXPECT_IDENT_REQ_PARAMETER, PM_ERR_EXPECT_LPAREN_REQ_PARAMETER, + PM_ERR_EXPECT_MESSAGE, PM_ERR_EXPECT_RBRACKET, PM_ERR_EXPECT_RPAREN, PM_ERR_EXPECT_RPAREN_AFTER_MULTI, @@ -133,6 +133,14 @@ typedef enum { PM_ERR_EXPECT_STRING_CONTENT, PM_ERR_EXPECT_WHEN_DELIMITER, PM_ERR_EXPRESSION_BARE_HASH, + PM_ERR_EXPRESSION_NOT_WRITABLE, + PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING, + PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE, + PM_ERR_EXPRESSION_NOT_WRITABLE_FILE, + PM_ERR_EXPRESSION_NOT_WRITABLE_LINE, + PM_ERR_EXPRESSION_NOT_WRITABLE_NIL, + PM_ERR_EXPRESSION_NOT_WRITABLE_SELF, + PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE, PM_ERR_FLOAT_PARSE, PM_ERR_FOR_COLLECTION, PM_ERR_FOR_IN, @@ -151,9 +159,12 @@ typedef enum { PM_ERR_INCOMPLETE_VARIABLE_INSTANCE, PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3_0, PM_ERR_INSTANCE_VARIABLE_BARE, + PM_ERR_INVALID_BLOCK_EXIT, PM_ERR_INVALID_CHARACTER, PM_ERR_INVALID_ENCODING_MAGIC_COMMENT, PM_ERR_INVALID_FLOAT_EXPONENT, + PM_ERR_INVALID_LOCAL_VARIABLE_READ, + PM_ERR_INVALID_LOCAL_VARIABLE_WRITE, PM_ERR_INVALID_MULTIBYTE_CHAR, PM_ERR_INVALID_MULTIBYTE_CHARACTER, PM_ERR_INVALID_MULTIBYTE_ESCAPE, @@ -164,8 +175,12 @@ typedef enum { PM_ERR_INVALID_NUMBER_UNDERSCORE, PM_ERR_INVALID_PERCENT, PM_ERR_INVALID_PRINTABLE_CHARACTER, + PM_ERR_INVALID_RETRY_AFTER_ELSE, + PM_ERR_INVALID_RETRY_AFTER_ENSURE, + PM_ERR_INVALID_RETRY_WITHOUT_RESCUE, PM_ERR_INVALID_VARIABLE_GLOBAL, PM_ERR_INVALID_VARIABLE_GLOBAL_3_3_0, + PM_ERR_INVALID_YIELD, PM_ERR_IT_NOT_ALLOWED_NUMBERED, PM_ERR_IT_NOT_ALLOWED_ORDINARY, PM_ERR_LAMBDA_OPEN, @@ -222,6 +237,7 @@ typedef enum { PM_ERR_PATTERN_HASH_KEY, PM_ERR_PATTERN_HASH_KEY_DUPLICATE, PM_ERR_PATTERN_HASH_KEY_LABEL, + PM_ERR_PATTERN_HASH_KEY_LOCALS, PM_ERR_PATTERN_IDENT_AFTER_HROCKET, PM_ERR_PATTERN_LABEL_AFTER_COMMA, PM_ERR_PATTERN_REST, @@ -259,6 +275,7 @@ typedef enum { PM_ERR_TERNARY_EXPRESSION_TRUE, PM_ERR_UNARY_RECEIVER, PM_ERR_UNDEF_ARGUMENT, + PM_ERR_UNEXPECTED_BLOCK_ARGUMENT, PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT, PM_ERR_UNEXPECTED_TOKEN_IGNORE, PM_ERR_UNTIL_TERM, @@ -294,6 +311,9 @@ typedef enum { PM_WARN_LITERAL_IN_CONDITION_VERBOSE, PM_WARN_SHEBANG_CARRIAGE_RETURN, PM_WARN_UNEXPECTED_CARRIAGE_RETURN, + PM_WARN_UNREACHABLE_STATEMENT, + PM_WARN_UNUSED_LOCAL_VARIABLE, + PM_WARN_VOID_STATEMENT, } pm_diagnostic_id_t; /** diff --git a/src/main/c/yarp/include/prism/node.h b/src/main/c/yarp/include/prism/node.h index a001b4a9e47..8736e59a94d 100644 --- a/src/main/c/yarp/include/prism/node.h +++ b/src/main/c/yarp/include/prism/node.h @@ -11,15 +11,11 @@ #include "prism/util/pm_buffer.h" /** - * Attempts to grow the node list to the next size. If there is already - * capacity in the list, this function does nothing. Otherwise it reallocates - * the list to be twice as large as it was before. If the reallocation fails, - * this function returns false, otherwise it returns true. - * - * @param list The list to grow. - * @return True if the list was successfully grown, false otherwise. + * Loop through each node in the node list, writing each node to the given + * pm_node_t pointer. */ -bool pm_node_list_grow(pm_node_list_t *list); +#define PM_NODE_LIST_FOREACH(list, index, node) \ + for (size_t index = 0; index < (list)->size && ((node) = (list)->nodes[index]); index++) /** * Append a new node onto the end of the node list. @@ -35,8 +31,15 @@ void pm_node_list_append(pm_node_list_t *list, pm_node_t *node); * @param list The list to prepend to. * @param node The node to prepend. */ -void -pm_node_list_prepend(pm_node_list_t *list, pm_node_t *node); +void pm_node_list_prepend(pm_node_list_t *list, pm_node_t *node); + +/** + * Concatenate the given node list onto the end of the other node list. + * + * @param list The list to concatenate onto. + * @param other The list to concatenate. + */ +void pm_node_list_concat(pm_node_list_t *list, pm_node_list_t *other); /** * Free the internal memory associated with the given node list. diff --git a/src/main/c/yarp/include/prism/parser.h b/src/main/c/yarp/include/prism/parser.h index f706a67de7c..b35026e6a25 100644 --- a/src/main/c/yarp/include/prism/parser.h +++ b/src/main/c/yarp/include/prism/parser.h @@ -13,7 +13,6 @@ #include "prism/util/pm_constant_pool.h" #include "prism/util/pm_list.h" #include "prism/util/pm_newline_list.h" -#include "prism/util/pm_state_stack.h" #include "prism/util/pm_string.h" #include @@ -268,12 +267,30 @@ typedef enum { /** a begin statement */ PM_CONTEXT_BEGIN, + /** an ensure statement with an explicit begin */ + PM_CONTEXT_BEGIN_ENSURE, + + /** a rescue else statement with an explicit begin */ + PM_CONTEXT_BEGIN_ELSE, + + /** a rescue statement with an explicit begin */ + PM_CONTEXT_BEGIN_RESCUE, + /** expressions in block arguments using braces */ PM_CONTEXT_BLOCK_BRACES, /** expressions in block arguments using do..end */ PM_CONTEXT_BLOCK_KEYWORDS, + /** an ensure statement within a do..end block */ + PM_CONTEXT_BLOCK_ENSURE, + + /** a rescue else statement within a do..end block */ + PM_CONTEXT_BLOCK_ELSE, + + /** a rescue statement within a do..end block */ + PM_CONTEXT_BLOCK_RESCUE, + /** a case when statements */ PM_CONTEXT_CASE_WHEN, @@ -283,12 +300,33 @@ typedef enum { /** a class declaration */ PM_CONTEXT_CLASS, + /** an ensure statement within a class statement */ + PM_CONTEXT_CLASS_ENSURE, + + /** a rescue else statement within a class statement */ + PM_CONTEXT_CLASS_ELSE, + + /** a rescue statement within a class statement */ + PM_CONTEXT_CLASS_RESCUE, + /** a method definition */ PM_CONTEXT_DEF, + /** an ensure statement within a method definition */ + PM_CONTEXT_DEF_ENSURE, + + /** a rescue else statement within a method definition */ + PM_CONTEXT_DEF_ELSE, + + /** a rescue statement within a method definition */ + PM_CONTEXT_DEF_RESCUE, + /** a method definition's parameters */ PM_CONTEXT_DEF_PARAMS, + /** a defined? expression */ + PM_CONTEXT_DEFINED, + /** a method definition's default parameter */ PM_CONTEXT_DEFAULT_PARAMS, @@ -301,12 +339,6 @@ typedef enum { /** an interpolated expression */ PM_CONTEXT_EMBEXPR, - /** an ensure statement */ - PM_CONTEXT_ENSURE, - - /** an ensure statement within a method definition */ - PM_CONTEXT_ENSURE_DEF, - /** a for loop */ PM_CONTEXT_FOR, @@ -322,12 +354,30 @@ typedef enum { /** a lambda expression with do..end */ PM_CONTEXT_LAMBDA_DO_END, + /** an ensure statement within a lambda expression */ + PM_CONTEXT_LAMBDA_ENSURE, + + /** a rescue else statement within a lambda expression */ + PM_CONTEXT_LAMBDA_ELSE, + + /** a rescue statement within a lambda expression */ + PM_CONTEXT_LAMBDA_RESCUE, + /** the top level context */ PM_CONTEXT_MAIN, /** a module declaration */ PM_CONTEXT_MODULE, + /** an ensure statement within a module statement */ + PM_CONTEXT_MODULE_ENSURE, + + /** a rescue else statement within a module statement */ + PM_CONTEXT_MODULE_ELSE, + + /** a rescue statement within a module statement */ + PM_CONTEXT_MODULE_RESCUE, + /** a parenthesized expression */ PM_CONTEXT_PARENS, @@ -340,20 +390,23 @@ typedef enum { /** a BEGIN block */ PM_CONTEXT_PREEXE, - /** a rescue else statement */ - PM_CONTEXT_RESCUE_ELSE, + /** a modifier rescue clause */ + PM_CONTEXT_RESCUE_MODIFIER, - /** a rescue else statement within a method definition */ - PM_CONTEXT_RESCUE_ELSE_DEF, + /** a singleton class definition */ + PM_CONTEXT_SCLASS, - /** a rescue statement */ - PM_CONTEXT_RESCUE, + /** an ensure statement with a singleton class */ + PM_CONTEXT_SCLASS_ENSURE, - /** a rescue statement within a method definition */ - PM_CONTEXT_RESCUE_DEF, + /** a rescue else statement with a singleton class */ + PM_CONTEXT_SCLASS_ELSE, - /** a singleton class definition */ - PM_CONTEXT_SCLASS, + /** a rescue statement with a singleton class */ + PM_CONTEXT_SCLASS_RESCUE, + + /** a ternary expression */ + PM_CONTEXT_TERNARY, /** an unless statement */ PM_CONTEXT_UNLESS, @@ -455,6 +508,43 @@ static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_LITERAL = static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_EVERYTHING = 0x2; static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_COPY = 0x4; +/** + * This tracks an individual local variable in a certain lexical context, as + * well as the number of times is it read. + */ +typedef struct { + /** The name of the local variable. */ + pm_constant_id_t name; + + /** The location of the local variable in the source. */ + pm_location_t location; + + /** The index of the local variable in the local table. */ + uint32_t index; + + /** The number of times the local variable is read. */ + uint32_t reads; + + /** The hash of the local variable. */ + uint32_t hash; +} pm_local_t; + +/** + * This is a set of local variables in a certain lexical context (method, class, + * module, etc.). We need to track how many times these variables are read in + * order to warn if they only get written. + */ +typedef struct pm_locals { + /** The number of local variables in the set. */ + uint32_t size; + + /** The capacity of the local variables set. */ + uint32_t capacity; + + /** The nullable allocated memory for the local variables in the set. */ + pm_local_t *locals; +} pm_locals_t; + /** * This struct represents a node in a linked list of scopes. Some scopes can see * into their parent scopes, while others cannot. @@ -464,7 +554,7 @@ typedef struct pm_scope { struct pm_scope *previous; /** The IDs of the locals in the given scope. */ - pm_constant_id_list_t locals; + pm_locals_t locals; /** * This is a bitfield that indicates the parameters that are being used in @@ -521,6 +611,11 @@ static const uint8_t PM_SCOPE_PARAMETERS_FORWARDING_ALL = 0x40; static const int8_t PM_SCOPE_NUMBERED_PARAMETERS_DISALLOWED = -1; static const int8_t PM_SCOPE_NUMBERED_PARAMETERS_NONE = 0; +/** + * A struct that represents a stack of boolean values. + */ +typedef uint32_t pm_state_stack_t; + /** * This struct represents the overall parser. It contains a reference to the * source file, as well as pointers that indicate where in the source it's @@ -713,8 +808,18 @@ struct pm_parser { */ const pm_encoding_t *explicit_encoding; - /** The current parameter name id on parsing its default value. */ - pm_constant_id_t current_param_name; + /** + * When parsing block exits (e.g., break, next, redo), we need to validate + * that they are in correct contexts. For the most part we can do this by + * looking at our parent contexts. However, modifier while and until + * expressions can change that context to make block exits valid. In these + * cases, we need to keep track of the block exits and then validate them + * after the expression has been parsed. + * + * We use a pointer here because we don't want to keep a whole list attached + * since this will only be used in the context of begin/end expressions. + */ + pm_node_list_t *current_block_exits; /** The version of prism that we should use to parse. */ pm_options_version_t version; @@ -732,6 +837,12 @@ struct pm_parser { */ int8_t frozen_string_literal; + /** + * Whether or not we are parsing an eval string. This impacts whether or not + * we should evaluate if block exits/yields are valid. + */ + bool parsing_eval; + /** Whether or not we're at the beginning of a command. */ bool command_start; diff --git a/src/main/c/yarp/include/prism/util/pm_constant_pool.h b/src/main/c/yarp/include/prism/util/pm_constant_pool.h index 19e406396e5..0fe16858a0d 100644 --- a/src/main/c/yarp/include/prism/util/pm_constant_pool.h +++ b/src/main/c/yarp/include/prism/util/pm_constant_pool.h @@ -51,6 +51,14 @@ typedef struct { */ void pm_constant_id_list_init(pm_constant_id_list_t *list); +/** + * Initialize a list of constant ids with a given capacity. + * + * @param list The list to initialize. + * @param capacity The initial capacity of the list. + */ +void pm_constant_id_list_init_capacity(pm_constant_id_list_t *list, size_t capacity); + /** * Append a constant id to a list of constant ids. Returns false if any * potential reallocations fail. @@ -61,6 +69,15 @@ void pm_constant_id_list_init(pm_constant_id_list_t *list); */ bool pm_constant_id_list_append(pm_constant_id_list_t *list, pm_constant_id_t id); +/** + * Insert a constant id into a list of constant ids at the specified index. + * + * @param list The list to insert into. + * @param index The index at which to insert. + * @param id The id to insert. + */ +void pm_constant_id_list_insert(pm_constant_id_list_t *list, size_t index, pm_constant_id_t id); + /** * Checks if the current constant id list includes the given constant id. * diff --git a/src/main/c/yarp/include/prism/util/pm_state_stack.h b/src/main/c/yarp/include/prism/util/pm_state_stack.h deleted file mode 100644 index 1ce57a2209f..00000000000 --- a/src/main/c/yarp/include/prism/util/pm_state_stack.h +++ /dev/null @@ -1,42 +0,0 @@ -/** - * @file pm_state_stack.h - * - * A stack of boolean values. - */ -#ifndef PRISM_STATE_STACK_H -#define PRISM_STATE_STACK_H - -#include "prism/defines.h" - -#include -#include - -/** - * A struct that represents a stack of boolean values. - */ -typedef uint32_t pm_state_stack_t; - -/** - * Pushes a value onto the stack. - * - * @param stack The stack to push the value onto. - * @param value The value to push onto the stack. - */ -void pm_state_stack_push(pm_state_stack_t *stack, bool value); - -/** - * Pops a value off the stack. - * - * @param stack The stack to pop the value off of. - */ -void pm_state_stack_pop(pm_state_stack_t *stack); - -/** - * Returns the value at the top of the stack. - * - * @param stack The stack to get the value from. - * @return The value at the top of the stack. - */ -bool pm_state_stack_p(pm_state_stack_t *stack); - -#endif diff --git a/src/main/c/yarp/include/prism/version.h b/src/main/c/yarp/include/prism/version.h index 237796815f9..0d25b2883c0 100644 --- a/src/main/c/yarp/include/prism/version.h +++ b/src/main/c/yarp/include/prism/version.h @@ -14,7 +14,7 @@ /** * The minor version of the Prism library as an int. */ -#define PRISM_VERSION_MINOR 24 +#define PRISM_VERSION_MINOR 27 /** * The patch version of the Prism library as an int. @@ -24,6 +24,6 @@ /** * The version of the Prism library as a constant string. */ -#define PRISM_VERSION "0.24.0" +#define PRISM_VERSION "0.27.0" #endif diff --git a/src/main/c/yarp/src/diagnostic.c b/src/main/c/yarp/src/diagnostic.c index f2cf10547fe..467803e4cb9 100644 --- a/src/main/c/yarp/src/diagnostic.c +++ b/src/main/c/yarp/src/diagnostic.c @@ -8,7 +8,7 @@ #include "prism/diagnostic.h" -#define PM_DIAGNOSTIC_ID_MAX 264 +#define PM_DIAGNOSTIC_ID_MAX 284 /** This struct holds the data for each diagnostic. */ typedef struct { @@ -107,13 +107,13 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_ARGUMENT_FORMAL_IVAR] = { "invalid formal argument; formal argument cannot be an instance variable", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARGUMENT_FORWARDING_UNBOUND] = { "unexpected `...` in an non-parenthesized call", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARGUMENT_IN] = { "unexpected `in` keyword in arguments", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_ARGUMENT_NO_FORWARDING_AMP] = { "unexpected `&` when the parent method is not forwarding", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES] = { "unexpected `...` when the parent method is not forwarding", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_ARGUMENT_NO_FORWARDING_STAR] = { "unexpected `*` when the parent method is not forwarding", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_ARGUMENT_NO_FORWARDING_STAR_STAR] = { "unexpected `**` when the parent method is not forwarding", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_NO_FORWARDING_AMP] = { "unexpected `&`; no anonymous block parameter", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES] = { "unexpected ... when the parent method is not forwarding", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_NO_FORWARDING_STAR] = { "unexpected `*`; no anonymous rest parameter", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_NO_FORWARDING_STAR_STAR] = { "unexpected `**`; no anonymous keyword rest parameter", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT] = { "unexpected `*` splat argument after a `**` keyword splat argument", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARGUMENT_SPLAT_AFTER_SPLAT] = { "unexpected `*` splat argument after a `*` splat argument", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_ARGUMENT_TERM_PAREN] = { "expected a `)` to close the arguments", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_TERM_PAREN] = { "unexpected %s; expected a `)` to close the arguments", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARGUMENT_UNEXPECTED_BLOCK] = { "unexpected `{` after a method call without parenthesis", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARRAY_ELEMENT] = { "expected an element for the array", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARRAY_EXPRESSION] = { "expected an expression for the array element", PM_ERROR_LEVEL_SYNTAX }, @@ -152,15 +152,14 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT] = { "expected a constant after the `::` operator", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_DEF_ENDLESS] = { "could not parse the endless method body", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_DEF_ENDLESS_SETTER] = { "invalid method name; a setter method cannot be defined in an endless method definition", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_DEF_NAME] = { "expected a method name", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_DEF_NAME_AFTER_RECEIVER] = { "expected a method name after the receiver", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_DEF_NAME] = { "unexpected %s; expected a method name", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_DEF_PARAMS_TERM] = { "expected a delimiter to close the parameters", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_DEF_PARAMS_TERM_PAREN] = { "expected a `)` to close the parameters", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_DEF_RECEIVER] = { "expected a receiver for the method definition", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_DEF_RECEIVER_TERM] = { "expected a `.` or `::` after the receiver in a method definition", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_DEF_TERM] = { "expected an `end` to close the `def` statement", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_DEFINED_EXPRESSION] = { "expected an expression after `defined?`", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_EMBDOC_TERM] = { "could not find a terminator for the embedded document", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EMBDOC_TERM] = { "embedded document meets end of file", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EMBEXPR_END] = { "expected a `}` to close the embedded expression", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EMBVAR_INVALID] = { "invalid embedded variable", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_END_UPCASE_BRACE] = { "expected a `{` after `END`", PM_ERROR_LEVEL_SYNTAX }, @@ -189,6 +188,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_EXPECT_EXPRESSION_AFTER_STAR] = { "expected an expression after `*`", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_IDENT_REQ_PARAMETER] = { "expected an identifier for the required parameter", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_LPAREN_REQ_PARAMETER] = { "expected a `(` to start a required parameter", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPECT_MESSAGE] = { "unexpected %s; expecting a message to send to the receiver", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_RBRACKET] = { "expected a matching `]`", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_RPAREN] = { "expected a matching `)`", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_RPAREN_AFTER_MULTI] = { "expected a `)` after multiple assignment", PM_ERROR_LEVEL_SYNTAX }, @@ -196,6 +196,14 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_EXPECT_STRING_CONTENT] = { "expected string content after opening string delimiter", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPECT_WHEN_DELIMITER] = { "expected a delimiter after the predicates of a `when` clause", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_EXPRESSION_BARE_HASH] = { "unexpected bare hash in expression", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPRESSION_NOT_WRITABLE] = { "unexpected '='; target cannot be written", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING] = { "Can't assign to __ENCODING__", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE] = { "Can't assign to false", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPRESSION_NOT_WRITABLE_FILE] = { "Can't assign to __FILE__", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPRESSION_NOT_WRITABLE_LINE] = { "Can't assign to __LINE__", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPRESSION_NOT_WRITABLE_NIL] = { "Can't assign to nil", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPRESSION_NOT_WRITABLE_SELF] = { "Can't change the value of self", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE] = { "Can't assign to true", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_FLOAT_PARSE] = { "could not parse the float '%.*s'", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_FOR_COLLECTION] = { "expected a collection after the `in` in a `for` statement", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_FOR_INDEX] = { "expected an index after `for`", PM_ERROR_LEVEL_SYNTAX }, @@ -214,7 +222,10 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3_0] = { "`%.*s' is not allowed as an instance variable name", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INCOMPLETE_VARIABLE_INSTANCE] = { "'%.*s' is not allowed as an instance variable name", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INSTANCE_VARIABLE_BARE] = { "'@' without identifiers is not allowed as an instance variable name", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_BLOCK_EXIT] = { "Invalid %s", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_FLOAT_EXPONENT] = { "invalid exponent", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_LOCAL_VARIABLE_READ] = { "identifier %.*s is not valid to get", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_LOCAL_VARIABLE_WRITE] = { "identifier %.*s is not valid to set", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_NUMBER_BINARY] = { "invalid binary number", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_NUMBER_DECIMAL] = { "invalid decimal number", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_NUMBER_HEXADECIMAL] = { "invalid hexadecimal number", PM_ERROR_LEVEL_SYNTAX }, @@ -226,8 +237,12 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_INVALID_MULTIBYTE_ESCAPE] = { "invalid multibyte escape: /%.*s/", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_PRINTABLE_CHARACTER] = { "invalid character `%c`", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_PERCENT] = { "invalid `%` token", PM_ERROR_LEVEL_SYNTAX }, // TODO WHAT? + [PM_ERR_INVALID_RETRY_AFTER_ELSE] = { "Invalid retry after else", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_RETRY_AFTER_ENSURE] = { "Invalid retry after ensure", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_RETRY_WITHOUT_RESCUE] = { "Invalid retry without rescue", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_VARIABLE_GLOBAL_3_3_0] = { "`%.*s' is not allowed as a global variable name", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_INVALID_VARIABLE_GLOBAL] = { "'%.*s' is not allowed as a global variable name", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_INVALID_YIELD] = { "Invalid yield", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_IT_NOT_ALLOWED_NUMBERED] = { "`it` is not allowed when an numbered parameter is defined", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_IT_NOT_ALLOWED_ORDINARY] = { "`it` is not allowed when an ordinary parameter is defined", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_LAMBDA_OPEN] = { "expected a `do` keyword or a `{` to open the lambda block", PM_ERROR_LEVEL_SYNTAX }, @@ -284,6 +299,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_PATTERN_HASH_KEY] = { "expected a key in the hash pattern", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PATTERN_HASH_KEY_DUPLICATE] = { "duplicated key name", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PATTERN_HASH_KEY_LABEL] = { "expected a label as the key in the hash pattern", PM_ERROR_LEVEL_SYNTAX }, // TODO // THIS // AND // ABOVE // IS WEIRD + [PM_ERR_PATTERN_HASH_KEY_LOCALS] = { "key must be valid as local variables", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PATTERN_IDENT_AFTER_HROCKET] = { "expected an identifier after the `=>` operator", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PATTERN_LABEL_AFTER_COMMA] = { "expected a label after the `,` in the hash pattern", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PATTERN_REST] = { "unexpected rest pattern", PM_ERROR_LEVEL_SYNTAX }, @@ -302,7 +318,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_RESCUE_MODIFIER_VALUE] = { "expected a value after the `rescue` modifier", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_RESCUE_TERM] = { "expected a closing delimiter for the `rescue` clause", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_RESCUE_VARIABLE] = { "expected an exception variable after `=>` in a rescue statement", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_RETURN_INVALID] = { "invalid `return` in a class or module body", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_RETURN_INVALID] = { "Invalid return in class/module body", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_SINGLETON_FOR_LITERALS] = { "cannot define singleton method for literals", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_STATEMENT_ALIAS] = { "unexpected an `alias` at a non-statement position", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_STATEMENT_POSTEXE_END] = { "unexpected an `END` at a non-statement position", PM_ERROR_LEVEL_SYNTAX }, @@ -320,6 +336,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_TERNARY_EXPRESSION_TRUE] = { "expected an expression after `?` in the ternary operator", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_UNDEF_ARGUMENT] = { "invalid argument being passed to `undef`; expected a bare word, constant, or symbol argument", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_UNARY_RECEIVER] = { "unexpected %s, expected a receiver for unary `%c`", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_UNEXPECTED_BLOCK_ARGUMENT] = { "block argument should not be given", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT] = { "unexpected %s, assuming it is closing the parent %s", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_UNEXPECTED_TOKEN_IGNORE] = { "unexpected %s, ignoring it", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_UNTIL_TERM] = { "expected an `end` to close the `until` statement", PM_ERROR_LEVEL_SYNTAX }, @@ -354,7 +371,10 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_WARN_LITERAL_IN_CONDITION_DEFAULT] = { "%sliteral in %s", PM_WARNING_LEVEL_DEFAULT }, [PM_WARN_LITERAL_IN_CONDITION_VERBOSE] = { "%sliteral in %s", PM_WARNING_LEVEL_VERBOSE }, [PM_WARN_SHEBANG_CARRIAGE_RETURN] = { "shebang line ending with \\r may cause problems", PM_WARNING_LEVEL_DEFAULT }, - [PM_WARN_UNEXPECTED_CARRIAGE_RETURN] = { "encountered \\r in middle of line, treated as a mere space", PM_WARNING_LEVEL_DEFAULT } + [PM_WARN_UNEXPECTED_CARRIAGE_RETURN] = { "encountered \\r in middle of line, treated as a mere space", PM_WARNING_LEVEL_DEFAULT }, + [PM_WARN_UNREACHABLE_STATEMENT] = { "statement not reached", PM_WARNING_LEVEL_VERBOSE }, + [PM_WARN_UNUSED_LOCAL_VARIABLE] = { "assigned but unused variable - %.*s", PM_WARNING_LEVEL_VERBOSE }, + [PM_WARN_VOID_STATEMENT] = { "possibly useless use of %.*s in void context", PM_WARNING_LEVEL_VERBOSE } }; /** @@ -424,7 +444,6 @@ pm_diagnostic_id_human(pm_diagnostic_id_t diag_id) { case PM_ERR_DEF_ENDLESS: return "def_endless"; case PM_ERR_DEF_ENDLESS_SETTER: return "def_endless_setter"; case PM_ERR_DEF_NAME: return "def_name"; - case PM_ERR_DEF_NAME_AFTER_RECEIVER: return "def_name_after_receiver"; case PM_ERR_DEF_PARAMS_TERM: return "def_params_term"; case PM_ERR_DEF_PARAMS_TERM_PAREN: return "def_params_term_paren"; case PM_ERR_DEF_RECEIVER: return "def_receiver"; @@ -461,6 +480,7 @@ pm_diagnostic_id_human(pm_diagnostic_id_t diag_id) { case PM_ERR_EXPECT_EXPRESSION_AFTER_STAR: return "expect_expression_after_star"; case PM_ERR_EXPECT_IDENT_REQ_PARAMETER: return "expect_ident_req_parameter"; case PM_ERR_EXPECT_LPAREN_REQ_PARAMETER: return "expect_lparen_req_parameter"; + case PM_ERR_EXPECT_MESSAGE: return "expect_message"; case PM_ERR_EXPECT_RBRACKET: return "expect_rbracket"; case PM_ERR_EXPECT_RPAREN: return "expect_rparen"; case PM_ERR_EXPECT_RPAREN_AFTER_MULTI: return "expect_rparen_after_multi"; @@ -468,6 +488,14 @@ pm_diagnostic_id_human(pm_diagnostic_id_t diag_id) { case PM_ERR_EXPECT_STRING_CONTENT: return "expect_string_content"; case PM_ERR_EXPECT_WHEN_DELIMITER: return "expect_when_delimiter"; case PM_ERR_EXPRESSION_BARE_HASH: return "expression_bare_hash"; + case PM_ERR_EXPRESSION_NOT_WRITABLE: return "expression_not_writable"; + case PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING: return "expression_not_writable_encoding"; + case PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE: return "expression_not_writable_false"; + case PM_ERR_EXPRESSION_NOT_WRITABLE_FILE: return "expression_not_writable_file"; + case PM_ERR_EXPRESSION_NOT_WRITABLE_LINE: return "expression_not_writable_line"; + case PM_ERR_EXPRESSION_NOT_WRITABLE_NIL: return "expression_not_writable_nil"; + case PM_ERR_EXPRESSION_NOT_WRITABLE_SELF: return "expression_not_writable_self"; + case PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE: return "expression_not_writable_true"; case PM_ERR_FLOAT_PARSE: return "float_parse"; case PM_ERR_FOR_COLLECTION: return "for_collection"; case PM_ERR_FOR_IN: return "for_in"; @@ -486,9 +514,12 @@ pm_diagnostic_id_human(pm_diagnostic_id_t diag_id) { case PM_ERR_INCOMPLETE_VARIABLE_INSTANCE: return "incomplete_variable_instance"; case PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3_0: return "incomplete_variable_instance_3_3_0"; case PM_ERR_INSTANCE_VARIABLE_BARE: return "instance_variable_bare"; + case PM_ERR_INVALID_BLOCK_EXIT: return "invalid_block_exit"; case PM_ERR_INVALID_CHARACTER: return "invalid_character"; case PM_ERR_INVALID_ENCODING_MAGIC_COMMENT: return "invalid_encoding_magic_comment"; case PM_ERR_INVALID_FLOAT_EXPONENT: return "invalid_float_exponent"; + case PM_ERR_INVALID_LOCAL_VARIABLE_READ: return "invalid_local_variable_read"; + case PM_ERR_INVALID_LOCAL_VARIABLE_WRITE: return "invalid_local_variable_write"; case PM_ERR_INVALID_MULTIBYTE_CHAR: return "invalid_multibyte_char"; case PM_ERR_INVALID_MULTIBYTE_CHARACTER: return "invalid_multibyte_character"; case PM_ERR_INVALID_MULTIBYTE_ESCAPE: return "invalid_multibyte_escape"; @@ -499,8 +530,12 @@ pm_diagnostic_id_human(pm_diagnostic_id_t diag_id) { case PM_ERR_INVALID_NUMBER_UNDERSCORE: return "invalid_number_underscore"; case PM_ERR_INVALID_PERCENT: return "invalid_percent"; case PM_ERR_INVALID_PRINTABLE_CHARACTER: return "invalid_printable_character"; + case PM_ERR_INVALID_RETRY_AFTER_ELSE: return "invalid_retry_after_else"; + case PM_ERR_INVALID_RETRY_AFTER_ENSURE: return "invalid_retry_after_ensure"; + case PM_ERR_INVALID_RETRY_WITHOUT_RESCUE: return "invalid_retry_without_rescue"; case PM_ERR_INVALID_VARIABLE_GLOBAL: return "invalid_variable_global"; case PM_ERR_INVALID_VARIABLE_GLOBAL_3_3_0: return "invalid_variable_global_3_3_0"; + case PM_ERR_INVALID_YIELD: return "invalid_yield"; case PM_ERR_IT_NOT_ALLOWED_NUMBERED: return "it_not_allowed_numbered"; case PM_ERR_IT_NOT_ALLOWED_ORDINARY: return "it_not_allowed_ordinary"; case PM_ERR_LAMBDA_OPEN: return "lambda_open"; @@ -557,6 +592,7 @@ pm_diagnostic_id_human(pm_diagnostic_id_t diag_id) { case PM_ERR_PATTERN_HASH_KEY: return "pattern_hash_key"; case PM_ERR_PATTERN_HASH_KEY_DUPLICATE: return "pattern_hash_key_duplicate"; case PM_ERR_PATTERN_HASH_KEY_LABEL: return "pattern_hash_key_label"; + case PM_ERR_PATTERN_HASH_KEY_LOCALS: return "pattern_hash_key_locals"; case PM_ERR_PATTERN_IDENT_AFTER_HROCKET: return "pattern_ident_after_hrocket"; case PM_ERR_PATTERN_LABEL_AFTER_COMMA: return "pattern_label_after_comma"; case PM_ERR_PATTERN_REST: return "pattern_rest"; @@ -594,6 +630,7 @@ pm_diagnostic_id_human(pm_diagnostic_id_t diag_id) { case PM_ERR_TERNARY_EXPRESSION_TRUE: return "ternary_expression_true"; case PM_ERR_UNARY_RECEIVER: return "unary_receiver"; case PM_ERR_UNDEF_ARGUMENT: return "undef_argument"; + case PM_ERR_UNEXPECTED_BLOCK_ARGUMENT: return "unexpected_block_argument"; case PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT: return "unexpected_token_close_context"; case PM_ERR_UNEXPECTED_TOKEN_IGNORE: return "unexpected_token_ignore"; case PM_ERR_UNTIL_TERM: return "until_term"; @@ -627,6 +664,9 @@ pm_diagnostic_id_human(pm_diagnostic_id_t diag_id) { case PM_WARN_LITERAL_IN_CONDITION_VERBOSE: return "literal_in_condition_verbose"; case PM_WARN_SHEBANG_CARRIAGE_RETURN: return "shebang_carriage_return"; case PM_WARN_UNEXPECTED_CARRIAGE_RETURN: return "unexpected_carriage_return"; + case PM_WARN_UNREACHABLE_STATEMENT: return "unreachable_statement"; + case PM_WARN_UNUSED_LOCAL_VARIABLE: return "unused_local_variable"; + case PM_WARN_VOID_STATEMENT: return "void_statement"; } assert(false && "unreachable"); diff --git a/src/main/c/yarp/src/node.c b/src/main/c/yarp/src/node.c index 862502aeae9..27b4841b475 100644 --- a/src/main/c/yarp/src/node.c +++ b/src/main/c/yarp/src/node.c @@ -17,11 +17,9 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize); */ static size_t pm_node_list_memsize(pm_node_list_t *node_list, pm_memsize_t *memsize) { - size_t size = sizeof(pm_node_list_t) + (node_list->capacity * sizeof(pm_node_t *)); - for (size_t index = 0; index < node_list->size; index++) { - pm_node_memsize_node(node_list->nodes[index], memsize); - } - return size; + pm_node_t *node; + PM_NODE_LIST_FOREACH(node_list, index, node) pm_node_memsize_node(node, memsize); + return sizeof(pm_node_list_t) + (node_list->capacity * sizeof(pm_node_t *)); } /** @@ -30,13 +28,36 @@ pm_node_list_memsize(pm_node_list_t *node_list, pm_memsize_t *memsize) { * the list to be twice as large as it was before. If the reallocation fails, * this function returns false, otherwise it returns true. */ -bool -pm_node_list_grow(pm_node_list_t *list) { - if (list->size == list->capacity) { - list->capacity = list->capacity == 0 ? 4 : list->capacity * 2; - list->nodes = (pm_node_t **) xrealloc(list->nodes, sizeof(pm_node_t *) * list->capacity); - return list->nodes != NULL; +static bool +pm_node_list_grow(pm_node_list_t *list, size_t size) { + size_t requested_size = list->size + size; + + // If the requested size caused overflow, return false. + if (requested_size < list->size) return false; + + // If the requested size is within the existing capacity, return true. + if (requested_size < list->capacity) return true; + + // Otherwise, reallocate the list to be twice as large as it was before. + size_t next_capacity = list->capacity == 0 ? 4 : list->capacity * 2; + + // If multiplying by 2 caused overflow, return false. + if (next_capacity < list->capacity) return false; + + // If we didn't get enough by doubling, keep doubling until we do. + while (requested_size > next_capacity) { + size_t double_capacity = next_capacity * 2; + + // Ensure we didn't overflow by multiplying by 2. + if (double_capacity < next_capacity) return false; + next_capacity = double_capacity; } + + pm_node_t **nodes = (pm_node_t **) xrealloc(list->nodes, sizeof(pm_node_t *) * next_capacity); + if (nodes == NULL) return false; + + list->nodes = nodes; + list->capacity = next_capacity; return true; } @@ -45,7 +66,7 @@ pm_node_list_grow(pm_node_list_t *list) { */ void pm_node_list_append(pm_node_list_t *list, pm_node_t *node) { - if (pm_node_list_grow(list)) { + if (pm_node_list_grow(list, 1)) { list->nodes[list->size++] = node; } } @@ -55,13 +76,24 @@ pm_node_list_append(pm_node_list_t *list, pm_node_t *node) { */ void pm_node_list_prepend(pm_node_list_t *list, pm_node_t *node) { - if (pm_node_list_grow(list)) { + if (pm_node_list_grow(list, 1)) { memmove(list->nodes + 1, list->nodes, list->size * sizeof(pm_node_t *)); list->nodes[0] = node; list->size++; } } +/** + * Concatenate the given node list onto the end of the other node list. + */ +void +pm_node_list_concat(pm_node_list_t *list, pm_node_list_t *other) { + if (other->size > 0 && pm_node_list_grow(list, other->size)) { + memcpy(list->nodes + list->size, other->nodes, other->size * sizeof(pm_node_t *)); + list->size += other->size; + } +} + /** * Free the internal memory associated with the given node list. */ @@ -81,10 +113,8 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node); */ static void pm_node_list_destroy(pm_parser_t *parser, pm_node_list_t *list) { - for (size_t index = 0; index < list->size; index++) { - pm_node_destroy(parser, list->nodes[index]); - } - + pm_node_t *node; + PM_NODE_LIST_FOREACH(list, index, node) pm_node_destroy(parser, node); pm_node_list_free(list); } @@ -96,47 +126,47 @@ pm_node_list_destroy(pm_parser_t *parser, pm_node_list_t *list) { PRISM_EXPORTED_FUNCTION void pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { switch (PM_NODE_TYPE(node)) { -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_ALIAS_GLOBAL_VARIABLE_NODE: { pm_alias_global_variable_node_t *cast = (pm_alias_global_variable_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->new_name); pm_node_destroy(parser, (pm_node_t *)cast->old_name); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_ALIAS_METHOD_NODE: { pm_alias_method_node_t *cast = (pm_alias_method_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->new_name); pm_node_destroy(parser, (pm_node_t *)cast->old_name); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_ALTERNATION_PATTERN_NODE: { pm_alternation_pattern_node_t *cast = (pm_alternation_pattern_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->left); pm_node_destroy(parser, (pm_node_t *)cast->right); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_AND_NODE: { pm_and_node_t *cast = (pm_and_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->left); pm_node_destroy(parser, (pm_node_t *)cast->right); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_ARGUMENTS_NODE: { pm_arguments_node_t *cast = (pm_arguments_node_t *) node; pm_node_list_destroy(parser, &cast->arguments); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_ARRAY_NODE: { pm_array_node_t *cast = (pm_array_node_t *) node; pm_node_list_destroy(parser, &cast->elements); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_ARRAY_PATTERN_NODE: { pm_array_pattern_node_t *cast = (pm_array_pattern_node_t *) node; if (cast->constant != NULL) { @@ -149,14 +179,14 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { pm_node_list_destroy(parser, &cast->posts); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_ASSOC_NODE: { pm_assoc_node_t *cast = (pm_assoc_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->key); pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_ASSOC_SPLAT_NODE: { pm_assoc_splat_node_t *cast = (pm_assoc_splat_node_t *) node; if (cast->value != NULL) { @@ -164,11 +194,11 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_BACK_REFERENCE_READ_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_BEGIN_NODE: { pm_begin_node_t *cast = (pm_begin_node_t *) node; if (cast->statements != NULL) { @@ -185,7 +215,7 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_BLOCK_ARGUMENT_NODE: { pm_block_argument_node_t *cast = (pm_block_argument_node_t *) node; if (cast->expression != NULL) { @@ -193,11 +223,11 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_BLOCK_LOCAL_VARIABLE_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_BLOCK_NODE: { pm_block_node_t *cast = (pm_block_node_t *) node; pm_constant_id_list_free(&cast->locals); @@ -209,11 +239,11 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_BLOCK_PARAMETER_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_BLOCK_PARAMETERS_NODE: { pm_block_parameters_node_t *cast = (pm_block_parameters_node_t *) node; if (cast->parameters != NULL) { @@ -222,7 +252,7 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { pm_node_list_destroy(parser, &cast->locals); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_BREAK_NODE: { pm_break_node_t *cast = (pm_break_node_t *) node; if (cast->arguments != NULL) { @@ -230,7 +260,7 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CALL_AND_WRITE_NODE: { pm_call_and_write_node_t *cast = (pm_call_and_write_node_t *) node; if (cast->receiver != NULL) { @@ -239,7 +269,7 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CALL_NODE: { pm_call_node_t *cast = (pm_call_node_t *) node; if (cast->receiver != NULL) { @@ -253,7 +283,7 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CALL_OPERATOR_WRITE_NODE: { pm_call_operator_write_node_t *cast = (pm_call_operator_write_node_t *) node; if (cast->receiver != NULL) { @@ -262,7 +292,7 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CALL_OR_WRITE_NODE: { pm_call_or_write_node_t *cast = (pm_call_or_write_node_t *) node; if (cast->receiver != NULL) { @@ -271,20 +301,20 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CALL_TARGET_NODE: { pm_call_target_node_t *cast = (pm_call_target_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->receiver); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CAPTURE_PATTERN_NODE: { pm_capture_pattern_node_t *cast = (pm_capture_pattern_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); pm_node_destroy(parser, (pm_node_t *)cast->target); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CASE_MATCH_NODE: { pm_case_match_node_t *cast = (pm_case_match_node_t *) node; if (cast->predicate != NULL) { @@ -296,7 +326,7 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CASE_NODE: { pm_case_node_t *cast = (pm_case_node_t *) node; if (cast->predicate != NULL) { @@ -308,7 +338,7 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CLASS_NODE: { pm_class_node_t *cast = (pm_class_node_t *) node; pm_constant_id_list_free(&cast->locals); @@ -321,64 +351,64 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CLASS_VARIABLE_AND_WRITE_NODE: { pm_class_variable_and_write_node_t *cast = (pm_class_variable_and_write_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: { pm_class_variable_operator_write_node_t *cast = (pm_class_variable_operator_write_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CLASS_VARIABLE_OR_WRITE_NODE: { pm_class_variable_or_write_node_t *cast = (pm_class_variable_or_write_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CLASS_VARIABLE_READ_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CLASS_VARIABLE_TARGET_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CLASS_VARIABLE_WRITE_NODE: { pm_class_variable_write_node_t *cast = (pm_class_variable_write_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CONSTANT_AND_WRITE_NODE: { pm_constant_and_write_node_t *cast = (pm_constant_and_write_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CONSTANT_OPERATOR_WRITE_NODE: { pm_constant_operator_write_node_t *cast = (pm_constant_operator_write_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CONSTANT_OR_WRITE_NODE: { pm_constant_or_write_node_t *cast = (pm_constant_or_write_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CONSTANT_PATH_AND_WRITE_NODE: { pm_constant_path_and_write_node_t *cast = (pm_constant_path_and_write_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->target); pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CONSTANT_PATH_NODE: { pm_constant_path_node_t *cast = (pm_constant_path_node_t *) node; if (cast->parent != NULL) { @@ -387,21 +417,21 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { pm_node_destroy(parser, (pm_node_t *)cast->child); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: { pm_constant_path_operator_write_node_t *cast = (pm_constant_path_operator_write_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->target); pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CONSTANT_PATH_OR_WRITE_NODE: { pm_constant_path_or_write_node_t *cast = (pm_constant_path_or_write_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->target); pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CONSTANT_PATH_TARGET_NODE: { pm_constant_path_target_node_t *cast = (pm_constant_path_target_node_t *) node; if (cast->parent != NULL) { @@ -410,28 +440,28 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { pm_node_destroy(parser, (pm_node_t *)cast->child); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CONSTANT_PATH_WRITE_NODE: { pm_constant_path_write_node_t *cast = (pm_constant_path_write_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->target); pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CONSTANT_READ_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CONSTANT_TARGET_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_CONSTANT_WRITE_NODE: { pm_constant_write_node_t *cast = (pm_constant_write_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_DEF_NODE: { pm_def_node_t *cast = (pm_def_node_t *) node; if (cast->receiver != NULL) { @@ -446,13 +476,13 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { pm_constant_id_list_free(&cast->locals); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_DEFINED_NODE: { pm_defined_node_t *cast = (pm_defined_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_ELSE_NODE: { pm_else_node_t *cast = (pm_else_node_t *) node; if (cast->statements != NULL) { @@ -460,7 +490,7 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_EMBEDDED_STATEMENTS_NODE: { pm_embedded_statements_node_t *cast = (pm_embedded_statements_node_t *) node; if (cast->statements != NULL) { @@ -468,13 +498,13 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_EMBEDDED_VARIABLE_NODE: { pm_embedded_variable_node_t *cast = (pm_embedded_variable_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->variable); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_ENSURE_NODE: { pm_ensure_node_t *cast = (pm_ensure_node_t *) node; if (cast->statements != NULL) { @@ -482,11 +512,11 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_FALSE_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_FIND_PATTERN_NODE: { pm_find_pattern_node_t *cast = (pm_find_pattern_node_t *) node; if (cast->constant != NULL) { @@ -497,7 +527,7 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { pm_node_destroy(parser, (pm_node_t *)cast->right); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_FLIP_FLOP_NODE: { pm_flip_flop_node_t *cast = (pm_flip_flop_node_t *) node; if (cast->left != NULL) { @@ -508,11 +538,11 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_FLOAT_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_FOR_NODE: { pm_for_node_t *cast = (pm_for_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->index); @@ -522,15 +552,15 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_FORWARDING_ARGUMENTS_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_FORWARDING_PARAMETER_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_FORWARDING_SUPER_NODE: { pm_forwarding_super_node_t *cast = (pm_forwarding_super_node_t *) node; if (cast->block != NULL) { @@ -538,45 +568,45 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: { pm_global_variable_and_write_node_t *cast = (pm_global_variable_and_write_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: { pm_global_variable_operator_write_node_t *cast = (pm_global_variable_operator_write_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: { pm_global_variable_or_write_node_t *cast = (pm_global_variable_or_write_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_GLOBAL_VARIABLE_READ_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_GLOBAL_VARIABLE_TARGET_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_GLOBAL_VARIABLE_WRITE_NODE: { pm_global_variable_write_node_t *cast = (pm_global_variable_write_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_HASH_NODE: { pm_hash_node_t *cast = (pm_hash_node_t *) node; pm_node_list_destroy(parser, &cast->elements); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_HASH_PATTERN_NODE: { pm_hash_pattern_node_t *cast = (pm_hash_pattern_node_t *) node; if (cast->constant != NULL) { @@ -588,7 +618,7 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_IF_NODE: { pm_if_node_t *cast = (pm_if_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->predicate); @@ -600,23 +630,23 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_IMAGINARY_NODE: { pm_imaginary_node_t *cast = (pm_imaginary_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->numeric); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_IMPLICIT_NODE: { pm_implicit_node_t *cast = (pm_implicit_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_IMPLICIT_REST_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_IN_NODE: { pm_in_node_t *cast = (pm_in_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->pattern); @@ -625,7 +655,7 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_INDEX_AND_WRITE_NODE: { pm_index_and_write_node_t *cast = (pm_index_and_write_node_t *) node; if (cast->receiver != NULL) { @@ -640,7 +670,7 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_INDEX_OPERATOR_WRITE_NODE: { pm_index_operator_write_node_t *cast = (pm_index_operator_write_node_t *) node; if (cast->receiver != NULL) { @@ -655,7 +685,7 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_INDEX_OR_WRITE_NODE: { pm_index_or_write_node_t *cast = (pm_index_or_write_node_t *) node; if (cast->receiver != NULL) { @@ -670,7 +700,7 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_INDEX_TARGET_NODE: { pm_index_target_node_t *cast = (pm_index_target_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->receiver); @@ -682,89 +712,89 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: { pm_instance_variable_and_write_node_t *cast = (pm_instance_variable_and_write_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: { pm_instance_variable_operator_write_node_t *cast = (pm_instance_variable_operator_write_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: { pm_instance_variable_or_write_node_t *cast = (pm_instance_variable_or_write_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_INSTANCE_VARIABLE_READ_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_INSTANCE_VARIABLE_TARGET_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_INSTANCE_VARIABLE_WRITE_NODE: { pm_instance_variable_write_node_t *cast = (pm_instance_variable_write_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_INTEGER_NODE: { pm_integer_node_t *cast = (pm_integer_node_t *) node; pm_integer_free(&cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: { pm_interpolated_match_last_line_node_t *cast = (pm_interpolated_match_last_line_node_t *) node; pm_node_list_destroy(parser, &cast->parts); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: { pm_interpolated_regular_expression_node_t *cast = (pm_interpolated_regular_expression_node_t *) node; pm_node_list_destroy(parser, &cast->parts); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_INTERPOLATED_STRING_NODE: { pm_interpolated_string_node_t *cast = (pm_interpolated_string_node_t *) node; pm_node_list_destroy(parser, &cast->parts); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_INTERPOLATED_SYMBOL_NODE: { pm_interpolated_symbol_node_t *cast = (pm_interpolated_symbol_node_t *) node; pm_node_list_destroy(parser, &cast->parts); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_INTERPOLATED_X_STRING_NODE: { pm_interpolated_x_string_node_t *cast = (pm_interpolated_x_string_node_t *) node; pm_node_list_destroy(parser, &cast->parts); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_IT_PARAMETERS_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_KEYWORD_HASH_NODE: { pm_keyword_hash_node_t *cast = (pm_keyword_hash_node_t *) node; pm_node_list_destroy(parser, &cast->elements); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_KEYWORD_REST_PARAMETER_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_LAMBDA_NODE: { pm_lambda_node_t *cast = (pm_lambda_node_t *) node; pm_constant_id_list_free(&cast->locals); @@ -776,70 +806,70 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_LOCAL_VARIABLE_AND_WRITE_NODE: { pm_local_variable_and_write_node_t *cast = (pm_local_variable_and_write_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: { pm_local_variable_operator_write_node_t *cast = (pm_local_variable_operator_write_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_LOCAL_VARIABLE_OR_WRITE_NODE: { pm_local_variable_or_write_node_t *cast = (pm_local_variable_or_write_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_LOCAL_VARIABLE_READ_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_LOCAL_VARIABLE_TARGET_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_LOCAL_VARIABLE_WRITE_NODE: { pm_local_variable_write_node_t *cast = (pm_local_variable_write_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_MATCH_LAST_LINE_NODE: { pm_match_last_line_node_t *cast = (pm_match_last_line_node_t *) node; pm_string_free(&cast->unescaped); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_MATCH_PREDICATE_NODE: { pm_match_predicate_node_t *cast = (pm_match_predicate_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); pm_node_destroy(parser, (pm_node_t *)cast->pattern); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_MATCH_REQUIRED_NODE: { pm_match_required_node_t *cast = (pm_match_required_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); pm_node_destroy(parser, (pm_node_t *)cast->pattern); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_MATCH_WRITE_NODE: { pm_match_write_node_t *cast = (pm_match_write_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->call); pm_node_list_destroy(parser, &cast->targets); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_MISSING_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_MODULE_NODE: { pm_module_node_t *cast = (pm_module_node_t *) node; pm_constant_id_list_free(&cast->locals); @@ -849,7 +879,7 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_MULTI_TARGET_NODE: { pm_multi_target_node_t *cast = (pm_multi_target_node_t *) node; pm_node_list_destroy(parser, &cast->lefts); @@ -859,7 +889,7 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { pm_node_list_destroy(parser, &cast->rights); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_MULTI_WRITE_NODE: { pm_multi_write_node_t *cast = (pm_multi_write_node_t *) node; pm_node_list_destroy(parser, &cast->lefts); @@ -870,7 +900,7 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_NEXT_NODE: { pm_next_node_t *cast = (pm_next_node_t *) node; if (cast->arguments != NULL) { @@ -878,42 +908,42 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_NIL_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_NO_KEYWORDS_PARAMETER_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_NUMBERED_PARAMETERS_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_NUMBERED_REFERENCE_READ_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_OPTIONAL_KEYWORD_PARAMETER_NODE: { pm_optional_keyword_parameter_node_t *cast = (pm_optional_keyword_parameter_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_OPTIONAL_PARAMETER_NODE: { pm_optional_parameter_node_t *cast = (pm_optional_parameter_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->value); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_OR_NODE: { pm_or_node_t *cast = (pm_or_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->left); pm_node_destroy(parser, (pm_node_t *)cast->right); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_PARAMETERS_NODE: { pm_parameters_node_t *cast = (pm_parameters_node_t *) node; pm_node_list_destroy(parser, &cast->requireds); @@ -931,7 +961,7 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_PARENTHESES_NODE: { pm_parentheses_node_t *cast = (pm_parentheses_node_t *) node; if (cast->body != NULL) { @@ -939,19 +969,19 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_PINNED_EXPRESSION_NODE: { pm_pinned_expression_node_t *cast = (pm_pinned_expression_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->expression); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_PINNED_VARIABLE_NODE: { pm_pinned_variable_node_t *cast = (pm_pinned_variable_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->variable); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_POST_EXECUTION_NODE: { pm_post_execution_node_t *cast = (pm_post_execution_node_t *) node; if (cast->statements != NULL) { @@ -959,7 +989,7 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_PRE_EXECUTION_NODE: { pm_pre_execution_node_t *cast = (pm_pre_execution_node_t *) node; if (cast->statements != NULL) { @@ -967,14 +997,14 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_PROGRAM_NODE: { pm_program_node_t *cast = (pm_program_node_t *) node; pm_constant_id_list_free(&cast->locals); pm_node_destroy(parser, (pm_node_t *)cast->statements); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_RANGE_NODE: { pm_range_node_t *cast = (pm_range_node_t *) node; if (cast->left != NULL) { @@ -985,38 +1015,38 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_RATIONAL_NODE: { pm_rational_node_t *cast = (pm_rational_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->numeric); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_REDO_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_REGULAR_EXPRESSION_NODE: { pm_regular_expression_node_t *cast = (pm_regular_expression_node_t *) node; pm_string_free(&cast->unescaped); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_REQUIRED_KEYWORD_PARAMETER_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_REQUIRED_PARAMETER_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_RESCUE_MODIFIER_NODE: { pm_rescue_modifier_node_t *cast = (pm_rescue_modifier_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->expression); pm_node_destroy(parser, (pm_node_t *)cast->rescue_expression); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_RESCUE_NODE: { pm_rescue_node_t *cast = (pm_rescue_node_t *) node; pm_node_list_destroy(parser, &cast->exceptions); @@ -1031,15 +1061,15 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_REST_PARAMETER_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_RETRY_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_RETURN_NODE: { pm_return_node_t *cast = (pm_return_node_t *) node; if (cast->arguments != NULL) { @@ -1047,17 +1077,17 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_SELF_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_SHAREABLE_CONSTANT_NODE: { pm_shareable_constant_node_t *cast = (pm_shareable_constant_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->write); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_SINGLETON_CLASS_NODE: { pm_singleton_class_node_t *cast = (pm_singleton_class_node_t *) node; pm_constant_id_list_free(&cast->locals); @@ -1067,21 +1097,21 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_SOURCE_ENCODING_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_SOURCE_FILE_NODE: { pm_source_file_node_t *cast = (pm_source_file_node_t *) node; pm_string_free(&cast->filepath); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_SOURCE_LINE_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_SPLAT_NODE: { pm_splat_node_t *cast = (pm_splat_node_t *) node; if (cast->expression != NULL) { @@ -1089,19 +1119,19 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_STATEMENTS_NODE: { pm_statements_node_t *cast = (pm_statements_node_t *) node; pm_node_list_destroy(parser, &cast->body); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_STRING_NODE: { pm_string_node_t *cast = (pm_string_node_t *) node; pm_string_free(&cast->unescaped); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_SUPER_NODE: { pm_super_node_t *cast = (pm_super_node_t *) node; if (cast->arguments != NULL) { @@ -1112,23 +1142,23 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_SYMBOL_NODE: { pm_symbol_node_t *cast = (pm_symbol_node_t *) node; pm_string_free(&cast->unescaped); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_TRUE_NODE: { break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_UNDEF_NODE: { pm_undef_node_t *cast = (pm_undef_node_t *) node; pm_node_list_destroy(parser, &cast->names); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_UNLESS_NODE: { pm_unless_node_t *cast = (pm_unless_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->predicate); @@ -1140,7 +1170,7 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_UNTIL_NODE: { pm_until_node_t *cast = (pm_until_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->predicate); @@ -1149,7 +1179,7 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_WHEN_NODE: { pm_when_node_t *cast = (pm_when_node_t *) node; pm_node_list_destroy(parser, &cast->conditions); @@ -1158,7 +1188,7 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_WHILE_NODE: { pm_while_node_t *cast = (pm_while_node_t *) node; pm_node_destroy(parser, (pm_node_t *)cast->predicate); @@ -1167,13 +1197,13 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_X_STRING_NODE: { pm_x_string_node_t *cast = (pm_x_string_node_t *) node; pm_string_free(&cast->unescaped); break; } -#line 93 "node.c.erb" +#line 123 "node.c.erb" case PM_YIELD_NODE: { pm_yield_node_t *cast = (pm_yield_node_t *) node; if (cast->arguments != NULL) { @@ -1181,7 +1211,7 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { } break; } -#line 122 "node.c.erb" +#line 152 "node.c.erb" default: assert(false && "unreachable"); break; @@ -1198,7 +1228,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { // as it should never be generated case PM_SCOPE_NODE: return; -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_ALIAS_GLOBAL_VARIABLE_NODE: { pm_alias_global_variable_node_t *cast = (pm_alias_global_variable_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1206,7 +1236,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { pm_node_memsize_node((pm_node_t *)cast->old_name, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_ALIAS_METHOD_NODE: { pm_alias_method_node_t *cast = (pm_alias_method_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1214,7 +1244,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { pm_node_memsize_node((pm_node_t *)cast->old_name, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_ALTERNATION_PATTERN_NODE: { pm_alternation_pattern_node_t *cast = (pm_alternation_pattern_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1222,7 +1252,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { pm_node_memsize_node((pm_node_t *)cast->right, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_AND_NODE: { pm_and_node_t *cast = (pm_and_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1230,21 +1260,21 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { pm_node_memsize_node((pm_node_t *)cast->right, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_ARGUMENTS_NODE: { pm_arguments_node_t *cast = (pm_arguments_node_t *) node; memsize->memsize += sizeof(*cast); memsize->memsize += (pm_node_list_memsize(&cast->arguments, memsize) - sizeof(pm_node_list_t)); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_ARRAY_NODE: { pm_array_node_t *cast = (pm_array_node_t *) node; memsize->memsize += sizeof(*cast); memsize->memsize += (pm_node_list_memsize(&cast->elements, memsize) - sizeof(pm_node_list_t)); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_ARRAY_PATTERN_NODE: { pm_array_pattern_node_t *cast = (pm_array_pattern_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1258,7 +1288,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { memsize->memsize += (pm_node_list_memsize(&cast->posts, memsize) - sizeof(pm_node_list_t)); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_ASSOC_NODE: { pm_assoc_node_t *cast = (pm_assoc_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1266,7 +1296,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_ASSOC_SPLAT_NODE: { pm_assoc_splat_node_t *cast = (pm_assoc_splat_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1275,13 +1305,13 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_BACK_REFERENCE_READ_NODE: { pm_back_reference_read_node_t *cast = (pm_back_reference_read_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_BEGIN_NODE: { pm_begin_node_t *cast = (pm_begin_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1299,7 +1329,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_BLOCK_ARGUMENT_NODE: { pm_block_argument_node_t *cast = (pm_block_argument_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1308,13 +1338,13 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_BLOCK_LOCAL_VARIABLE_NODE: { pm_block_local_variable_node_t *cast = (pm_block_local_variable_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_BLOCK_NODE: { pm_block_node_t *cast = (pm_block_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1327,13 +1357,13 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_BLOCK_PARAMETER_NODE: { pm_block_parameter_node_t *cast = (pm_block_parameter_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_BLOCK_PARAMETERS_NODE: { pm_block_parameters_node_t *cast = (pm_block_parameters_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1343,7 +1373,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { memsize->memsize += (pm_node_list_memsize(&cast->locals, memsize) - sizeof(pm_node_list_t)); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_BREAK_NODE: { pm_break_node_t *cast = (pm_break_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1352,7 +1382,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CALL_AND_WRITE_NODE: { pm_call_and_write_node_t *cast = (pm_call_and_write_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1362,7 +1392,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CALL_NODE: { pm_call_node_t *cast = (pm_call_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1377,7 +1407,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CALL_OPERATOR_WRITE_NODE: { pm_call_operator_write_node_t *cast = (pm_call_operator_write_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1387,7 +1417,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CALL_OR_WRITE_NODE: { pm_call_or_write_node_t *cast = (pm_call_or_write_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1397,14 +1427,14 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CALL_TARGET_NODE: { pm_call_target_node_t *cast = (pm_call_target_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->receiver, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CAPTURE_PATTERN_NODE: { pm_capture_pattern_node_t *cast = (pm_capture_pattern_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1412,7 +1442,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { pm_node_memsize_node((pm_node_t *)cast->target, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CASE_MATCH_NODE: { pm_case_match_node_t *cast = (pm_case_match_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1425,7 +1455,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CASE_NODE: { pm_case_node_t *cast = (pm_case_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1438,7 +1468,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CLASS_NODE: { pm_class_node_t *cast = (pm_class_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1452,68 +1482,68 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CLASS_VARIABLE_AND_WRITE_NODE: { pm_class_variable_and_write_node_t *cast = (pm_class_variable_and_write_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE: { pm_class_variable_operator_write_node_t *cast = (pm_class_variable_operator_write_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CLASS_VARIABLE_OR_WRITE_NODE: { pm_class_variable_or_write_node_t *cast = (pm_class_variable_or_write_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CLASS_VARIABLE_READ_NODE: { pm_class_variable_read_node_t *cast = (pm_class_variable_read_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CLASS_VARIABLE_TARGET_NODE: { pm_class_variable_target_node_t *cast = (pm_class_variable_target_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CLASS_VARIABLE_WRITE_NODE: { pm_class_variable_write_node_t *cast = (pm_class_variable_write_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CONSTANT_AND_WRITE_NODE: { pm_constant_and_write_node_t *cast = (pm_constant_and_write_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CONSTANT_OPERATOR_WRITE_NODE: { pm_constant_operator_write_node_t *cast = (pm_constant_operator_write_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CONSTANT_OR_WRITE_NODE: { pm_constant_or_write_node_t *cast = (pm_constant_or_write_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CONSTANT_PATH_AND_WRITE_NODE: { pm_constant_path_and_write_node_t *cast = (pm_constant_path_and_write_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1521,7 +1551,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CONSTANT_PATH_NODE: { pm_constant_path_node_t *cast = (pm_constant_path_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1531,7 +1561,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { pm_node_memsize_node((pm_node_t *)cast->child, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CONSTANT_PATH_OPERATOR_WRITE_NODE: { pm_constant_path_operator_write_node_t *cast = (pm_constant_path_operator_write_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1539,7 +1569,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CONSTANT_PATH_OR_WRITE_NODE: { pm_constant_path_or_write_node_t *cast = (pm_constant_path_or_write_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1547,7 +1577,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CONSTANT_PATH_TARGET_NODE: { pm_constant_path_target_node_t *cast = (pm_constant_path_target_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1557,7 +1587,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { pm_node_memsize_node((pm_node_t *)cast->child, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CONSTANT_PATH_WRITE_NODE: { pm_constant_path_write_node_t *cast = (pm_constant_path_write_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1565,26 +1595,26 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CONSTANT_READ_NODE: { pm_constant_read_node_t *cast = (pm_constant_read_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CONSTANT_TARGET_NODE: { pm_constant_target_node_t *cast = (pm_constant_target_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_CONSTANT_WRITE_NODE: { pm_constant_write_node_t *cast = (pm_constant_write_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_DEF_NODE: { pm_def_node_t *cast = (pm_def_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1600,14 +1630,14 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { memsize->memsize += (pm_constant_id_list_memsize(&cast->locals) - sizeof(pm_constant_id_list_t)); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_DEFINED_NODE: { pm_defined_node_t *cast = (pm_defined_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_ELSE_NODE: { pm_else_node_t *cast = (pm_else_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1616,7 +1646,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_EMBEDDED_STATEMENTS_NODE: { pm_embedded_statements_node_t *cast = (pm_embedded_statements_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1625,14 +1655,14 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_EMBEDDED_VARIABLE_NODE: { pm_embedded_variable_node_t *cast = (pm_embedded_variable_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->variable, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_ENSURE_NODE: { pm_ensure_node_t *cast = (pm_ensure_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1641,13 +1671,13 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_FALSE_NODE: { pm_false_node_t *cast = (pm_false_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_FIND_PATTERN_NODE: { pm_find_pattern_node_t *cast = (pm_find_pattern_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1659,7 +1689,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { pm_node_memsize_node((pm_node_t *)cast->right, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_FLIP_FLOP_NODE: { pm_flip_flop_node_t *cast = (pm_flip_flop_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1671,13 +1701,13 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_FLOAT_NODE: { pm_float_node_t *cast = (pm_float_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_FOR_NODE: { pm_for_node_t *cast = (pm_for_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1688,19 +1718,19 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_FORWARDING_ARGUMENTS_NODE: { pm_forwarding_arguments_node_t *cast = (pm_forwarding_arguments_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_FORWARDING_PARAMETER_NODE: { pm_forwarding_parameter_node_t *cast = (pm_forwarding_parameter_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_FORWARDING_SUPER_NODE: { pm_forwarding_super_node_t *cast = (pm_forwarding_super_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1709,54 +1739,54 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_GLOBAL_VARIABLE_AND_WRITE_NODE: { pm_global_variable_and_write_node_t *cast = (pm_global_variable_and_write_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: { pm_global_variable_operator_write_node_t *cast = (pm_global_variable_operator_write_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_GLOBAL_VARIABLE_OR_WRITE_NODE: { pm_global_variable_or_write_node_t *cast = (pm_global_variable_or_write_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_GLOBAL_VARIABLE_READ_NODE: { pm_global_variable_read_node_t *cast = (pm_global_variable_read_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_GLOBAL_VARIABLE_TARGET_NODE: { pm_global_variable_target_node_t *cast = (pm_global_variable_target_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_GLOBAL_VARIABLE_WRITE_NODE: { pm_global_variable_write_node_t *cast = (pm_global_variable_write_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_HASH_NODE: { pm_hash_node_t *cast = (pm_hash_node_t *) node; memsize->memsize += sizeof(*cast); memsize->memsize += (pm_node_list_memsize(&cast->elements, memsize) - sizeof(pm_node_list_t)); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_HASH_PATTERN_NODE: { pm_hash_pattern_node_t *cast = (pm_hash_pattern_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1769,7 +1799,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_IF_NODE: { pm_if_node_t *cast = (pm_if_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1782,27 +1812,27 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_IMAGINARY_NODE: { pm_imaginary_node_t *cast = (pm_imaginary_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->numeric, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_IMPLICIT_NODE: { pm_implicit_node_t *cast = (pm_implicit_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_IMPLICIT_REST_NODE: { pm_implicit_rest_node_t *cast = (pm_implicit_rest_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_IN_NODE: { pm_in_node_t *cast = (pm_in_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1812,7 +1842,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_INDEX_AND_WRITE_NODE: { pm_index_and_write_node_t *cast = (pm_index_and_write_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1828,7 +1858,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_INDEX_OPERATOR_WRITE_NODE: { pm_index_operator_write_node_t *cast = (pm_index_operator_write_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1844,7 +1874,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_INDEX_OR_WRITE_NODE: { pm_index_or_write_node_t *cast = (pm_index_or_write_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1860,7 +1890,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_INDEX_TARGET_NODE: { pm_index_target_node_t *cast = (pm_index_target_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1873,108 +1903,108 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_INSTANCE_VARIABLE_AND_WRITE_NODE: { pm_instance_variable_and_write_node_t *cast = (pm_instance_variable_and_write_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: { pm_instance_variable_operator_write_node_t *cast = (pm_instance_variable_operator_write_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_INSTANCE_VARIABLE_OR_WRITE_NODE: { pm_instance_variable_or_write_node_t *cast = (pm_instance_variable_or_write_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_INSTANCE_VARIABLE_READ_NODE: { pm_instance_variable_read_node_t *cast = (pm_instance_variable_read_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_INSTANCE_VARIABLE_TARGET_NODE: { pm_instance_variable_target_node_t *cast = (pm_instance_variable_target_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_INSTANCE_VARIABLE_WRITE_NODE: { pm_instance_variable_write_node_t *cast = (pm_instance_variable_write_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_INTEGER_NODE: { pm_integer_node_t *cast = (pm_integer_node_t *) node; memsize->memsize += sizeof(*cast); memsize->memsize += (pm_integer_memsize(&cast->value) - sizeof(pm_integer_t)); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_INTERPOLATED_MATCH_LAST_LINE_NODE: { pm_interpolated_match_last_line_node_t *cast = (pm_interpolated_match_last_line_node_t *) node; memsize->memsize += sizeof(*cast); memsize->memsize += (pm_node_list_memsize(&cast->parts, memsize) - sizeof(pm_node_list_t)); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: { pm_interpolated_regular_expression_node_t *cast = (pm_interpolated_regular_expression_node_t *) node; memsize->memsize += sizeof(*cast); memsize->memsize += (pm_node_list_memsize(&cast->parts, memsize) - sizeof(pm_node_list_t)); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_INTERPOLATED_STRING_NODE: { pm_interpolated_string_node_t *cast = (pm_interpolated_string_node_t *) node; memsize->memsize += sizeof(*cast); memsize->memsize += (pm_node_list_memsize(&cast->parts, memsize) - sizeof(pm_node_list_t)); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_INTERPOLATED_SYMBOL_NODE: { pm_interpolated_symbol_node_t *cast = (pm_interpolated_symbol_node_t *) node; memsize->memsize += sizeof(*cast); memsize->memsize += (pm_node_list_memsize(&cast->parts, memsize) - sizeof(pm_node_list_t)); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_INTERPOLATED_X_STRING_NODE: { pm_interpolated_x_string_node_t *cast = (pm_interpolated_x_string_node_t *) node; memsize->memsize += sizeof(*cast); memsize->memsize += (pm_node_list_memsize(&cast->parts, memsize) - sizeof(pm_node_list_t)); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_IT_PARAMETERS_NODE: { pm_it_parameters_node_t *cast = (pm_it_parameters_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_KEYWORD_HASH_NODE: { pm_keyword_hash_node_t *cast = (pm_keyword_hash_node_t *) node; memsize->memsize += sizeof(*cast); memsize->memsize += (pm_node_list_memsize(&cast->elements, memsize) - sizeof(pm_node_list_t)); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_KEYWORD_REST_PARAMETER_NODE: { pm_keyword_rest_parameter_node_t *cast = (pm_keyword_rest_parameter_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_LAMBDA_NODE: { pm_lambda_node_t *cast = (pm_lambda_node_t *) node; memsize->memsize += sizeof(*cast); @@ -1987,54 +2017,54 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_LOCAL_VARIABLE_AND_WRITE_NODE: { pm_local_variable_and_write_node_t *cast = (pm_local_variable_and_write_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: { pm_local_variable_operator_write_node_t *cast = (pm_local_variable_operator_write_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_LOCAL_VARIABLE_OR_WRITE_NODE: { pm_local_variable_or_write_node_t *cast = (pm_local_variable_or_write_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_LOCAL_VARIABLE_READ_NODE: { pm_local_variable_read_node_t *cast = (pm_local_variable_read_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_LOCAL_VARIABLE_TARGET_NODE: { pm_local_variable_target_node_t *cast = (pm_local_variable_target_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_LOCAL_VARIABLE_WRITE_NODE: { pm_local_variable_write_node_t *cast = (pm_local_variable_write_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_MATCH_LAST_LINE_NODE: { pm_match_last_line_node_t *cast = (pm_match_last_line_node_t *) node; memsize->memsize += sizeof(*cast); memsize->memsize += (pm_string_memsize(&cast->unescaped) - sizeof(pm_string_t)); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_MATCH_PREDICATE_NODE: { pm_match_predicate_node_t *cast = (pm_match_predicate_node_t *) node; memsize->memsize += sizeof(*cast); @@ -2042,7 +2072,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { pm_node_memsize_node((pm_node_t *)cast->pattern, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_MATCH_REQUIRED_NODE: { pm_match_required_node_t *cast = (pm_match_required_node_t *) node; memsize->memsize += sizeof(*cast); @@ -2050,7 +2080,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { pm_node_memsize_node((pm_node_t *)cast->pattern, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_MATCH_WRITE_NODE: { pm_match_write_node_t *cast = (pm_match_write_node_t *) node; memsize->memsize += sizeof(*cast); @@ -2058,13 +2088,13 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { memsize->memsize += (pm_node_list_memsize(&cast->targets, memsize) - sizeof(pm_node_list_t)); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_MISSING_NODE: { pm_missing_node_t *cast = (pm_missing_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_MODULE_NODE: { pm_module_node_t *cast = (pm_module_node_t *) node; memsize->memsize += sizeof(*cast); @@ -2075,7 +2105,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_MULTI_TARGET_NODE: { pm_multi_target_node_t *cast = (pm_multi_target_node_t *) node; memsize->memsize += sizeof(*cast); @@ -2086,7 +2116,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { memsize->memsize += (pm_node_list_memsize(&cast->rights, memsize) - sizeof(pm_node_list_t)); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_MULTI_WRITE_NODE: { pm_multi_write_node_t *cast = (pm_multi_write_node_t *) node; memsize->memsize += sizeof(*cast); @@ -2098,7 +2128,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_NEXT_NODE: { pm_next_node_t *cast = (pm_next_node_t *) node; memsize->memsize += sizeof(*cast); @@ -2107,45 +2137,45 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_NIL_NODE: { pm_nil_node_t *cast = (pm_nil_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_NO_KEYWORDS_PARAMETER_NODE: { pm_no_keywords_parameter_node_t *cast = (pm_no_keywords_parameter_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_NUMBERED_PARAMETERS_NODE: { pm_numbered_parameters_node_t *cast = (pm_numbered_parameters_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_NUMBERED_REFERENCE_READ_NODE: { pm_numbered_reference_read_node_t *cast = (pm_numbered_reference_read_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_OPTIONAL_KEYWORD_PARAMETER_NODE: { pm_optional_keyword_parameter_node_t *cast = (pm_optional_keyword_parameter_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_OPTIONAL_PARAMETER_NODE: { pm_optional_parameter_node_t *cast = (pm_optional_parameter_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->value, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_OR_NODE: { pm_or_node_t *cast = (pm_or_node_t *) node; memsize->memsize += sizeof(*cast); @@ -2153,7 +2183,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { pm_node_memsize_node((pm_node_t *)cast->right, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_PARAMETERS_NODE: { pm_parameters_node_t *cast = (pm_parameters_node_t *) node; memsize->memsize += sizeof(*cast); @@ -2172,7 +2202,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_PARENTHESES_NODE: { pm_parentheses_node_t *cast = (pm_parentheses_node_t *) node; memsize->memsize += sizeof(*cast); @@ -2181,21 +2211,21 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_PINNED_EXPRESSION_NODE: { pm_pinned_expression_node_t *cast = (pm_pinned_expression_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->expression, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_PINNED_VARIABLE_NODE: { pm_pinned_variable_node_t *cast = (pm_pinned_variable_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->variable, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_POST_EXECUTION_NODE: { pm_post_execution_node_t *cast = (pm_post_execution_node_t *) node; memsize->memsize += sizeof(*cast); @@ -2204,7 +2234,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_PRE_EXECUTION_NODE: { pm_pre_execution_node_t *cast = (pm_pre_execution_node_t *) node; memsize->memsize += sizeof(*cast); @@ -2213,7 +2243,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_PROGRAM_NODE: { pm_program_node_t *cast = (pm_program_node_t *) node; memsize->memsize += sizeof(*cast); @@ -2221,7 +2251,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { pm_node_memsize_node((pm_node_t *)cast->statements, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_RANGE_NODE: { pm_range_node_t *cast = (pm_range_node_t *) node; memsize->memsize += sizeof(*cast); @@ -2233,39 +2263,39 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_RATIONAL_NODE: { pm_rational_node_t *cast = (pm_rational_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->numeric, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_REDO_NODE: { pm_redo_node_t *cast = (pm_redo_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_REGULAR_EXPRESSION_NODE: { pm_regular_expression_node_t *cast = (pm_regular_expression_node_t *) node; memsize->memsize += sizeof(*cast); memsize->memsize += (pm_string_memsize(&cast->unescaped) - sizeof(pm_string_t)); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_REQUIRED_KEYWORD_PARAMETER_NODE: { pm_required_keyword_parameter_node_t *cast = (pm_required_keyword_parameter_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_REQUIRED_PARAMETER_NODE: { pm_required_parameter_node_t *cast = (pm_required_parameter_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_RESCUE_MODIFIER_NODE: { pm_rescue_modifier_node_t *cast = (pm_rescue_modifier_node_t *) node; memsize->memsize += sizeof(*cast); @@ -2273,7 +2303,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { pm_node_memsize_node((pm_node_t *)cast->rescue_expression, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_RESCUE_NODE: { pm_rescue_node_t *cast = (pm_rescue_node_t *) node; memsize->memsize += sizeof(*cast); @@ -2289,19 +2319,19 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_REST_PARAMETER_NODE: { pm_rest_parameter_node_t *cast = (pm_rest_parameter_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_RETRY_NODE: { pm_retry_node_t *cast = (pm_retry_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_RETURN_NODE: { pm_return_node_t *cast = (pm_return_node_t *) node; memsize->memsize += sizeof(*cast); @@ -2310,20 +2340,20 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_SELF_NODE: { pm_self_node_t *cast = (pm_self_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_SHAREABLE_CONSTANT_NODE: { pm_shareable_constant_node_t *cast = (pm_shareable_constant_node_t *) node; memsize->memsize += sizeof(*cast); pm_node_memsize_node((pm_node_t *)cast->write, memsize); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_SINGLETON_CLASS_NODE: { pm_singleton_class_node_t *cast = (pm_singleton_class_node_t *) node; memsize->memsize += sizeof(*cast); @@ -2334,26 +2364,26 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_SOURCE_ENCODING_NODE: { pm_source_encoding_node_t *cast = (pm_source_encoding_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_SOURCE_FILE_NODE: { pm_source_file_node_t *cast = (pm_source_file_node_t *) node; memsize->memsize += sizeof(*cast); memsize->memsize += (pm_string_memsize(&cast->filepath) - sizeof(pm_string_t)); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_SOURCE_LINE_NODE: { pm_source_line_node_t *cast = (pm_source_line_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_SPLAT_NODE: { pm_splat_node_t *cast = (pm_splat_node_t *) node; memsize->memsize += sizeof(*cast); @@ -2362,21 +2392,21 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_STATEMENTS_NODE: { pm_statements_node_t *cast = (pm_statements_node_t *) node; memsize->memsize += sizeof(*cast); memsize->memsize += (pm_node_list_memsize(&cast->body, memsize) - sizeof(pm_node_list_t)); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_STRING_NODE: { pm_string_node_t *cast = (pm_string_node_t *) node; memsize->memsize += sizeof(*cast); memsize->memsize += (pm_string_memsize(&cast->unescaped) - sizeof(pm_string_t)); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_SUPER_NODE: { pm_super_node_t *cast = (pm_super_node_t *) node; memsize->memsize += sizeof(*cast); @@ -2388,27 +2418,27 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_SYMBOL_NODE: { pm_symbol_node_t *cast = (pm_symbol_node_t *) node; memsize->memsize += sizeof(*cast); memsize->memsize += (pm_string_memsize(&cast->unescaped) - sizeof(pm_string_t)); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_TRUE_NODE: { pm_true_node_t *cast = (pm_true_node_t *) node; memsize->memsize += sizeof(*cast); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_UNDEF_NODE: { pm_undef_node_t *cast = (pm_undef_node_t *) node; memsize->memsize += sizeof(*cast); memsize->memsize += (pm_node_list_memsize(&cast->names, memsize) - sizeof(pm_node_list_t)); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_UNLESS_NODE: { pm_unless_node_t *cast = (pm_unless_node_t *) node; memsize->memsize += sizeof(*cast); @@ -2421,7 +2451,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_UNTIL_NODE: { pm_until_node_t *cast = (pm_until_node_t *) node; memsize->memsize += sizeof(*cast); @@ -2431,7 +2461,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_WHEN_NODE: { pm_when_node_t *cast = (pm_when_node_t *) node; memsize->memsize += sizeof(*cast); @@ -2441,7 +2471,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_WHILE_NODE: { pm_while_node_t *cast = (pm_while_node_t *) node; memsize->memsize += sizeof(*cast); @@ -2451,14 +2481,14 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_X_STRING_NODE: { pm_x_string_node_t *cast = (pm_x_string_node_t *) node; memsize->memsize += sizeof(*cast); memsize->memsize += (pm_string_memsize(&cast->unescaped) - sizeof(pm_string_t)); break; } -#line 140 "node.c.erb" +#line 170 "node.c.erb" case PM_YIELD_NODE: { pm_yield_node_t *cast = (pm_yield_node_t *) node; memsize->memsize += sizeof(*cast); @@ -2467,7 +2497,7 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) { } break; } -#line 168 "node.c.erb" +#line 198 "node.c.erb" } } diff --git a/src/main/c/yarp/src/options.c b/src/main/c/yarp/src/options.c index 2854b765b93..4d0d6dbc49a 100644 --- a/src/main/c/yarp/src/options.c +++ b/src/main/c/yarp/src/options.c @@ -47,29 +47,40 @@ pm_options_command_line_set(pm_options_t *options, uint8_t command_line) { */ PRISM_EXPORTED_FUNCTION bool pm_options_version_set(pm_options_t *options, const char *version, size_t length) { - if (version == NULL && length == 0) { - options->version = PM_OPTIONS_VERSION_LATEST; - return true; - } + switch (length) { + case 0: + if (version == NULL) { + options->version = PM_OPTIONS_VERSION_LATEST; + return true; + } - if (length == 5) { - if (strncmp(version, "3.3.0", length) == 0) { - options->version = PM_OPTIONS_VERSION_CRUBY_3_3_0; - return true; - } + return false; + case 5: + assert(version != NULL); - if (strncmp(version, "3.4.0", length) == 0) { - options->version = PM_OPTIONS_VERSION_LATEST; - return true; - } - } + if (strncmp(version, "3.3.0", length) == 0) { + options->version = PM_OPTIONS_VERSION_CRUBY_3_3_0; + return true; + } - if (length == 6 && strncmp(version, "latest", length) == 0) { - options->version = PM_OPTIONS_VERSION_LATEST; - return true; - } + if (strncmp(version, "3.4.0", length) == 0) { + options->version = PM_OPTIONS_VERSION_LATEST; + return true; + } + + return false; + case 6: + assert(version != NULL); - return false; + if (strncmp(version, "latest", length) == 0) { + options->version = PM_OPTIONS_VERSION_LATEST; + return true; + } + + return false; + default: + return false; + } } // For some reason, GCC analyzer thinks we're leaking allocated scopes and diff --git a/src/main/c/yarp/src/prism.c b/src/main/c/yarp/src/prism.c index b6a00cddf9d..d2fcf3e3723 100644 --- a/src/main/c/yarp/src/prism.c +++ b/src/main/c/yarp/src/prism.c @@ -33,39 +33,57 @@ PRISM_ATTRIBUTE_UNUSED static const char * debug_context(pm_context_t context) { switch (context) { case PM_CONTEXT_BEGIN: return "BEGIN"; - case PM_CONTEXT_CLASS: return "CLASS"; + case PM_CONTEXT_BEGIN_ENSURE: return "BEGIN_ENSURE"; + case PM_CONTEXT_BEGIN_ELSE: return "BEGIN_ELSE"; + case PM_CONTEXT_BEGIN_RESCUE: return "BEGIN_RESCUE"; + case PM_CONTEXT_BLOCK_BRACES: return "BLOCK_BRACES"; + case PM_CONTEXT_BLOCK_KEYWORDS: return "BLOCK_KEYWORDS"; + case PM_CONTEXT_BLOCK_ENSURE: return "BLOCK_ENSURE"; + case PM_CONTEXT_BLOCK_ELSE: return "BLOCK_ELSE"; + case PM_CONTEXT_BLOCK_RESCUE: return "BLOCK_RESCUE"; case PM_CONTEXT_CASE_IN: return "CASE_IN"; case PM_CONTEXT_CASE_WHEN: return "CASE_WHEN"; + case PM_CONTEXT_CLASS: return "CLASS"; + case PM_CONTEXT_CLASS_ELSE: return "CLASS_ELSE"; + case PM_CONTEXT_CLASS_ENSURE: return "CLASS_ENSURE"; + case PM_CONTEXT_CLASS_RESCUE: return "CLASS_RESCUE"; case PM_CONTEXT_DEF: return "DEF"; case PM_CONTEXT_DEF_PARAMS: return "DEF_PARAMS"; + case PM_CONTEXT_DEF_ENSURE: return "DEF_ENSURE"; + case PM_CONTEXT_DEF_ELSE: return "DEF_ELSE"; + case PM_CONTEXT_DEF_RESCUE: return "DEF_RESCUE"; case PM_CONTEXT_DEFAULT_PARAMS: return "DEFAULT_PARAMS"; - case PM_CONTEXT_ENSURE: return "ENSURE"; - case PM_CONTEXT_ENSURE_DEF: return "ENSURE_DEF"; + case PM_CONTEXT_DEFINED: return "DEFINED"; case PM_CONTEXT_ELSE: return "ELSE"; case PM_CONTEXT_ELSIF: return "ELSIF"; case PM_CONTEXT_EMBEXPR: return "EMBEXPR"; - case PM_CONTEXT_BLOCK_BRACES: return "BLOCK_BRACES"; - case PM_CONTEXT_BLOCK_KEYWORDS: return "BLOCK_KEYWORDS"; - case PM_CONTEXT_FOR: return "FOR"; case PM_CONTEXT_FOR_INDEX: return "FOR_INDEX"; + case PM_CONTEXT_FOR: return "FOR"; case PM_CONTEXT_IF: return "IF"; + case PM_CONTEXT_LAMBDA_BRACES: return "LAMBDA_BRACES"; + case PM_CONTEXT_LAMBDA_DO_END: return "LAMBDA_DO_END"; + case PM_CONTEXT_LAMBDA_ENSURE: return "LAMBDA_ENSURE"; + case PM_CONTEXT_LAMBDA_ELSE: return "LAMBDA_ELSE"; + case PM_CONTEXT_LAMBDA_RESCUE: return "LAMBDA_RESCUE"; case PM_CONTEXT_MAIN: return "MAIN"; case PM_CONTEXT_MODULE: return "MODULE"; + case PM_CONTEXT_MODULE_ELSE: return "MODULE_ELSE"; + case PM_CONTEXT_MODULE_ENSURE: return "MODULE_ENSURE"; + case PM_CONTEXT_MODULE_RESCUE: return "MODULE_RESCUE"; case PM_CONTEXT_NONE: return "NONE"; case PM_CONTEXT_PARENS: return "PARENS"; case PM_CONTEXT_POSTEXE: return "POSTEXE"; case PM_CONTEXT_PREDICATE: return "PREDICATE"; case PM_CONTEXT_PREEXE: return "PREEXE"; - case PM_CONTEXT_RESCUE: return "RESCUE"; - case PM_CONTEXT_RESCUE_ELSE: return "RESCUE_ELSE"; - case PM_CONTEXT_RESCUE_ELSE_DEF: return "RESCUE_ELSE_DEF"; - case PM_CONTEXT_RESCUE_DEF: return "RESCUE_DEF"; + case PM_CONTEXT_RESCUE_MODIFIER: return "RESCUE_MODIFIER"; case PM_CONTEXT_SCLASS: return "SCLASS"; + case PM_CONTEXT_SCLASS_ENSURE: return "SCLASS_ENSURE"; + case PM_CONTEXT_SCLASS_ELSE: return "SCLASS_ELSE"; + case PM_CONTEXT_SCLASS_RESCUE: return "SCLASS_RESCUE"; + case PM_CONTEXT_TERNARY: return "TERNARY"; case PM_CONTEXT_UNLESS: return "UNLESS"; case PM_CONTEXT_UNTIL: return "UNTIL"; case PM_CONTEXT_WHILE: return "WHILE"; - case PM_CONTEXT_LAMBDA_BRACES: return "LAMBDA_BRACES"; - case PM_CONTEXT_LAMBDA_DO_END: return "LAMBDA_DO_END"; } return NULL; } @@ -654,6 +672,375 @@ pm_parser_warn_node(pm_parser_t *parser, const pm_node_t *node, pm_diagnostic_id #define PM_PARSER_WARN_NODE_FORMAT(parser, node, diag_id, ...) \ PM_PARSER_WARN_FORMAT(parser, (node)->location.start, (node)->location.end, diag_id, __VA_ARGS__) +/******************************************************************************/ +/* Scope-related functions */ +/******************************************************************************/ + +/** + * Allocate and initialize a new scope. Push it onto the scope stack. + */ +static bool +pm_parser_scope_push(pm_parser_t *parser, bool closed) { + pm_scope_t *scope = (pm_scope_t *) xmalloc(sizeof(pm_scope_t)); + if (scope == NULL) return false; + + *scope = (pm_scope_t) { + .previous = parser->current_scope, + .locals = { 0 }, + .parameters = PM_SCOPE_PARAMETERS_NONE, + .numbered_parameters = PM_SCOPE_NUMBERED_PARAMETERS_NONE, + .shareable_constant = (closed || parser->current_scope == NULL) ? PM_SCOPE_SHAREABLE_CONSTANT_NONE : parser->current_scope->shareable_constant, + .closed = closed + }; + + parser->current_scope = scope; + return true; +} + +/** + * Determine if the current scope is at the top level. This means it is either + * the top-level scope or it is open to the top-level. + */ +static bool +pm_parser_scope_toplevel_p(pm_parser_t *parser) { + pm_scope_t *scope = parser->current_scope; + + do { + if (scope->previous == NULL) return true; + if (scope->closed) return false; + } while ((scope = scope->previous) != NULL); + + assert(false && "unreachable"); + return true; +} + +/** + * Retrieve the scope at the given depth. + */ +static pm_scope_t * +pm_parser_scope_find(pm_parser_t *parser, uint32_t depth) { + pm_scope_t *scope = parser->current_scope; + + while (depth-- > 0) { + assert(scope != NULL); + scope = scope->previous; + } + + return scope; +} + +static void +pm_parser_scope_forwarding_param_check(pm_parser_t *parser, const pm_token_t * token, const uint8_t mask, pm_diagnostic_id_t diag) { + pm_scope_t *scope = parser->current_scope; + while (scope) { + if (scope->parameters & mask) { + if (!scope->closed) { + pm_parser_err_token(parser, token, diag); + return; + } + return; + } + if (scope->closed) break; + scope = scope->previous; + } + + pm_parser_err_token(parser, token, diag); +} + +static inline void +pm_parser_scope_forwarding_block_check(pm_parser_t *parser, const pm_token_t * token) { + pm_parser_scope_forwarding_param_check(parser, token, PM_SCOPE_PARAMETERS_FORWARDING_BLOCK, PM_ERR_ARGUMENT_NO_FORWARDING_AMP); +} + +static inline void +pm_parser_scope_forwarding_positionals_check(pm_parser_t *parser, const pm_token_t * token) { + pm_parser_scope_forwarding_param_check(parser, token, PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS, PM_ERR_ARGUMENT_NO_FORWARDING_STAR); +} + +static inline void +pm_parser_scope_forwarding_all_check(pm_parser_t *parser, const pm_token_t * token) { + pm_parser_scope_forwarding_param_check(parser, token, PM_SCOPE_PARAMETERS_FORWARDING_ALL, PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES); +} + +static inline void +pm_parser_scope_forwarding_keywords_check(pm_parser_t *parser, const pm_token_t * token) { + pm_parser_scope_forwarding_param_check(parser, token, PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS, PM_ERR_ARGUMENT_NO_FORWARDING_STAR_STAR); +} + +/** + * Get the current state of constant shareability. + */ +static inline pm_shareable_constant_value_t +pm_parser_scope_shareable_constant_get(pm_parser_t *parser) { + return parser->current_scope->shareable_constant; +} + +/** + * Set the current state of constant shareability. We'll set it on all of the + * open scopes so that reads are quick. + */ +static void +pm_parser_scope_shareable_constant_set(pm_parser_t *parser, pm_shareable_constant_value_t shareable_constant) { + pm_scope_t *scope = parser->current_scope; + + do { + scope->shareable_constant = shareable_constant; + } while (!scope->closed && (scope = scope->previous) != NULL); +} + +/******************************************************************************/ +/* Local variable-related functions */ +/******************************************************************************/ + +/** + * The point at which the set of locals switches from being a list to a hash. + */ +#define PM_LOCALS_HASH_THRESHOLD 9 + +static void +pm_locals_free(pm_locals_t *locals) { + if (locals->capacity > 0) { + xfree(locals->locals); + } +} + +/** + * Use as simple and fast a hash function as we can that still properly mixes + * the bits. + */ +static uint32_t +pm_locals_hash(pm_constant_id_t name) { + name = ((name >> 16) ^ name) * 0x45d9f3b; + name = ((name >> 16) ^ name) * 0x45d9f3b; + name = (name >> 16) ^ name; + return name; +} + +/** + * Resize the locals list to be twice its current size. If the next capacity is + * above the threshold for switching to a hash, then we'll switch to a hash. + */ +static void +pm_locals_resize(pm_locals_t *locals) { + uint32_t next_capacity = locals->capacity == 0 ? 4 : (locals->capacity * 2); + assert(next_capacity > locals->capacity); + + pm_local_t *next_locals = xcalloc(next_capacity, sizeof(pm_local_t)); + if (next_locals == NULL) abort(); + + if (next_capacity < PM_LOCALS_HASH_THRESHOLD) { + if (locals->size > 0) { + memcpy(next_locals, locals->locals, locals->size * sizeof(pm_local_t)); + } + } else { + // If we just switched from a list to a hash, then we need to fill in + // the hash values of all of the locals. + bool hash_needed = (locals->capacity <= PM_LOCALS_HASH_THRESHOLD); + uint32_t mask = next_capacity - 1; + + for (uint32_t index = 0; index < locals->capacity; index++) { + pm_local_t *local = &locals->locals[index]; + + if (local->name != PM_CONSTANT_ID_UNSET) { + if (hash_needed) local->hash = pm_locals_hash(local->name); + + uint32_t hash = local->hash; + while (next_locals[hash & mask].name != PM_CONSTANT_ID_UNSET) hash++; + next_locals[hash & mask] = *local; + } + } + } + + pm_locals_free(locals); + locals->locals = next_locals; + locals->capacity = next_capacity; +} + +/** + * Add a new local to the set of locals. This will automatically rehash the + * locals if the size is greater than 3/4 of the capacity. + * + * @param locals The set of locals to add to. + * @param name The name of the local. + * @param start The source location that represents the start of the local. This + * is used for the location of the warning in case this local is not read. + * @param end The source location that represents the end of the local. This is + * used for the location of the warning in case this local is not read. + * @param reads The initial number of reads for this local. Usually this is set + * to 0, but for some locals (like parameters) we want to initialize it with + * 1 so that we never warn on unused parameters. + * @return True if the local was added, and false if the local already exists. + */ +static bool +pm_locals_write(pm_locals_t *locals, pm_constant_id_t name, const uint8_t *start, const uint8_t *end, uint32_t reads) { + if (locals->size >= (locals->capacity / 4 * 3)) { + pm_locals_resize(locals); + } + + if (locals->capacity < PM_LOCALS_HASH_THRESHOLD) { + for (uint32_t index = 0; index < locals->capacity; index++) { + pm_local_t *local = &locals->locals[index]; + + if (local->name == PM_CONSTANT_ID_UNSET) { + *local = (pm_local_t) { + .name = name, + .location = { .start = start, .end = end }, + .index = locals->size++, + .reads = reads, + .hash = 0 + }; + return true; + } else if (local->name == name) { + return false; + } + } + } else { + uint32_t mask = locals->capacity - 1; + uint32_t hash = pm_locals_hash(name); + uint32_t initial_hash = hash; + + do { + pm_local_t *local = &locals->locals[hash & mask]; + + if (local->name == PM_CONSTANT_ID_UNSET) { + *local = (pm_local_t) { + .name = name, + .location = { .start = start, .end = end }, + .index = locals->size++, + .reads = reads, + .hash = initial_hash + }; + return true; + } else if (local->name == name) { + return false; + } else { + hash++; + } + } while ((hash & mask) != initial_hash); + } + + assert(false && "unreachable"); + return true; +} + +/** + * Finds the index of a local variable in the locals set. If it is not found, + * this returns UINT32_MAX. + */ +static uint32_t +pm_locals_find(pm_locals_t *locals, pm_constant_id_t name) { + if (locals->capacity < PM_LOCALS_HASH_THRESHOLD) { + for (uint32_t index = 0; index < locals->size; index++) { + pm_local_t *local = &locals->locals[index]; + if (local->name == name) return index; + } + } else { + uint32_t mask = locals->capacity - 1; + uint32_t hash = pm_locals_hash(name); + uint32_t initial_hash = hash & mask; + + do { + pm_local_t *local = &locals->locals[hash & mask]; + + if (local->name == PM_CONSTANT_ID_UNSET) { + return UINT32_MAX; + } else if (local->name == name) { + return hash & mask; + } else { + hash++; + } + } while ((hash & mask) != initial_hash); + } + + return UINT32_MAX; +} + +/** + * Called when a variable is read in a certain lexical context. Tracks the read + * by adding to the reads count. + */ +static void +pm_locals_read(pm_locals_t *locals, pm_constant_id_t name) { + uint32_t index = pm_locals_find(locals, name); + assert(index != UINT32_MAX); + + pm_local_t *local = &locals->locals[index]; + assert(local->reads < UINT32_MAX); + + local->reads++; +} + +/** + * Called when a variable read is transformed into a variable write, because a + * write operator is found after the variable name. + */ +static void +pm_locals_unread(pm_locals_t *locals, pm_constant_id_t name) { + uint32_t index = pm_locals_find(locals, name); + assert(index != UINT32_MAX); + + pm_local_t *local = &locals->locals[index]; + assert(local->reads > 0); + + local->reads--; +} + +/** + * Returns the current number of reads for a local variable. + */ +static uint32_t +pm_locals_reads(pm_locals_t *locals, pm_constant_id_t name) { + uint32_t index = pm_locals_find(locals, name); + assert(index != UINT32_MAX); + + return locals->locals[index].reads; +} + +/** + * Write out the locals into the given list of constant ids in the correct + * order. This is used to set the list of locals on the nodes in the tree once + * we're sure no additional locals will be added to the set. + * + * This function is also responsible for warning when a local variable has been + * written but not read in certain contexts. + */ +static void +pm_locals_order(PRISM_ATTRIBUTE_UNUSED pm_parser_t *parser, pm_locals_t *locals, pm_constant_id_list_t *list, bool toplevel) { + pm_constant_id_list_init_capacity(list, locals->size); + + // If we're still below the threshold for switching to a hash, then we only + // need to loop over the locals until we hit the size because the locals are + // stored in a list. + uint32_t capacity = locals->capacity < PM_LOCALS_HASH_THRESHOLD ? locals->size : locals->capacity; + + // We will only warn for unused variables if we're not at the top level, or + // if we're parsing a file outside of eval or -e. + bool warn_unused = !toplevel || (!parser->parsing_eval && !PM_PARSER_COMMAND_LINE_OPTION_E(parser)); + + for (uint32_t index = 0; index < capacity; index++) { + pm_local_t *local = &locals->locals[index]; + + if (local->name != PM_CONSTANT_ID_UNSET) { + pm_constant_id_list_insert(list, (size_t) local->index, local->name); + + if (warn_unused && local->reads == 0) { + pm_constant_t *constant = pm_constant_pool_id_to_constant(&parser->constant_pool, local->name); + + if (constant->length >= 1 && *constant->start != '_') { + PM_PARSER_WARN_FORMAT( + parser, + local->location.start, + local->location.end, + PM_WARN_UNUSED_LOCAL_VARIABLE, + (int) constant->length, + (const char *) constant->start + ); + } + } + } + } +} + /******************************************************************************/ /* Node-related functions */ /******************************************************************************/ @@ -795,6 +1182,156 @@ pm_assert_value_expression(pm_parser_t *parser, pm_node_t *node) { } } +/** + * Warn if the given node is a "void" statement. + */ +static void +pm_void_statement_check(pm_parser_t *parser, const pm_node_t *node) { + const char *type = NULL; + int length = 0; + + switch (PM_NODE_TYPE(node)) { + case PM_BACK_REFERENCE_READ_NODE: + case PM_CLASS_VARIABLE_READ_NODE: + case PM_GLOBAL_VARIABLE_READ_NODE: + case PM_INSTANCE_VARIABLE_READ_NODE: + case PM_LOCAL_VARIABLE_READ_NODE: + case PM_NUMBERED_REFERENCE_READ_NODE: + type = "a variable"; + length = 10; + break; + case PM_CALL_NODE: { + const pm_call_node_t *cast = (const pm_call_node_t *) node; + if (cast->call_operator_loc.start != NULL || cast->message_loc.start == NULL) break; + + const pm_constant_t *message = pm_constant_pool_id_to_constant(&parser->constant_pool, cast->name); + switch (message->length) { + case 1: + switch (message->start[0]) { + case '+': + case '-': + case '*': + case '/': + case '%': + case '|': + case '^': + case '&': + case '>': + case '<': + type = (const char *) message->start; + length = 1; + break; + } + break; + case 2: + switch (message->start[1]) { + case '=': + if (message->start[0] == '<' || message->start[0] == '>' || message->start[0] == '!' || message->start[0] == '=') { + type = (const char *) message->start; + length = 2; + } + break; + case '@': + if (message->start[0] == '+' || message->start[0] == '-') { + type = (const char *) message->start; + length = 2; + } + break; + case '*': + if (message->start[0] == '*') { + type = (const char *) message->start; + length = 2; + } + break; + } + break; + case 3: + if (memcmp(message->start, "<=>", 3) == 0) { + type = "<=>"; + length = 3; + } + break; + } + + break; + } + case PM_CONSTANT_PATH_NODE: + type = "::"; + length = 2; + break; + case PM_CONSTANT_READ_NODE: + type = "a constant"; + length = 10; + break; + case PM_DEFINED_NODE: + type = "defined?"; + length = 8; + break; + case PM_FALSE_NODE: + type = "false"; + length = 5; + break; + case PM_FLOAT_NODE: + case PM_IMAGINARY_NODE: + case PM_INTEGER_NODE: + case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE: + case PM_INTERPOLATED_STRING_NODE: + case PM_RATIONAL_NODE: + case PM_REGULAR_EXPRESSION_NODE: + case PM_SOURCE_ENCODING_NODE: + case PM_SOURCE_FILE_NODE: + case PM_SOURCE_LINE_NODE: + case PM_STRING_NODE: + case PM_SYMBOL_NODE: + type = "a literal"; + length = 9; + break; + case PM_NIL_NODE: + type = "nil"; + length = 3; + break; + case PM_RANGE_NODE: { + const pm_range_node_t *cast = (const pm_range_node_t *) node; + + if (PM_NODE_FLAG_P(cast, PM_RANGE_FLAGS_EXCLUDE_END)) { + type = "..."; + length = 3; + } else { + type = ".."; + length = 2; + } + + break; + } + case PM_SELF_NODE: + type = "self"; + length = 4; + break; + case PM_TRUE_NODE: + type = "true"; + length = 4; + break; + default: + break; + } + + if (type != NULL) { + PM_PARSER_WARN_NODE_FORMAT(parser, node, PM_WARN_VOID_STATEMENT, length, type); + } +} + +/** + * Warn if any of the statements that are not the last statement in the list are + * a "void" statement. + */ +static void +pm_void_statements_check(pm_parser_t *parser, const pm_statements_node_t *node) { + assert(node->body.size > 0); + for (size_t index = 0; index < node->body.size - 1; index++) { + pm_void_statement_check(parser, node->body.nodes[index]); + } +} + /** * When we're handling the predicate of a conditional, we need to know our * context in order to determine the kind of warning we should deliver to the @@ -832,11 +1369,14 @@ pm_conditional_predicate_warn_write_literal_p(const pm_node_t *node) { switch (PM_NODE_TYPE(node)) { case PM_ARRAY_NODE: { const pm_array_node_t *cast = (const pm_array_node_t *) node; - for (size_t index = 0; index < cast->elements.size; index++) { - if (!pm_conditional_predicate_warn_write_literal_p(cast->elements.nodes[index])) { + const pm_node_t *element; + + PM_NODE_LIST_FOREACH(&cast->elements, index, element) { + if (!pm_conditional_predicate_warn_write_literal_p(element)) { return false; } } + return true; } case PM_FALSE_NODE: @@ -1355,7 +1895,7 @@ static pm_statements_node_t * pm_statements_node_create(pm_parser_t *parser); static void -pm_statements_node_body_append(pm_statements_node_t *node, pm_node_t *statement); +pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node, pm_node_t *statement); static size_t pm_statements_node_body_length(pm_statements_node_t *node); @@ -1610,9 +2150,9 @@ pm_array_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *node // For now we're going to just copy over each pointer manually. This could be // much more efficient, as we could instead resize the node list. bool found_rest = false; - for (size_t index = 0; index < nodes->size; index++) { - pm_node_t *child = nodes->nodes[index]; + pm_node_t *child; + PM_NODE_LIST_FOREACH(nodes, index, child) { if (!found_rest && (PM_NODE_TYPE_P(child, PM_SPLAT_NODE) || PM_NODE_TYPE_P(child, PM_IMPLICIT_REST_NODE))) { node->rest = child; found_rest = true; @@ -2234,6 +2774,7 @@ pm_call_node_not_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *me if (arguments->closing_loc.start != NULL) { node->base.location.end = arguments->closing_loc.end; } else { + assert(receiver != NULL); node->base.location.end = receiver->location.end; } @@ -3727,8 +4268,8 @@ pm_hash_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *eleme .closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE }; - for (size_t index = 0; index < elements->size; index++) { - pm_node_t *element = elements->nodes[index]; + pm_node_t *element; + PM_NODE_LIST_FOREACH(elements, index, element) { pm_node_list_append(&node->elements, element); } @@ -4014,7 +4555,7 @@ pm_if_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_t pm_if_node_t *node = PM_ALLOC_NODE(parser, pm_if_node_t); pm_statements_node_t *statements = pm_statements_node_create(parser); - pm_statements_node_body_append(statements, statement); + pm_statements_node_body_append(parser, statements, statement); *node = (pm_if_node_t) { { @@ -4045,10 +4586,10 @@ pm_if_node_ternary_create(pm_parser_t *parser, pm_node_t *predicate, const pm_to pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL); pm_statements_node_t *if_statements = pm_statements_node_create(parser); - pm_statements_node_body_append(if_statements, true_expression); + pm_statements_node_body_append(parser, if_statements, true_expression); pm_statements_node_t *else_statements = pm_statements_node_create(parser); - pm_statements_node_body_append(else_statements, false_expression); + pm_statements_node_body_append(parser, else_statements, false_expression); pm_token_t end_keyword = not_provided(parser); pm_else_node_t *else_node = pm_else_node_create(parser, colon, else_statements, &end_keyword); @@ -4486,8 +5027,9 @@ pm_interpolated_string_node_create(pm_parser_t *parser, const pm_token_t *openin }; if (parts != NULL) { - for (size_t index = 0; index < parts->size; index++) { - pm_interpolated_string_node_append(parser, node, parts->nodes[index]); + pm_node_t *part; + PM_NODE_LIST_FOREACH(parts, index, part) { + pm_interpolated_string_node_append(parser, node, part); } } @@ -4549,8 +5091,9 @@ pm_interpolated_symbol_node_create(pm_parser_t *parser, const pm_token_t *openin }; if (parts != NULL) { - for (size_t index = 0; index < parts->size; index++) { - pm_interpolated_symbol_node_append(node, parts->nodes[index]); + pm_node_t *part; + PM_NODE_LIST_FOREACH(parts, index, part) { + pm_interpolated_symbol_node_append(node, part); } } @@ -4839,10 +5382,8 @@ pm_local_variable_or_write_node_create(pm_parser_t *parser, pm_node_t *target, c * Allocate a new LocalVariableReadNode node with constant_id. */ static pm_local_variable_read_node_t * -pm_local_variable_read_node_create_constant_id(pm_parser_t *parser, const pm_token_t *name, pm_constant_id_t name_id, uint32_t depth) { - if (parser->current_param_name == name_id) { - PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, *name, PM_ERR_PARAMETER_CIRCULAR); - } +pm_local_variable_read_node_create_constant_id(pm_parser_t *parser, const pm_token_t *name, pm_constant_id_t name_id, uint32_t depth, bool missing) { + if (!missing) pm_locals_read(&pm_parser_scope_find(parser, depth)->locals, name_id); pm_local_variable_read_node_t *node = PM_ALLOC_NODE(parser, pm_local_variable_read_node_t); @@ -4859,12 +5400,22 @@ pm_local_variable_read_node_create_constant_id(pm_parser_t *parser, const pm_tok } /** - * Allocate a new LocalVariableReadNode node. + * Allocate and initialize a new LocalVariableReadNode node. */ static pm_local_variable_read_node_t * pm_local_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name, uint32_t depth) { pm_constant_id_t name_id = pm_parser_constant_id_token(parser, name); - return pm_local_variable_read_node_create_constant_id(parser, name, name_id, depth); + return pm_local_variable_read_node_create_constant_id(parser, name, name_id, depth, false); +} + +/** + * Allocate and initialize a new LocalVariableReadNode node for a missing local + * variable. (This will only happen when there is a syntax error.) + */ +static pm_local_variable_read_node_t * +pm_local_variable_read_node_missing_create(pm_parser_t *parser, const pm_token_t *name, uint32_t depth) { + pm_constant_id_t name_id = pm_parser_constant_id_token(parser, name); + return pm_local_variable_read_node_create_constant_id(parser, name, name_id, depth, true); } /** @@ -6059,8 +6610,25 @@ pm_statements_node_body_update(pm_statements_node_t *node, pm_node_t *statement) * Append a new node to the given StatementsNode node's body. */ static void -pm_statements_node_body_append(pm_statements_node_t *node, pm_node_t *statement) { +pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node, pm_node_t *statement) { pm_statements_node_body_update(node, statement); + + if (node->body.size > 0) { + const pm_node_t *previous = node->body.nodes[node->body.size - 1]; + + switch (PM_NODE_TYPE(previous)) { + case PM_BREAK_NODE: + case PM_NEXT_NODE: + case PM_REDO_NODE: + case PM_RETRY_NODE: + case PM_RETURN_NODE: + pm_parser_warn_node(parser, previous, PM_WARN_UNREACHABLE_STATEMENT); + break; + default: + break; + } + } + pm_node_list_append(&node->body, statement); pm_node_flag_set(statement, PM_NODE_FLAG_NEWLINE); } @@ -6623,7 +7191,7 @@ pm_unless_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_unless_node_t *node = PM_ALLOC_NODE(parser, pm_unless_node_t); pm_statements_node_t *statements = pm_statements_node_create(parser); - pm_statements_node_body_append(statements, statement); + pm_statements_node_body_append(parser, statements, statement); *node = (pm_unless_node_t) { { @@ -6901,117 +7469,6 @@ pm_yield_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_lo #undef PM_ALLOC_NODE -/******************************************************************************/ -/* Scope-related functions */ -/******************************************************************************/ - -/** - * Allocate and initialize a new scope. Push it onto the scope stack. - */ -static bool -pm_parser_scope_push(pm_parser_t *parser, bool closed) { - pm_scope_t *scope = (pm_scope_t *) xmalloc(sizeof(pm_scope_t)); - if (scope == NULL) return false; - - *scope = (pm_scope_t) { - .previous = parser->current_scope, - .locals = { 0 }, - .parameters = PM_SCOPE_PARAMETERS_NONE, - .numbered_parameters = PM_SCOPE_NUMBERED_PARAMETERS_NONE, - .shareable_constant = (closed || parser->current_scope == NULL) ? PM_SCOPE_SHAREABLE_CONSTANT_NONE : parser->current_scope->shareable_constant, - .closed = closed - }; - - parser->current_scope = scope; - return true; -} - -static void -pm_parser_scope_forwarding_param_check(pm_parser_t *parser, const pm_token_t * token, const uint8_t mask, pm_diagnostic_id_t diag) { - pm_scope_t *scope = parser->current_scope; - while (scope) { - if (scope->parameters & mask) { - if (!scope->closed) { - pm_parser_err_token(parser, token, diag); - return; - } - return; - } - if (scope->closed) break; - scope = scope->previous; - } - - pm_parser_err_token(parser, token, diag); -} - -static inline void -pm_parser_scope_forwarding_block_check(pm_parser_t *parser, const pm_token_t * token) { - pm_parser_scope_forwarding_param_check(parser, token, PM_SCOPE_PARAMETERS_FORWARDING_BLOCK, PM_ERR_ARGUMENT_NO_FORWARDING_AMP); -} - -static inline void -pm_parser_scope_forwarding_positionals_check(pm_parser_t *parser, const pm_token_t * token) { - pm_parser_scope_forwarding_param_check(parser, token, PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS, PM_ERR_ARGUMENT_NO_FORWARDING_STAR); -} - -static inline void -pm_parser_scope_forwarding_all_check(pm_parser_t *parser, const pm_token_t * token) { - pm_parser_scope_forwarding_param_check(parser, token, PM_SCOPE_PARAMETERS_FORWARDING_ALL, PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES); -} - -static inline void -pm_parser_scope_forwarding_keywords_check(pm_parser_t *parser, const pm_token_t * token) { - pm_parser_scope_forwarding_param_check(parser, token, PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS, PM_ERR_ARGUMENT_NO_FORWARDING_STAR_STAR); -} - -/** - * Get the current state of constant shareability. - */ -static inline pm_shareable_constant_value_t -pm_parser_scope_shareable_constant_get(pm_parser_t *parser) { - return parser->current_scope->shareable_constant; -} - -/** - * Set the current state of constant shareability. We'll set it on all of the - * open scopes so that reads are quick. - */ -static void -pm_parser_scope_shareable_constant_set(pm_parser_t *parser, pm_shareable_constant_value_t shareable_constant) { - pm_scope_t *scope = parser->current_scope; - - do { - scope->shareable_constant = shareable_constant; - } while (!scope->closed && (scope = scope->previous) != NULL); -} - -/** - * Save the current param name as the return value and set it to the given - * constant id. - */ -static inline pm_constant_id_t -pm_parser_current_param_name_set(pm_parser_t *parser, pm_constant_id_t current_param_name) { - pm_constant_id_t saved_param_name = parser->current_param_name; - parser->current_param_name = current_param_name; - return saved_param_name; -} - -/** - * Save the current param name as the return value and clear it. - */ -static inline pm_constant_id_t -pm_parser_current_param_name_unset(pm_parser_t *parser) { - return pm_parser_current_param_name_set(parser, PM_CONSTANT_ID_UNSET); -} - -/** - * Restore the current param name from the given value. - */ -static inline void -pm_parser_current_param_name_restore(pm_parser_t *parser, pm_constant_id_t saved_param_name) { - parser->current_param_name = saved_param_name; -} - /** * Check if any of the currently visible scopes contain a local variable * described by the given constant id. @@ -7022,7 +7479,7 @@ pm_parser_local_depth_constant_id(pm_parser_t *parser, pm_constant_id_t constant int depth = 0; while (scope != NULL) { - if (pm_constant_id_list_includes(&scope->locals, constant_id)) return depth; + if (pm_locals_find(&scope->locals, constant_id) != UINT32_MAX) return depth; if (scope->closed) break; scope = scope->previous; @@ -7046,19 +7503,17 @@ pm_parser_local_depth(pm_parser_t *parser, pm_token_t *token) { * Add a constant id to the local table of the current scope. */ static inline void -pm_parser_local_add(pm_parser_t *parser, pm_constant_id_t constant_id) { - if (!pm_constant_id_list_includes(&parser->current_scope->locals, constant_id)) { - pm_constant_id_list_append(&parser->current_scope->locals, constant_id); - } +pm_parser_local_add(pm_parser_t *parser, pm_constant_id_t constant_id, const uint8_t *start, const uint8_t *end, uint32_t reads) { + pm_locals_write(&parser->current_scope->locals, constant_id, start, end, reads); } /** * Add a local variable from a location to the current scope. */ static pm_constant_id_t -pm_parser_local_add_location(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) { +pm_parser_local_add_location(pm_parser_t *parser, const uint8_t *start, const uint8_t *end, uint32_t reads) { pm_constant_id_t constant_id = pm_parser_constant_id_location(parser, start, end); - if (constant_id != 0) pm_parser_local_add(parser, constant_id); + if (constant_id != 0) pm_parser_local_add(parser, constant_id, start, end, reads); return constant_id; } @@ -7066,8 +7521,8 @@ pm_parser_local_add_location(pm_parser_t *parser, const uint8_t *start, const ui * Add a local variable from a token to the current scope. */ static inline pm_constant_id_t -pm_parser_local_add_token(pm_parser_t *parser, pm_token_t *token) { - return pm_parser_local_add_location(parser, token->start, token->end); +pm_parser_local_add_token(pm_parser_t *parser, pm_token_t *token, uint32_t reads) { + return pm_parser_local_add_location(parser, token->start, token->end, reads); } /** @@ -7076,7 +7531,7 @@ pm_parser_local_add_token(pm_parser_t *parser, pm_token_t *token) { static pm_constant_id_t pm_parser_local_add_owned(pm_parser_t *parser, uint8_t *start, size_t length) { pm_constant_id_t constant_id = pm_parser_constant_id_owned(parser, start, length); - if (constant_id != 0) pm_parser_local_add(parser, constant_id); + if (constant_id != 0) pm_parser_local_add(parser, constant_id, parser->start, parser->start, 1); return constant_id; } @@ -7086,7 +7541,7 @@ pm_parser_local_add_owned(pm_parser_t *parser, uint8_t *start, size_t length) { static pm_constant_id_t pm_parser_local_add_constant(pm_parser_t *parser, const char *start, size_t length) { pm_constant_id_t constant_id = pm_parser_constant_id_constant(parser, start, length); - if (constant_id != 0) pm_parser_local_add(parser, constant_id); + if (constant_id != 0) pm_parser_local_add(parser, constant_id, parser->start, parser->start, 1); return constant_id; } @@ -7108,9 +7563,9 @@ pm_local_variable_read_node_create_it(pm_parser_t *parser, const pm_token_t *nam parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_IT; pm_constant_id_t name_id = pm_parser_constant_id_constant(parser, "0it", 3); - pm_parser_local_add(parser, name_id); + pm_parser_local_add(parser, name_id, name->start, name->end, 0); - return pm_local_variable_read_node_create_constant_id(parser, name, name_id, 0); + return pm_local_variable_read_node_create_constant_id(parser, name, name_id, 0, false); } /** @@ -7152,7 +7607,7 @@ pm_parser_parameter_name_check(pm_parser_t *parser, const pm_token_t *name) { // whether it's already in the current scope. pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, name); - if (pm_constant_id_list_includes(&parser->current_scope->locals, constant_id)) { + if (pm_locals_find(&parser->current_scope->locals, constant_id) != UINT32_MAX) { // Add an error if the parameter doesn't start with _ and has been seen before if ((name->start < name->end) && (*name->start != '_')) { pm_parser_err_token(parser, name, PM_ERR_PARAMETER_NAME_DUPLICATED); @@ -7163,14 +7618,13 @@ pm_parser_parameter_name_check(pm_parser_t *parser, const pm_token_t *name) { } /** - * Pop the current scope off the scope stack. Note that we specifically do not - * free the associated constant list because we assume that we have already - * transferred ownership of the list to the AST somewhere. + * Pop the current scope off the scope stack. */ static void pm_parser_scope_pop(pm_parser_t *parser) { pm_scope_t *scope = parser->current_scope; parser->current_scope = scope->previous; + pm_locals_free(&scope->locals); xfree(scope); } @@ -7178,6 +7632,30 @@ pm_parser_scope_pop(pm_parser_t *parser) { /* Stack helpers */ /******************************************************************************/ +/** + * Pushes a value onto the stack. + */ +static inline void +pm_state_stack_push(pm_state_stack_t *stack, bool value) { + *stack = (*stack << 1) | (value & 1); +} + +/** + * Pops a value off the stack. + */ +static inline void +pm_state_stack_pop(pm_state_stack_t *stack) { + *stack >>= 1; +} + +/** + * Returns the value at the top of the stack. + */ +static inline bool +pm_state_stack_p(const pm_state_stack_t *stack) { + return *stack & 1; +} + static inline void pm_accepts_block_stack_push(pm_parser_t *parser, bool value) { // Use the negation of the value to prevent stack overflow. @@ -7591,6 +8069,9 @@ context_terminator(pm_context_t context, pm_token_t *token) { switch (context) { case PM_CONTEXT_MAIN: case PM_CONTEXT_DEF_PARAMS: + case PM_CONTEXT_DEFINED: + case PM_CONTEXT_TERNARY: + case PM_CONTEXT_RESCUE_MODIFIER: return token->type == PM_TOKEN_EOF; case PM_CONTEXT_DEFAULT_PARAMS: return token->type == PM_TOKEN_COMMA || token->type == PM_TOKEN_PARENTHESIS_RIGHT; @@ -7608,8 +8089,13 @@ context_terminator(pm_context_t context, pm_token_t *token) { case PM_CONTEXT_UNTIL: case PM_CONTEXT_ELSE: case PM_CONTEXT_FOR: - case PM_CONTEXT_ENSURE: - case PM_CONTEXT_ENSURE_DEF: + case PM_CONTEXT_BEGIN_ENSURE: + case PM_CONTEXT_BLOCK_ENSURE: + case PM_CONTEXT_CLASS_ENSURE: + case PM_CONTEXT_DEF_ENSURE: + case PM_CONTEXT_LAMBDA_ENSURE: + case PM_CONTEXT_MODULE_ENSURE: + case PM_CONTEXT_SCLASS_ENSURE: return token->type == PM_TOKEN_KEYWORD_END; case PM_CONTEXT_FOR_INDEX: return token->type == PM_TOKEN_KEYWORD_IN; @@ -7629,11 +8115,21 @@ context_terminator(pm_context_t context, pm_token_t *token) { case PM_CONTEXT_PARENS: return token->type == PM_TOKEN_PARENTHESIS_RIGHT; case PM_CONTEXT_BEGIN: - case PM_CONTEXT_RESCUE: - case PM_CONTEXT_RESCUE_DEF: + case PM_CONTEXT_BEGIN_RESCUE: + case PM_CONTEXT_BLOCK_RESCUE: + case PM_CONTEXT_CLASS_RESCUE: + case PM_CONTEXT_DEF_RESCUE: + case PM_CONTEXT_LAMBDA_RESCUE: + case PM_CONTEXT_MODULE_RESCUE: + case PM_CONTEXT_SCLASS_RESCUE: return token->type == PM_TOKEN_KEYWORD_ENSURE || token->type == PM_TOKEN_KEYWORD_RESCUE || token->type == PM_TOKEN_KEYWORD_ELSE || token->type == PM_TOKEN_KEYWORD_END; - case PM_CONTEXT_RESCUE_ELSE: - case PM_CONTEXT_RESCUE_ELSE_DEF: + case PM_CONTEXT_BEGIN_ELSE: + case PM_CONTEXT_BLOCK_ELSE: + case PM_CONTEXT_CLASS_ELSE: + case PM_CONTEXT_DEF_ELSE: + case PM_CONTEXT_LAMBDA_ELSE: + case PM_CONTEXT_MODULE_ELSE: + case PM_CONTEXT_SCLASS_ELSE: return token->type == PM_TOKEN_KEYWORD_ENSURE || token->type == PM_TOKEN_KEYWORD_END; case PM_CONTEXT_LAMBDA_BRACES: return token->type == PM_TOKEN_BRACE_RIGHT; @@ -7706,13 +8202,22 @@ context_def_p(const pm_parser_t *parser) { switch (context_node->context) { case PM_CONTEXT_DEF: case PM_CONTEXT_DEF_PARAMS: - case PM_CONTEXT_ENSURE_DEF: - case PM_CONTEXT_RESCUE_DEF: - case PM_CONTEXT_RESCUE_ELSE_DEF: + case PM_CONTEXT_DEF_ENSURE: + case PM_CONTEXT_DEF_RESCUE: + case PM_CONTEXT_DEF_ELSE: return true; case PM_CONTEXT_CLASS: + case PM_CONTEXT_CLASS_ENSURE: + case PM_CONTEXT_CLASS_RESCUE: + case PM_CONTEXT_CLASS_ELSE: case PM_CONTEXT_MODULE: + case PM_CONTEXT_MODULE_ENSURE: + case PM_CONTEXT_MODULE_RESCUE: + case PM_CONTEXT_MODULE_ELSE: case PM_CONTEXT_SCLASS: + case PM_CONTEXT_SCLASS_ENSURE: + case PM_CONTEXT_SCLASS_RESCUE: + case PM_CONTEXT_SCLASS_ELSE: return false; default: context_node = context_node->prev; @@ -7741,11 +8246,24 @@ context_human(pm_context_t context) { case PM_CONTEXT_DEF: return "method definition"; case PM_CONTEXT_DEF_PARAMS: return "method parameters"; case PM_CONTEXT_DEFAULT_PARAMS: return "parameter default value"; - case PM_CONTEXT_ELSE: return "'else' clause"; + case PM_CONTEXT_DEFINED: return "'defined?' expression"; + case PM_CONTEXT_ELSE: + case PM_CONTEXT_BEGIN_ELSE: + case PM_CONTEXT_BLOCK_ELSE: + case PM_CONTEXT_CLASS_ELSE: + case PM_CONTEXT_DEF_ELSE: + case PM_CONTEXT_LAMBDA_ELSE: + case PM_CONTEXT_MODULE_ELSE: + case PM_CONTEXT_SCLASS_ELSE: return "'else' clause"; case PM_CONTEXT_ELSIF: return "'elsif' clause"; case PM_CONTEXT_EMBEXPR: return "embedded expression"; - case PM_CONTEXT_ENSURE: return "'ensure' clause"; - case PM_CONTEXT_ENSURE_DEF: return "'ensure' clause"; + case PM_CONTEXT_BEGIN_ENSURE: + case PM_CONTEXT_BLOCK_ENSURE: + case PM_CONTEXT_CLASS_ENSURE: + case PM_CONTEXT_DEF_ENSURE: + case PM_CONTEXT_LAMBDA_ENSURE: + case PM_CONTEXT_MODULE_ENSURE: + case PM_CONTEXT_SCLASS_ENSURE: return "'ensure' clause"; case PM_CONTEXT_FOR: return "for loop"; case PM_CONTEXT_FOR_INDEX: return "for loop index"; case PM_CONTEXT_IF: return "if statement"; @@ -7757,11 +8275,16 @@ context_human(pm_context_t context) { case PM_CONTEXT_POSTEXE: return "'END' block"; case PM_CONTEXT_PREDICATE: return "predicate"; case PM_CONTEXT_PREEXE: return "'BEGIN' block"; - case PM_CONTEXT_RESCUE_ELSE: return "'else' clause"; - case PM_CONTEXT_RESCUE_ELSE_DEF: return "'else' clause"; - case PM_CONTEXT_RESCUE: return "'rescue' clause"; - case PM_CONTEXT_RESCUE_DEF: return "'rescue' clause"; + case PM_CONTEXT_BEGIN_RESCUE: + case PM_CONTEXT_BLOCK_RESCUE: + case PM_CONTEXT_CLASS_RESCUE: + case PM_CONTEXT_DEF_RESCUE: + case PM_CONTEXT_LAMBDA_RESCUE: + case PM_CONTEXT_MODULE_RESCUE: + case PM_CONTEXT_RESCUE_MODIFIER: + case PM_CONTEXT_SCLASS_RESCUE: return "'rescue' clause"; case PM_CONTEXT_SCLASS: return "singleton class definition"; + case PM_CONTEXT_TERNARY: return "ternary expression"; case PM_CONTEXT_UNLESS: return "unless statement"; case PM_CONTEXT_UNTIL: return "until statement"; case PM_CONTEXT_WHILE: return "while statement"; @@ -8073,6 +8596,10 @@ lex_global_variable(pm_parser_t *parser) { do { parser->current.end += width; } while (parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0); + } else if (pm_char_is_whitespace(peek(parser))) { + // If we get here, then we have a $ followed by whitespace, + // which is not allowed. + pm_parser_err_token(parser, &parser->current, PM_ERR_GLOBAL_VARIABLE_BARE); } else { // If we get here, then we have a $ followed by something that // isn't recognized as a global variable. @@ -9103,15 +9630,23 @@ lex_embdoc(pm_parser_t *parser) { pm_comment_t *comment = parser_comment(parser, PM_COMMENT_EMBDOC); if (comment == NULL) return PM_TOKEN_EOF; - // Now, loop until we find the end of the embedded documentation or the end of - // the file. + // Now, loop until we find the end of the embedded documentation or the end + // of the file. while (parser->current.end + 4 <= parser->end) { parser->current.start = parser->current.end; - // If we've hit the end of the embedded documentation then we'll return that - // token here. - if (memcmp(parser->current.end, "=end", 4) == 0 && - (parser->current.end + 4 == parser->end || pm_char_is_whitespace(parser->current.end[4]))) { + // If we've hit the end of the embedded documentation then we'll return + // that token here. + if ( + (memcmp(parser->current.end, "=end", 4) == 0) && + ( + (parser->current.end + 4 == parser->end) || // end of file + pm_char_is_whitespace(parser->current.end[4]) || // whitespace + (parser->current.end[4] == '\0') || // NUL or end of script + (parser->current.end[4] == '\004') || // ^D + (parser->current.end[4] == '\032') // ^Z + ) + ) { const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end); if (newline == NULL) { @@ -9923,9 +10458,13 @@ parser_lex(pm_parser_t *parser) { // = => =~ == === =begin case '=': - if (current_token_starts_line(parser) && (parser->current.end + 5 <= parser->end) && memcmp(parser->current.end, "begin", 5) == 0 && pm_char_is_whitespace(peek_offset(parser, 5))) { + if ( + current_token_starts_line(parser) && + (parser->current.end + 5 <= parser->end) && + memcmp(parser->current.end, "begin", 5) == 0 && + (pm_char_is_whitespace(peek_offset(parser, 5)) || (peek_offset(parser, 5) == '\0')) + ) { pm_token_type_t type = lex_embdoc(parser); - if (type == PM_TOKEN_EOF) { LEX(type); } @@ -12002,7 +12541,8 @@ static pm_node_t * parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool accepts_command_call, pm_diagnostic_id_t diag_id); /** - * This is a wrapper of parse_expression, which also checks whether the resulting node is value expression. + * This is a wrapper of parse_expression, which also checks whether the + * resulting node is a value expression. */ static pm_node_t * parse_value_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool accepts_command_call, pm_diagnostic_id_t diag_id) { @@ -12152,13 +12692,19 @@ parse_target(pm_parser_t *parser, pm_node_t *target) { assert(sizeof(pm_global_variable_target_node_t) == sizeof(pm_global_variable_read_node_t)); target->type = PM_GLOBAL_VARIABLE_TARGET_NODE; return target; - case PM_LOCAL_VARIABLE_READ_NODE: + case PM_LOCAL_VARIABLE_READ_NODE: { pm_refute_numbered_parameter(parser, target->location.start, target->location.end); + const pm_local_variable_read_node_t *cast = (const pm_local_variable_read_node_t *) target; + uint32_t name = cast->name; + uint32_t depth = cast->depth; + pm_locals_unread(&pm_parser_scope_find(parser, depth)->locals, name); + assert(sizeof(pm_local_variable_target_node_t) == sizeof(pm_local_variable_read_node_t)); target->type = PM_LOCAL_VARIABLE_TARGET_NODE; return target; + } case PM_INSTANCE_VARIABLE_READ_NODE: assert(sizeof(pm_instance_variable_target_node_t) == sizeof(pm_instance_variable_read_node_t)); target->type = PM_INSTANCE_VARIABLE_TARGET_NODE; @@ -12198,20 +12744,12 @@ parse_target(pm_parser_t *parser, pm_node_t *target) { // When it was parsed in the prefix position, foo was seen as a // method call with no receiver and no arguments. Now we have an // =, so we know it's a local variable write. - const pm_location_t message = call->message_loc; + const pm_location_t message_loc = call->message_loc; - pm_parser_local_add_location(parser, message.start, message.end); + pm_constant_id_t name = pm_parser_local_add_location(parser, message_loc.start, message_loc.end, 0); pm_node_destroy(parser, target); - uint32_t depth = 0; - const pm_token_t name = { .type = PM_TOKEN_IDENTIFIER, .start = message.start, .end = message.end }; - target = (pm_node_t *) pm_local_variable_read_node_create(parser, &name, depth); - - assert(sizeof(pm_local_variable_target_node_t) == sizeof(pm_local_variable_read_node_t)); - target->type = PM_LOCAL_VARIABLE_TARGET_NODE; - - pm_refute_numbered_parameter(parser, message.start, message.end); - return target; + return (pm_node_t *) pm_local_variable_target_node_create(parser, &message_loc, name, 0); } if (*call->message_loc.start == '_' || parser->encoding->alnum_char(call->message_loc.start, call->message_loc.end - call->message_loc.start)) { @@ -12311,13 +12849,14 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod pm_refute_numbered_parameter(parser, target->location.start, target->location.end); pm_local_variable_read_node_t *local_read = (pm_local_variable_read_node_t *) target; - pm_constant_id_t constant_id = local_read->name; + pm_constant_id_t name = local_read->name; uint32_t depth = local_read->depth; + pm_locals_unread(&pm_parser_scope_find(parser, depth)->locals, name); pm_location_t name_loc = target->location; pm_node_destroy(parser, target); - return (pm_node_t *) pm_local_variable_write_node_create(parser, constant_id, depth, value, &name_loc, operator); + return (pm_node_t *) pm_local_variable_write_node_create(parser, name, depth, value, &name_loc, operator); } case PM_INSTANCE_VARIABLE_READ_NODE: { pm_node_t *write_node = (pm_node_t *) pm_instance_variable_write_node_create(parser, (pm_instance_variable_read_node_t *) target, operator, value); @@ -12364,7 +12903,7 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod // =, so we know it's a local variable write. const pm_location_t message = call->message_loc; - pm_parser_local_add_location(parser, message.start, message.end); + pm_parser_local_add_location(parser, message.start, message.end, 0); pm_node_destroy(parser, target); pm_constant_id_t constant_id = pm_parser_constant_id_location(parser, message.start, message.end); @@ -12431,6 +12970,32 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod } } +/** + * Certain expressions are not writable, but in order to provide a better + * experience we give a specific error message. In order to maintain as much + * information in the tree as possible, we replace them with local variable + * writes. + */ +static pm_node_t * +parse_unwriteable_write(pm_parser_t *parser, pm_node_t *target, const pm_token_t *equals, pm_node_t *value) { + switch (PM_NODE_TYPE(target)) { + case PM_SOURCE_ENCODING_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING); break; + case PM_FALSE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE); break; + case PM_SOURCE_FILE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_FILE); break; + case PM_SOURCE_LINE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_LINE); break; + case PM_NIL_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_NIL); break; + case PM_SELF_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_SELF); break; + case PM_TRUE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE); break; + default: break; + } + + pm_constant_id_t name = pm_parser_constant_id_location(parser, target->location.start, target->location.end); + pm_local_variable_write_node_t *result = pm_local_variable_write_node_create(parser, name, 0, value, &target->location, equals); + + pm_node_destroy(parser, target); + return (pm_node_t *) result; +} + /** * Parse a list of targets for assignment. This is used in the case of a for * loop or a multi-assignment. For example, in the following code: @@ -12522,7 +13087,7 @@ parse_statements(pm_parser_t *parser, pm_context_t context) { while (true) { pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_STATEMENT, true, PM_ERR_CANNOT_PARSE_EXPRESSION); - pm_statements_node_body_append(statements, node); + pm_statements_node_body_append(parser, statements, node); // If we're recovering from a syntax error, then we need to stop parsing the // statements now. @@ -12576,6 +13141,8 @@ parse_statements(pm_parser_t *parser, pm_context_t context) { } context_pop(parser); + pm_void_statements_check(parser, statements); + return statements; } @@ -12667,9 +13234,15 @@ parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *nod pm_token_t constant = { .type = PM_TOKEN_CONSTANT, .start = label.start, .end = label.end - 1 }; value = (pm_node_t *) pm_constant_read_node_create(parser, &constant); } else { - int depth = pm_parser_local_depth(parser, &((pm_token_t) { .type = PM_TOKEN_IDENTIFIER, .start = label.start, .end = label.end - 1 })); + int depth = -1; pm_token_t identifier = { .type = PM_TOKEN_IDENTIFIER, .start = label.start, .end = label.end - 1 }; + if (identifier.end[-1] == '!' || identifier.end[-1] == '?') { + PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, identifier, PM_ERR_INVALID_LOCAL_VARIABLE_READ); + } else { + depth = pm_parser_local_depth(parser, &identifier); + } + if (depth == -1) { value = (pm_node_t *) pm_call_node_variable_call_create(parser, &identifier); } else { @@ -12808,7 +13381,6 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for if (token_begins_expression_p(parser->current.type)) { expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_EXPECT_ARGUMENT); } else { - // A block forwarding in a method having `...` parameter (e.g. `def foo(...); bar(&); end`) is available. pm_parser_scope_forwarding_block_check(parser, &operator); } @@ -12891,7 +13463,7 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for pm_static_literals_t literals = { 0 }; pm_hash_key_static_literals_add(parser, &literals, argument); - // Finish parsing the one we are part way through + // Finish parsing the one we are part way through. pm_node_t *value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_HASH_VALUE); argument = (pm_node_t *) pm_assoc_node_create(parser, argument, &operator, value); @@ -12990,7 +13562,7 @@ parse_required_destructured_parameter(pm_parser_t *parser) { if (pm_parser_parameter_name_check(parser, &name)) { pm_node_flag_set_repeated_parameter(value); } - pm_parser_local_add_token(parser, &name); + pm_parser_local_add_token(parser, &name, 1); } param = (pm_node_t *) pm_splat_node_create(parser, &star, value); @@ -13002,7 +13574,7 @@ parse_required_destructured_parameter(pm_parser_t *parser) { if (pm_parser_parameter_name_check(parser, &name)) { pm_node_flag_set_repeated_parameter(param); } - pm_parser_local_add_token(parser, &name); + pm_parser_local_add_token(parser, &name, 1); } pm_multi_target_node_targets_append(parser, node, param); @@ -13123,7 +13695,7 @@ parse_parameters( if (accept1(parser, PM_TOKEN_IDENTIFIER)) { name = parser->previous; repeated = pm_parser_parameter_name_check(parser, &name); - pm_parser_local_add_token(parser, &name); + pm_parser_local_add_token(parser, &name, 1); } else { name = not_provided(parser); parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_BLOCK; @@ -13151,7 +13723,6 @@ parse_parameters( update_parameter_state(parser, &parser->current, &order); parser_lex(parser); - parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_BLOCK; parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_ALL; pm_forwarding_parameter_node_t *param = pm_forwarding_parameter_node_create(parser, &parser->previous); @@ -13205,22 +13776,30 @@ parse_parameters( pm_token_t name = parser->previous; bool repeated = pm_parser_parameter_name_check(parser, &name); - pm_parser_local_add_token(parser, &name); + pm_parser_local_add_token(parser, &name, 1); if (accept1(parser, PM_TOKEN_EQUAL)) { pm_token_t operator = parser->previous; context_push(parser, PM_CONTEXT_DEFAULT_PARAMS); - pm_constant_id_t saved_param_name = pm_parser_current_param_name_set(parser, pm_parser_constant_id_token(parser, &name)); - pm_node_t *value = parse_value_expression(parser, binding_power, false, PM_ERR_PARAMETER_NO_DEFAULT); + pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &name); + uint32_t reads = pm_locals_reads(&parser->current_scope->locals, name_id); + pm_node_t *value = parse_value_expression(parser, binding_power, false, PM_ERR_PARAMETER_NO_DEFAULT); pm_optional_parameter_node_t *param = pm_optional_parameter_node_create(parser, &name, &operator, value); + if (repeated) { pm_node_flag_set_repeated_parameter((pm_node_t *)param); } pm_parameters_node_optionals_append(params, param); - pm_parser_current_param_name_restore(parser, saved_param_name); + // If the value of the parameter increased the number of + // reads of that parameter, then we need to warn that we + // have a circular definition. + if (pm_locals_reads(&parser->current_scope->locals, name_id) != reads) { + PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, name, PM_ERR_PARAMETER_CIRCULAR); + } + context_pop(parser); // If parsing the value of the parameter resulted in error recovery, @@ -13256,7 +13835,7 @@ parse_parameters( local.end -= 1; bool repeated = pm_parser_parameter_name_check(parser, &local); - pm_parser_local_add_token(parser, &local); + pm_parser_local_add_token(parser, &local, 1); switch (parser->current.type) { case PM_TOKEN_COMMA: @@ -13289,12 +13868,15 @@ parse_parameters( if (token_begins_expression_p(parser->current.type)) { context_push(parser, PM_CONTEXT_DEFAULT_PARAMS); - pm_constant_id_t saved_param_name = pm_parser_current_param_name_set(parser, pm_parser_constant_id_token(parser, &local)); + pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &local); + uint32_t reads = pm_locals_reads(&parser->current_scope->locals, name_id); pm_node_t *value = parse_value_expression(parser, binding_power, false, PM_ERR_PARAMETER_NO_DEFAULT_KW); - pm_parser_current_param_name_restore(parser, saved_param_name); - context_pop(parser); + if (pm_locals_reads(&parser->current_scope->locals, name_id) != reads) { + PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, local, PM_ERR_PARAMETER_CIRCULAR); + } + context_pop(parser); param = (pm_node_t *) pm_optional_keyword_parameter_node_create(parser, &name, value); } else { @@ -13330,7 +13912,7 @@ parse_parameters( if (accept1(parser, PM_TOKEN_IDENTIFIER)) { name = parser->previous; repeated = pm_parser_parameter_name_check(parser, &name); - pm_parser_local_add_token(parser, &name); + pm_parser_local_add_token(parser, &name, 1); } else { name = not_provided(parser); parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS; @@ -13366,7 +13948,7 @@ parse_parameters( if (accept1(parser, PM_TOKEN_IDENTIFIER)) { name = parser->previous; repeated = pm_parser_parameter_name_check(parser, &name); - pm_parser_local_add_token(parser, &name); + pm_parser_local_add_token(parser, &name, 1); } else { name = not_provided(parser); parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS; @@ -13425,12 +14007,22 @@ parse_parameters( return params; } +typedef enum { + PM_RESCUES_BEGIN = 1, + PM_RESCUES_BLOCK, + PM_RESCUES_CLASS, + PM_RESCUES_DEF, + PM_RESCUES_LAMBDA, + PM_RESCUES_MODULE, + PM_RESCUES_SCLASS +} pm_rescues_type_t; + /** * Parse any number of rescue clauses. This will form a linked list of if * nodes pointing to each other from the top. */ static inline void -parse_rescues(pm_parser_t *parser, pm_begin_node_t *parent_node, bool def_p) { +parse_rescues(pm_parser_t *parser, pm_begin_node_t *parent_node, pm_rescues_type_t type) { pm_rescue_node_t *current = NULL; while (accept1(parser, PM_TOKEN_KEYWORD_RESCUE)) { @@ -13493,10 +14085,22 @@ parse_rescues(pm_parser_t *parser, pm_begin_node_t *parent_node, bool def_p) { if (!match3(parser, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_END)) { pm_accepts_block_stack_push(parser, true); - pm_statements_node_t *statements = parse_statements(parser, def_p ? PM_CONTEXT_RESCUE_DEF : PM_CONTEXT_RESCUE); - if (statements) { - pm_rescue_node_statements_set(rescue, statements); + pm_context_t context; + + switch (type) { + case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_RESCUE; break; + case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_RESCUE; break; + case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_RESCUE; break; + case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_RESCUE; break; + case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_RESCUE; break; + case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_RESCUE; break; + case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_RESCUE; break; + default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_RESCUE; break; } + + pm_statements_node_t *statements = parse_statements(parser, context); + if (statements != NULL) pm_rescue_node_statements_set(rescue, statements); + pm_accepts_block_stack_pop(parser); accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); } @@ -13529,8 +14133,22 @@ parse_rescues(pm_parser_t *parser, pm_begin_node_t *parent_node, bool def_p) { pm_statements_node_t *else_statements = NULL; if (!match2(parser, PM_TOKEN_KEYWORD_END, PM_TOKEN_KEYWORD_ENSURE)) { pm_accepts_block_stack_push(parser, true); - else_statements = parse_statements(parser, def_p ? PM_CONTEXT_RESCUE_ELSE_DEF : PM_CONTEXT_RESCUE_ELSE); + pm_context_t context; + + switch (type) { + case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_ELSE; break; + case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_ELSE; break; + case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_ELSE; break; + case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_ELSE; break; + case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_ELSE; break; + case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_ELSE; break; + case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_ELSE; break; + default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_RESCUE; break; + } + + else_statements = parse_statements(parser, context); pm_accepts_block_stack_pop(parser); + accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); } @@ -13545,8 +14163,22 @@ parse_rescues(pm_parser_t *parser, pm_begin_node_t *parent_node, bool def_p) { pm_statements_node_t *ensure_statements = NULL; if (!match1(parser, PM_TOKEN_KEYWORD_END)) { pm_accepts_block_stack_push(parser, true); - ensure_statements = parse_statements(parser, def_p ? PM_CONTEXT_ENSURE_DEF : PM_CONTEXT_ENSURE); + pm_context_t context; + + switch (type) { + case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_ENSURE; break; + case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_ENSURE; break; + case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_ENSURE; break; + case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_ENSURE; break; + case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_ENSURE; break; + case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_ENSURE; break; + case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_ENSURE; break; + default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_RESCUE; break; + } + + ensure_statements = parse_statements(parser, context); pm_accepts_block_stack_pop(parser); + accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); } @@ -13562,13 +14194,19 @@ parse_rescues(pm_parser_t *parser, pm_begin_node_t *parent_node, bool def_p) { } } -static inline pm_begin_node_t * -parse_rescues_as_begin(pm_parser_t *parser, const uint8_t *start, pm_statements_node_t *statements, bool def_p) { - pm_token_t no_begin_token = not_provided(parser); - pm_begin_node_t *begin_node = pm_begin_node_create(parser, &no_begin_token, statements); - parse_rescues(parser, begin_node, def_p); - begin_node->base.location.start = start; - return begin_node; +/** + * Parse a set of rescue clauses with an implicit begin (for example when on a + * class, module, def, etc.). + */ +static pm_begin_node_t * +parse_rescues_implicit_begin(pm_parser_t *parser, const uint8_t *start, pm_statements_node_t *statements, pm_rescues_type_t type) { + pm_token_t begin_keyword = not_provided(parser); + pm_begin_node_t *node = pm_begin_node_create(parser, &begin_keyword, statements); + + parse_rescues(parser, node, type); + node->base.location.start = start; + + return node; } /** @@ -13621,12 +14259,11 @@ parse_block_parameters( } bool repeated = pm_parser_parameter_name_check(parser, &parser->previous); - pm_parser_local_add_token(parser, &parser->previous); + pm_parser_local_add_token(parser, &parser->previous, 1); pm_block_local_variable_node_t *local = pm_block_local_variable_node_create(parser, &parser->previous); - if (repeated) { - pm_node_flag_set_repeated_parameter((pm_node_t *)local); - } + if (repeated) pm_node_flag_set_repeated_parameter((pm_node_t *) local); + pm_block_parameters_node_append_local(block_parameters, local); } while (accept1(parser, PM_TOKEN_COMMA)); } @@ -13677,7 +14314,6 @@ parse_block(pm_parser_t *parser) { pm_token_t opening = parser->previous; accept1(parser, PM_TOKEN_NEWLINE); - pm_constant_id_t saved_param_name = pm_parser_current_param_name_unset(parser); pm_accepts_block_stack_push(parser, true); pm_parser_scope_push(parser, false); @@ -13721,19 +14357,19 @@ parse_block(pm_parser_t *parser) { if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) { assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE)); - statements = (pm_node_t *) parse_rescues_as_begin(parser, opening.start, (pm_statements_node_t *) statements, false); + statements = (pm_node_t *) parse_rescues_implicit_begin(parser, opening.start, (pm_statements_node_t *) statements, PM_RESCUES_BLOCK); } } expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BLOCK_TERM_END); } - pm_constant_id_list_t locals = parser->current_scope->locals; + pm_constant_id_list_t locals; + pm_locals_order(parser, &parser->current_scope->locals, &locals, pm_parser_scope_toplevel_p(parser)); pm_node_t *parameters = parse_blocklike_parameters(parser, (pm_node_t *) block_parameters, &opening, &parser->previous); pm_parser_scope_pop(parser); pm_accepts_block_stack_pop(parser); - pm_parser_current_param_name_restore(parser, saved_param_name); return pm_block_node_create(parser, &locals, &opening, parameters, statements, &parser->previous); } @@ -13756,9 +14392,14 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accept } else { pm_accepts_block_stack_push(parser, true); parse_arguments(parser, arguments, true, PM_TOKEN_PARENTHESIS_RIGHT); - expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_ARGUMENT_TERM_PAREN); - pm_accepts_block_stack_pop(parser); + if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_ARGUMENT_TERM_PAREN, pm_token_type_human(parser->current.type)); + parser->previous.start = parser->previous.end; + parser->previous.type = PM_TOKEN_MISSING; + } + + pm_accepts_block_stack_pop(parser); arguments->closing_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous); } } else if (accepts_command_call && (token_begins_expression_p(parser->current.type) || match3(parser, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR, PM_TOKEN_UAMPERSAND)) && !match1(parser, PM_TOKEN_BRACE_LEFT)) { @@ -13818,6 +14459,160 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accept return found; } +/** + * Check that the block exit (next, break, redo) is allowed in the current + * context. If it isn't, add an error to the parser. + */ +static void +parse_block_exit(pm_parser_t *parser, pm_node_t *node, const char *type) { + pm_context_node_t *context_node = parser->current_context; + bool through_expression = false; + + while (context_node != NULL) { + switch (context_node->context) { + case PM_CONTEXT_BLOCK_BRACES: + case PM_CONTEXT_BLOCK_KEYWORDS: + case PM_CONTEXT_BLOCK_ELSE: + case PM_CONTEXT_BLOCK_ENSURE: + case PM_CONTEXT_BLOCK_RESCUE: + case PM_CONTEXT_DEFINED: + case PM_CONTEXT_FOR: + case PM_CONTEXT_LAMBDA_BRACES: + case PM_CONTEXT_LAMBDA_DO_END: + case PM_CONTEXT_LAMBDA_ELSE: + case PM_CONTEXT_LAMBDA_ENSURE: + case PM_CONTEXT_LAMBDA_RESCUE: + case PM_CONTEXT_POSTEXE: + case PM_CONTEXT_UNTIL: + case PM_CONTEXT_WHILE: + // These are the good cases. We're allowed to have a block exit + // in these contexts. + return; + case PM_CONTEXT_DEF: + case PM_CONTEXT_DEF_PARAMS: + case PM_CONTEXT_DEF_ELSE: + case PM_CONTEXT_DEF_ENSURE: + case PM_CONTEXT_DEF_RESCUE: + case PM_CONTEXT_MAIN: + case PM_CONTEXT_PREEXE: + case PM_CONTEXT_SCLASS: + case PM_CONTEXT_SCLASS_ELSE: + case PM_CONTEXT_SCLASS_ENSURE: + case PM_CONTEXT_SCLASS_RESCUE: + // These are the bad cases. We're not allowed to have a block + // exit in these contexts. + + if (through_expression) { + // If we get here, then we're about to mark this block exit + // as invalid. However, it could later _become_ valid if we + // find a trailing while/until on the expression. In this + // case instead of adding the error here, we'll add the + // block exit to the list of exits for the expression, and + // the node parsing will handle validating it instead. + assert(parser->current_block_exits != NULL); + pm_node_list_append(parser->current_block_exits, node); + } else { + // Otherwise, if we haven't gone through an expression + // context, then this is just invalid and we'll add the + // error here. + PM_PARSER_ERR_NODE_FORMAT(parser, node, PM_ERR_INVALID_BLOCK_EXIT, type); + } + + return; + case PM_CONTEXT_NONE: + // This case should never happen. + assert(false && "unreachable"); + break; + case PM_CONTEXT_BEGIN: + case PM_CONTEXT_BEGIN_ELSE: + case PM_CONTEXT_BEGIN_ENSURE: + case PM_CONTEXT_BEGIN_RESCUE: + case PM_CONTEXT_CASE_IN: + case PM_CONTEXT_CASE_WHEN: + case PM_CONTEXT_CLASS: + case PM_CONTEXT_CLASS_ELSE: + case PM_CONTEXT_CLASS_ENSURE: + case PM_CONTEXT_CLASS_RESCUE: + case PM_CONTEXT_ELSE: + case PM_CONTEXT_ELSIF: + case PM_CONTEXT_IF: + case PM_CONTEXT_MODULE: + case PM_CONTEXT_MODULE_ELSE: + case PM_CONTEXT_MODULE_ENSURE: + case PM_CONTEXT_MODULE_RESCUE: + case PM_CONTEXT_PARENS: + case PM_CONTEXT_RESCUE_MODIFIER: + case PM_CONTEXT_TERNARY: + case PM_CONTEXT_UNLESS: + // If we got to an expression that could be modified by a + // trailing while/until, then we'll track that we have gotten + // here because we need to know it if this block exit is later + // marked as invalid. + through_expression = true; + break; + case PM_CONTEXT_EMBEXPR: + case PM_CONTEXT_DEFAULT_PARAMS: + case PM_CONTEXT_FOR_INDEX: + case PM_CONTEXT_PREDICATE: + // In these contexts we should continue walking up the list of + // contexts. + break; + } + + context_node = context_node->prev; + } +} + +/** + * When we hit an expression that could contain block exits, we need to stash + * the previous set and create a new one. + */ +static pm_node_list_t * +push_block_exits(pm_parser_t *parser, pm_node_list_t *current_block_exits) { + pm_node_list_t *previous_block_exits = parser->current_block_exits; + parser->current_block_exits = current_block_exits; + return previous_block_exits; +} + +/** + * Pop the current level of block exits from the parser, and add errors to the + * parser if any of them are deemed to be invalid. + */ +static void +pop_block_exits(pm_parser_t *parser, pm_node_list_t *previous_block_exits) { + if (match2(parser, PM_TOKEN_KEYWORD_WHILE_MODIFIER, PM_TOKEN_KEYWORD_UNTIL_MODIFIER)) { + // If we matched a trailing while/until, then all of the block exits in + // the contained list are valid. In this case we do not need to do + // anything. + } else if (previous_block_exits != NULL) { + // If we did not matching a trailing while/until, then all of the block + // exits contained in the list are invalid for this specific context. + // However, they could still become valid in a higher level context if + // there is another list above this one. In this case we'll push all of + // the block exits up to the previous list. + pm_node_list_concat(previous_block_exits, parser->current_block_exits); + } else { + // If we did not match a trailing while/until and this was the last + // chance to do so, then all of the block exits in the list are invalid + // and we need to add an error for each of them. + pm_node_t *block_exit; + PM_NODE_LIST_FOREACH(parser->current_block_exits, index, block_exit) { + const char *type; + + switch (PM_NODE_TYPE(block_exit)) { + case PM_BREAK_NODE: type = "break"; break; + case PM_NEXT_NODE: type = "next"; break; + case PM_REDO_NODE: type = "redo"; break; + default: assert(false && "unreachable"); type = ""; break; + } + + PM_PARSER_ERR_NODE_FORMAT(parser, block_exit, PM_ERR_INVALID_BLOCK_EXIT, type); + } + } + + parser->current_block_exits = previous_block_exits; +} + static inline pm_node_t * parse_predicate(pm_parser_t *parser, pm_binding_power_t binding_power, pm_context_t context, pm_token_t *then_keyword) { context_push(parser, PM_CONTEXT_PREDICATE); @@ -13842,6 +14637,9 @@ parse_predicate(pm_parser_t *parser, pm_binding_power_t binding_power, pm_contex static inline pm_node_t * parse_conditional(pm_parser_t *parser, pm_context_t context) { + pm_node_list_t current_block_exits = { 0 }; + pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits); + pm_token_t keyword = parser->previous; pm_token_t then_keyword = not_provided(parser); @@ -13921,7 +14719,8 @@ parse_conditional(pm_parser_t *parser, pm_context_t context) { break; } } else { - // We should specialize this error message to refer to 'if' or 'unless' explicitly. + // We should specialize this error message to refer to 'if' or 'unless' + // explicitly. expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM); } @@ -13958,6 +14757,9 @@ parse_conditional(pm_parser_t *parser, pm_context_t context) { break; } + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + return parent; } @@ -14493,7 +15295,7 @@ parse_variable(pm_parser_t *parser) { // Finally we can create the local variable read node. pm_constant_id_t name_id = pm_parser_local_add_constant(parser, pm_numbered_parameter_names[numbered_parameters - 1], 2); - return pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, 0); + return pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, 0, false); } } @@ -14541,6 +15343,7 @@ parse_method_definition_name(pm_parser_t *parser) { parser_lex(parser); return parser->previous; default: + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_DEF_NAME, pm_token_type_human(parser->current.type)); return (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->current.start, .end = parser->current.end }; } } @@ -14597,9 +15400,8 @@ parse_heredoc_dedent(pm_parser_t *parser, pm_node_list_t *nodes, size_t common_w // the whitespace from a node, then we'll drop it from the list entirely. size_t write_index = 0; - for (size_t read_index = 0; read_index < nodes->size; read_index++) { - pm_node_t *node = nodes->nodes[read_index]; - + pm_node_t *node; + PM_NODE_LIST_FOREACH(nodes, read_index, node) { // We're not manipulating child nodes that aren't strings. In this case // we'll skip past it and indicate that the subsequent node should not // be dedented. @@ -14787,7 +15589,7 @@ parse_pattern_rest(pm_parser_t *parser, pm_constant_id_list_t *captures) { int depth; if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) { - pm_parser_local_add(parser, constant_id); + pm_parser_local_add(parser, constant_id, identifier.start, identifier.end, 0); } parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&identifier)); @@ -14823,7 +15625,7 @@ parse_pattern_keyword_rest(pm_parser_t *parser, pm_constant_id_list_t *captures) int depth; if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) { - pm_parser_local_add(parser, constant_id); + pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0); } parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous)); @@ -14847,9 +15649,16 @@ parse_pattern_hash_implicit_value(pm_parser_t *parser, pm_constant_id_list_t *ca const pm_location_t *value_loc = &((pm_symbol_node_t *) key)->value_loc; pm_constant_id_t constant_id = pm_parser_constant_id_location(parser, value_loc->start, value_loc->end); - int depth; - if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) { - pm_parser_local_add(parser, constant_id); + int depth = -1; + if (value_loc->end[-1] == '!' || value_loc->end[-1] == '?') { + pm_parser_err(parser, key->base.location.start, key->base.location.end, PM_ERR_PATTERN_HASH_KEY_LOCALS); + PM_PARSER_ERR_LOCATION_FORMAT(parser, value_loc, PM_ERR_INVALID_LOCAL_VARIABLE_WRITE, (int) (value_loc->end - value_loc->start), (const char *) value_loc->start); + } else { + depth = pm_parser_local_depth_constant_id(parser, constant_id); + } + + if (depth == -1) { + pm_parser_local_add(parser, constant_id, value_loc->start, value_loc->end, 0); } parse_pattern_capture(parser, captures, constant_id, value_loc); @@ -14986,7 +15795,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm int depth; if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) { - pm_parser_local_add(parser, constant_id); + pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0); } parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous)); @@ -15167,7 +15976,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm variable = (pm_node_t *) read; } else { PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, parser->previous, PM_ERR_NO_LOCAL_VARIABLE); - variable = (pm_node_t *) pm_local_variable_read_node_create(parser, &parser->previous, 0); + variable = (pm_node_t *) pm_local_variable_read_node_missing_create(parser, &parser->previous, 0); } } @@ -15321,7 +16130,7 @@ parse_pattern_primitives(pm_parser_t *parser, pm_constant_id_list_t *captures, p int depth; if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) { - pm_parser_local_add(parser, constant_id); + pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0); } parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous)); @@ -15700,6 +16509,173 @@ pm_parser_err_prefix(pm_parser_t *parser, pm_diagnostic_id_t diag_id) { } } +/** + * Ensures that the current retry token is valid in the current context. + */ +static void +parse_retry(pm_parser_t *parser, const pm_node_t *node) { + pm_context_node_t *context_node = parser->current_context; + + while (context_node != NULL) { + switch (context_node->context) { + case PM_CONTEXT_BEGIN_RESCUE: + case PM_CONTEXT_BLOCK_RESCUE: + case PM_CONTEXT_CLASS_RESCUE: + case PM_CONTEXT_DEF_RESCUE: + case PM_CONTEXT_LAMBDA_RESCUE: + case PM_CONTEXT_MODULE_RESCUE: + case PM_CONTEXT_SCLASS_RESCUE: + case PM_CONTEXT_DEFINED: + case PM_CONTEXT_RESCUE_MODIFIER: + // These are the good cases. We're allowed to have a retry here. + return; + case PM_CONTEXT_CLASS: + case PM_CONTEXT_DEF: + case PM_CONTEXT_DEF_PARAMS: + case PM_CONTEXT_MAIN: + case PM_CONTEXT_MODULE: + case PM_CONTEXT_PREEXE: + case PM_CONTEXT_SCLASS: + // These are the bad cases. We're not allowed to have a retry in + // these contexts. + pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_WITHOUT_RESCUE); + return; + case PM_CONTEXT_BEGIN_ELSE: + case PM_CONTEXT_BLOCK_ELSE: + case PM_CONTEXT_CLASS_ELSE: + case PM_CONTEXT_DEF_ELSE: + case PM_CONTEXT_LAMBDA_ELSE: + case PM_CONTEXT_MODULE_ELSE: + case PM_CONTEXT_SCLASS_ELSE: + // These are also bad cases, but with a more specific error + // message indicating the else. + pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_AFTER_ELSE); + return; + case PM_CONTEXT_BEGIN_ENSURE: + case PM_CONTEXT_BLOCK_ENSURE: + case PM_CONTEXT_CLASS_ENSURE: + case PM_CONTEXT_DEF_ENSURE: + case PM_CONTEXT_LAMBDA_ENSURE: + case PM_CONTEXT_MODULE_ENSURE: + case PM_CONTEXT_SCLASS_ENSURE: + // These are also bad cases, but with a more specific error + // message indicating the ensure. + pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_AFTER_ENSURE); + return; + case PM_CONTEXT_NONE: + // This case should never happen. + assert(false && "unreachable"); + break; + case PM_CONTEXT_BEGIN: + case PM_CONTEXT_BLOCK_BRACES: + case PM_CONTEXT_BLOCK_KEYWORDS: + case PM_CONTEXT_CASE_IN: + case PM_CONTEXT_CASE_WHEN: + case PM_CONTEXT_DEFAULT_PARAMS: + case PM_CONTEXT_ELSE: + case PM_CONTEXT_ELSIF: + case PM_CONTEXT_EMBEXPR: + case PM_CONTEXT_FOR_INDEX: + case PM_CONTEXT_FOR: + case PM_CONTEXT_IF: + case PM_CONTEXT_LAMBDA_BRACES: + case PM_CONTEXT_LAMBDA_DO_END: + case PM_CONTEXT_PARENS: + case PM_CONTEXT_POSTEXE: + case PM_CONTEXT_PREDICATE: + case PM_CONTEXT_TERNARY: + case PM_CONTEXT_UNLESS: + case PM_CONTEXT_UNTIL: + case PM_CONTEXT_WHILE: + // In these contexts we should continue walking up the list of + // contexts. + break; + } + + context_node = context_node->prev; + } +} + +/** + * Ensures that the current yield token is valid in the current context. + */ +static void +parse_yield(pm_parser_t *parser, const pm_node_t *node) { + pm_context_node_t *context_node = parser->current_context; + + while (context_node != NULL) { + switch (context_node->context) { + case PM_CONTEXT_DEF: + case PM_CONTEXT_DEF_PARAMS: + case PM_CONTEXT_DEFINED: + case PM_CONTEXT_DEF_ENSURE: + case PM_CONTEXT_DEF_RESCUE: + case PM_CONTEXT_DEF_ELSE: + // These are the good cases. We're allowed to have a block exit + // in these contexts. + return; + case PM_CONTEXT_CLASS: + case PM_CONTEXT_CLASS_ENSURE: + case PM_CONTEXT_CLASS_RESCUE: + case PM_CONTEXT_CLASS_ELSE: + case PM_CONTEXT_MAIN: + case PM_CONTEXT_MODULE: + case PM_CONTEXT_MODULE_ENSURE: + case PM_CONTEXT_MODULE_RESCUE: + case PM_CONTEXT_MODULE_ELSE: + case PM_CONTEXT_SCLASS: + case PM_CONTEXT_SCLASS_RESCUE: + case PM_CONTEXT_SCLASS_ENSURE: + case PM_CONTEXT_SCLASS_ELSE: + // These are the bad cases. We're not allowed to have a retry in + // these contexts. + pm_parser_err_node(parser, node, PM_ERR_INVALID_YIELD); + return; + case PM_CONTEXT_NONE: + // This case should never happen. + assert(false && "unreachable"); + break; + case PM_CONTEXT_BEGIN: + case PM_CONTEXT_BEGIN_ELSE: + case PM_CONTEXT_BEGIN_ENSURE: + case PM_CONTEXT_BEGIN_RESCUE: + case PM_CONTEXT_BLOCK_BRACES: + case PM_CONTEXT_BLOCK_KEYWORDS: + case PM_CONTEXT_BLOCK_ELSE: + case PM_CONTEXT_BLOCK_ENSURE: + case PM_CONTEXT_BLOCK_RESCUE: + case PM_CONTEXT_CASE_IN: + case PM_CONTEXT_CASE_WHEN: + case PM_CONTEXT_DEFAULT_PARAMS: + case PM_CONTEXT_ELSE: + case PM_CONTEXT_ELSIF: + case PM_CONTEXT_EMBEXPR: + case PM_CONTEXT_FOR_INDEX: + case PM_CONTEXT_FOR: + case PM_CONTEXT_IF: + case PM_CONTEXT_LAMBDA_BRACES: + case PM_CONTEXT_LAMBDA_DO_END: + case PM_CONTEXT_LAMBDA_ELSE: + case PM_CONTEXT_LAMBDA_ENSURE: + case PM_CONTEXT_LAMBDA_RESCUE: + case PM_CONTEXT_PARENS: + case PM_CONTEXT_POSTEXE: + case PM_CONTEXT_PREDICATE: + case PM_CONTEXT_PREEXE: + case PM_CONTEXT_RESCUE_MODIFIER: + case PM_CONTEXT_TERNARY: + case PM_CONTEXT_UNLESS: + case PM_CONTEXT_UNTIL: + case PM_CONTEXT_WHILE: + // In these contexts we should continue walking up the list of + // contexts. + break; + } + + context_node = context_node->prev; + } +} + /** * Parse an expression that begins with the previous node that we just lexed. */ @@ -15803,6 +16779,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b case PM_TOKEN_PARENTHESIS_LEFT: case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES: { pm_token_t opening = parser->current; + + pm_node_list_t current_block_exits = { 0 }; + pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits); + parser_lex(parser); while (accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE)); @@ -15810,6 +16790,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // we have an empty parentheses node, and we can immediately return. if (match2(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_EOF)) { expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN); + + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + return (pm_node_t *) pm_parentheses_node_create(parser, &opening, NULL, &parser->previous); } @@ -15835,9 +16819,13 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (opening.type == PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES) { lex_state_set(parser, PM_LEX_STATE_ENDARG); } + parser_lex(parser); pm_accepts_block_stack_pop(parser); + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE) || PM_NODE_TYPE_P(statement, PM_SPLAT_NODE)) { // If we have a single statement and are ending on a right // parenthesis, then we need to check if this is possibly a @@ -15873,7 +16861,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // and we didn't return a multiple assignment node, then we can return a // regular parentheses node now. pm_statements_node_t *statements = pm_statements_node_create(parser); - pm_statements_node_body_append(statements, statement); + pm_statements_node_body_append(parser, statements, statement); return (pm_node_t *) pm_parentheses_node_create(parser, &opening, (pm_node_t *) statements, &parser->previous); } @@ -15883,7 +16871,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // We'll do that here. context_push(parser, PM_CONTEXT_PARENS); pm_statements_node_t *statements = pm_statements_node_create(parser); - pm_statements_node_body_append(statements, statement); + pm_statements_node_body_append(parser, statements, statement); // If we didn't find a terminator and we didn't find a right // parenthesis, then this is a syntax error. @@ -15894,7 +16882,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // Parse each statement within the parentheses. while (true) { pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_STATEMENT, true, PM_ERR_CANNOT_PARSE_EXPRESSION); - pm_statements_node_body_append(statements, node); + pm_statements_node_body_append(parser, statements, node); // If we're recovering from a syntax error, then we need to stop // parsing the statements now. @@ -15925,6 +16913,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_accepts_block_stack_pop(parser); expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN); + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + + pm_void_statements_check(parser, statements); return (pm_node_t *) pm_parentheses_node_create(parser, &opening, (pm_node_t *) statements, &parser->previous); } case PM_TOKEN_BRACE_LEFT: { @@ -16356,6 +17348,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t case_keyword = parser->previous; pm_node_t *predicate = NULL; + pm_node_list_t current_block_exits = { 0 }; + pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits); + if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) { while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)); predicate = NULL; @@ -16369,6 +17364,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } if (accept1(parser, PM_TOKEN_KEYWORD_END)) { + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS); return (pm_node_t *) pm_case_node_create(parser, &case_keyword, predicate, &parser->previous); } @@ -16548,6 +17546,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_case_match_node_end_keyword_loc_set((pm_case_match_node_t *) node, &parser->previous); } + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + return node; } case PM_TOKEN_KEYWORD_BEGIN: { @@ -16555,6 +17556,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t begin_keyword = parser->previous; accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); + + pm_node_list_t current_block_exits = { 0 }; + pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits); pm_statements_node_t *begin_statements = NULL; if (!match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_END)) { @@ -16565,7 +17569,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_begin_node_t *begin_node = pm_begin_node_create(parser, &begin_keyword, begin_statements); - parse_rescues(parser, begin_node, false); + parse_rescues(parser, begin_node, PM_RESCUES_BEGIN); expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BEGIN_TERM); begin_node->base.location.end = parser->previous.end; @@ -16575,6 +17579,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_parser_err_node(parser, (pm_node_t *) begin_node->else_clause, PM_ERR_BEGIN_LONELY_ELSE); } + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + return (pm_node_t *) begin_node; } case PM_TOKEN_KEYWORD_BEGIN_UPCASE: { @@ -16616,10 +17623,16 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } switch (keyword.type) { - case PM_TOKEN_KEYWORD_BREAK: - return (pm_node_t *) pm_break_node_create(parser, &keyword, arguments.arguments); - case PM_TOKEN_KEYWORD_NEXT: - return (pm_node_t *) pm_next_node_create(parser, &keyword, arguments.arguments); + case PM_TOKEN_KEYWORD_BREAK: { + pm_node_t *node = (pm_node_t *) pm_break_node_create(parser, &keyword, arguments.arguments); + if (!parser->parsing_eval) parse_block_exit(parser, node, "break"); + return node; + } + case PM_TOKEN_KEYWORD_NEXT: { + pm_node_t *node = (pm_node_t *) pm_next_node_create(parser, &keyword, arguments.arguments); + if (!parser->parsing_eval) parse_block_exit(parser, node, "next"); + return node; + } case PM_TOKEN_KEYWORD_RETURN: { if ( (parser->current_context->context == PM_CONTEXT_CLASS) || @@ -16658,7 +17671,20 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_arguments_t arguments = { 0 }; parse_arguments_list(parser, &arguments, false, accepts_command_call); - return (pm_node_t *) pm_yield_node_create(parser, &keyword, &arguments.opening_loc, arguments.arguments, &arguments.closing_loc); + // It's possible that we've parsed a block argument through our + // call to parse_arguments_list. If we found one, we should mark it + // as invalid and destroy it, as we don't have a place for it on the + // yield node. + if (arguments.block != NULL) { + pm_parser_err_node(parser, arguments.block, PM_ERR_UNEXPECTED_BLOCK_ARGUMENT); + pm_node_destroy(parser, arguments.block); + arguments.block = NULL; + } + + pm_node_t *node = (pm_node_t *) pm_yield_node_create(parser, &keyword, &arguments.opening_loc, arguments.arguments, &arguments.closing_loc); + if (!parser->parsing_eval) parse_yield(parser, node); + + return node; } case PM_TOKEN_KEYWORD_CLASS: { parser_lex(parser); @@ -16669,7 +17695,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t operator = parser->previous; pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_NOT, true, PM_ERR_EXPECT_EXPRESSION_AFTER_LESS_LESS); - pm_constant_id_t saved_param_name = pm_parser_current_param_name_unset(parser); pm_parser_scope_push(parser, true); accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON); @@ -16682,19 +17707,23 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) { assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE)); - statements = (pm_node_t *) parse_rescues_as_begin(parser, class_keyword.start, (pm_statements_node_t *) statements, false); + statements = (pm_node_t *) parse_rescues_implicit_begin(parser, class_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_SCLASS); } expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM); - pm_constant_id_list_t locals = parser->current_scope->locals; + + pm_constant_id_list_t locals; + pm_locals_order(parser, &parser->current_scope->locals, &locals, false); pm_parser_scope_pop(parser); pm_do_loop_stack_pop(parser); - pm_parser_current_param_name_restore(parser, saved_param_name); return (pm_node_t *) pm_singleton_class_node_create(parser, &locals, &class_keyword, &operator, expression, statements, &parser->previous); } + pm_node_list_t current_block_exits = { 0 }; + pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits); + pm_node_t *constant_path = parse_expression(parser, PM_BINDING_POWER_INDEX, false, PM_ERR_CLASS_NAME); pm_token_t name = parser->previous; if (name.type != PM_TOKEN_CONSTANT) { @@ -16717,7 +17746,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b superclass = NULL; } - pm_constant_id_t saved_param_name = pm_parser_current_param_name_unset(parser); pm_parser_scope_push(parser, true); if (inheritance_operator.type != PM_TOKEN_NOT_PROVIDED) { @@ -16735,7 +17763,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) { assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE)); - statements = (pm_node_t *) parse_rescues_as_begin(parser, class_keyword.start, (pm_statements_node_t *) statements, false); + statements = (pm_node_t *) parse_rescues_implicit_begin(parser, class_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_CLASS); } expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM); @@ -16744,16 +17772,19 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_parser_err_token(parser, &class_keyword, PM_ERR_CLASS_IN_METHOD); } - pm_constant_id_list_t locals = parser->current_scope->locals; + pm_constant_id_list_t locals; + pm_locals_order(parser, &parser->current_scope->locals, &locals, false); pm_parser_scope_pop(parser); pm_do_loop_stack_pop(parser); - pm_parser_current_param_name_restore(parser, saved_param_name); if (!PM_NODE_TYPE_P(constant_path, PM_CONSTANT_PATH_NODE) && !(PM_NODE_TYPE_P(constant_path, PM_CONSTANT_READ_NODE))) { pm_parser_err_node(parser, constant_path, PM_ERR_CLASS_NAME); } + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + return (pm_node_t *) pm_class_node_create(parser, &locals, &class_keyword, constant_path, &name, &inheritance_operator, superclass, statements, &parser->previous); } case PM_TOKEN_KEYWORD_DEF: { @@ -16761,19 +17792,16 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_node_t *receiver = NULL; pm_token_t operator = not_provided(parser); - pm_token_t name = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = def_keyword.end, .end = def_keyword.end }; + pm_token_t name; // This context is necessary for lexing `...` in a bare params // correctly. It must be pushed before lexing the first param, so it // is here. context_push(parser, PM_CONTEXT_DEF_PARAMS); - pm_constant_id_t saved_param_name; - parser_lex(parser); switch (parser->current.type) { case PM_CASE_OPERATOR: - saved_param_name = pm_parser_current_param_name_unset(parser); pm_parser_scope_push(parser, true); lex_state_set(parser, PM_LEX_STATE_ENDFN); parser_lex(parser); @@ -16787,7 +17815,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b receiver = parse_variable_call(parser); receiver = pm_node_check_it(parser, receiver); - saved_param_name = pm_parser_current_param_name_unset(parser); pm_parser_scope_push(parser, true); lex_state_set(parser, PM_LEX_STATE_FNAME); parser_lex(parser); @@ -16795,7 +17822,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b operator = parser->previous; name = parse_method_definition_name(parser); } else { - saved_param_name = pm_parser_current_param_name_unset(parser); pm_refute_numbered_parameter(parser, parser->previous.start, parser->previous.end); pm_parser_scope_push(parser, true); @@ -16815,7 +17841,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b case PM_TOKEN_KEYWORD___FILE__: case PM_TOKEN_KEYWORD___LINE__: case PM_TOKEN_KEYWORD___ENCODING__: { - saved_param_name = pm_parser_current_param_name_unset(parser); pm_parser_scope_push(parser, true); parser_lex(parser); @@ -16849,7 +17874,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b receiver = (pm_node_t *) pm_true_node_create(parser, &identifier); break; case PM_TOKEN_KEYWORD_FALSE: - receiver = (pm_node_t *)pm_false_node_create(parser, &identifier); + receiver = (pm_node_t *) pm_false_node_create(parser, &identifier); break; case PM_TOKEN_KEYWORD___FILE__: receiver = (pm_node_t *) pm_source_file_node_create(parser, &identifier); @@ -16871,9 +17896,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b break; } case PM_TOKEN_PARENTHESIS_LEFT: { - // The current context is `PM_CONTEXT_DEF_PARAMS`, however the inner expression - // of this parenthesis should not be processed under this context. - // Thus, the context is popped here. + // The current context is `PM_CONTEXT_DEF_PARAMS`, however + // the inner expression of this parenthesis should not be + // processed under this context. Thus, the context is popped + // here. context_pop(parser); parser_lex(parser); @@ -16890,28 +17916,19 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b operator = parser->previous; receiver = (pm_node_t *) pm_parentheses_node_create(parser, &lparen, expression, &rparen); - saved_param_name = pm_parser_current_param_name_unset(parser); + // To push `PM_CONTEXT_DEF_PARAMS` again is for the same + // reason as described the above. pm_parser_scope_push(parser, true); - - // To push `PM_CONTEXT_DEF_PARAMS` again is for the same reason as described the above. context_push(parser, PM_CONTEXT_DEF_PARAMS); name = parse_method_definition_name(parser); break; } default: - saved_param_name = pm_parser_current_param_name_unset(parser); pm_parser_scope_push(parser, true); - name = parse_method_definition_name(parser); break; } - // If, after all that, we were unable to find a method name, add an - // error to the error list. - if (name.type == PM_TOKEN_MISSING) { - pm_parser_err_previous(parser, PM_ERR_DEF_NAME); - } - pm_token_t lparen; pm_token_t rparen; pm_parameters_node_t *params; @@ -16972,13 +17989,16 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_node_t *statement = parse_expression(parser, PM_BINDING_POWER_DEFINED + 1, binding_power < PM_BINDING_POWER_COMPOSITION, PM_ERR_DEF_ENDLESS); if (accept1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) { + context_push(parser, PM_CONTEXT_RESCUE_MODIFIER); + pm_token_t rescue_keyword = parser->previous; pm_node_t *value = parse_expression(parser, binding_power, false, PM_ERR_RESCUE_MODIFIER_VALUE); - pm_rescue_modifier_node_t *rescue_node = pm_rescue_modifier_node_create(parser, statement, &rescue_keyword, value); - statement = (pm_node_t *)rescue_node; + context_pop(parser); + + statement = (pm_node_t *) pm_rescue_modifier_node_create(parser, statement, &rescue_keyword, value); } - pm_statements_node_body_append((pm_statements_node_t *) statements, statement); + pm_statements_node_body_append(parser, (pm_statements_node_t *) statements, statement); pm_do_loop_stack_pop(parser); context_pop(parser); end_keyword = not_provided(parser); @@ -17004,7 +18024,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) { assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE)); - statements = (pm_node_t *) parse_rescues_as_begin(parser, def_keyword.start, (pm_statements_node_t *) statements, true); + statements = (pm_node_t *) parse_rescues_implicit_begin(parser, def_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_DEF); } pm_accepts_block_stack_pop(parser); @@ -17013,10 +18033,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b end_keyword = parser->previous; } - pm_constant_id_list_t locals = parser->current_scope->locals; - + pm_constant_id_list_t locals; + pm_locals_order(parser, &parser->current_scope->locals, &locals, false); pm_parser_scope_pop(parser); - pm_parser_current_param_name_restore(parser, saved_param_name); /** * If the final character is @. As is the case when defining @@ -17048,6 +18067,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t lparen; pm_token_t rparen; pm_node_t *expression; + context_push(parser, PM_CONTEXT_DEFINED); if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) { lparen = parser->previous; @@ -17066,6 +18086,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_DEFINED_EXPRESSION); } + context_pop(parser); return (pm_node_t *) pm_defined_node_create( parser, &lparen, @@ -17223,15 +18244,21 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b parser_lex(parser); return parse_conditional(parser, PM_CONTEXT_UNLESS); case PM_TOKEN_KEYWORD_MODULE: { - parser_lex(parser); + pm_node_list_t current_block_exits = { 0 }; + pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits); + parser_lex(parser); pm_token_t module_keyword = parser->previous; + pm_node_t *constant_path = parse_expression(parser, PM_BINDING_POWER_INDEX, false, PM_ERR_MODULE_NAME); pm_token_t name; // If we can recover from a syntax error that occurred while parsing // the name of the module, then we'll handle that here. if (PM_NODE_TYPE_P(constant_path, PM_MISSING_NODE)) { + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + pm_token_t missing = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; return (pm_node_t *) pm_module_node_create(parser, NULL, &module_keyword, constant_path, &missing, NULL, &missing); } @@ -17253,9 +18280,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_parser_err_token(parser, &name, PM_ERR_MODULE_NAME); } - pm_constant_id_t saved_param_name = pm_parser_current_param_name_unset(parser); pm_parser_scope_push(parser, true); - accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE); pm_node_t *statements = NULL; @@ -17267,30 +18292,43 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) { assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE)); - statements = (pm_node_t *) parse_rescues_as_begin(parser, module_keyword.start, (pm_statements_node_t *) statements, false); + statements = (pm_node_t *) parse_rescues_implicit_begin(parser, module_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_MODULE); } - pm_constant_id_list_t locals = parser->current_scope->locals; - pm_parser_scope_pop(parser); - pm_parser_current_param_name_restore(parser, saved_param_name); + pm_constant_id_list_t locals; + pm_locals_order(parser, &parser->current_scope->locals, &locals, false); + pm_parser_scope_pop(parser); expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_MODULE_TERM); if (context_def_p(parser)) { pm_parser_err_token(parser, &module_keyword, PM_ERR_MODULE_IN_METHOD); } + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + return (pm_node_t *) pm_module_node_create(parser, &locals, &module_keyword, constant_path, &name, statements, &parser->previous); } case PM_TOKEN_KEYWORD_NIL: parser_lex(parser); return (pm_node_t *) pm_nil_node_create(parser, &parser->previous); - case PM_TOKEN_KEYWORD_REDO: + case PM_TOKEN_KEYWORD_REDO: { parser_lex(parser); - return (pm_node_t *) pm_redo_node_create(parser, &parser->previous); - case PM_TOKEN_KEYWORD_RETRY: + + pm_node_t *node = (pm_node_t *) pm_redo_node_create(parser, &parser->previous); + if (!parser->parsing_eval) parse_block_exit(parser, node, "redo"); + + return node; + } + case PM_TOKEN_KEYWORD_RETRY: { parser_lex(parser); - return (pm_node_t *) pm_retry_node_create(parser, &parser->previous); + + pm_node_t *node = (pm_node_t *) pm_retry_node_create(parser, &parser->previous); + parse_retry(parser, node); + + return node; + } case PM_TOKEN_KEYWORD_SELF: parser_lex(parser); return (pm_node_t *) pm_self_node_create(parser, &parser->previous); @@ -17954,7 +18992,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b parser_lex(parser); pm_token_t operator = parser->previous; - pm_constant_id_t saved_param_name = pm_parser_current_param_name_unset(parser); pm_parser_scope_push(parser, false); pm_block_parameters_node_t *block_parameters; @@ -18018,18 +19055,18 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) { assert(body == NULL || PM_NODE_TYPE_P(body, PM_STATEMENTS_NODE)); - body = (pm_node_t *) parse_rescues_as_begin(parser, opening.start, (pm_statements_node_t *) body, false); + body = (pm_node_t *) parse_rescues_implicit_begin(parser, opening.start, (pm_statements_node_t *) body, PM_RESCUES_LAMBDA); } expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_LAMBDA_TERM_END); } - pm_constant_id_list_t locals = parser->current_scope->locals; + pm_constant_id_list_t locals; + pm_locals_order(parser, &parser->current_scope->locals, &locals, pm_parser_scope_toplevel_p(parser)); pm_node_t *parameters = parse_blocklike_parameters(parser, (pm_node_t *) block_parameters, &operator, &parser->previous); pm_parser_scope_pop(parser); pm_accepts_block_stack_pop(parser); - pm_parser_current_param_name_restore(parser, saved_param_name); return (pm_node_t *) pm_lambda_node_create(parser, &locals, &operator, &opening, &parser->previous, parameters, body); } @@ -18081,15 +19118,29 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } } -static inline pm_node_t * +/** + * Parse a value that is going to be written to some kind of variable or method + * call. We need to handle this separately because the rescue modifier is + * permitted on the end of the these expressions, which is a deviation from its + * normal binding power. + * + * Note that this will only be called after an operator write, as in &&=, ||=, + * or any of the binary operators that can be written to a variable. + */ +static pm_node_t * parse_assignment_value(pm_parser_t *parser, pm_binding_power_t previous_binding_power, pm_binding_power_t binding_power, bool accepts_command_call, pm_diagnostic_id_t diag_id) { pm_node_t *value = parse_value_expression(parser, binding_power, previous_binding_power == PM_BINDING_POWER_ASSIGNMENT ? accepts_command_call : previous_binding_power < PM_BINDING_POWER_MATCH, diag_id); - // Contradicting binding powers, the right-hand-side value of rthe assignment allows the `rescue` modifier. + // Contradicting binding powers, the right-hand-side value of the assignment + // allows the `rescue` modifier. if (match1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) { + context_push(parser, PM_CONTEXT_RESCUE_MODIFIER); + pm_token_t rescue = parser->current; parser_lex(parser); + pm_node_t *right = parse_expression(parser, binding_power, false, PM_ERR_RESCUE_MODIFIER_VALUE); + context_pop(parser); return (pm_node_t *) pm_rescue_modifier_node_create(parser, value, &rescue, right); } @@ -18097,14 +19148,63 @@ parse_assignment_value(pm_parser_t *parser, pm_binding_power_t previous_binding_ return value; } +/** + * When a local variable write node is the value being written in a different + * write, the local variable is considered "used". + */ +static void +parse_assignment_value_local(pm_parser_t *parser, const pm_node_t *node) { + switch (PM_NODE_TYPE(node)) { + case PM_BEGIN_NODE: { + const pm_begin_node_t *cast = (const pm_begin_node_t *) node; + if (cast->statements != NULL) parse_assignment_value_local(parser, (const pm_node_t *) cast->statements); + break; + } + case PM_LOCAL_VARIABLE_WRITE_NODE: { + const pm_local_variable_write_node_t *cast = (const pm_local_variable_write_node_t *) node; + pm_locals_read(&pm_parser_scope_find(parser, cast->depth)->locals, cast->name); + break; + } + case PM_PARENTHESES_NODE: { + const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node; + if (cast->body != NULL) parse_assignment_value_local(parser, cast->body); + break; + } + case PM_STATEMENTS_NODE: { + const pm_statements_node_t *cast = (const pm_statements_node_t *) node; + const pm_node_t *statement; -static inline pm_node_t * + PM_NODE_LIST_FOREACH(&cast->body, index, statement) { + parse_assignment_value_local(parser, statement); + } + break; + } + default: + break; + } +} + +/** + * Parse the value (or values, through an implicit array) that is going to be + * written to some kind of variable or method call. We need to handle this + * separately because the rescue modifier is permitted on the end of the these + * expressions, which is a deviation from its normal binding power. + * + * Additionally, if the value is a local variable write node (e.g., a = a = 1), + * the "a" is marked as being used so the parser should not warn on it. + * + * Note that this will only be called after an = operator, as that is the only + * operator that allows multiple values after it. + */ +static pm_node_t * parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding_power, pm_binding_power_t binding_power, bool accepts_command_call, pm_diagnostic_id_t diag_id) { pm_node_t *value = parse_starred_expression(parser, binding_power, previous_binding_power == PM_BINDING_POWER_ASSIGNMENT ? accepts_command_call : previous_binding_power < PM_BINDING_POWER_MATCH, diag_id); - bool single_value = true; + parse_assignment_value_local(parser, value); + bool single_value = true; if (previous_binding_power == PM_BINDING_POWER_STATEMENT && (PM_NODE_TYPE_P(value, PM_SPLAT_NODE) || match1(parser, PM_TOKEN_COMMA))) { single_value = false; + pm_token_t opening = not_provided(parser); pm_array_node_t *array = pm_array_node_create(parser, &opening); @@ -18113,14 +19213,19 @@ parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding while (accept1(parser, PM_TOKEN_COMMA)) { pm_node_t *element = parse_starred_expression(parser, binding_power, false, PM_ERR_ARRAY_ELEMENT); + pm_array_node_elements_append(array, element); if (PM_NODE_TYPE_P(element, PM_MISSING_NODE)) break; + + parse_assignment_value_local(parser, element); } } // Contradicting binding powers, the right-hand-side value of the assignment // allows the `rescue` modifier. if ((single_value || (binding_power == (PM_BINDING_POWER_MULTI_ASSIGNMENT + 1))) && match1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) { + context_push(parser, PM_CONTEXT_RESCUE_MODIFIER); + pm_token_t rescue = parser->current; parser_lex(parser); @@ -18136,6 +19241,7 @@ parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding } pm_node_t *right = parse_expression(parser, binding_power, accepts_command_call_inner, PM_ERR_RESCUE_MODIFIER_VALUE); + context_pop(parser); return (pm_node_t *) pm_rescue_modifier_node_create(parser, value, &rescue, right); } @@ -18255,7 +19361,7 @@ parse_regular_expression_named_captures(pm_parser_t *parser, const pm_string_t * // it to the local table unless it's a keyword. if (pm_local_is_keyword((const char *) source, length)) continue; - pm_parser_local_add(parser, name); + pm_parser_local_add(parser, name, location.start, location.end, 0); } // Here we lazily create the MatchWriteNode since we know we're @@ -18298,7 +19404,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t // is parsed because it could be referenced in the value. pm_call_node_t *call_node = (pm_call_node_t *) node; if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) { - pm_parser_local_add_location(parser, call_node->message_loc.start, call_node->message_loc.end); + pm_parser_local_add_location(parser, call_node->message_loc.start, call_node->message_loc.end, 0); } } /* fallthrough */ @@ -18315,13 +19421,25 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t pm_node_t *value = parse_assignment_values(parser, previous_binding_power, PM_BINDING_POWER_MULTI_ASSIGNMENT + 1, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL); return parse_write(parser, (pm_node_t *) multi_target, &token, value); } + case PM_SOURCE_ENCODING_NODE: + case PM_FALSE_NODE: + case PM_SOURCE_FILE_NODE: + case PM_SOURCE_LINE_NODE: + case PM_NIL_NODE: + case PM_SELF_NODE: + case PM_TRUE_NODE: { + // In these special cases, we have specific error messages + // and we will replace them with local variable writes. + parser_lex(parser); + pm_node_t *value = parse_assignment_values(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL); + return parse_unwriteable_write(parser, node, &token, value); + } default: + // In this case we have an = sign, but we don't know what + // it's for. We need to treat it as an error. We'll mark it + // as an error and skip past it. parser_lex(parser); - - // In this case we have an = sign, but we don't know what it's for. We - // need to treat it as an error. For now, we'll mark it as an error - // and just skip right past it. - pm_parser_err_token(parser, &token, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL); + pm_parser_err_token(parser, &token, PM_ERR_EXPRESSION_NOT_WRITABLE); return node; } } @@ -18396,7 +19514,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t pm_location_t *message_loc = &cast->message_loc; pm_refute_numbered_parameter(parser, message_loc->start, message_loc->end); - pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc->start, message_loc->end); + pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc->start, message_loc->end, 1); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ); pm_node_t *result = (pm_node_t *) pm_local_variable_and_write_node_create(parser, (pm_node_t *) cast, &token, value, constant_id, 0); @@ -18509,7 +19627,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t pm_location_t *message_loc = &cast->message_loc; pm_refute_numbered_parameter(parser, message_loc->start, message_loc->end); - pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc->start, message_loc->end); + pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc->start, message_loc->end, 1); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ); pm_node_t *result = (pm_node_t *) pm_local_variable_or_write_node_create(parser, (pm_node_t *) cast, &token, value, constant_id, 0); @@ -18632,7 +19750,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t pm_location_t *message_loc = &cast->message_loc; pm_refute_numbered_parameter(parser, message_loc->start, message_loc->end); - pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc->start, message_loc->end); + pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc->start, message_loc->end, 1); pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR); pm_node_t *result = (pm_node_t *) pm_local_variable_operator_write_node_create(parser, (pm_node_t *) cast, &token, value, constant_id, 0); @@ -18716,9 +19834,8 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t bool interpolated = false; size_t total_length = 0; - for (size_t index = 0; index < parts->size; index++) { - pm_node_t *part = parts->nodes[index]; - + pm_node_t *part; + PM_NODE_LIST_FOREACH(parts, index, part) { if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) { total_length += pm_string_length(&((pm_string_node_t *) part)->unescaped); } else { @@ -18732,8 +19849,8 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t if (!memory) abort(); uint8_t *cursor = memory; - for (size_t index = 0; index < parts->size; index++) { - pm_string_t *unescaped = &((pm_string_node_t *) parts->nodes[index])->unescaped; + PM_NODE_LIST_FOREACH(parts, index, part) { + pm_string_t *unescaped = &((pm_string_node_t *) part)->unescaped; size_t length = pm_string_length(unescaped); memcpy(cursor, pm_string_source(unescaped), length); @@ -18817,7 +19934,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t break; } default: { - pm_parser_err_current(parser, PM_ERR_DEF_NAME); + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_MESSAGE, pm_token_type_human(parser->current.type)); message = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; } } @@ -18864,7 +19981,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t case PM_TOKEN_KEYWORD_UNTIL_MODIFIER: { parser_lex(parser); pm_statements_node_t *statements = pm_statements_node_create(parser); - pm_statements_node_body_append(statements, node); + pm_statements_node_body_append(parser, statements, node); pm_node_t *predicate = parse_value_expression(parser, binding_power, true, PM_ERR_CONDITIONAL_UNTIL_PREDICATE); return (pm_node_t *) pm_until_node_modifier_create(parser, &token, predicate, statements, PM_NODE_TYPE_P(node, PM_BEGIN_NODE) ? PM_LOOP_FLAGS_BEGIN_MODIFIER : 0); @@ -18872,14 +19989,19 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t case PM_TOKEN_KEYWORD_WHILE_MODIFIER: { parser_lex(parser); pm_statements_node_t *statements = pm_statements_node_create(parser); - pm_statements_node_body_append(statements, node); + pm_statements_node_body_append(parser, statements, node); pm_node_t *predicate = parse_value_expression(parser, binding_power, true, PM_ERR_CONDITIONAL_WHILE_PREDICATE); return (pm_node_t *) pm_while_node_modifier_create(parser, &token, predicate, statements, PM_NODE_TYPE_P(node, PM_BEGIN_NODE) ? PM_LOOP_FLAGS_BEGIN_MODIFIER : 0); } case PM_TOKEN_QUESTION_MARK: { + context_push(parser, PM_CONTEXT_TERNARY); + pm_node_list_t current_block_exits = { 0 }; + pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits); + pm_token_t qmark = parser->current; parser_lex(parser); + pm_node_t *true_expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_TERNARY_EXPRESSION_TRUE); if (parser->recovering) { @@ -18892,6 +20014,10 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t pm_token_t colon = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; pm_node_t *false_expression = (pm_node_t *) pm_missing_node_create(parser, colon.start, colon.end); + context_pop(parser); + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + return (pm_node_t *) pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression); } @@ -18901,6 +20027,10 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t pm_token_t colon = parser->previous; pm_node_t *false_expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_TERNARY_EXPRESSION_FALSE); + context_pop(parser); + pop_block_exits(parser, previous_block_exits); + pm_node_list_free(¤t_block_exits); + return (pm_node_t *) pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression); } case PM_TOKEN_COLON_COLON: { @@ -18976,9 +20106,12 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t } } case PM_TOKEN_KEYWORD_RESCUE_MODIFIER: { + context_push(parser, PM_CONTEXT_RESCUE_MODIFIER); parser_lex(parser); accept1(parser, PM_TOKEN_NEWLINE); + pm_node_t *value = parse_expression(parser, binding_power, true, PM_ERR_RESCUE_MODIFIER_VALUE); + context_pop(parser); return (pm_node_t *) pm_rescue_modifier_node_create(parser, node, &token, value); } @@ -19201,7 +20334,7 @@ wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) { (pm_node_t *) pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$_", 2)) ); - pm_statements_node_body_append(statements, (pm_node_t *) pm_call_node_fcall_synthesized_create( + pm_statements_node_body_append(parser, statements, (pm_node_t *) pm_call_node_fcall_synthesized_create( parser, arguments, pm_parser_constant_id_constant(parser, "print", 5) @@ -19247,7 +20380,7 @@ wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) { } pm_statements_node_t *wrapped_statements = pm_statements_node_create(parser); - pm_statements_node_body_append(wrapped_statements, (pm_node_t *) pm_while_node_synthesized_create( + pm_statements_node_body_append(parser, wrapped_statements, (pm_node_t *) pm_while_node_synthesized_create( parser, (pm_node_t *) pm_call_node_fcall_synthesized_create(parser, arguments, pm_parser_constant_id_constant(parser, "gets", 4)), statements @@ -19273,10 +20406,19 @@ parse_program(pm_parser_t *parser) { parser_lex(parser); pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_MAIN); - if (!statements) { + + if (statements == NULL) { statements = pm_statements_node_create(parser); + } else if (!parser->parsing_eval) { + // If we have statements, then the top-level statement should be + // explicitly checked as well. We have to do this here because + // everywhere else we check all but the last statement. + assert(statements->body.size > 0); + pm_void_statement_check(parser, statements->body.nodes[statements->body.size - 1]); } - pm_constant_id_list_t locals = parser->current_scope->locals; + + pm_constant_id_list_t locals; + pm_locals_order(parser, &parser->current_scope->locals, &locals, true); pm_parser_scope_pop(parser); // If this is an empty file, then we're still going to parse all of the @@ -19375,12 +20517,13 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm .start_line = 1, .explicit_encoding = NULL, .command_line = 0, + .parsing_eval = false, .command_start = true, .recovering = false, .encoding_changed = false, .pattern_matching_newlines = false, .in_keyword_arg = false, - .current_param_name = 0, + .current_block_exits = NULL, .semantic_token_seen = false, .frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_UNSET, .current_regular_expression_ascii_only = false @@ -19434,6 +20577,8 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm parser->version = options->version; // scopes option + parser->parsing_eval = options->scopes_count > 0; + for (size_t scope_index = 0; scope_index < options->scopes_count; scope_index++) { const pm_options_scope_t *scope = pm_options_scope_get(options, scope_index); pm_parser_scope_push(parser, scope_index == 0); @@ -19597,7 +20742,6 @@ pm_parser_free(pm_parser_t *parser) { // assumed that ownership has transferred to the AST. However if we have // scopes while we're freeing the parser, it's likely they came from // eval scopes and we need to free them explicitly here. - pm_constant_id_list_free(&parser->current_scope->locals); pm_parser_scope_pop(parser); } @@ -19721,7 +20865,7 @@ pm_parse_success_p(const uint8_t *source, size_t size, const char *data) { pm_node_t *node = pm_parse(&parser); pm_node_destroy(&parser, node); - bool result = parser.error_list.size == 0 && parser.warning_list.size == 0; + bool result = parser.error_list.size == 0; pm_parser_free(&parser); pm_options_free(&options); @@ -20123,6 +21267,8 @@ pm_parser_errors_format(const pm_parser_t *parser, const pm_list_t *error_list, if (inline_messages) { pm_buffer_append_byte(buffer, ' '); + assert(error->error != NULL); + const char *message = error->error->message; pm_buffer_append_string(buffer, message, strlen(message)); } diff --git a/src/main/c/yarp/src/token_type.c b/src/main/c/yarp/src/token_type.c index fc53aa061ce..ea3c1db5252 100644 --- a/src/main/c/yarp/src/token_type.c +++ b/src/main/c/yarp/src/token_type.c @@ -540,7 +540,7 @@ pm_token_type_human(pm_token_type_t token_type) { case PM_TOKEN_KEYWORD_RESCUE: return "'rescue'"; case PM_TOKEN_KEYWORD_RESCUE_MODIFIER: - return "'rescue'"; + return "'rescue' modifier"; case PM_TOKEN_KEYWORD_RETRY: return "'retry'"; case PM_TOKEN_KEYWORD_RETURN: @@ -658,13 +658,13 @@ pm_token_type_human(pm_token_type_t token_type) { case PM_TOKEN_STAR_STAR_EQUAL: return "'**='"; case PM_TOKEN_STRING_BEGIN: - return "string beginning"; + return "string literal"; case PM_TOKEN_STRING_CONTENT: return "string content"; case PM_TOKEN_STRING_END: return "string ending"; case PM_TOKEN_SYMBOL_BEGIN: - return "symbol beginning"; + return "symbol literal"; case PM_TOKEN_TILDE: return "'~'"; case PM_TOKEN_UAMPERSAND: diff --git a/src/main/c/yarp/src/util/pm_constant_pool.c b/src/main/c/yarp/src/util/pm_constant_pool.c index 741a036cf70..2a3203f4c46 100644 --- a/src/main/c/yarp/src/util/pm_constant_pool.c +++ b/src/main/c/yarp/src/util/pm_constant_pool.c @@ -10,6 +10,18 @@ pm_constant_id_list_init(pm_constant_id_list_t *list) { list->capacity = 0; } +/** + * Initialize a list of constant ids with a given capacity. + */ +void +pm_constant_id_list_init_capacity(pm_constant_id_list_t *list, size_t capacity) { + list->ids = xcalloc(capacity, sizeof(pm_constant_id_t)); + if (list->ids == NULL) abort(); + + list->size = 0; + list->capacity = capacity; +} + /** * Append a constant id to a list of constant ids. Returns false if any * potential reallocations fail. @@ -26,6 +38,18 @@ pm_constant_id_list_append(pm_constant_id_list_t *list, pm_constant_id_t id) { return true; } +/** + * Insert a constant id into a list of constant ids at the specified index. + */ +void +pm_constant_id_list_insert(pm_constant_id_list_t *list, size_t index, pm_constant_id_t id) { + assert(index < list->capacity); + assert(list->ids[index] == PM_CONSTANT_ID_UNSET); + + list->ids[index] = id; + list->size++; +} + /** * Checks if the current constant id list includes the given constant id. */ diff --git a/src/main/c/yarp/src/util/pm_integer.c b/src/main/c/yarp/src/util/pm_integer.c index 8f8b75474df..e523bae90b1 100644 --- a/src/main/c/yarp/src/util/pm_integer.c +++ b/src/main/c/yarp/src/util/pm_integer.c @@ -135,12 +135,19 @@ karatsuba_multiply(pm_integer_t *destination, pm_integer_t *left, pm_integer_t * } if (left_length * 2 <= right_length) { - uint32_t *values = (uint32_t*) xcalloc(left_length + right_length, sizeof(uint32_t)); + uint32_t *values = (uint32_t *) xcalloc(left_length + right_length, sizeof(uint32_t)); for (size_t start_offset = 0; start_offset < right_length; start_offset += left_length) { size_t end_offset = start_offset + left_length; if (end_offset > right_length) end_offset = right_length; + pm_integer_t sliced_left = { + .value = 0, + .length = left_length, + .values = left_values, + .negative = false + }; + pm_integer_t sliced_right = { .value = 0, .length = end_offset - start_offset, @@ -149,7 +156,7 @@ karatsuba_multiply(pm_integer_t *destination, pm_integer_t *left, pm_integer_t * }; pm_integer_t product; - karatsuba_multiply(&product, left, &sliced_right, base); + karatsuba_multiply(&product, &sliced_left, &sliced_right, base); uint32_t carry = 0; for (size_t index = 0; index < product.length; index++) { @@ -172,21 +179,21 @@ karatsuba_multiply(pm_integer_t *destination, pm_integer_t *left, pm_integer_t * pm_integer_t y0 = { 0, half, right_values, false }; pm_integer_t y1 = { 0, right_length - half, right_values + half, false }; - pm_integer_t z0; + pm_integer_t z0 = { 0 }; karatsuba_multiply(&z0, &x0, &y0, base); - pm_integer_t z2; + pm_integer_t z2 = { 0 }; karatsuba_multiply(&z2, &x1, &y1, base); // For simplicity to avoid considering negative values, // use `z1 = (x0 + x1) * (y0 + y1) - z0 - z2` instead of original karatsuba algorithm. - pm_integer_t x01; + pm_integer_t x01 = { 0 }; big_add(&x01, &x0, &x1, base); - pm_integer_t y01; + pm_integer_t y01 = { 0 }; big_add(&y01, &y0, &y1, base); - pm_integer_t xy; + pm_integer_t xy = { 0 }; karatsuba_multiply(&xy, &x01, &y01, base); pm_integer_t z1; @@ -194,7 +201,11 @@ karatsuba_multiply(pm_integer_t *destination, pm_integer_t *left, pm_integer_t * size_t length = left_length + right_length; uint32_t *values = (uint32_t*) xcalloc(length, sizeof(uint32_t)); + + assert(z0.values != NULL); memcpy(values, z0.values, sizeof(uint32_t) * z0.length); + + assert(z2.values != NULL); memcpy(values + 2 * half, z2.values, sizeof(uint32_t) * z2.length); uint32_t carry = 0; @@ -326,6 +337,8 @@ pm_integer_convert_base(pm_integer_t *destination, const pm_integer_t *source, u INTEGER_EXTRACT(source, source_length, source_values) size_t bigints_length = (source_length + 1) / 2; + assert(bigints_length > 0); + pm_integer_t *bigints = (pm_integer_t *) xcalloc(bigints_length, sizeof(pm_integer_t)); if (bigints == NULL) return; @@ -345,13 +358,13 @@ pm_integer_convert_base(pm_integer_t *destination, const pm_integer_t *source, u base = next_base; size_t next_length = (bigints_length + 1) / 2; - pm_integer_t *next_bigints = (pm_integer_t *) xmalloc(sizeof(pm_integer_t) * next_length); + pm_integer_t *next_bigints = (pm_integer_t *) xcalloc(next_length, sizeof(pm_integer_t)); for (size_t bigints_index = 0; bigints_index < bigints_length; bigints_index += 2) { if (bigints_index + 1 == bigints_length) { next_bigints[bigints_index / 2] = bigints[bigints_index]; } else { - pm_integer_t multiplied; + pm_integer_t multiplied = { 0 }; karatsuba_multiply(&multiplied, &base, &bigints[bigints_index + 1], base_to); big_add(&next_bigints[bigints_index / 2], &bigints[bigints_index], &multiplied, base_to); @@ -584,7 +597,7 @@ pm_integer_string(pm_buffer_t *buffer, const pm_integer_t *integer) { } // Otherwise, first we'll convert the base from 1<<32 to 10**9. - pm_integer_t converted; + pm_integer_t converted = { 0 }; pm_integer_convert_base(&converted, integer, (uint64_t) 1 << 32, 1000000000); if (converted.values == NULL) { diff --git a/src/main/c/yarp/src/util/pm_state_stack.c b/src/main/c/yarp/src/util/pm_state_stack.c deleted file mode 100644 index 2a424b4c03c..00000000000 --- a/src/main/c/yarp/src/util/pm_state_stack.c +++ /dev/null @@ -1,25 +0,0 @@ -#include "prism/util/pm_state_stack.h" - -/** - * Pushes a value onto the stack. - */ -void -pm_state_stack_push(pm_state_stack_t *stack, bool value) { - *stack = (*stack << 1) | (value & 1); -} - -/** - * Pops a value off the stack. - */ -void -pm_state_stack_pop(pm_state_stack_t *stack) { - *stack >>= 1; -} - -/** - * Returns the value at the top of the stack. - */ -bool -pm_state_stack_p(pm_state_stack_t *stack) { - return *stack & 1; -} diff --git a/src/yarp/java/org/prism/Loader.java b/src/yarp/java/org/prism/Loader.java index 615f593c4e4..ab829ad4591 100644 --- a/src/yarp/java/org/prism/Loader.java +++ b/src/yarp/java/org/prism/Loader.java @@ -102,7 +102,7 @@ protected ParseResult load() { expect((byte) 'M', "incorrect prism header"); expect((byte) 0, "prism major version does not match"); - expect((byte) 24, "prism minor version does not match"); + expect((byte) 27, "prism minor version does not match"); expect((byte) 0, "prism patch version does not match"); expect((byte) 1, "Loader.java requires no location fields in the serialized output"); @@ -211,7 +211,7 @@ private ParseResult.Warning[] loadWarnings() { // warning messages only contain ASCII characters for (int i = 0; i < count; i++) { - Nodes.WarningType type = Nodes.WARNING_TYPES[loadVarUInt() - 240]; + Nodes.WarningType type = Nodes.WARNING_TYPES[loadVarUInt() - 257]; byte[] bytes = loadEmbeddedString(); String message = new String(bytes, StandardCharsets.US_ASCII); Nodes.Location location = loadLocation(); diff --git a/src/yarp/java/org/prism/Nodes.java b/src/yarp/java/org/prism/Nodes.java index b5da0cab867..adfe0c98e8d 100644 --- a/src/yarp/java/org/prism/Nodes.java +++ b/src/yarp/java/org/prism/Nodes.java @@ -1028,7 +1028,23 @@ public boolean isForcedUsAsciiEncoding() { * */ public static final class AliasGlobalVariableNode extends Node { + /** + *
+         * Represents the new name of the global variable that can be used after aliasing. This can be either a global variable, a back reference, or a numbered reference.
+         *
+         *     alias $foo $bar
+         *           ^^^^
+         * 
+ */ public final Node new_name; + /** + *
+         * Represents the old name of the global variable that could be used before aliasing. This can be either a global variable, a back reference, or a numbered reference.
+         *
+         *     alias $foo $bar
+         *                ^^^^
+         * 
+ */ public final Node old_name; public AliasGlobalVariableNode(Node new_name, Node old_name, int startOffset, int length) { @@ -1313,6 +1329,11 @@ protected String toString(String indent) { */ public static final class ArrayNode extends Node { public final short flags; + /** + *
+         * Represent the list of zero or more [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression) within the array.
+         * 
+ */ public final Node[] elements; public ArrayNode(short flags, Node[] elements, int startOffset, int length) { @@ -2022,6 +2043,14 @@ protected String toString(String indent) { * */ public static final class BreakNode extends Node { + /** + *
+         * The arguments to the break statement, if present. These can be any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
+         *
+         *     break foo
+         *           ^^^
+         * 
+ */ @Nullable public final ArgumentsNode arguments; @@ -3078,8 +3107,7 @@ public static final class ClassVariableWriteNode extends Node { public final String name; /** *
-         * The value to assign to the class variable. Can be any node that
-         * represents a non-void expression.
+         * The value to write to the class variable. This can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
          *
          *     @@foo = :bar
          *             ^^^^
@@ -3343,8 +3371,37 @@ protected String toString(String indent) {
      * 
*/ public static final class ConstantPathNode extends Node { + /** + *
+         * The left-hand node of the path, if present. It can be `nil` or any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). It will be `nil` when the constant lookup is at the root of the module tree.
+         *
+         *     Foo::Bar
+         *     ^^^
+         *
+         *     self::Test
+         *     ^^^^
+         *
+         *     a.b::C
+         *     ^^^
+         * 
+ */ @Nullable public final Node parent; + /** + *
+         * The right-hand node of the path. Always a `ConstantReadNode` in a
+         * valid Ruby syntax tree.
+         *
+         *     ::Foo
+         *       ^^^
+         *
+         *     self::Test
+         *           ^^^^
+         *
+         *     a.b::C
+         *          ^
+         * 
+ */ @UnionType({ ConstantReadNode.class, MissingNode.class }) public final Node child; @@ -3563,7 +3620,26 @@ protected String toString(String indent) { * */ public static final class ConstantPathWriteNode extends Node { + /** + *
+         * A node representing the constant path being written to.
+         *
+         *     Foo::Bar = 1
+         *     ^^^^^^^^
+         *
+         *     ::Foo = :abc
+         *     ^^^^^
+         * 
+ */ public final ConstantPathNode target; + /** + *
+         * The value to write to the constant path. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
+         *
+         *     FOO::BAR = :abc
+         *                ^^^^
+         * 
+ */ public final Node value; public ConstantPathWriteNode(ConstantPathNode target, Node value, int startOffset, int length) { @@ -3710,7 +3786,27 @@ protected String toString(String indent) { * */ public static final class ConstantWriteNode extends Node { + /** + *
+         * The name of the [constant](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#constants).
+         *
+         *     Foo = :bar # name `:Foo`
+         *
+         *     XYZ = 1    # name `:XYZ`
+         * 
+ */ public final String name; + /** + *
+         * The value to write to the constant. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
+         *
+         *     FOO = :bar
+         *           ^^^^
+         *
+         *     MyClass = Class.new
+         *               ^^^^^^^^^
+         * 
+ */ public final Node value; public ConstantWriteNode(String name, Node value, int startOffset, int length) { @@ -4749,7 +4845,27 @@ protected String toString(String indent) { * */ public static final class GlobalVariableWriteNode extends Node { + /** + *
+         * The name of the global variable, which is a `$` followed by an [identifier](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#identifier). Alternatively, it can be one of the special global variables designated by a symbol.
+         *
+         *     $foo = :bar  # name `:$foo`
+         *
+         *     $_Test = 123 # name `:$_Test`
+         * 
+ */ public final String name; + /** + *
+         * The value to write to the global variable. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
+         *
+         *     $foo = :bar
+         *            ^^^^
+         *
+         *     $-xyz = 123
+         *             ^^^
+         * 
+ */ public final Node value; public GlobalVariableWriteNode(String name, Node value, int startOffset, int length) { @@ -4932,19 +5048,67 @@ protected String toString(String indent) { /** *
-     * Represents the use of the `if` keyword, either in the block form or the modifier form.
+     * Represents the use of the `if` keyword, either in the block form or the modifier form, or a ternary expression.
      *
      *     bar if foo
      *     ^^^^^^^^^^
      *
      *     if foo then bar end
      *     ^^^^^^^^^^^^^^^^^^^
+     *
+     *     foo ? bar : baz
+     *     ^^^^^^^^^^^^^^^
      * 
*/ public static final class IfNode extends Node { + /** + *
+         * The node for the condition the `IfNode` is testing.
+         *
+         *     if foo
+         *        ^^^
+         *       bar
+         *     end
+         *
+         *     bar if foo
+         *            ^^^
+         *
+         *     foo ? bar : baz
+         *     ^^^
+         * 
+ */ public final Node predicate; + /** + *
+         * Represents the body of statements that will be executed when the predicate is evaluated as truthy. Will be `nil` when no body is provided.
+         *
+         *     if foo
+         *       bar
+         *       ^^^
+         *       baz
+         *       ^^^
+         *     end
+         * 
+ */ @Nullable public final StatementsNode statements; + /** + *
+         * Represents an `ElseNode` or an `IfNode` when there is an `else` or an `elsif` in the `if` statement.
+         *
+         *     if foo
+         *       bar
+         *     elsif baz
+         *     ^^^^^^^^^
+         *       qux
+         *       ^^^
+         *     end
+         *     ^^^
+         *
+         *     if foo then bar else baz end
+         *                     ^^^^^^^^^^^^
+         * 
+ */ @Nullable public final Node consequent; @@ -5846,8 +6010,7 @@ public static final class InstanceVariableWriteNode extends Node { public final String name; /** *
-         * The value to assign to the instance variable. Can be any node that
-         * represents a non-void expression.
+         * The value to write to the instance variable. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
          *
          *     @foo = :bar
          *            ^^^^
@@ -6919,8 +7082,43 @@ protected String toString(String indent) {
      * 
*/ public static final class LocalVariableWriteNode extends Node { + /** + *
+         * The name of the local variable, which is an [identifier](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#identifiers).
+         *
+         *     foo = :bar # name `:foo`
+         *
+         *     abc = 123  # name `:abc`
+         * 
+ */ public final String name; + /** + *
+         * The number of semantic scopes we have to traverse to find the declaration of this variable.
+         *
+         *     foo = 1         # depth 0
+         *
+         *     tap { foo = 1 } # depth 1
+         *
+         * The specific rules for calculating the depth may differ from individual Ruby implementations, as they are not specified by the language. For more information, see [the Prism documentation](https://github.com/ruby/prism/blob/main/docs/local_variable_depth.md).
+         * 
+ */ public final int depth; + /** + *
+         * The value to write to the local variable. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
+         *
+         *     foo = :bar
+         *           ^^^^
+         *
+         *     abc = 1234
+         *           ^^^^
+         *
+         * Note that since the name of a local variable is known before the value is parsed, it is valid for a local variable to appear within the value of its own write.
+         *
+         *     foo = foo
+         * 
+ */ public final Node value; public LocalVariableWriteNode(String name, int depth, Node value, int startOffset, int length) { @@ -9184,6 +9382,11 @@ protected String toString(String indent) { */ public static final class SourceFileNode extends Node { public final short flags; + /** + *
+         * Represents the file path being parsed. This corresponds directly to the `filepath` option given to the various `Prism::parse*` APIs.
+         * 
+ */ public final byte[] filepath; public SourceFileNode(short flags, byte[] filepath, int startOffset, int length) { @@ -9676,9 +9879,37 @@ protected String toString(String indent) { * */ public static final class UnlessNode extends Node { + /** + *
+         * The condition to be evaluated for the unless expression. It can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
+         *
+         *     unless cond then bar end
+         *            ^^^^
+         *
+         *     bar unless cond
+         *                ^^^^
+         * 
+ */ public final Node predicate; + /** + *
+         * The body of statements that will executed if the unless condition is
+         * falsey. Will be `nil` if no body is provided.
+         *
+         *     unless cond then bar end
+         *                      ^^^
+         * 
+ */ @Nullable public final StatementsNode statements; + /** + *
+         * The else clause of the unless expression, if present.
+         *
+         *     unless cond then bar else baz end
+         *                          ^^^^^^^^
+         * 
+ */ @Nullable public final ElseNode consequent; @@ -10107,7 +10338,6 @@ public enum ErrorType { DEF_ENDLESS, DEF_ENDLESS_SETTER, DEF_NAME, - DEF_NAME_AFTER_RECEIVER, DEF_PARAMS_TERM, DEF_PARAMS_TERM_PAREN, DEF_RECEIVER, @@ -10144,6 +10374,7 @@ public enum ErrorType { EXPECT_EXPRESSION_AFTER_STAR, EXPECT_IDENT_REQ_PARAMETER, EXPECT_LPAREN_REQ_PARAMETER, + EXPECT_MESSAGE, EXPECT_RBRACKET, EXPECT_RPAREN, EXPECT_RPAREN_AFTER_MULTI, @@ -10151,6 +10382,14 @@ public enum ErrorType { EXPECT_STRING_CONTENT, EXPECT_WHEN_DELIMITER, EXPRESSION_BARE_HASH, + EXPRESSION_NOT_WRITABLE, + EXPRESSION_NOT_WRITABLE_ENCODING, + EXPRESSION_NOT_WRITABLE_FALSE, + EXPRESSION_NOT_WRITABLE_FILE, + EXPRESSION_NOT_WRITABLE_LINE, + EXPRESSION_NOT_WRITABLE_NIL, + EXPRESSION_NOT_WRITABLE_SELF, + EXPRESSION_NOT_WRITABLE_TRUE, FLOAT_PARSE, FOR_COLLECTION, FOR_IN, @@ -10169,9 +10408,12 @@ public enum ErrorType { INCOMPLETE_VARIABLE_INSTANCE, INCOMPLETE_VARIABLE_INSTANCE_3_3_0, INSTANCE_VARIABLE_BARE, + INVALID_BLOCK_EXIT, INVALID_CHARACTER, INVALID_ENCODING_MAGIC_COMMENT, INVALID_FLOAT_EXPONENT, + INVALID_LOCAL_VARIABLE_READ, + INVALID_LOCAL_VARIABLE_WRITE, INVALID_MULTIBYTE_CHAR, INVALID_MULTIBYTE_CHARACTER, INVALID_MULTIBYTE_ESCAPE, @@ -10182,8 +10424,12 @@ public enum ErrorType { INVALID_NUMBER_UNDERSCORE, INVALID_PERCENT, INVALID_PRINTABLE_CHARACTER, + INVALID_RETRY_AFTER_ELSE, + INVALID_RETRY_AFTER_ENSURE, + INVALID_RETRY_WITHOUT_RESCUE, INVALID_VARIABLE_GLOBAL, INVALID_VARIABLE_GLOBAL_3_3_0, + INVALID_YIELD, IT_NOT_ALLOWED_NUMBERED, IT_NOT_ALLOWED_ORDINARY, LAMBDA_OPEN, @@ -10240,6 +10486,7 @@ public enum ErrorType { PATTERN_HASH_KEY, PATTERN_HASH_KEY_DUPLICATE, PATTERN_HASH_KEY_LABEL, + PATTERN_HASH_KEY_LOCALS, PATTERN_IDENT_AFTER_HROCKET, PATTERN_LABEL_AFTER_COMMA, PATTERN_REST, @@ -10277,6 +10524,7 @@ public enum ErrorType { TERNARY_EXPRESSION_TRUE, UNARY_RECEIVER, UNDEF_ARGUMENT, + UNEXPECTED_BLOCK_ARGUMENT, UNEXPECTED_TOKEN_CLOSE_CONTEXT, UNEXPECTED_TOKEN_IGNORE, UNTIL_TERM, @@ -10315,6 +10563,9 @@ public enum WarningType { LITERAL_IN_CONDITION_VERBOSE, SHEBANG_CARRIAGE_RETURN, UNEXPECTED_CARRIAGE_RETURN, + UNREACHABLE_STATEMENT, + UNUSED_LOCAL_VARIABLE, + VOID_STATEMENT, } public static WarningType[] WARNING_TYPES = WarningType.values(); diff --git a/test/mri/excludes/TestParse.rb b/test/mri/excludes/TestParse.rb index 9f311584d77..0cefcf375ce 100644 --- a/test/mri/excludes/TestParse.rb +++ b/test/mri/excludes/TestParse.rb @@ -1,5 +1,4 @@ exclude :test_disallowed_class_variable, "/Users/andrykonchin/projects/truffleruby-ws/truffleruby/test/mri/tests/ruby/test_parse.rb:380:in `block in assert_disallowed_variable'." -exclude :test_disallowed_gloal_variable, "https://github.com/ruby/prism/issues/2673" exclude :test_disallowed_instance_variable, "/Users/andrykonchin/projects/truffleruby-ws/truffleruby/test/mri/tests/ruby/test_parse.rb:380:in `block in assert_disallowed_variable'." exclude :test_dynamic_constant_assignment, "/Users/andrykonchin/projects/truffleruby-ws/truffleruby/test/mri/tests/ruby/test_parse.rb:155:in `test_dynamic_constant_assignment'." exclude :test_else_without_rescue, "[\"/Users/andrykonchin/projects/truffleruby-ws/truffleruby/test/mri/tests/ruby/test_parse.rb\", 21]." diff --git a/test/mri/tests/ruby/test_parse.rb b/test/mri/tests/ruby/test_parse.rb index 9932c26cbc8..75923f9b7d7 100644 --- a/test/mri/tests/ruby/test_parse.rb +++ b/test/mri/tests/ruby/test_parse.rb @@ -377,10 +377,10 @@ def test_dsym def assert_disallowed_variable(type, noname, invalid) noname.each do |name| - assert_syntax_error("proc{a = #{name} }", /`#{Regexp.escape noname[0]}' without identifiers is not allowed as #{type} variable name|`#{Regexp.escape noname[0]}' is not allowed as #{type} variable name/) + assert_syntax_error("proc{a = #{name} }", /(`|')#{Regexp.escape noname[0]}' without identifiers is not allowed as #{type} variable name|(`|')#{Regexp.escape noname[0]}' is not allowed as #{type} variable name/) end invalid.each do |name| - assert_syntax_error("proc {a = #{name} }", /`#{Regexp.escape name}' is not allowed as #{type} variable name|`#{Regexp.escape noname[0]}' is not allowed as #{type} variable name/) + assert_syntax_error("proc {a = #{name} }", /(`|')#{Regexp.escape name}' is not allowed as #{type} variable name|(`|')#{Regexp.escape noname[0]}' is not allowed as #{type} variable name/) end end