prism 1.5.1 → 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 71ec272c4385e48796979636d3d37547841cf1b27de8d61dbfc69b9300fefc5e
4
- data.tar.gz: 58d2105009b3edfe9246deffaec5989ba8a63da2f68d770b80393e4970bfa89a
3
+ metadata.gz: 978703c8505b23f80431d302e3d46c42ebb753dfcf69b59f09a86b5eba91ee38
4
+ data.tar.gz: d993141f8bcffd753f46367684e37910c01ff614bcde9e51548e16a1c5482a46
5
5
  SHA512:
6
- metadata.gz: ad2f9f3ca25f79b5414f8144b23a529ae4cbe23e7f5fc619da54cea7676ecfc93f03bd7548824d523352765d66fe157f67fb989350c4f4b4a6f29782c796539e
7
- data.tar.gz: 9e447578eb512232efe4c9263adc786701cbecbd94f2f4f6d87f2ec5f0961a5ec568acbe20aef23e810fe8f7998a8fd4f9c522cb9a9b986074f7a6e8baa4f6eb
6
+ metadata.gz: 33f416b233e73ce8c395b9a1cbff869de36fa2ca9bdae893f4c716e53e01a408d632b7802776e62afecb2c19bce18004d66dba219831c57894e9436b3d64b294
7
+ data.tar.gz: 122a557835cb1adb140fa0615cb983e156602b7f910e7680c6688668b2fece74cc246e3a6b8207d5916250cf3f0845f8deb5538bf3b6953981f2d387ae37f02b
data/CHANGELOG.md CHANGED
@@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.5.2] - 2025-10-09
10
+
11
+ ### Changed
12
+
13
+ - Fix character literal forced encoding when a unicode escape sequence is used.
14
+ - Reject `1 if foo = bar baz`.
15
+ - Clear static literal flag on interpolated strings.
16
+ - Reject optional argument/endless method definition ambiguity.
17
+
9
18
  ## [1.5.1] - 2025-09-13
10
19
 
11
20
  ### Changed
@@ -676,7 +685,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
676
685
 
677
686
  - 🎉 Initial release! 🎉
678
687
 
679
- [unreleased]: https://github.com/ruby/prism/compare/v1.5.1...HEAD
688
+ [unreleased]: https://github.com/ruby/prism/compare/v1.5.2...HEAD
689
+ [1.5.2]: https://github.com/ruby/prism/compare/v1.5.1...v1.5.2
680
690
  [1.5.1]: https://github.com/ruby/prism/compare/v1.5.0...v1.5.1
681
691
  [1.5.0]: https://github.com/ruby/prism/compare/v1.4.0...v1.5.0
682
692
  [1.4.0]: https://github.com/ruby/prism/compare/v1.3.0...v1.4.0
data/Makefile CHANGED
@@ -15,6 +15,7 @@ CFLAGS := -g -O2 -std=c99 -Wall -Werror -Wextra -Wpedantic -Wundef -Wconversion
15
15
  JAVA_WASM_CFLAGS := -g -Oz -std=c99 -Wall -Werror -Wextra -Wpedantic -Wundef -Wconversion -Wno-missing-braces -fPIC -fvisibility=hidden -Wimplicit-fallthrough $(JAVA_WASM_CFLAGS)
16
16
  CC ?= cc
17
17
  AR ?= ar
18
+ ARFLAGS ?= -r$(V0:1=v)
18
19
  WASI_SDK_PATH := /opt/wasi-sdk
19
20
 
20
21
  MAKEDIRS ?= mkdir -p
@@ -38,7 +39,7 @@ build/libprism.$(SOEXT): $(SHARED_OBJECTS)
38
39
 
39
40
  build/libprism.a: $(STATIC_OBJECTS)
40
41
  $(ECHO) "building $@ with $(AR)"
41
- $(Q) $(AR) $(ARFLAGS) $@ $(STATIC_OBJECTS) $(Q1:0=>/dev/null)
42
+ $(Q) $(AR) $(ARFLAGS) $@ $(STATIC_OBJECTS)
42
43
 
43
44
  javascript/src/prism.wasm: Makefile $(SOURCES) $(HEADERS)
44
45
  $(ECHO) "building $@"
