Uh oh!
There was an error while loading. Please reload this page.
Fix xpath functions related to names - #343
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes XPath name-related functions (name(), local-name(), namespace-uri()) so they correctly use the first document-ordered node (even if it’s not a named node), rather than skipping unnamed nodes and selecting a later named node.
Changes:
- Refactors
local_name,name, andnamespace_urito share a simplified “pick first document-ordered node” helper. - Updates and expands
test_local_nameto avoid whitespace-text-node sensitivity and to cover unnamed-node behavior. - Adds new test cases for attribute and non-named node inputs.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
lib/rexml/functions.rb | Simplifies node selection logic for name-related XPath functions via a new helper method. |
test/functions/test_local_name.rb | Adjusts existing node-set construction and adds new cases to validate the corrected behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Uh oh!
There was an error while loading. Please reload this page.
| when nil | ||
| node = @context[:node] | ||
| when Array | ||
| node = XPathParser.sort(node_set).first | ||
| end |
There was a problem hiding this comment.
There is regression.
- before(master)
> doc=REXML::Document.new("<root xmlns:x='http://example.com/x/'><x:child/></root>")
> REXML::Functions.local_name(doc.root.elements[1])#=> "child"
> REXML::Functions.local_name("not a node")#=> ""- after(this PR)
> doc=REXML::Document.new("<root xmlns:x='http://example.com/x/'><x:child/></root>")
> REXML::Functions.local_name(doc.root.elements[1])#=> ""
> REXML::Functions.local_name("not a node")#=> ""| whennil | |
| node=@context[:node] | |
| whenArray | |
| node=XPathParser.sort(node_set).first | |
| end | |
| whennil | |
| node=@context[:node] | |
| whenArray | |
| node=XPathParser.sort(node_set).first | |
| else | |
| node=node_set | |
| end |
There was a problem hiding this comment.
Is it really a regression? I think it's a dead branch which should be cleaned up.
XPath values are: boolean, string, number, and nodeset. there is no non-nodeset-single-node.
There was a problem hiding this comment.
If REXML::Functions.local_name(node) #=> node.local_name is considered a public API, it is better to not change the behavior. What do you think?
There was a problem hiding this comment.
REXML::Functions.local_name(node) #=> node.local_name is not part of the public API.
This change is not a regression.
I'm sorry.
https://docs.ruby-lang.org/ja/latest/class/REXML=3a=3aFunctions.html
内部用なのでユーザは使わないでください。
(I'm sorry for writing in Japanese.)
There was a problem hiding this comment.
I rewrote the code so that it doesn't use REXML::Functions.local_name(node).
- before(rexml 3.4.4)
> doc=REXML::Document.new("<root xmlns:x='http://example.com/x/'><x:child/></root>")
> child=doc.root.elements[1]#=> <x:child/>
> REXML::XPath.match(doc,"local-name($x)",nil,{"x"=>child})#=> ["child"]
> REXML::XPath.match(doc,"local-name('not a node')")#=> [""]- after(this PR)
> doc=REXML::Document.new("<root xmlns:x='http://example.com/x/'><x:child/></root>")
> child=doc.root.elements[1]#=> <x:child/>
> REXML::XPath.match(doc,"local-name($x)",nil,{"x"=>child})#=> [""]
> REXML::XPath.match(doc,"local-name('not a node')")#=> [""]XPath values are: boolean, string, number, and nodeset. there is no non-nodeset-single-node.
However, I agree that “single nodes do not need to be considered.”
There was a problem hiding this comment.
The variable in the code below is invalid for now.
REXML::XPath.match(doc,"local-name($x)",nil,{"x"=>child})Example:
REXML::XPath.match(doc,"($x)/root",nil,{"x"=>[doc]})# => [<root xmlns:x='http://example.com/x/'> ... </>]REXML::XPath.match(doc,"($x)/root",nil,{"x"=>doc})# => [<UNDEFINED> ... </>]It will be valid in #342 (comment)
There was a problem hiding this comment.
It will be valid in #342 (comment)
After rebasing, I confirmed that it works as expected.
Thank you.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
naitoh
commented
Jul 29, 2026
@tompng |
Fix and simplify name(nodesets), local-name(nodesets) and namespace-uri(nodesets) node select logic. All functions that uses a single node should use the first document-ordered node, but these functions were wrongly skipping un-named nodes.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
lib/rexml/functions.rb:75
- The PR changes
name()/namespace-uri()selection semantics viatarget_named_node, but the added regression tests only coverlocal-name(). There’s currently no test that exercisesname(node-set)andnamespace-uri(node-set)when the first document-ordered node is non-named (e.g.,node()returning text/comment before an element), which is the class of bug described in the PR.
Add tests similar to test_non_named that assert REXML::XPath.match(doc, 'name(root/node())') == [''] and namespace-uri(root/node()) == [''] (and an attribute case for namespace-uri()).
def name( node_set=nil )
target_named_node(node_set)&.expanded_name || ""
end
tompng
commented
Jul 29, 2026
Rebase done. - node_set = [document.root.attributes.to_a.last]+ node_set = [document.root.attributes.get_attribute("x:attr")]Other copilot suggestion: ignored. |
Uh oh!
There was an error while loading. Please reload this page.
Co-authored-by: NAITOH Jun <naitoh@gmail.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Suppressed comments (2)
test/functions/test_local_name.rb:41
- This test only asserts
local_name, but this PR also changesname()andnamespace_uri()to use the first document-ordered node (including non-named nodes). Adding assertions here for attribute nodes would help prevent regressions in the other two functions.
document = REXML::Document.new("<root xmlns:x='http://example.com/x/' x:attr='value' />")
node_set = [document.root.attributes.get_attribute("x:attr")]
assert_equal("attr", REXML::Functions.local_name(node_set))
test/functions/test_local_name.rb:49
test_non_namedvalidateslocal_namefor text/comment nodes, but the same selection logic is now shared byname()andnamespace_uri(). Adding assertions for those functions here would better cover the intended bug fix across all three functions.
document = REXML::Document.new("<root>text<!-- comment --><a/></root>")
children = document.root.children
assert_equal("", REXML::Functions.local_name([children[0]]))
assert_equal("", REXML::Functions.local_name([children[1]]))
assert_equal("", REXML::Functions.local_name(children))
Uh oh!
There was an error while loading. Please reload this page.
Fix and simplify name(nodesets), local-name(nodesets) and namespace-uri(nodesets) node select logic. All functions that uses a single node should use the first document-ordered node, but these functions were wrongly skipping un-named nodes.