herb 0.8.8-arm-linux-gnu → 0.8.10-arm-linux-gnu

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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/Makefile +22 -8
  3. data/config.yml +7 -0
  4. data/ext/herb/error_helpers.c +25 -0
  5. data/ext/herb/nodes.c +1 -1
  6. data/lib/herb/3.0/herb.so +0 -0
  7. data/lib/herb/3.1/herb.so +0 -0
  8. data/lib/herb/3.2/herb.so +0 -0
  9. data/lib/herb/3.3/herb.so +0 -0
  10. data/lib/herb/3.4/herb.so +0 -0
  11. data/lib/herb/4.0/herb.so +0 -0
  12. data/lib/herb/engine/compiler.rb +5 -3
  13. data/lib/herb/engine/debug_visitor.rb +38 -3
  14. data/lib/herb/engine.rb +16 -4
  15. data/lib/herb/errors.rb +20 -0
  16. data/lib/herb/version.rb +1 -1
  17. data/sig/herb/engine/debug_visitor.rbs +6 -0
  18. data/sig/herb/engine.rbs +2 -0
  19. data/sig/herb/errors.rbs +10 -0
  20. data/sig/serialized_ast_errors.rbs +3 -0
  21. data/src/analyze.c +28 -12
  22. data/src/analyze_helpers.c +32 -9
  23. data/src/errors.c +37 -0
  24. data/src/extract.c +6 -3
  25. data/src/include/analyze_helpers.h +18 -15
  26. data/src/include/errors.h +8 -0
  27. data/src/include/util/string.h +11 -0
  28. data/src/include/version.h +1 -1
  29. data/src/main.c +32 -44
  30. data/src/parser.c +6 -5
  31. data/vendor/prism/config.yml +4 -4
  32. data/vendor/prism/include/prism/ast.h +4 -4
  33. data/vendor/prism/include/prism/version.h +2 -2
  34. data/vendor/prism/src/prism.c +1 -1
  35. data/vendor/prism/templates/java/org/prism/Loader.java.erb +1 -1
  36. data/vendor/prism/templates/javascript/src/deserialize.js.erb +1 -1
  37. data/vendor/prism/templates/lib/prism/node.rb.erb +23 -15
  38. data/vendor/prism/templates/lib/prism/serialize.rb.erb +1 -1
  39. data/vendor/prism/templates/rbi/prism/node.rbi.erb +3 -0
  40. data/vendor/prism/templates/sig/prism/node.rbs.erb +3 -0
  41. data/vendor/prism/templates/sig/prism.rbs.erb +9 -10
  42. metadata +2 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 888f3c26f99723abfd1d2ebdc4326a7a4df2081b0bff44726f921cffc6252d82
4
- data.tar.gz: '087fea21f47e3ce9b75d7ef4bd9526e114ad08758c44bfcbac82b287b28f1aae'
3
+ metadata.gz: cce970c625f82f590b7b864222c558590b780b8beb41832ee1fcabbcc4921dc8
4
+ data.tar.gz: 87551da6a74a277c15dadf68d4b365a9f42461a05c1f9c6f22738da825e9b853
5
5
  SHA512:
6
- metadata.gz: 3e14ed07d6c6d027eb4aad725bb1394fae4f1eeda95c9dcbdb615d567096669f1632d93a41bcdf44e59fd4a8d1262232820beecde7ad3276b4f2ed417b8a227e
7
- data.tar.gz: 39fc879d1a0504869907fee059b17a79e3dbeb40cd336526cb3048ae25fb6350035bbd4ef5fbb756ea279a4c786544cde7224b5a937a6b519edc2f9aea037064
6
+ metadata.gz: c0d464f05bf5126d41ed6b0d82354733b09f67f5392c82aae800b90c61e8e30bc8aed4f213022b447548636e1707142e2e4c30c1c0d3fb7f6123d4a4029228b6
7
+ data.tar.gz: 2b11b9480eecddcede77573534358a3bb42b9539bc0a40f5ab3d3a0c06c51dc711a1d15e8205d77f61654d9013fa611c7fc76d91ee28ab704cdc9601bcca93f8
data/Makefile CHANGED
@@ -67,15 +67,19 @@ ifeq ($(os),Linux)
67
67
  endif
68
68
 
69
69
  ifeq ($(os),Darwin)
