From f7ad602bfcd79ef10a25361c66e3bc7b3d7c127e Mon Sep 17 00:00:00 2001 From: Dylan Thacker-Smith Date: Wed, 18 Dec 2019 17:37:45 -0500 Subject: [PATCH] Fix liquid tag nested in outer block --- .rubocop_todo.yml | 4 ++++ lib/liquid/block_body.rb | 15 ++++++++++++++- test/integration/tags/liquid_tag_test.rb | 24 +++++++++++++++++++----- test/test_helper.rb | 17 +++++++++++++++++ 4 files changed, 54 insertions(+), 6 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index fc0daf1..742b1de 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -21,6 +21,10 @@ Lint/InheritException: Layout/LineLength: Max: 294 +# Offense count: 1 +Metrics/BlockNesting: + Max: 4 + # Offense count: 44 Naming/ConstantName: Exclude: diff --git a/lib/liquid/block_body.rb b/lib/liquid/block_body.rb index 6a412d2..879f000 100644 --- a/lib/liquid/block_body.rb +++ b/lib/liquid/block_body.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'English' + module Liquid class BlockBody LiquidTagToken = /\A\s*(\w+)\s*(.*?)\z/o @@ -51,6 +53,13 @@ module Liquid yield nil, nil end + # @api private + def self.unknown_tag_in_liquid_tag(end_tag_name, end_tag_markup) + yield end_tag_name, end_tag_markup + ensure + Usage.increment("liquid_tag_contains_outer_tag") unless $ERROR_INFO.is_a?(SyntaxError) + end + private def parse_for_document(tokenizer, parse_context, &block) while (token = tokenizer.shift) next if token.empty? @@ -71,7 +80,11 @@ module Liquid if tag_name == 'liquid' liquid_tag_tokenizer = Tokenizer.new(markup, line_number: parse_context.line_number, for_liquid_tag: true) - next parse_for_liquid_tag(liquid_tag_tokenizer, parse_context, &block) + parse_for_liquid_tag(liquid_tag_tokenizer, parse_context) do |end_tag_name, end_tag_markup| + next unless end_tag_name + self.class.unknown_tag_in_liquid_tag(end_tag_name, end_tag_markup, &block) + end + next end unless (tag = registered_tags[tag_name]) diff --git a/test/integration/tags/liquid_tag_test.rb b/test/integration/tags/liquid_tag_test.rb index b5f6b49..c1c267e 100644 --- a/test/integration/tags/liquid_tag_test.rb +++ b/test/integration/tags/liquid_tag_test.rb @@ -81,6 +81,18 @@ class LiquidTagTest < Minitest::Test assert_match_syntax_error("syntax error (line 3): Unknown tag 'error'", "{% liquid echo ''\n \n error %}") end + def test_nested_liquid_tag + assert_usage_increment("liquid_tag_contains_outer_tag", times: 0) do + assert_template_result('good', <<~LIQUID) + {%- if true %} + {%- liquid + echo "good" + %} + {%- endif -%} + LIQUID + end + end + def test_cannot_open_blocks_living_past_a_liquid_tag assert_match_syntax_error("syntax error (line 3): 'if' tag was never closed", <<~LIQUID) {%- liquid @@ -91,11 +103,13 @@ class LiquidTagTest < Minitest::Test end def test_quirk_can_close_blocks_created_before_a_liquid_tag - assert_template_result("42", <<~LIQUID) - {%- if true -%} - 42 - {%- liquid endif -%} - LIQUID + assert_usage_increment("liquid_tag_contains_outer_tag") do + assert_template_result("42", <<~LIQUID) + {%- if true -%} + 42 + {%- liquid endif -%} + LIQUID + end end def test_liquid_tag_in_raw diff --git a/test/test_helper.rb b/test/test_helper.rb index 7a2596e..fd08f8e 100755 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -58,6 +58,23 @@ module Minitest assert_match(match, exception.message) end + def assert_usage_increment(name, times: 1) + old_method = Liquid::Usage.method(:increment) + calls = 0 + begin + Liquid::Usage.singleton_class.send(:remove_method, :increment) + Liquid::Usage.define_singleton_method(:increment) do |got_name| + calls += 1 if got_name == name + old_method.call(got_name) + end + yield + ensure + Liquid::Usage.singleton_class.send(:remove_method, :increment) + Liquid::Usage.define_singleton_method(:increment, old_method) + end + assert_equal(times, calls, "Number of calls to Usage.increment with #{name.inspect}") + end + def with_global_filter(*globals) original_global_filters = Liquid::StrainerFactory.instance_variable_get(:@global_filters) Liquid::StrainerFactory.instance_variable_set(:@global_filters, [])