diff --git a/exist-core/src/main/java/org/exist/xquery/ContextItemExpression.java b/exist-core/src/main/java/org/exist/xquery/ContextItemExpression.java index 23d18223b70..a67e7efb8ae 100644 --- a/exist-core/src/main/java/org/exist/xquery/ContextItemExpression.java +++ b/exist-core/src/main/java/org/exist/xquery/ContextItemExpression.java @@ -29,8 +29,6 @@ public class ContextItemExpression extends LocationStep { - private int returnType = Type.ITEM; - public ContextItemExpression(final XQueryContext context) { // TODO: create class AnyItemTest (one private implementation found in saxon) super(context, Constants.SELF_AXIS, new AnyNodeTest()); @@ -40,7 +38,7 @@ public ContextItemExpression(final XQueryContext context) { public void analyze(AnalyzeContextInfo contextInfo) throws XPathException { contextInfo.addFlag(DOT_TEST); // set return type to static type to allow for index use in optimization step - returnType = contextInfo.getStaticType(); + staticReturnType = contextInfo.getStaticType(); super.analyze(contextInfo); } @@ -69,7 +67,8 @@ public Sequence eval(final Sequence contextSequence, final Item contextItem) thr @Override public int returnsType() { - return returnType; + // "." will have the same type as the parent expression + return staticReturnType; } @Override diff --git a/exist-core/src/main/java/org/exist/xquery/LocationStep.java b/exist-core/src/main/java/org/exist/xquery/LocationStep.java index e902a369b53..624795add20 100644 --- a/exist-core/src/main/java/org/exist/xquery/LocationStep.java +++ b/exist-core/src/main/java/org/exist/xquery/LocationStep.java @@ -263,40 +263,61 @@ public void analyze(final AnalyzeContextInfo contextInfo) throws XPathException this.axis = Constants.DESCENDANT_AXIS; } + final Expression contextStep; + final NodeTest stepTest; + final NodeTest contextStepTest; + // static analysis for empty-sequence switch (axis) { case Constants.SELF_AXIS: - if (getTest().getType() != Type.NODE) { - final Expression contextStep = contextInfo.getContextStep(); - if (contextStep instanceof LocationStep cStep) { - - // WM: the following checks will only work on simple filters like //a[self::b], so we - // have to make sure they are not applied to more complex expression types - if (parent.getSubExpressionCount() == 1 && !Type.subTypeOf(getTest().getType(), cStep.getTest().getType())) { - throw new XPathException(this, - ErrorCodes.XPST0005, "Got nothing from self::" + getTest() + ", because parent node kind " + Type.getTypeName(cStep.getTest().getType())); - } + if (getTest().getType() == Type.NODE) { + break; + } - if (parent.getSubExpressionCount() == 1 && !(cStep.getTest().isWildcardTest() || getTest().isWildcardTest()) && !cStep.getTest().equals(getTest())) { - throw new XPathException(this, - ErrorCodes.XPST0005, "Self::" + getTest() + " called on set of nodes which do not contain any nodes of this name."); - } - } + // WM: the following checks will only work on simple filters like //a[self::b], so we + // have to make sure they are not applied to more complex expression types + if (parent.getSubExpressionCount() > 1) { + break; + } + + contextStep = contextInfo.getContextStep(); + if (!(contextStep instanceof LocationStep cStep)) { + break; + } + + stepTest = getTest(); + contextStepTest = cStep.getTest(); + + if (!Type.subTypeOf(stepTest.getType(), contextStepTest.getType())) { + // return empty sequence + contextInfo.setStaticType(Type.EMPTY_SEQUENCE); + staticReturnType = Type.EMPTY_SEQUENCE; + break; + } + + if (!stepTest.isWildcardTest() && + !contextStepTest.isWildcardTest() && + !contextStepTest.equals(stepTest)) { + // return empty sequence + contextInfo.setStaticType(Type.EMPTY_SEQUENCE); + staticReturnType = Type.EMPTY_SEQUENCE; } break; -// case Constants.DESCENDANT_AXIS: +// case Constants.DESCENDANT_AXIS: case Constants.DESCENDANT_SELF_AXIS: - final Expression contextStep = contextInfo.getContextStep(); - if (contextStep instanceof LocationStep cStep) { - - if (( - cStep.getTest().getType() == Type.ATTRIBUTE || - cStep.getTest().getType() == Type.TEXT - ) - && cStep.getTest() != getTest()) { - throw new XPathException(this, - ErrorCodes.XPST0005, "Descendant-or-self::" + getTest() + " from an attribute gets nothing."); - } + contextStep = contextInfo.getContextStep(); + if (!(contextStep instanceof LocationStep cStep)) { + break; + } + + stepTest = getTest(); + contextStepTest = cStep.getTest(); + + if ((contextStepTest.getType() == Type.ATTRIBUTE || contextStepTest.getType() == Type.TEXT) && + contextStepTest != stepTest) { + // return empty sequence + contextInfo.setStaticType(Type.EMPTY_SEQUENCE); + staticReturnType = Type.EMPTY_SEQUENCE; } break; // case Constants.PARENT_AXIS: diff --git a/exist-core/src/test/xquery/no-static-typing.xqm b/exist-core/src/test/xquery/no-static-typing.xqm new file mode 100644 index 00000000000..200cfc9fda1 --- /dev/null +++ b/exist-core/src/test/xquery/no-static-typing.xqm @@ -0,0 +1,66 @@ +(: + : eXist-db Open Source Native XML Database + : Copyright (C) 2001 The eXist-db Authors + : + : info@exist-db.org + : http://www.exist-db.org + : + : This library is free software; you can redistribute it and/or + : modify it under the terms of the GNU Lesser General Public + : License as published by the Free Software Foundation; either + : version 2.1 of the License, or (at your option) any later version. + : + : This library is distributed in the hope that it will be useful, + : but WITHOUT ANY WARRANTY; without even the implied warranty of + : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + : Lesser General Public License for more details. + : + : You should have received a copy of the GNU Lesser General Public + : License along with this library; if not, write to the Free Software + : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + :) +xquery version "3.1"; + +(: + : Tests created for issue https://github.com/exist-db/exist/issues/2445 + :) +module namespace tnst="http://exist-db.org/xquery/test/no-static-typing"; + +import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql"; + +declare + %test:assertEmpty +function tnst:self-other-node-name() { + [self::b] +}; + +declare + %test:assertEmpty +function tnst:element-after-attribute() { + /@b/c +}; + +declare + %test:assertEmpty +function tnst:element-after-text() { + /text()/b +}; + +declare + %test:assertEmpty +function tnst:descendant-or-self-other-node-name() { + [descendant-or-self::b] +}; + +declare + %test:assertEmpty +function tnst:element-after-descendant-or-self-attribute() { + /descendant-or-self::attribute(b)/c +}; + +declare + %test:assertEmpty +function tnst:element-after-descendant-or-self-text() { + /descendant-or-self::text()/b +}; +