70
- brew_prefix := $(shell brew --prefix check)
71
- test_cflags = $(test_flags) -I$(brew_prefix)/include
72
- test_ldflags = -L$(brew_prefix)/lib -lcheck -lm $(prism_ldflags)
73
- llvm_path = $(shell brew --prefix llvm@21)
74
- cc = $(llvm_path)/bin/clang
75
- clang_format = $(llvm_path)/bin/clang-format
76
- clang_tidy = $(llvm_path)/bin/clang-tidy
70
+ llvm_version := 21
71
+ llvm_prefix ?= $(shell brew --prefix llvm@$(llvm_version))
72
+ check_prefix ?= $(shell brew --prefix check)
73
+
74
+ cc ?= $(llvm_prefix)/bin/clang
75
+ clang_format ?= $(llvm_prefix)/bin/clang-format
76
+ clang_tidy ?= $(llvm_prefix)/bin/clang-tidy
77
+
78
+ test_cflags = $(test_flags) -I$(check_prefix)/include
79
+ test_ldflags = -L$(check_prefix)/lib -lcheck -lm $(prism_ldflags)
77
80
  endif
78
81
 
82
+ .PHONY: all
79
83
  all: templates prism $(exec) $(lib_name) $(static_lib_name) test wasm clangd_config
80
84
 
81
85
  $(exec): $(objects)
@@ -91,39 +95,49 @@ $(static_lib_name): $(objects)
91
95
  src/%.o: src/%.c templates
92
96
  $(cc) -c $(flags) -fPIC $< -o $@
93
97
 
94
- test/%.o: test/%.c templates
98
+ test/%.o: test/%.c templates prism
95
99
  $(cc) -c $(test_cflags) $(test_flags) $(prism_flags) $< -o $@
96
100
 
101
+ .PHONY: test
97
102
  test: $(test_objects) $(non_main_objects)
98
103
  $(cc) $(test_objects) $(non_main_objects) $(test_cflags) $(test_ldflags) -o $(test_exec)
99
104
 
105
+ .PHONY: clean
100
106
  clean:
101
107
  rm -f $(exec) $(test_exec) $(lib_name) $(shared_lib_name) $(ruby_extension)
102
108
  rm -rf $(objects) $(test_objects) $(extension_objects) lib/herb/*.bundle tmp
103
109
  rm -rf $(prism_path)
104
110
  rake prism:clean
105
111
 
112
+ .PHONY: bundle_install
106
113
  bundle_install:
107
114
  bundle install
108
115
 
116
+ .PHONY: templates
109
117
  templates: bundle_install
110
118
  bundle exec rake templates
111
119
 
120
+ .PHONY: prism
112
121
  prism: bundle_install
113
122
  cd $(prism_path) && ruby templates/template.rb && make static && cd -
114
123
  rake prism:vendor
115
124
 
125
+ .PHONY: format
116
126
  format:
117
127
  $(clang_format) -i $(project_and_extension_files)
118
128
 
129
+ .PHONY: lint
119
130
  lint:
120
131
  $(clang_format) --dry-run --Werror $(project_and_extension_files)
121
132
 
133
+ .PHONY: tidy
122
134
  tidy:
123
135
  $(clang_tidy) $(project_files) -- $(flags)
124
136
 
137
+ .PHONY: clangd_config
125
138
  clangd_config:
126
139
  @echo "$(flags) $(test_cflags)" | tr ' ' '\n' | sort -u > compile_flags.txt
127
140
 
141
+ .PHONY: wasm
128
142
  wasm:
129
143
  cd wasm && make
data/config.yml CHANGED
@@ -178,6 +178,13 @@ errors:
178
178
 
179
179
  fields: []
180
180
 
181
+ - name: ERBCaseWithConditionsError
182
+ message:
183
+ template: "A `case` statement with `when`/`in` in a single ERB tag cannot be formatted. Use separate tags for `case` and its conditions."
184
+ arguments: []
185
+
186
+ fields: []
187
+
181
188
  warnings:
182
189
  fields: []
183
190
  types: []
@@ -341,6 +341,30 @@ static VALUE rb_erb_multiple_blocks_in_tag_error_from_c_struct(ERB_MULTIPLE_BLOC
341
341
  return rb_class_new_instance(3, args, ERBMultipleBlocksInTagError);
342
342
  };
343
343
 
344
+ static VALUE rb_erb_case_with_conditions_error_from_c_struct(ERB_CASE_WITH_CONDITIONS_ERROR_T* erb_case_with_conditions_error) {
345
+ if (erb_case_with_conditions_error == NULL) { return Qnil; }
346
+
347
+ ERROR_T* error = &erb_case_with_conditions_error->base;
348
+
349
+ VALUE Herb = rb_define_module("Herb");
350
+ VALUE Errors = rb_define_module_under(Herb, "Errors");
351
+ VALUE Error = rb_define_class_under(Errors, "Error", rb_cObject);
352
+ VALUE ERBCaseWithConditionsError = rb_define_class_under(Errors, "ERBCaseWithConditionsError", Error);
353
+
354
+ VALUE type = rb_utf8_str_new_cstr(error_type_to_string(error));
355
+ VALUE location = rb_location_from_c_struct(error->location);
356
+ VALUE message = rb_utf8_str_new_cstr(error->message);
357
+
358
+
359
+ VALUE args[3] = {
360
+ type,
361
+ location,
362
+ message
363
+ };
364
+
365
+ return rb_class_new_instance(3, args, ERBCaseWithConditionsError);
366
+ };
367
+
344
368
 
345
369
  VALUE rb_error_from_c_struct(ERROR_T* error) {
346
370
  if (!error) { return Qnil; }
@@ -358,6 +382,7 @@ VALUE rb_error_from_c_struct(ERROR_T* error) {
358
382
  case ERB_CONTROL_FLOW_SCOPE_ERROR: return rb_erb_control_flow_scope_error_from_c_struct((ERB_CONTROL_FLOW_SCOPE_ERROR_T*) error); break;
359
383
  case MISSINGERB_END_TAG_ERROR: return rb_missingerb_end_tag_error_from_c_struct((MISSINGERB_END_TAG_ERROR_T*) error); break;
360
384
  case ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR: return rb_erb_multiple_blocks_in_tag_error_from_c_struct((ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR_T*) error); break;
385
+ case ERB_CASE_WITH_CONDITIONS_ERROR: return rb_erb_case_with_conditions_error_from_c_struct((ERB_CASE_WITH_CONDITIONS_ERROR_T*) error); break;
361
386
  }
362
387
 
363
388
  return Qnil;
data/ext/herb/nodes.c CHANGED
@@ -464,7 +464,7 @@ static VALUE rb_erb_content_node_from_c_struct(AST_ERB_CONTENT_NODE_T* erb_conte
464
464
  VALUE erb_content_node_tag_opening = rb_token_from_c_struct(erb_content_node->tag_opening);
465
465
  VALUE erb_content_node_content = rb_token_from_c_struct(erb_content_node->content);
466
466
  VALUE erb_content_node_tag_closing = rb_token_from_c_struct(erb_content_node->tag_closing);
467
- /* #<Herb::Template::AnalyzedRubyField:0x00007fc7890faa98 @name="analyzed_ruby", @options={kind: nil}> */
467
+ /* #<Herb::Template::AnalyzedRubyField:0x00007fb54f94c300 @name="analyzed_ruby", @options={kind: nil}> */
468
468
  VALUE erb_content_node_analyzed_ruby = Qnil;