data/README.md CHANGED
@@ -135,6 +135,7 @@ Prism has been integrated into the majority of Ruby runtimes, many libraries, an
135
135
  * [sorbet-eraser](https://github.com/kddnewton/sorbet-eraser/pull/25)
136
136
  * [synvert](https://github.com/xinminlabs/synvert-core-ruby)
137
137
  * [typeprof](https://github.com/ruby/typeprof)
138
+ * [unparser](https://github.com/mbj/unparser) (via parser translator)
138
139
 
139
140
  ### Applications
140
141
 
data/config.yml CHANGED
@@ -60,6 +60,7 @@ errors:
60
60
  - CONDITIONAL_WHILE_PREDICATE
61
61
  - CONSTANT_PATH_COLON_COLON_CONSTANT
62
62
  - DEF_ENDLESS
63
+ - DEF_ENDLESS_PARAMETERS
63
64
  - DEF_ENDLESS_SETTER
64
65
  - DEF_NAME
65
66
  - DEF_PARAMS_TERM
@@ -1800,7 +1801,7 @@ nodes:
1800
1801
  Represents the predicate of the case statement. This can be either `nil` or any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
1801
1802
 
1802
1803
  case true; when false; end
1803
- ^^^^
1804
+ ^^^^
1804
1805
  - name: conditions
1805
1806
  type: node[]
1806
1807
  kind: WhenNode
@@ -20,5 +20,5 @@ Prism::Translation::ParserCurrent.parse("puts 'Hello World!'")
20
20
 
21
21
  All the parsers are autoloaded, so you don't have to worry about requiring them yourself.
22
22
 
23
- If you also need to parse Ruby versions below 3.3 (which `prism` has no support for), check out
23
+ If you also need to parse Ruby versions below 3.3 (for which the `prism` translation layer does not have explicit support), check out
24
24
  [this guide](https://github.com/whitequark/parser/blob/master/doc/PRISM_TRANSLATION.md) from the `parser` gem on how to use both in conjunction.
data/docs/releasing.md CHANGED
@@ -47,7 +47,9 @@ bundle install
47
47
  * Update the version-specific lockfiles:
48
48
 
49
49
  ```sh
50
- bin/prism bundle install
50
+ for VERSION in "2.7" "3.0" "3.1" "3.2" "3.3" "3.4"; do docker run -it --rm -v "$PWD":/usr/src/app -w /usr/src/app -e BUNDLE_GEMFILE="gemfiles/$VERSION/Gemfile" "ruby:$VERSION" bundle update; done
51
+ docker run -it --rm -v "$PWD":/usr/src/app -w /usr/src/app -e BUNDLE_GEMFILE="gemfiles/3.5/Gemfile" ruby:3.5.0-preview1 bundle update
52
+ BUNDLE_GEMFILE=gemfiles/truffleruby/Gemfile chruby-exec truffleruby -- bundle update
51
53
  ```
52
54
 
53
55
  * Update the cargo lockfiles:
@@ -1,7 +1,7 @@
1
1
  #ifndef PRISM_EXT_NODE_H
2
2
  #define PRISM_EXT_NODE_H
3
3
 
4
- #define EXPECTED_PRISM_VERSION "1.5.1"
4
+ #define EXPECTED_PRISM_VERSION "1.5.2"
5
5
 
6
6
  #include <ruby.h>
7
7
  #include <ruby/encoding.h>
data/include/prism/ast.h CHANGED
@@ -2638,7 +2638,7 @@ typedef struct pm_case_node {
2638
2638
  * Represents the predicate of the case statement. This can be either `nil` or any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
2639
2639
  *
2640
2640
  * case true; when false; end
2641
- * ^^^^
2641
+ * ^^^^
2642
2642
  */
2643
2643
  struct pm_node *predicate;
2644
2644
 
@@ -7990,6 +7990,8 @@ typedef enum pm_arguments_node_flags {
7990
7990
 
7991
7991
  /** if the arguments contain multiple splats */
7992
7992
  PM_ARGUMENTS_NODE_FLAGS_CONTAINS_MULTIPLE_SPLATS = 64,
7993
+
7994
+ PM_ARGUMENTS_NODE_FLAGS_LAST,
7993
7995
  } pm_arguments_node_flags_t;
7994
7996
 
7995
7997
  /**
@@ -7998,6 +8000,8 @@ typedef enum pm_arguments_node_flags {
7998
8000
  typedef enum pm_array_node_flags {
7999
8001
  /** if array contains splat nodes */
8000
8002
  PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT = 4,
8003
+
8004
+ PM_ARRAY_NODE_FLAGS_LAST,
8001
8005
  } pm_array_node_flags_t;
8002
8006
 
8003
8007
  /**
@@ -8015,6 +8019,8 @@ typedef enum pm_call_node_flags {
8015
8019
 
8016
8020
  /** a call that ignores method visibility */
8017
8021
  PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY = 32,
8022
+
8023
+ PM_CALL_NODE_FLAGS_LAST,
8018
8024
  } pm_call_node_flags_t;
8019
8025
 
8020
8026
  /**
@@ -8026,6 +8032,8 @@ typedef enum pm_encoding_flags {
8026
8032
 
8027
8033
  /** internal bytes forced the encoding to binary */
8028
8034
  PM_ENCODING_FLAGS_FORCED_BINARY_ENCODING = 8,
8035
+
8036
+ PM_ENCODING_FLAGS_LAST,
8029
8037
  } pm_encoding_flags_t;
8030
8038
 
8031
8039
  /**
@@ -8043,6 +8051,8 @@ typedef enum pm_integer_base_flags {
8043
8051
 
8044
8052
  /** 0x prefix */
8045
8053
  PM_INTEGER_BASE_FLAGS_HEXADECIMAL = 32,
8054
+
8055
+ PM_INTEGER_BASE_FLAGS_LAST,
8046
8056
  } pm_integer_base_flags_t;
8047
8057
 
8048
8058
  /**
@@ -8054,6 +8064,8 @@ typedef enum pm_interpolated_string_node_flags {
8054
8064
 
8055
8065
  /** mutable by virtue of a `frozen_string_literal: false` comment or `--disable-frozen-string-literal`; only for adjacent string literals like `'a' 'b'` */
8056
8066
  PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE = 8,
8067
+
8068
+ PM_INTERPOLATED_STRING_NODE_FLAGS_LAST,
8057
8069
  } pm_interpolated_string_node_flags_t;
8058
8070
 
8059
8071
  /**
@@ -8062,6 +8074,8 @@ typedef enum pm_interpolated_string_node_flags {
8062
8074
  typedef enum pm_keyword_hash_node_flags {
8063
8075
  /** a keyword hash which only has `AssocNode` elements all with symbol keys, which means the elements can be treated as keyword arguments */
8064
8076
  PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS = 4,
8077
+
8078
+ PM_KEYWORD_HASH_NODE_FLAGS_LAST,
8065
8079
  } pm_keyword_hash_node_flags_t;
