aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Newton <kddnewton@gmail.com>2024-01-02 11:18:29 -0500
committergit <svn-admin@ruby-lang.org>2024-01-02 18:51:18 +0000
commit23beceedb76a47b3f4497809eefb7338690df191 (patch)
treee482db37df537fe2548b906c29b3f376b365555c
parent04f64608e8bfb50f62633864e68f83b2a6099714 (diff)
downloadruby-23beceedb76a47b3f4497809eefb7338690df191.tar.gz
[ruby/prism] IndexTargetNode should always have ATTRIBUTE_WRITE
Because this is a user-facing change, we also need to deal with the fact that CRuby 3.3.0 was just released. In order to support workflows that want to parse exactly as CRuby parses in a specific version, this PR introduces a new option to the options struct that is "version". This allows you to specify that you want "3.3.0" parsing. I'm not sure if this is the correct solution. Another solution is to just fork and keep around the old branch for security patches. Or we could keep around a copy of the source files within this repository as another directory and only update when necessary. There are a lot of potential solutions here. Because this change is so small and the check for it is so minimal, I've decided to go with this enum. If this ends up entirely cluttering the codebase with version checks, we'll come up with another solution. But for now this works, so we're going to go in this direction for a bit until we determine it's no longer working. https://github.com/ruby/prism/commit/d8c7e6bd10
-rw-r--r--lib/prism/ffi.rb3
-rw-r--r--prism/extension.c10
-rw-r--r--prism/options.c28
-rw-r--r--prism/options.h40
-rw-r--r--prism/parser.h10
-rw-r--r--prism/prism.c10
-rw-r--r--test/prism/snapshots/arrays.txt8
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/assignment.txt8
-rw-r--r--test/prism/snapshots/whitequark/masgn_attr.txt2
9 files changed, 106 insertions, 13 deletions
diff --git a/lib/prism/ffi.rb b/lib/prism/ffi.rb
index 8324f722a7..4c42e5061b 100644
--- a/lib/prism/ffi.rb
+++ b/lib/prism/ffi.rb
@@ -314,6 +314,9 @@ module Prism
template << "C"
values << (options.fetch(:verbose, true) ? 0 : 1)
+ template << "C"
+ values << { nil => 0, "3.3.0" => 1, "latest" => 0 }.fetch(options[:version])
+
template << "L"
if (scopes = options[:scopes])
values << scopes.length
diff --git a/prism/extension.c b/prism/extension.c
index 6cf007b4df..6dc1993657 100644
--- a/prism/extension.c
+++ b/prism/extension.c
@@ -22,6 +22,7 @@ ID rb_option_id_encoding;
ID rb_option_id_line;
ID rb_option_id_frozen_string_literal;
ID rb_option_id_verbose;
+ID rb_option_id_version;
ID rb_option_id_scopes;
/******************************************************************************/
@@ -131,6 +132,14 @@ build_options_i(VALUE key, VALUE value, VALUE argument) {
if (!NIL_P(value)) pm_options_frozen_string_literal_set(options, value == Qtrue);
} else if (key_id == rb_option_id_verbose) {
pm_options_suppress_warnings_set(options, value != Qtrue);
+ } else if (key_id == rb_option_id_version) {
+ if (!NIL_P(value)) {
+ const char *version = check_string(value);
+
+ if (!pm_options_version_set(options, version, RSTRING_LEN(value))) {
+ rb_raise(rb_eArgError, "invalid version: %"PRIsVALUE, value);
+ }
+ }
} else if (key_id == rb_option_id_scopes) {
if (!NIL_P(value)) build_options_scopes(options, value);
} else {
@@ -1013,6 +1022,7 @@ Init_prism(void) {
rb_option_id_line = rb_intern_const("line");
rb_option_id_frozen_string_literal = rb_intern_const("frozen_string_literal");
rb_option_id_verbose = rb_intern_const("verbose");
+ rb_option_id_version = rb_intern_const("version");
rb_option_id_scopes = rb_intern_const("scopes");
/**
diff --git a/prism/options.c b/prism/options.c
index 85d04d6272..0dcae0d16f 100644
--- a/prism/options.c
+++ b/prism/options.c
@@ -41,6 +41,33 @@ pm_options_suppress_warnings_set(pm_options_t *options, bool suppress_warnings)
}
/**
+ * Set the version option on the given options struct by parsing the given
+ * string. If the string contains an invalid option, this returns false.
+ * Otherwise, it returns true.
+ */
+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;
+ }
+
+ if (length == 5) {
+ if (strncmp(version, "3.3.0", 5) == 0) {
+ options->version = PM_OPTIONS_VERSION_CRUBY_3_3_0;
+ return true;
+ }
+
+ if (strncmp(version, "latest", 6) == 0) {
+ options->version = PM_OPTIONS_VERSION_LATEST;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
* Allocate and zero out the scopes array on the given options struct.
*/
PRISM_EXPORTED_FUNCTION void
@@ -163,6 +190,7 @@ pm_options_read(pm_options_t *options, const char *data) {
options->frozen_string_literal = *data++;
options->suppress_warnings = *data++;
+ options->version = (pm_options_version_t) *data++;
uint32_t scopes_count = pm_options_read_u32(data);
data += 4;
diff --git a/prism/options.h b/prism/options.h
index 8608838da8..130d635b98 100644
--- a/prism/options.h
+++ b/prism/options.h
@@ -25,6 +25,19 @@ typedef struct pm_options_scope {
} pm_options_scope_t;
/**
+ * The version of prism that we should be parsing with. This is used to allow
+ * consumers to specify which behavior they want in case they need to parse
+ * exactly as a specific version of CRuby.
+ */
+typedef enum {
+ /** The current version of prism. */
+ PM_OPTIONS_VERSION_LATEST = 0,
+
+ /** The vendored version of prism in CRuby 3.3.0. */
+ PM_OPTIONS_VERSION_CRUBY_3_3_0 = 1
+} pm_options_version_t;
+
+/**
* The options that can be passed to the parser.
*/
typedef struct {
@@ -55,6 +68,13 @@ typedef struct {
*/
pm_options_scope_t *scopes;
+ /**
+ * The version of prism that we should be parsing with. This is used to
+ * allow consumers to specify which behavior they want in case they need to
+ * parse exactly as a specific version of CRuby.
+ */
+ pm_options_version_t version;
+
/** Whether or not the frozen string literal option has been set. */
bool frozen_string_literal;
@@ -107,6 +127,18 @@ PRISM_EXPORTED_FUNCTION void pm_options_frozen_string_literal_set(pm_options_t *
PRISM_EXPORTED_FUNCTION void pm_options_suppress_warnings_set(pm_options_t *options, bool suppress_warnings);
/**
+ * Set the version option on the given options struct by parsing the given
+ * string. If the string contains an invalid option, this returns false.
+ * Otherwise, it returns true.
+ *
+ * @param options The options struct to set the version on.
+ * @param version The version to set.
+ * @param length The length of the version string.
+ * @return Whether or not the version was parsed successfully.
+ */
+PRISM_EXPORTED_FUNCTION bool pm_options_version_set(pm_options_t *options, const char *version, size_t length);
+
+/**
* Allocate and zero out the scopes array on the given options struct.
*
* @param options The options struct to initialize the scopes array on.
@@ -167,9 +199,17 @@ PRISM_EXPORTED_FUNCTION void pm_options_free(pm_options_t *options);
* | ... | the encoding bytes |
* | `1` | frozen string literal |
* | `1` | suppress warnings |
+ * | `1` | the version |
* | `4` | the number of scopes |
* | ... | the scopes |
*
+ * The version field is an enum, so it should be one of the following values:
+ *
+ * | value | version |
+ * | ----- | ------------------------- |
+ * | `0` | use the latest version of prism |
+ * | `1` | use the version of prism that is vendored in CRuby 3.3.0 |
+ *
* Each scope is layed out as follows:
*
* | # bytes | field |
diff --git a/prism/parser.h b/prism/parser.h
index 2c58131b19..9f38dc5830 100644
--- a/prism/parser.h
+++ b/prism/parser.h
@@ -9,6 +9,7 @@
#include "prism/ast.h"
#include "prism/defines.h"
#include "prism/encoding.h"
+#include "prism/options.h"
#include "prism/util/pm_constant_pool.h"
#include "prism/util/pm_list.h"
#include "prism/util/pm_newline_list.h"
@@ -662,6 +663,12 @@ 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;
+
+ /** The version of prism that we should use to parse. */
+ pm_options_version_t version;
+
/** Whether or not we're at the beginning of a command. */
bool command_start;
@@ -684,9 +691,6 @@ struct pm_parser {
/** This flag indicates that we are currently parsing a keyword argument. */
bool in_keyword_arg;
- /** The current parameter name id on parsing its default value. */
- pm_constant_id_t current_param_name;
-
/**
* Whether or not the parser has seen a token that has semantic meaning
* (i.e., a token that is not a comment or whitespace).
diff --git a/prism/prism.c b/prism/prism.c
index ec319f2cac..03df698449 100644
--- a/prism/prism.c
+++ b/prism/prism.c
@@ -2172,11 +2172,16 @@ pm_call_target_node_create(pm_parser_t *parser, pm_call_node_t *target) {
static pm_index_target_node_t *
pm_index_target_node_create(pm_parser_t *parser, pm_call_node_t *target) {
pm_index_target_node_t *node = PM_ALLOC_NODE(parser, pm_index_target_node_t);
+ pm_node_flags_t flags = target->base.flags;
+
+ if (parser->version != PM_OPTIONS_VERSION_CRUBY_3_3_0) {
+ flags |= PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE;
+ }
*node = (pm_index_target_node_t) {
{
.type = PM_INDEX_TARGET_NODE,
- .flags = target->base.flags,
+ .flags = flags,
.location = target->base.location
},
.receiver = target->receiver,
@@ -17320,6 +17325,9 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm
parser->suppress_warnings = true;
}
+ // version option
+ parser->version = options->version;
+
// scopes option
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);
diff --git a/test/prism/snapshots/arrays.txt b/test/prism/snapshots/arrays.txt
index 93f5c8fbc1..40d4cbed4f 100644
--- a/test/prism/snapshots/arrays.txt
+++ b/test/prism/snapshots/arrays.txt
@@ -430,7 +430,7 @@
├── @ MultiWriteNode (location: (41,0)-(41,21))
│ ├── lefts: (length: 2)
│ │ ├── @ IndexTargetNode (location: (41,0)-(41,6))
- │ │ │ ├── flags: ∅
+ │ │ │ ├── flags: attribute_write
│ │ │ ├── receiver:
│ │ │ │ @ CallNode (location: (41,0)-(41,3))
│ │ │ │ ├── flags: variable_call
@@ -452,7 +452,7 @@
│ │ │ ├── closing_loc: (41,5)-(41,6) = "]"
│ │ │ └── block: ∅
│ │ └── @ IndexTargetNode (location: (41,8)-(41,14))
- │ │ ├── flags: ∅
+ │ │ ├── flags: attribute_write
│ │ ├── receiver:
│ │ │ @ CallNode (location: (41,8)-(41,11))
│ │ │ ├── flags: variable_call
@@ -2166,7 +2166,7 @@
│ │ │ ├── operator_loc: (140,17)-(140,19) = "=>"
│ │ │ ├── reference:
│ │ │ │ @ IndexTargetNode (location: (140,20)-(140,24))
- │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── flags: attribute_write
│ │ │ │ ├── receiver:
│ │ │ │ │ @ CallNode (location: (140,20)-(140,21))
│ │ │ │ │ ├── flags: variable_call
@@ -2229,7 +2229,7 @@
│ │ ├── operator_loc: (142,17)-(142,19) = "=>"
│ │ ├── reference:
│ │ │ @ IndexTargetNode (location: (142,20)-(142,27))
- │ │ │ ├── flags: ∅
+ │ │ │ ├── flags: attribute_write
│ │ │ ├── receiver:
│ │ │ │ @ CallNode (location: (142,20)-(142,21))
│ │ │ │ ├── flags: variable_call
diff --git a/test/prism/snapshots/unparser/corpus/literal/assignment.txt b/test/prism/snapshots/unparser/corpus/literal/assignment.txt
index 18edafe415..4cb19d2867 100644
--- a/test/prism/snapshots/unparser/corpus/literal/assignment.txt
+++ b/test/prism/snapshots/unparser/corpus/literal/assignment.txt
@@ -319,7 +319,7 @@
├── @ MultiWriteNode (location: (15,0)-(15,24))
│ ├── lefts: (length: 2)
│ │ ├── @ IndexTargetNode (location: (15,1)-(15,8))
- │ │ │ ├── flags: ∅
+ │ │ │ ├── flags: attribute_write
│ │ │ ├── receiver:
│ │ │ │ @ LocalVariableReadNode (location: (15,1)-(15,2))
│ │ │ │ ├── name: :a
@@ -338,7 +338,7 @@
│ │ │ ├── closing_loc: (15,7)-(15,8) = "]"
│ │ │ └── block: ∅
│ │ └── @ IndexTargetNode (location: (15,10)-(15,14))
- │ │ ├── flags: ∅
+ │ │ ├── flags: attribute_write
│ │ ├── receiver:
│ │ │ @ LocalVariableReadNode (location: (15,10)-(15,11))
│ │ │ ├── name: :a
@@ -370,7 +370,7 @@
├── @ MultiWriteNode (location: (16,0)-(16,21))
│ ├── lefts: (length: 2)
│ │ ├── @ IndexTargetNode (location: (16,1)-(16,5))
- │ │ │ ├── flags: ∅
+ │ │ │ ├── flags: attribute_write
│ │ │ ├── receiver:
│ │ │ │ @ LocalVariableReadNode (location: (16,1)-(16,2))
│ │ │ │ ├── name: :a
@@ -385,7 +385,7 @@
│ │ │ ├── closing_loc: (16,4)-(16,5) = "]"
│ │ │ └── block: ∅
│ │ └── @ IndexTargetNode (location: (16,7)-(16,11))
- │ │ ├── flags: ∅
+ │ │ ├── flags: attribute_write
│ │ ├── receiver:
│ │ │ @ LocalVariableReadNode (location: (16,7)-(16,8))
│ │ │ ├── name: :a
diff --git a/test/prism/snapshots/whitequark/masgn_attr.txt b/test/prism/snapshots/whitequark/masgn_attr.txt
index d87d7a6cb1..2a4dc38b7c 100644
--- a/test/prism/snapshots/whitequark/masgn_attr.txt
+++ b/test/prism/snapshots/whitequark/masgn_attr.txt
@@ -34,7 +34,7 @@
│ │ │ ├── name: :a=
│ │ │ └── message_loc: (3,5)-(3,6) = "a"
│ │ └── @ IndexTargetNode (location: (3,8)-(3,18))
- │ │ ├── flags: ∅
+ │ │ ├── flags: attribute_write
│ │ ├── receiver:
│ │ │ @ SelfNode (location: (3,8)-(3,12))
│ │ ├── opening_loc: (3,12)-(3,13) = "["