Skip to content

Commit f7f94c9

Browse files
authored
Parser: Introduce VoidElementContentError (#1454)
This pull request updates the parser to introduce a new `VoidElementContentError` that gets added to a `HTMLElementNode` that was transformed from a `tag` or `content_tag` call when the tag used is a void element. For example: ```erb <%= content_tag :img, "/image.png" %> ``` Now gets parsed as the following with `action_view_helpers: true`: ```js @ DocumentNode (location: (1:0)-(1:37)) └── children: (1 item) └── @ HTMLElementNode (location: (1:0)-(1:37)) β”œβ”€β”€ errors: (1 error) β”‚ └── @ VoidElementContentError (location: (1:0)-(1:37)) β”‚ β”œβ”€β”€ message: "Void element `img` cannot have content. `img` does not accept positional arguments." β”‚ └── tag_name: "img" (location: (1:4)-(1:15)) β”‚ β”œβ”€β”€ open_tag: β”‚ └── @ ERBOpenTagNode (location: (1:0)-(1:37)) β”‚ β”œβ”€β”€ tag_opening: "<%=" (location: (1:0)-(1:3)) β”‚ β”œβ”€β”€ content: " content_tag :img, "/image.png" " (location: (1:3)-(1:35)) β”‚ β”œβ”€β”€ tag_closing: "%>" (location: (1:35)-(1:37)) β”‚ β”œβ”€β”€ tag_name: "img" (location: (1:4)-(1:15)) β”‚ └── children: [] β”‚ β”œβ”€β”€ tag_name: "img" (location: (1:4)-(1:15)) β”œβ”€β”€ body: (1 item) β”‚ └── @ HTMLTextNode (location: (1:0)-(1:37)) β”‚ └── content: "/image.png" β”‚ β”œβ”€β”€ close_tag: βˆ… β”œβ”€β”€ is_void: true └── element_source: "ActionView::Helpers::TagHelper#content_tag" ``` It also catches: ```erb <%= tag.img "/image.png" %> ``` ```js @ DocumentNode (location: (1:0)-(1:27)) └── children: (1 item) └── @ HTMLElementNode (location: (1:0)-(1:27)) β”œβ”€β”€ errors: (1 error) β”‚ └── @ VoidElementContentError (location: (1:0)-(1:27)) β”‚ β”œβ”€β”€ message: "Void element `img` cannot have content. `img` does not accept positional arguments." β”‚ └── tag_name: "img" (location: (1:8)-(1:11)) β”‚ β”œβ”€β”€ open_tag: β”‚ └── @ ERBOpenTagNode (location: (1:0)-(1:27)) β”‚ β”œβ”€β”€ tag_opening: "<%=" (location: (1:0)-(1:3)) β”‚ β”œβ”€β”€ content: " tag.img "/image.png" " (location: (1:3)-(1:25)) β”‚ β”œβ”€β”€ tag_closing: "%>" (location: (1:25)-(1:27)) β”‚ β”œβ”€β”€ tag_name: "img" (location: (1:8)-(1:11)) β”‚ └── children: [] β”‚ β”œβ”€β”€ tag_name: "img" (location: (1:8)-(1:11)) β”œβ”€β”€ body: (1 item) β”‚ └── @ HTMLTextNode (location: (1:0)-(1:27)) β”‚ └── content: "/image.png" β”‚ β”œβ”€β”€ close_tag: βˆ… β”œβ”€β”€ is_void: true └── element_source: "ActionView::Helpers::TagHelper#tag" ```
1 parent 5db6954 commit f7f94c9

12 files changed

+227
-18
lines changed

β€Žconfig.ymlβ€Ž

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,17 @@ errors:
458458

459459
fields: []
460460

461+
- name: VoidElementContentError
462+
message:
463+
template: "Void element `%s` cannot have content. `%s` does not accept positional arguments."
464+
arguments:
465+
- tag_name->value
466+
- tag_name->value
467+
468+
fields:
469+
- name: tag_name
470+
type: token
471+
461472
- name: DotNotationCasingError
462473
message:
463474
template: "Dot-notation component tags require the first segment to start with an uppercase letter. `%s` does not start with an uppercase letter."

β€Žsig/herb/errors.rbsβ€Ž

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žsrc/analyze/action_view/tag_helpers.cβ€Ž

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "../../include/lib/hb_allocator.h"
99
#include "../../include/lib/hb_array.h"
1010
#include "../../include/lib/hb_string.h"
11+
#include "../../include/lib/string.h"
1112
#include "../../include/location/position.h"
1213
#include "../../include/parser/parser_helpers.h"
1314
#include "../../include/util/html_util.h"
@@ -431,10 +432,22 @@ static AST_NODE_T* transform_tag_helper_with_attributes(
431432
);
432433

433434
hb_array_T* body = hb_array_init(1, allocator);
434-
bool is_void = tag_name && ((strcmp(handler->name, "tag") == 0) || (strcmp(handler->name, "image_tag") == 0))
435-
&& is_void_element(hb_string_from_c_string(tag_name));
435+
hb_array_T* element_errors = hb_array_init(0, allocator);
436+
bool is_void = tag_name && is_void_element(hb_string_from_c_string(tag_name))
437+
&& (string_equals(handler->name, "tag") || string_equals(handler->name, "content_tag")
438+
|| string_equals(handler->name, "image_tag"));
436439

437440
if (helper_content) {
441+
if (is_void) {
442+
append_void_element_content_error(
443+
tag_name_token,
444+
erb_node->base.location.start,
445+
erb_node->base.location.end,
446+
allocator,
447+
element_errors
448+
);
449+
}
450+
438451
append_body_content_node(
439452
body,
440453
helper_content,
@@ -468,7 +481,7 @@ static AST_NODE_T* transform_tag_helper_with_attributes(
468481
handler->source,
469482
erb_node->base.location.start,
470483
erb_node->base.location.end,
471-
hb_array_init(0, allocator),
484+
element_errors,
472485
allocator
473486
);
474487

β€Žtest/analyze/action_view/tag_helper/content_tag_test.rbβ€Ž

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,5 +257,17 @@ class ContentTagTest < Minitest::Spec
257257
<%= content_tag(:script, "alert('Hello')", nonce: false) %>
258258
HTML
259259
end
260+
261+
test "content_tag with void element img and content argument reports error" do
262+
assert_parsed_snapshot(<<~HTML, action_view_helpers: true)
263+
<%= content_tag :img, "hello" %>
264+
HTML
265+
end
266+
267+
test "content_tag with void element br and content argument reports error" do
268+
assert_parsed_snapshot(<<~HTML, action_view_helpers: true)
269+
<%= content_tag :br, "hello" %>
270+
HTML
271+
end
260272
end
261273
end

β€Žtest/analyze/action_view/tag_helper/tag_test.rbβ€Ž

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,5 +346,17 @@ class TagTest < Minitest::Spec
346346
<%= tag.script(nonce: false) { "alert('Hello')".html_safe } %>
347347
HTML
348348
end
349+
350+
test "tag.img with content argument reports void element content error" do
351+
assert_parsed_snapshot(<<~HTML, action_view_helpers: true)
352+
<%= tag.img "/image.png" %>
353+
HTML
354+
end
355+
356+
test "tag.img with content argument and data attributes reports void element content error" do
357+
assert_parsed_snapshot(<<~HTML, action_view_helpers: true)
358+
<%= tag.img "/image.png", data: { controller: "image" } %>
359+
HTML
360+
end
349361
end
350362
end

β€Žtest/snapshots/analyze/action_view/tag_helper/content_tag_test/test_0014_content_tag_with_void_element_br_6610ebc45e248144b0795a8f8b1f87f0-ef4af315cb33925c38d24ea3c2e8a1cd.txtβ€Ž

Lines changed: 2 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žtest/snapshots/analyze/action_view/tag_helper/content_tag_test/test_0015_content_tag_with_void_element_hr_with_attributes_2a6b2adf7ac8bf91d0a5a8a8dd096a8b-ef4af315cb33925c38d24ea3c2e8a1cd.txtβ€Ž

Lines changed: 2 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žtest/snapshots/analyze/action_view/tag_helper/content_tag_test/test_0016_content_tag_with_void_element_img_with_attributes_c1825e08bbcdcebd231eb18e614274ee-ef4af315cb33925c38d24ea3c2e8a1cd.txtβ€Ž

Lines changed: 2 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žtest/snapshots/analyze/action_view/tag_helper/content_tag_test/test_0037_content_tag_with_void_element_img_and_content_argument_reports_error_5bdb8a95466d4783db7ad280fd6d71ca-ef4af315cb33925c38d24ea3c2e8a1cd.txtβ€Ž

Lines changed: 33 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žtest/snapshots/analyze/action_view/tag_helper/content_tag_test/test_0038_content_tag_with_void_element_br_and_content_argument_reports_error_29c54479d2c6354145cc9b90d440d7b8-ef4af315cb33925c38d24ea3c2e8a1cd.txtβ€Ž

Lines changed: 33 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
Β (0)