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()/"))