estools / esquery

ECMAScript AST query library.
http://estools.github.io/esquery/
Other
815 stars 88 forks source link

`:has(selector)` matches nodes matched by `selector` #122

Open mdjermanovic opened 3 years ago

mdjermanovic commented 3 years ago

esquery v1.4.0

const literal = espree.parse("1").body[0].expression;
const selector = esquery.parse(":has(Literal)");

esquery.matches(literal, selector); // true

Or. in the esquery demo, enter 1 as code, :has(Literal) as selector. It finds "Program", "ExpressionStatement", and "Literal".

I was expecting that selectors in :has() can match only descendants of the node, but in the above examples :has(Literal) matches a Literal node. Is this the intended behavior?

lukaw3d commented 1 month ago

Can still reproduce this, e.g. if (y) { y += 'z'; } matches BlockStatement:has(BlockStatement:has(BlockStatement))

The cause is https://github.com/estools/esquery/blob/07ee329d6aaa6e468114687468e5c6f28a7b7beb/esquery.js#L179-L191 enter and leave visit node too. A simple fix is https://github.com/estools/esquery/compare/master...lukaw3d:esquery:remove-parent-from-has

                estraverse.traverse(node, {
                    enter (descendant, parent) {
                        if (node === descendant) return;
                        ...

                    leave (descendant) {
                        if (node === descendant) return;
                        ...