469
469
  VALUE erb_content_node_parsed = (erb_content_node->parsed) ? Qtrue : Qfalse;
470
470
  VALUE erb_content_node_valid = (erb_content_node->valid) ? Qtrue : Qfalse;
data/lib/herb/3.0/herb.so CHANGED
Binary file
data/lib/herb/3.1/herb.so CHANGED
Binary file
data/lib/herb/3.2/herb.so CHANGED
Binary file
data/lib/herb/3.3/herb.so CHANGED
Binary file
data/lib/herb/3.4/herb.so CHANGED
Binary file
data/lib/herb/4.0/herb.so CHANGED
Binary file
@@ -297,18 +297,20 @@ module Herb
297
297
  end
298
298
 
299
299
  def add_context_aware_expression(code, context)
300
+ closing = code.include?("#") ? "\n))" : "))"
301
+
300
302
  case context
301
303
  when :attribute_value
302
304
  @engine.send(:with_buffer) {
303
- @engine.src << " << #{@attrfunc}((" << code << "))"
305
+ @engine.src << " << #{@attrfunc}((" << code << closing
304
306
  }
305
307
  when :script_content
306
308
  @engine.send(:with_buffer) {
307
- @engine.src << " << #{@jsfunc}((" << code << "))"
309
+ @engine.src << " << #{@jsfunc}((" << code << closing
308
310
  }
309
311
  when :style_content
310
312
  @engine.send(:with_buffer) {
311
- @engine.src << " << #{@cssfunc}((" << code << "))"
313
+ @engine.src << " << #{@cssfunc}((" << code << closing
312
314
  }
313
315
  else
314
316
  @engine.send(:add_expression_result_escaped, code)
@@ -178,7 +178,7 @@ module Herb
178
178
 
179
179
  debug_attributes = [
180
180
  create_debug_attribute("data-herb-debug-outline-type", view_type),
181
- create_debug_attribute("data-herb-debug-file-name", @filename&.basename&.to_s || "unknown"),
181
+ create_debug_attribute("data-herb-debug-file-name", component_display_name),
182
182
  create_debug_attribute("data-herb-debug-file-relative-path", @relative_file_path || "unknown"),
183
183
  create_debug_attribute("data-herb-debug-file-full-path", @filename&.to_s || "unknown")
184
184
  ]