8066
8080
 
8067
8081
  /**
@@ -8070,6 +8084,8 @@ typedef enum pm_keyword_hash_node_flags {
8070
8084
  typedef enum pm_loop_flags {
8071
8085
  /** a loop after a begin statement, so the body is executed first before the condition */
8072
8086
  PM_LOOP_FLAGS_BEGIN_MODIFIER = 4,
8087
+
8088
+ PM_LOOP_FLAGS_LAST,
8073
8089
  } pm_loop_flags_t;
8074
8090
 
8075
8091
  /**
@@ -8078,6 +8094,8 @@ typedef enum pm_loop_flags {
8078
8094
  typedef enum pm_parameter_flags {
8079
8095
  /** a parameter name that has been repeated in the method signature */
8080
8096
  PM_PARAMETER_FLAGS_REPEATED_PARAMETER = 4,
8097
+
8098
+ PM_PARAMETER_FLAGS_LAST,
8081
8099
  } pm_parameter_flags_t;
8082
8100
 
8083
8101
  /**
@@ -8086,6 +8104,8 @@ typedef enum pm_parameter_flags {
8086
8104
  typedef enum pm_parentheses_node_flags {
8087
8105
  /** parentheses that contain multiple potentially void statements */
8088
8106
  PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS = 4,
8107
+
8108
+ PM_PARENTHESES_NODE_FLAGS_LAST,
8089
8109
  } pm_parentheses_node_flags_t;
8090
8110
 
8091
8111
  /**
@@ -8094,6 +8114,8 @@ typedef enum pm_parentheses_node_flags {
8094
8114
  typedef enum pm_range_flags {
8095
8115
  /** ... operator */
8096
8116
  PM_RANGE_FLAGS_EXCLUDE_END = 4,
