diff --git a/lib/rexml/parsers/xpathparser.rb b/lib/rexml/parsers/xpathparser.rb index 7cca0975..38516158 100644 --- a/lib/rexml/parsers/xpathparser.rb +++ b/lib/rexml/parsers/xpathparser.rb @@ -13,6 +13,10 @@ class XPathParser # :nodoc: include XMLTokens LITERAL = /^'([^']*)'|^"([^"]*)"/u + def initialize(strict: false) + @strict = strict + end + def namespaces=( namespaces ) Functions::namespace_context = namespaces @namespaces = namespaces @@ -653,6 +657,7 @@ def PrimaryExpr path, parsed #arry << @variables[ varname ] when /^(\w[-\w]*)(?:\()/ fname = $1 + return path if @strict && fname.include?("_") tmp = $' return path if fname =~ NT path = tmp diff --git a/lib/rexml/xpath_parser.rb b/lib/rexml/xpath_parser.rb index 761b5281..2e6deb58 100644 --- a/lib/rexml/xpath_parser.rb +++ b/lib/rexml/xpath_parser.rb @@ -60,7 +60,7 @@ class XPathParser def initialize(strict: false) @debug = DEBUG - @parser = REXML::Parsers::XPathParser.new + @parser = REXML::Parsers::XPathParser.new(strict: strict) @namespaces = nil @variables = {} @functions = FunctionsClass.new diff --git a/test/functions/test_base.rb b/test/functions/test_base.rb index b63f3d5a..5f6c8110 100644 --- a/test/functions/test_base.rb +++ b/test/functions/test_base.rb @@ -290,11 +290,18 @@ def test_string_nil_without_context {"n" => nil})) end - def test_unregistered_method + def test_unregistered_method_with_underscore doc = Document.new("") assert_nil(XPath::first(doc.root, "to_s()")) end + def test_unregistered_method_with_underscore_in_strict_mode + doc = Document.new("") + assert_raise(REXML::ParseException) do + XPath::first(doc.root, "to_s()", nil, {}, strict: true) + end + end + def test_nonexistent_function doc = Document.new("") # TODO: Maybe, this is not XPath spec behavior. diff --git a/test/parser/test_xpath.rb b/test/parser/test_xpath.rb index bb20c1fd..a011f619 100644 --- a/test/parser/test_xpath.rb +++ b/test/parser/test_xpath.rb @@ -1,6 +1,7 @@ # frozen_string_literal: false require "test/unit" +require "rexml/parseexception" require "rexml/parsers/xpathparser" module REXMLTests @@ -66,6 +67,18 @@ def test_function abbreviate("string-length(a/b[last()])")) end + def test_function_with_underscore + assert_equal("local_name(*)", + abbreviate("local_name(*)")) + end + + def test_function_with_underscore_in_strict_mode + parser = REXML::Parsers::XPathParser.new(strict: true) + assert_raise(REXML::ParseException) do + parser.abbreviate("local_name(*)") + end + end + def test_descendant_or_self_only assert_equal("//", abbreviate("/descendant-or-self::node()/"))