@@ -231,7 +231,7 @@ module Herb
231
231
  debug_attributes = [
232
232
  create_debug_attribute("data-herb-debug-outline-type", outline_type),
233
233
  create_debug_attribute("data-herb-debug-erb", escaped_erb),
234
- create_debug_attribute("data-herb-debug-file-name", @filename&.basename&.to_s || "unknown"),
234
+ create_debug_attribute("data-herb-debug-file-name", component_display_name),
235
235
  create_debug_attribute("data-herb-debug-file-relative-path", @relative_file_path || "unknown"),
236
236
  create_debug_attribute("data-herb-debug-file-full-path", @filename&.to_s || "unknown"),
237
237
  create_debug_attribute("data-herb-debug-inserted", "true")
@@ -288,8 +288,43 @@ module Herb
288
288
  def component?
289
289
  return false unless @filename
290
290
 
291
+ @filename.to_s.match?(%r{(^|/)app/components/})
292
+ end
293
+
294
+ def sidecar_component?
295
+ return false unless component?
296
+ return false unless @filename
297
+
298
+ @filename.basename.to_s.match?(/\Acomponent\.(html\.erb|html\.herb|erb|herb)\z/)
299
+ end
300
+
301
+ def component_display_name
302
+ return @filename&.basename&.to_s || "unknown" unless @filename
303
+
304
+ basename = @filename.basename.to_s
291
305
  path = @filename.to_s
292
- path.include?("/components/")
306
+
307
+ if sidecar_component? && (match = path.match(%r{/components/(.+)/component\.[^/]+\z}))
308
+ return match[1].split("/").map { |s| classify(s) }.join("::")
309
+ end
310
+
311
+ if component?
312
+ path_without_ext = path.sub(/\.(?:html\.erb|html\.herb|erb|herb)\z/, "")
313
+
314
+ if (match = path_without_ext.match(%r{/components/(.+)\z}))
315
+ return match[1].split("/").map { |s| classify(s) }.join("::")
316
+ end
317
+ end
318
+
319
+ basename
320
+ end
321
+
322
+ def classify(name)
323
+ if name.respond_to?(:camelize)
324
+ name.camelize
325
+ else
326
+ name.split(/[_-]/).map(&:capitalize).join
327
+ end
293
328
  end
294
329
 
295
330
  def in_head_context?
data/lib/herb/engine.rb CHANGED
@@ -212,11 +212,15 @@ module Herb
212
212
  end
213
213
 
214
214
  def add_expression_result(code)
215
- with_buffer { @src << " << (" << code << ").to_s" }
215
+ with_buffer {
216
+ @src << " << (" << code << comment_aware_newline(code) << ").to_s"
217
+ }
216
218
  end
217
219
 
218
220
  def add_expression_result_escaped(code)
219
- with_buffer { @src << " << " << @escapefunc << "((" << code << "))" }
221
+ with_buffer {
222
+ @src << " << " << @escapefunc << "((" << code << comment_aware_newline(code) << "))"
223
+ }
220
224
  end
221
225
 
222
226
  def add_expression_block(indicator, code)
@@ -228,11 +232,19 @@ module Herb
228
232
  end
229
233
 
230
234
  def add_expression_block_result(code)
231
- with_buffer { @src << " << " << code }
235
+ with_buffer {
236
+ @src << " << " << code << comment_aware_newline(code)
237
+ }
232
238
  end
233
239
 
234
240
  def add_expression_block_result_escaped(code)
235
- with_buffer { @src << " << " << @escapefunc << "(" << code << ")" }
241
+ with_buffer {
242
+ @src << " << " << @escapefunc << "(" << code << comment_aware_newline(code) << ")"
243
+ }
244
+ end
245
+
246
+ def comment_aware_newline(code)
247
+ code.include?("#") ? "\n" : ""
236
248
  end
237
249
 
238
250
  def add_postamble(postamble)
data/lib/herb/errors.rb CHANGED
@@ -518,5 +518,25 @@ module Herb
518
518
  end
519
519
  end
520
520
 
521
+ class ERBCaseWithConditionsError < Error
522
+ include Colors
523
+
524
+ #: () -> String
525
+ def inspect
526
+ tree_inspect.rstrip.gsub(/\s+$/, "")
527
+ end
528
+
529
+ #: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
530
+ def tree_inspect(indent: 0, depth: 0, depth_limit: 25)
531
+ output = +""
532
+
533
+ output += white("@ #{bold(red(error_name))} #{dimmed("(location: #{location.tree_inspect})\n")}")
534
+ output += white("└── message: #{green(message.inspect)}\n")
535
+ output += %(\n)
536
+
537
+ output.gsub(/^/, " " * indent)
538
+ end
539
+ end
540
+
521
541
  end
522
542
  end
data/lib/herb/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # typed: true
3
3
 
4
4
  module Herb
5
- VERSION = "0.8.8"
5
+ VERSION = "0.8.10"
6
6
  end
@@ -57,6 +57,12 @@ module Herb
57
57
 
58
58
  def component?: () -> untyped
59
59
 
60
+ def sidecar_component?: () -> untyped
61
+
62
+ def component_display_name: () -> untyped
63
+
64
+ def classify: (untyped name) -> untyped
65
+
60
66
  def in_head_context?: () -> untyped
61
67
 
62
68
  def in_script_or_style_context?: () -> untyped
data/sig/herb/engine.rbs CHANGED
@@ -51,6 +51,8 @@ module Herb
51
51
 
52
52
  def add_expression_block_result_escaped: (untyped code) -> untyped
53
53
 
54
+ def comment_aware_newline: (untyped code) -> untyped
55
+
54
56
  def add_postamble: (untyped postamble) -> untyped
55
57
 
56
58
  def with_buffer: () ?{ (?) -> untyped } -> untyped
data/sig/herb/errors.rbs CHANGED
@@ -253,5 +253,15 @@ module Herb
253
253
  # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
254
254
  def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
255
255
  end
256
+
257
+ class ERBCaseWithConditionsError < Error
258
+ include Colors
259
+
260
+ # : () -> String
261
+ def inspect: () -> String
262
+
263
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
264
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
265
+ end
256
266
  end
257
267
  end
@@ -61,4 +61,7 @@ module Herb
61
61
  type serialized_erb_multiple_blocks_in_tag_error = serialized_error & {
62
62
  }
63
63
 
64
+ type serialized_erb_case_with_conditions_error = serialized_error & {
65
+ }
66
+
64
67
  end
data/src/analyze.c CHANGED
@@ -14,6 +14,7 @@
14
14
  #include "include/util.h"
15
15
  #include "include/util/hb_array.h"
16
16
  #include "include/util/hb_string.h"
17
+ #include "include/util/string.h"
17
18
  #include "include/visitor.h"
18
19
 
19
20
  #include <prism.h>
@@ -34,17 +35,20 @@ static analyzed_ruby_T* herb_analyze_ruby(hb_string_T source) {
34
35
  pm_visit_node(analyzed->root, search_until_nodes, analyzed);
35
36
  pm_visit_node(analyzed->root, search_begin_nodes, analyzed);
36
37
  pm_visit_node(analyzed->root, search_unless_nodes, analyzed);
38
+ pm_visit_node(analyzed->root, search_when_nodes, analyzed);
39
+ pm_visit_node(analyzed->root, search_in_nodes, analyzed);
37
40
 
38
- search_elsif_nodes(analyzed);
39
- search_else_nodes(analyzed);
40
- search_end_nodes(analyzed);
41
- search_when_nodes(analyzed);
42
- search_in_nodes(analyzed);
43
- search_rescue_nodes(analyzed);
44
- search_ensure_nodes(analyzed);
41
+ search_unexpected_elsif_nodes(analyzed);
42
+ search_unexpected_else_nodes(analyzed);
43
+ search_unexpected_end_nodes(analyzed);
44
+ search_unexpected_when_nodes(analyzed);
45
+ search_unexpected_in_nodes(analyzed);
46
+
47
+ search_unexpected_rescue_nodes(analyzed);
48
+ search_unexpected_ensure_nodes(analyzed);
45
49
  search_yield_nodes(analyzed->root, analyzed);
46
50
  search_then_keywords(analyzed->root, analyzed);
47
- search_block_closing_nodes(analyzed);
51
+ search_unexpected_block_closing_nodes(analyzed);
48
52
 
49
53
  if (!analyzed->valid) { pm_visit_node(analyzed->root, search_unclosed_control_flows, analyzed); }
50
54
 
@@ -57,8 +61,8 @@ static bool analyze_erb_content(const AST_NODE_T* node, void* data) {
57
61
 
58
62
  const char* opening = erb_content_node->tag_opening->value;
59
63
 
60
- if (strcmp(opening, "<%%") != 0 && strcmp(opening, "<%%=") != 0 && strcmp(opening, "<%#") != 0
61
- && strcmp(opening, "<%graphql") != 0) {
64
+ if (!string_equals(opening, "<%%") && !string_equals(opening, "<%%=") && !string_equals(opening, "<%#")
65
+ && !string_equals(opening, "<%graphql")) {
62
66
  analyzed_ruby_T* analyzed = herb_analyze_ruby(hb_string(erb_content_node->content->value));
63
67
 
64
68
  erb_content_node->parsed = true;
@@ -72,6 +76,16 @@ static bool analyze_erb_content(const AST_NODE_T* node, void* data) {
72
76
  erb_content_node->base.errors
73
77
  );
74
78
  }
79
+
80
+ if (!analyzed->valid
81
+ && ((analyzed->case_node_count > 0 && analyzed->when_node_count > 0)
82
+ || (analyzed->case_match_node_count > 0 && analyzed->in_node_count > 0))) {
83
+ append_erb_case_with_conditions_error(
84
+ erb_content_node->base.location.start,
85
+ erb_content_node->base.location.end,
86
+ erb_content_node->base.errors
87
+ );
88
+ }
75
89
  } else {
76
90
  erb_content_node->parsed = false;
77
91
  erb_content_node->valid = true;
@@ -274,12 +288,14 @@ static control_type_t detect_control_type(AST_ERB_CONTENT_NODE_T* erb_node) {
274
288
  if (has_elsif_node(ruby)) { return CONTROL_TYPE_ELSIF; }
275
289
  if (has_else_node(ruby)) { return CONTROL_TYPE_ELSE; }
276
290
  if (has_end(ruby)) { return CONTROL_TYPE_END; }
277
- if (has_when_node(ruby)) { return CONTROL_TYPE_WHEN; }
278
- if (has_in_node(ruby)) { return CONTROL_TYPE_IN; }
291
+ if (has_when_node(ruby) && !has_case_node(ruby)) { return CONTROL_TYPE_WHEN; }
292
+ if (has_in_node(ruby) && !has_case_match_node(ruby)) { return CONTROL_TYPE_IN; }
279
293
  if (has_rescue_node(ruby)) { return CONTROL_TYPE_RESCUE; }
280
294
  if (has_ensure_node(ruby)) { return CONTROL_TYPE_ENSURE; }
281
295
  if (has_block_closing(ruby)) { return CONTROL_TYPE_BLOCK_CLOSE; }
282
296
 
297
+ if (ruby->unclosed_control_flow_count == 0 && !has_yield_node(ruby)) { return CONTROL_TYPE_UNKNOWN; }
298
+
283
299
  return find_earliest_control_keyword(root, ruby->parser.start);
284
300
  }
285
301
 
@@ -3,6 +3,7 @@
3
3
  #include <string.h>
4
4
 
5
5
  #include "include/analyzed_ruby.h"
6
+ #include "include/util/string.h"
6
7
 
7
8
  bool has_if_node(analyzed_ruby_T* analyzed) {
8
9
  return analyzed->if_node_count > 0;
@@ -83,7 +84,7 @@ bool has_then_keyword(analyzed_ruby_T* analyzed) {
83
84
  bool has_error_message(analyzed_ruby_T* anlayzed, const char* message) {
84
85
  for (const pm_diagnostic_t* error = (const pm_diagnostic_t*) anlayzed->parser.error_list.head; error != NULL;
85
86
  error = (const pm_diagnostic_t*) error->node.next) {
86
- if (strcmp(error->message, message) == 0) { return true; }
87
+ if (string_equals(error->message, message)) { return true; }
87
88
  }
88
89
 
89
90
  return false;
@@ -242,7 +243,28 @@ bool search_unless_nodes(const pm_node_t* node, void* data) {
242
243
  return false;
243
244
  }
244
245
 
245
- bool search_elsif_nodes(analyzed_ruby_T* analyzed) {
246
+ bool search_when_nodes(const pm_node_t* node, void* data) {
247
+ analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
248
+
249
+ if (node->type == PM_WHEN_NODE) { analyzed->when_node_count++; }
250
+
251
+ pm_visit_child_nodes(node, search_when_nodes, analyzed);
252
+
253
+ return false;
254
+ }
255
+
256
+ bool search_in_nodes(const pm_node_t* node, void* data) {
257
+ analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
258
+
259
+ if (node->type == PM_IN_NODE) { analyzed->in_node_count++; }
260
+ if (node->type == PM_MATCH_PREDICATE_NODE) { analyzed->in_node_count++; }
261
+
262
+ pm_visit_child_nodes(node, search_in_nodes, analyzed);
263
+
264
+ return false;
265
+ }
266
+
267
+ bool search_unexpected_elsif_nodes(analyzed_ruby_T* analyzed) {
246
268
  if (has_error_message(analyzed, "unexpected 'elsif', ignoring it")) {
247
269
  analyzed->elsif_node_count++;
248
270
  return true;
@@ -251,7 +273,7 @@ bool search_elsif_nodes(analyzed_ruby_T* analyzed) {
251
273
  return false;
252
274
  }
253
275
 
254
- bool search_else_nodes(analyzed_ruby_T* analyzed) {
276
+ bool search_unexpected_else_nodes(analyzed_ruby_T* analyzed) {
255
277
  if (has_error_message(analyzed, "unexpected 'else', ignoring it")) {
256
278
  analyzed->else_node_count++;
257
279
  return true;
@@ -260,7 +282,7 @@ bool search_else_nodes(analyzed_ruby_T* analyzed) {
260
282
  return false;
261
283
  }
262
284
 
263
- bool search_end_nodes(analyzed_ruby_T* analyzed) {
285
+ bool search_unexpected_end_nodes(analyzed_ruby_T* analyzed) {
264
286
  if (has_error_message(analyzed, "unexpected 'end', ignoring it")) {
265
287
  if (has_error_message(analyzed, "unexpected '=', ignoring it")) {
266
288
  // `=end`
@@ -274,7 +296,7 @@ bool search_end_nodes(analyzed_ruby_T* analyzed) {
274
296
  return false;
275
297
  }
276
298
 
277
- bool search_block_closing_nodes(analyzed_ruby_T* analyzed) {
299
+ bool search_unexpected_block_closing_nodes(analyzed_ruby_T* analyzed) {
278
300
  if (has_error_message(analyzed, "unexpected '}', ignoring it")) {
279
301
  analyzed->block_closing_count++;
280
302
  return true;
@@ -283,7 +305,7 @@ bool search_block_closing_nodes(analyzed_ruby_T* analyzed) {
283
305
  return false;
284
306
  }
285
307
 
286
- bool search_when_nodes(analyzed_ruby_T* analyzed) {
308
+ bool search_unexpected_when_nodes(analyzed_ruby_T* analyzed) {
287
309
  if (has_error_message(analyzed, "unexpected 'when', ignoring it")) {
288
310
  analyzed->when_node_count++;
289
311
  return true;
@@ -292,7 +314,7 @@ bool search_when_nodes(analyzed_ruby_T* analyzed) {
292
314
  return false;
293
315
  }
294
316
 
295
- bool search_in_nodes(analyzed_ruby_T* analyzed) {
317
+ bool search_unexpected_in_nodes(analyzed_ruby_T* analyzed) {
296
318
  if (has_error_message(analyzed, "unexpected 'in', ignoring it")) {
297
319
  analyzed->in_node_count++;
298
320
  return true;
@@ -301,7 +323,7 @@ bool search_in_nodes(analyzed_ruby_T* analyzed) {
301
323
  return false;
302
324
  }
303
325
 
304
- bool search_rescue_nodes(analyzed_ruby_T* analyzed) {
326
+ bool search_unexpected_rescue_nodes(analyzed_ruby_T* analyzed) {
305
327
  if (has_error_message(analyzed, "unexpected 'rescue', ignoring it")) {
306
328
  analyzed->rescue_node_count++;
307
329
  return true;
@@ -310,7 +332,7 @@ bool search_rescue_nodes(analyzed_ruby_T* analyzed) {
310
332
  return false;
311
333
  }
312
334
 
313
- bool search_ensure_nodes(analyzed_ruby_T* analyzed) {
335
+ bool search_unexpected_ensure_nodes(analyzed_ruby_T* analyzed) {
314
336
  if (has_error_message(analyzed, "unexpected 'ensure', ignoring it")) {
315
337
  analyzed->ensure_node_count++;
316
338
  return true;
@@ -468,6 +490,7 @@ bool search_unclosed_control_flows(const pm_node_t* node, void* data) {
468
490
  if (has_opening && !has_valid_block_closing(block_node->opening_loc, block_node->closing_loc)) {
469
491
  analyzed->unclosed_control_flow_count++;
470
492
  }
493
+
471
494
  break;
472
495
  }
473
496
 
data/src/errors.c CHANGED
@@ -511,6 +511,20 @@ void append_erb_multiple_blocks_in_tag_error(position_T start, position_T end, h
511
511
  hb_array_append(errors, erb_multiple_blocks_in_tag_error_init(start, end));
512
512
  }
513
513
 
514
+ ERB_CASE_WITH_CONDITIONS_ERROR_T* erb_case_with_conditions_error_init(position_T start, position_T end) {
515
+ ERB_CASE_WITH_CONDITIONS_ERROR_T* erb_case_with_conditions_error = malloc(sizeof(ERB_CASE_WITH_CONDITIONS_ERROR_T));
516
+
517
+ error_init(&erb_case_with_conditions_error->base, ERB_CASE_WITH_CONDITIONS_ERROR, start, end);
518
+
519
+ erb_case_with_conditions_error->base.message = herb_strdup("A `case` statement with `when`/`in` in a single ERB tag cannot be formatted. Use separate tags for `case` and its conditions.");
520
+
521
+ return erb_case_with_conditions_error;
522
+ }
523
+
524
+ void append_erb_case_with_conditions_error(position_T start, position_T end, hb_array_T* errors) {
525
+ hb_array_append(errors, erb_case_with_conditions_error_init(start, end));
526
+ }
527
+
514
528
  const char* error_type_to_string(ERROR_T* error) {
515
529
  switch (error->type) {
516
530
  case UNEXPECTED_ERROR: return "UNEXPECTED_ERROR";
@@ -525,6 +539,7 @@ const char* error_type_to_string(ERROR_T* error) {
525
539
  case ERB_CONTROL_FLOW_SCOPE_ERROR: return "ERB_CONTROL_FLOW_SCOPE_ERROR";
526
540
  case MISSINGERB_END_TAG_ERROR: return "MISSINGERB_END_TAG_ERROR";
527
541
  case ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR: return "ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR";
542
+ case ERB_CASE_WITH_CONDITIONS_ERROR: return "ERB_CASE_WITH_CONDITIONS_ERROR";
528
543
  }
529
544
 
530
545
  return "Unknown error_type_T";
@@ -544,6 +559,7 @@ const char* error_human_type(ERROR_T* error) {
544
559
  case ERB_CONTROL_FLOW_SCOPE_ERROR: return "ERBControlFlowScopeError";
545
560
  case MISSINGERB_END_TAG_ERROR: return "MissingERBEndTagError";
546
561
  case ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR: return "ERBMultipleBlocksInTagError";
562
+ case ERB_CASE_WITH_CONDITIONS_ERROR: return "ERBCaseWithConditionsError";
547
563
  }
548
564
 
549
565
  return "Unknown error_type_T";
@@ -638,6 +654,12 @@ static void error_free_erb_multiple_blocks_in_tag_error(ERB_MULTIPLE_BLOCKS_IN_T
638
654
  error_free_base_error(&erb_multiple_blocks_in_tag_error->base);
639
655
  }
640
656
 
657
+ static void error_free_erb_case_with_conditions_error(ERB_CASE_WITH_CONDITIONS_ERROR_T* erb_case_with_conditions_error) {
658
+ /* no ERB_CASE_WITH_CONDITIONS_ERROR_T specific fields to free up */
659
+
660
+ error_free_base_error(&erb_case_with_conditions_error->base);
661
+ }
662
+
641
663
  void error_free(ERROR_T* error) {
642
664
  if (!error) { return; }
643
665
 
@@ -654,6 +676,7 @@ void error_free(ERROR_T* error) {
654
676
  case ERB_CONTROL_FLOW_SCOPE_ERROR: error_free_erb_control_flow_scope_error((ERB_CONTROL_FLOW_SCOPE_ERROR_T*) error); break;
655
677
  case MISSINGERB_END_TAG_ERROR: error_free_missingerb_end_tag_error((MISSINGERB_END_TAG_ERROR_T*) error); break;
656
678
  case ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR: error_free_erb_multiple_blocks_in_tag_error((ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR_T*) error); break;
679
+ case ERB_CASE_WITH_CONDITIONS_ERROR: error_free_erb_case_with_conditions_error((ERB_CASE_WITH_CONDITIONS_ERROR_T*) error); break;
657
680
  }
658
681
  }
659
682
 
@@ -877,6 +900,19 @@ static void error_pretty_print_erb_multiple_blocks_in_tag_error(ERB_MULTIPLE_BLO
877
900
  pretty_print_quoted_property(hb_string("message"), hb_string(error->base.message), indent, relative_indent, true, buffer);
878
901
  }
879
902
 
903
+ static void error_pretty_print_erb_case_with_conditions_error(ERB_CASE_WITH_CONDITIONS_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
904
+ if (!error) { return; }
905
+
906
+ hb_buffer_append(buffer, "@ ");
907
+ hb_buffer_append(buffer, error_human_type((ERROR_T*) error));
908
+ hb_buffer_append(buffer, " ");
909
+
910
+ pretty_print_location(error->base.location, buffer);
911
+ hb_buffer_append(buffer, "\n");
912
+
913
+ pretty_print_quoted_property(hb_string("message"), hb_string(error->base.message), indent, relative_indent, true, buffer);
914
+ }
915
+
880
916
  void error_pretty_print(ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
881
917
  if (!error) { return; }
882
918
 
@@ -893,5 +929,6 @@ void error_pretty_print(ERROR_T* error, const size_t indent, const size_t relati
893
929
  case ERB_CONTROL_FLOW_SCOPE_ERROR: error_pretty_print_erb_control_flow_scope_error((ERB_CONTROL_FLOW_SCOPE_ERROR_T*) error, indent, relative_indent, buffer); break;
894
930
  case MISSINGERB_END_TAG_ERROR: error_pretty_print_missingerb_end_tag_error((MISSINGERB_END_TAG_ERROR_T*) error, indent, relative_indent, buffer); break;
895
931
  case ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR: error_pretty_print_erb_multiple_blocks_in_tag_error((ERB_MULTIPLE_BLOCKS_IN_TAG_ERROR_T*) error, indent, relative_indent, buffer); break;
932
+ case ERB_CASE_WITH_CONDITIONS_ERROR: error_pretty_print_erb_case_with_conditions_error((ERB_CASE_WITH_CONDITIONS_ERROR_T*) error, indent, relative_indent, buffer); break;
896
933
  }
897
934
  }