8117
+
8118
+ PM_RANGE_FLAGS_LAST,
8097
8119
  } pm_range_flags_t;
8098
8120
 
8099
8121
  /**
@@ -8132,6 +8154,8 @@ typedef enum pm_regular_expression_flags {
8132
8154
 
8133
8155
  /** internal bytes forced the encoding to US-ASCII */
8134
8156
  PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING = 4096,
8157
+
8158
+ PM_REGULAR_EXPRESSION_FLAGS_LAST,
8135
8159
  } pm_regular_expression_flags_t;
8136
8160
 
8137
8161
  /**
@@ -8146,6 +8170,8 @@ typedef enum pm_shareable_constant_node_flags {
8146
8170
 
8147
8171
  /** constant writes that should be modified with shareable constant value experimental copy */
8148
8172
  PM_SHAREABLE_CONSTANT_NODE_FLAGS_EXPERIMENTAL_COPY = 16,
8173
+
8174
+ PM_SHAREABLE_CONSTANT_NODE_FLAGS_LAST,
8149
8175
  } pm_shareable_constant_node_flags_t;
8150
8176
 
8151
8177
  /**
@@ -8163,6 +8189,8 @@ typedef enum pm_string_flags {
8163
8189
 
8164
8190
  /** mutable by virtue of a `frozen_string_literal: false` comment or `--disable-frozen-string-literal` */
8165
8191
  PM_STRING_FLAGS_MUTABLE = 32,
8192
+
8193
+ PM_STRING_FLAGS_LAST,
8166
8194
  } pm_string_flags_t;
8167
8195
 
8168
8196
  /**
@@ -8177,6 +8205,8 @@ typedef enum pm_symbol_flags {
8177
8205
 
8178
8206
  /** internal bytes forced the encoding to US-ASCII */
8179
8207
  PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING = 16,
8208
+
8209
+ PM_SYMBOL_FLAGS_LAST,
8180
8210
  } pm_symbol_flags_t;
8181
8211
 
8182
8212
  /**
@@ -91,6 +91,7 @@ typedef enum {
91
91
  PM_ERR_CONDITIONAL_WHILE_PREDICATE,
92
92
  PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT,
93
93
  PM_ERR_DEF_ENDLESS,
94
+ PM_ERR_DEF_ENDLESS_PARAMETERS,
94
95
  PM_ERR_DEF_ENDLESS_SETTER,
95
96
  PM_ERR_DEF_NAME,
96
97
  PM_ERR_DEF_PARAMS_TERM,
@@ -19,11 +19,11 @@
19
19
  /**
20
20
  * The patch version of the Prism library as an int.
21
21
  */
22
- #define PRISM_VERSION_PATCH 1
22
+ #define PRISM_VERSION_PATCH 2
23
23
 
24
24
  /**
25
25
  * The version of the Prism library as a constant string.
26
26
  */
27
- #define PRISM_VERSION "1.5.1"
27
+ #define PRISM_VERSION "1.5.2"
28
28
 
29
29
  #endif
data/lib/prism/node.rb CHANGED
@@ -3732,7 +3732,7 @@ module Prism
3732
3732
  # Represents the predicate of the case statement. This can be either `nil` or any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression).
3733
3733
  #
3734
3734
  # case true; when false; end
3735
- # ^^^^
3735
+ # ^^^^
3736
3736
  attr_reader :predicate
3737
3737
 
3738
3738
  # Represents the conditions of the case statement.
@@ -7,17 +7,14 @@ if (method = Kernel.instance_method(:warn)).respond_to?(:parameters) ? method.pa
7
7
  Kernel.prepend(
8
8
  Module.new {
9
9
  def warn(*msgs, uplevel: nil, category: nil) # :nodoc:
10
- uplevel =
11
- case uplevel
12
- when nil
13
- 1
14
- when Integer
15
- uplevel + 1
16
- else
17
- uplevel.to_int + 1
18
- end
19
-
20
- super(*msgs, uplevel: uplevel)
10
+ case uplevel
11
+ when nil
12
+ super(*msgs)
13
+ when Integer
14
+ super(*msgs, uplevel: uplevel + 1)
15
+ else
16
+ super(*msgs, uplevel: uplevel.to_int + 1)
17
+ end
21
18
  end
22
19
  }
23
20
  )
@@ -25,17 +22,14 @@ if (method = Kernel.instance_method(:warn)).respond_to?(:parameters) ? method.pa
25
22
  Object.prepend(
26
23
  Module.new {
27
24
  def warn(*msgs, uplevel: nil, category: nil) # :nodoc:
28
- uplevel =
29
- case uplevel
30
- when nil
31
- 1
32
- when Integer
33
- uplevel + 1
34
- else
35
- uplevel.to_int + 1
36
- end
37
-
38
- super(*msgs, uplevel: uplevel)
25
+ case uplevel
26
+ when nil
27
+ super(*msgs)
28
+ when Integer
29
+ super(*msgs, uplevel: uplevel + 1)
30
+ else
31
+ super(*msgs, uplevel: uplevel.to_int + 1)
32
+ end
39
33
  end
40
34
  }
41
35
  )
@@ -25,7 +25,7 @@ module Prism
25
25
 
26
26
  # The patch version of prism that we are expecting to find in the serialized
27
27
  # strings.
28
- PATCH_VERSION = 1
28
+ PATCH_VERSION = 2
29
29
 
30
30
  # Deserialize the dumped output from a request to parse or parse_file.
31
31
  #
@@ -397,6 +397,7 @@ module Prism
397
397
  :conditional_while_predicate,
398
398
  :constant_path_colon_colon_constant,
399
399
  :def_endless,
400
+ :def_endless_parameters,
400
401
  :def_endless_setter,
401
402
  :def_name,
402
403
  :def_params_term,
@@ -19,6 +19,13 @@ module Prism
19
19
  # whitequark/parser gem's syntax tree. It inherits from the base parser for
20
20
  # the parser gem, and overrides the parse* methods to parse with prism and
21
21
  # then translate.
22
+ #
23
+ # Note that this version of the parser always parses using the latest
24
+ # version of Ruby syntax supported by Prism. If you want specific version
25
+ # support, use one of the version-specific subclasses, such as
26
+ # `Prism::Translation::Parser34`. If you want to parse using the same
27
+ # version of Ruby syntax as the currently running version of Ruby, use
28
+ # `Prism::Translation::ParserCurrent`.
22
29
  class Parser < ::Parser::Base
23
30
  Diagnostic = ::Parser::Diagnostic # :nodoc:
24
31
  private_constant :Diagnostic
@@ -77,7 +84,7 @@ module Prism
77
84
  end
78
85
 
79
86
  def version # :nodoc:
80
- 34
87
+ 35
81
88
  end
82
89
 
83
90
  # The default encoding for Ruby files is UTF-8.
@@ -152,7 +152,7 @@ module Prism
152
152
  # ^^
153
153
  # ```
154
154
  def visit_back_reference_read_node(node)
155
- s(node, :back_ref, node.name.name.delete_prefix("$").to_sym)
155
+ s(node, :back_ref, node.name.to_s.delete_prefix("$").to_sym)
156
156
  end
157
157
 
158
158
  # ```
data/prism.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = "prism"
5
- spec.version = "1.5.1"
5
+ spec.version = "1.5.2"
6
6
  spec.authors = ["Shopify"]
7
7
  spec.email = ["ruby@shopify.com"]
8
8
 
data/src/diagnostic.c CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  #include "prism/diagnostic.h"
12
12
 
13
- #define PM_DIAGNOSTIC_ID_MAX 321
13
+ #define PM_DIAGNOSTIC_ID_MAX 322
14
14
 
15
15
  /** This struct holds the data for each diagnostic. */
16
16
  typedef struct {
@@ -154,6 +154,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = {
154
154
  [PM_ERR_CONDITIONAL_WHILE_PREDICATE] = { "expected a predicate expression for the `while` statement", PM_ERROR_LEVEL_SYNTAX },
155
155
  [PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT] = { "expected a constant after the `::` operator", PM_ERROR_LEVEL_SYNTAX },
156
156
  [PM_ERR_DEF_ENDLESS] = { "could not parse the endless method body", PM_ERROR_LEVEL_SYNTAX },
157
+ [PM_ERR_DEF_ENDLESS_PARAMETERS] = { "could not parse the endless method parameters", PM_ERROR_LEVEL_SYNTAX },
157
158
  [PM_ERR_DEF_ENDLESS_SETTER] = { "invalid method name; a setter method cannot be defined in an endless method definition", PM_ERROR_LEVEL_SYNTAX },
158
159
  [PM_ERR_DEF_NAME] = { "unexpected %s; expected a method name", PM_ERROR_LEVEL_SYNTAX },
159
160
  [PM_ERR_DEF_PARAMS_TERM] = { "expected a delimiter to close the parameters", PM_ERROR_LEVEL_SYNTAX },
@@ -482,6 +483,7 @@ pm_diagnostic_id_human(pm_diagnostic_id_t diag_id) {
482
483
  case PM_ERR_CONDITIONAL_WHILE_PREDICATE: return "conditional_while_predicate";
483
484
  case PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT: return "constant_path_colon_colon_constant";
484
485
  case PM_ERR_DEF_ENDLESS: return "def_endless";
486
+ case PM_ERR_DEF_ENDLESS_PARAMETERS: return "def_endless_parameters";
485
487
  case PM_ERR_DEF_ENDLESS_SETTER: return "def_endless_setter";
486
488
  case PM_ERR_DEF_NAME: return "def_name";
487
489
  case PM_ERR_DEF_PARAMS_TERM: return "def_params_term";
data/src/prism.c CHANGED
@@ -2622,10 +2622,11 @@ pm_break_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_argument
2622
2622
  // There are certain flags that we want to use internally but don't want to
2623
2623
  // expose because they are not relevant beyond parsing. Therefore we'll define
2624
2624
  // them here and not define them in config.yml/a header file.
2625
- static const pm_node_flags_t PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY = 0x4;
2626
- static const pm_node_flags_t PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY = 0x40;
2627
- static const pm_node_flags_t PM_CALL_NODE_FLAGS_COMPARISON = 0x80;
2628
- static const pm_node_flags_t PM_CALL_NODE_FLAGS_INDEX = 0x100;
2625
+ static const pm_node_flags_t PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY = (1 << 2);
2626
+
2627
+ static const pm_node_flags_t PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY = ((PM_CALL_NODE_FLAGS_LAST - 1) << 1);
2628
+ static const pm_node_flags_t PM_CALL_NODE_FLAGS_COMPARISON = ((PM_CALL_NODE_FLAGS_LAST - 1) << 2);
2629
+ static const pm_node_flags_t PM_CALL_NODE_FLAGS_INDEX = ((PM_CALL_NODE_FLAGS_LAST - 1) << 3);
2629
2630
 
2630
2631
  /**
2631
2632
  * Allocate and initialize a new CallNode node. This sets everything to NULL or
@@ -5279,6 +5280,12 @@ pm_interpolated_string_node_append(pm_interpolated_string_node_t *node, pm_node_
5279
5280
 
5280
5281
  switch (PM_NODE_TYPE(part)) {
5281
5282
  case PM_STRING_NODE:
5283
+ // If inner string is not frozen, it stops being a static literal. We should *not* clear other flags,
5284
+ // because concatenating two frozen strings (`'foo' 'bar'`) is still frozen. This holds true for
5285
+ // as long as this interpolation only consists of other string literals.
5286
+ if (!PM_NODE_FLAG_P(part, PM_STRING_FLAGS_FROZEN)) {
5287
+ pm_node_flag_unset((pm_node_t *) node, PM_NODE_FLAG_STATIC_LITERAL);
5288
+ }
5282
5289
  part->flags = (pm_node_flags_t) ((part->flags | PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN) & ~PM_STRING_FLAGS_MUTABLE);
5283
5290
  break;
5284
5291
  case PM_INTERPOLATED_STRING_NODE:
@@ -14443,6 +14450,17 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for
14443
14450
  if (accepted_newline) {
14444
14451
  pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14445
14452
  }
14453
+
14454
+ // If this is a command call and an argument takes a block,
14455
+ // there can be no further arguments. For example,
14456
+ // `foo(bar 1 do end, 2)` should be rejected.
14457
+ if (PM_NODE_TYPE_P(argument, PM_CALL_NODE)) {
14458
+ pm_call_node_t *call = (pm_call_node_t *) argument;
14459
+ if (call->opening_loc.start == NULL && call->arguments != NULL && call->block != NULL) {
14460
+ pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14461
+ break;
14462
+ }
14463
+ }
14446
14464
  } else {
14447
14465
  // If there is no comma at the end of the argument list then we're
14448
14466
  // done parsing arguments and can break out of this loop.
@@ -14594,6 +14612,18 @@ update_parameter_state(pm_parser_t *parser, pm_token_t *token, pm_parameters_ord
14594
14612
  return true;
14595
14613
  }
14596
14614
 
14615
+ /**
14616
+ * Ensures that after parsing a parameter, the next token is not `=`.
14617
+ * Some parameters like `def(* = 1)` cannot become optional. When no parens
14618
+ * are present like in `def * = 1`, this creates ambiguity with endless method definitions.
14619
+ */
14620
+ static inline void
14621
+ refute_optional_parameter(pm_parser_t *parser) {
14622
+ if (match1(parser, PM_TOKEN_EQUAL)) {
14623
+ pm_parser_err_previous(parser, PM_ERR_DEF_ENDLESS_PARAMETERS);
14624
+ }
14625
+ }
14626
+
14597
14627
  /**
14598
14628
  * Parse a list of parameters on a method definition.
14599
14629
  */
@@ -14646,6 +14676,10 @@ parse_parameters(
14646
14676
  parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_BLOCK;
14647
14677
  }
14648
14678
 
14679
+ if (!uses_parentheses) {
14680
+ refute_optional_parameter(parser);
14681
+ }
14682
+
14649
14683
  pm_block_parameter_node_t *param = pm_block_parameter_node_create(parser, &name, &operator);
14650
14684
  if (repeated) {
14651
14685
  pm_node_flag_set_repeated_parameter((pm_node_t *)param);
@@ -14667,6 +14701,10 @@ parse_parameters(
14667
14701
  bool succeeded = update_parameter_state(parser, &parser->current, &order);
14668
14702
  parser_lex(parser);
14669
14703
 
14704
+ if (!uses_parentheses) {
14705
+ refute_optional_parameter(parser);
14706
+ }
14707
+
14670
14708
  parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_ALL;
14671
14709
  pm_forwarding_parameter_node_t *param = pm_forwarding_parameter_node_create(parser, &parser->previous);
14672
14710
 
@@ -14848,6 +14886,10 @@ parse_parameters(
14848
14886
  context_pop(parser);
14849
14887
  pm_parameters_node_keywords_append(params, param);
14850
14888
 
14889
+ if (!uses_parentheses) {
14890
+ refute_optional_parameter(parser);
14891
+ }
14892
+
14851
14893
  // If parsing the value of the parameter resulted in error recovery,
14852
14894
  // then we can put a missing node in its place and stop parsing the
14853
14895
  // parameters entirely now.
@@ -14879,6 +14921,10 @@ parse_parameters(
14879
14921
  parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS;
14880
14922
  }
14881
14923
 
14924
+ if (!uses_parentheses) {
14925
+ refute_optional_parameter(parser);
14926
+ }
14927
+
14882
14928
  pm_node_t *param = (pm_node_t *) pm_rest_parameter_node_create(parser, &operator, &name);
14883
14929
  if (repeated) {
14884
14930
  pm_node_flag_set_repeated_parameter(param);
@@ -14927,6 +14973,10 @@ parse_parameters(
14927
14973
  }
14928
14974
  }
14929
14975
 
14976
+ if (!uses_parentheses) {
14977
+ refute_optional_parameter(parser);
14978
+ }
14979
+
14930
14980
  if (params->keyword_rest == NULL) {
14931
14981
  pm_parameters_node_keyword_rest_set(params, param);
14932
14982
  } else {
@@ -18491,20 +18541,28 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
18491
18541
  return (pm_node_t *) node;
18492
18542
  }
18493
18543
  case PM_TOKEN_CHARACTER_LITERAL: {
18494
- parser_lex(parser);
18495
-
18496
- pm_token_t opening = parser->previous;
18497
- opening.type = PM_TOKEN_STRING_BEGIN;
18498
- opening.end = opening.start + 1;
18499
-
18500
- pm_token_t content = parser->previous;
18501
- content.type = PM_TOKEN_STRING_CONTENT;
18502
- content.start = content.start + 1;
18503
-
18504
18544
  pm_token_t closing = not_provided(parser);
18505
- pm_node_t *node = (pm_node_t *) pm_string_node_create_current_string(parser, &opening, &content, &closing);
18545
+ pm_node_t *node = (pm_node_t *) pm_string_node_create_current_string(
18546
+ parser,
18547
+ &(pm_token_t) {
18548
+ .type = PM_TOKEN_STRING_BEGIN,
18549
+ .start = parser->current.start,
18550
+ .end = parser->current.start + 1
18551
+ },
18552
+ &(pm_token_t) {
18553
+ .type = PM_TOKEN_STRING_CONTENT,
18554
+ .start = parser->current.start + 1,
18555
+ .end = parser->current.end
18556
+ },
18557
+ &closing
18558
+ );
18559
+
18506
18560
  pm_node_flag_set(node, parse_unescaped_encoding(parser));
18507
18561
 
18562
+ // Skip past the character literal here, since now we have handled
18563
+ // parser->explicit_encoding correctly.
18564
+ parser_lex(parser);
18565
+
18508
18566
  // Characters can be followed by strings in which case they are
18509
18567
  // automatically concatenated.
18510
18568
  if (match1(parser, PM_TOKEN_STRING_BEGIN)) {
@@ -20901,7 +20959,7 @@ parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding
20901
20959
  bool permitted = true;
20902
20960
  if (previous_binding_power != PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_USTAR)) permitted = false;
20903
20961
 
20904
- 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, (uint16_t) (depth + 1));
20962
+ 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_MODIFIER, diag_id, (uint16_t) (depth + 1));
20905
20963
  if (!permitted) pm_parser_err_node(parser, value, PM_ERR_UNEXPECTED_MULTI_WRITE);
20906
20964
 
20907
20965
  parse_assignment_value_local(parser, value);
@@ -22498,9 +22556,10 @@ parse_program(pm_parser_t *parser) {
22498
22556
  statements = wrap_statements(parser, statements);
22499
22557
  } else {
22500
22558
  flush_block_exits(parser, previous_block_exits);
22501
- pm_node_list_free(&current_block_exits);
22502
22559
  }
22503
22560
 
22561
+ pm_node_list_free(&current_block_exits);
22562
+
22504
22563
  // If this is an empty file, then we're still going to parse all of the
22505
22564
  // statements in order to gather up all of the comments and such. Here we'll
22506
22565
  // correct the location information.
data/src/util/pm_string.c CHANGED
@@ -1,5 +1,7 @@
1
1
  #include "prism/util/pm_string.h"
2
2
 
3
+ static const uint8_t empty_source[] = "";
4
+
3
5
  /**
4
6
  * Returns the size of the pm_string_t struct. This is necessary to allocate the
5
7
  * correct amount of memory in the FFI backend.
@@ -133,8 +135,7 @@ pm_string_mapped_init(pm_string_t *string, const char *filepath) {
133
135
  // the source to a constant empty string and return.
134
136
  if (file_size == 0) {
135
137
  pm_string_file_handle_close(&handle);
136
- const uint8_t source[] = "";
137
- *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 };
138
+ *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = empty_source, .length = 0 };
138
139
  return PM_STRING_INIT_SUCCESS;
139
140
  }
140
141
 
@@ -182,8 +183,7 @@ pm_string_mapped_init(pm_string_t *string, const char *filepath) {
182
183
 
183
184
  if (size == 0) {
184
185
  close(fd);
185
- const uint8_t source[] = "";
186
- *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 };
186
+ *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = empty_source, .length = 0 };
187
187
  return PM_STRING_INIT_SUCCESS;
188
188
  }
189
189
 
@@ -225,8 +225,7 @@ pm_string_file_init(pm_string_t *string, const char *filepath) {
225
225
  // the source to a constant empty string and return.
226
226
  if (file_size == 0) {
227
227
  pm_string_file_handle_close(&handle);
228
- const uint8_t source[] = "";
229
- *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 };
228
+ *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = empty_source, .length = 0 };
230
229
  return PM_STRING_INIT_SUCCESS;
231
230
  }
232
231
 
@@ -278,8 +277,7 @@ pm_string_file_init(pm_string_t *string, const char *filepath) {
278
277
  size_t size = (size_t) sb.st_size;
279
278
  if (size == 0) {
280
279
  close(fd);
281
- const uint8_t source[] = "";
282
- *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 };
280
+ *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = empty_source, .length = 0 };
283
281
  return PM_STRING_INIT_SUCCESS;
284
282
  }
285
283
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prism
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.1
4
+ version: 1.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-09-13 00:00:00.000000000 Z
10
+ date: 2025-10-09 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  email:
13
13
  - ruby@shopify.com