Project

Profile

Help

Bug #6238

closed

XPath error information is lost in XSLT

Added by Gerben Abbink 6 months ago. Updated 5 months ago.

Status:
Closed
Priority:
Normal
Assignee:
Category:
Diagnostics
Sprint/Milestone:
-
Start date:
2023-11-02
Due date:
% Done:

100%

Estimated time:
Legacy ID:
Applies to branch:
12, trunk
Fix Committed on Branch:
12, trunk
Fixed in Maintenance Release:
Platforms:
.NET, Java

Description

If I run the XPath processor with an XPath expression that has an error, I get an accurate error location by catching the SaxonApiException exception:

XPath expression: 1 , (4, 4) eq (88, 4)

try{
    XPathCompiler compiler = processor.newXPathCompiler();
}
cath(SaxonApiException e){
    XPathException cause = (XPathException)e.getCause();
    cause.getLocator().getColumnNumber(); // << accturate error position = 9
}

However, if I use the same XPath expression in an XSLT expression, such as <xsl:value-of select="..."/>, the XPath error location information is lost:

XsltCompiler compiler = processor.newXsltCompiler(); compiler.setErrorReporter(new MyErrorReporter));

MyErrorReporter.report(XmlProcessingError error) << this error only contains information about the location of the XSLT expression, the XPath error location is lost.

I have found this source code in StyleElement.java:

    public void compileError(XmlProcessingError error) {
        XmlProcessingIncident.maybeSetHostLanguage(error, HostLanguage.XSLT);
        // Set the location of the error if there is no current location information,
        // or if the current location information is local to an XPath expression, unless we are
        // positioned on an xsl:function or xsl:template, in which case this would lose too much information
        if (error.getLocation() == null ||
                ((error.getLocation() instanceof Loc ||
                          error.getLocation() instanceof Expression) && !(this instanceof StylesheetComponent))) {
            XmlProcessingIncident.maybeSetLocation(error, this);
        }
        getCompilation().reportError(error);
    }

It seems to overwrite the original XPath error location. Why is the original XPath error location not kept, for instance in a getCause().

Actions #1

Updated by Michael Kay 6 months ago

I ran this stylesheet:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet exclude-result-prefixes="#all" version="3.0"
   xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   
   <xsl:param name="p1" select="4, 4"/>
   <xsl:param name="p2" select="88, 6"/>
   <xsl:template name="xsl:initial-template">
    <out>
       <xsl:value-of select="1 , $p1 eq $p2"/>
    </out>
 </xsl:template>
   
</xsl:stylesheet>

and got this output:

Type error at char 5 in expression in xsl:value-of/@select on line 9 column 47 of test.xsl:
  XPTY0004  A sequence of more than one item is not allowed as the first operand of 'eq' (4, 4) 
at variable vv:gg854487022 on line 9 column 0 of test.xsl:
     invoked by optimizer-generated global variable select="fn:string-join(...)" at file:/Users/mike/Desktop/temp/test.xsl#9
at template xsl:initial-template on line 7 column 46 of test.xsl:
A sequence of more than one item is not allowed as the first operand of 'eq' (4, 4)

The error information displayed by the standard error reporter includes the character offset within the XPath expression (5), the attribute in which this expression appears (xsl:value-of/@select), and the SAX-reported line and column of the stylesheet for the xsl:value-of element (line 9, col 47). So I don't think we're losing anything.

The above example forced the error to be detected at run time (by using xsl:param).

If we change this to a type error detected statically by changing xsl:param to xsl:variable, then we get a different message:

Error in xsl:value-of/@select on line 9 column 47 of test.xsl:
  XPTY0004  Required cardinality of first operand of 'eq' is zero or one; supplied value has
  cardinality more than one
Errors were reported during stylesheet compilation

which does indeed drop the character position within the XPath expression.

There are some complications here. The main one is attribute value normalization: the XSLT processor only gets to see the value of the attribute after the XML parser has applied AVN, which means everything is on one line and the character positions do not reflect the original. So we can't produce line/column positions within the attribute value, and character positions can be misleading. For this reason, with compile time errors we generally prefer to display nearby text (error near {$p1 * $p2} in xsl:value-of/@select at line X...) rather than character offset within the XPath expression text. This example, however, is a statically-reported semantic error, and for those cases we don't have the "nearby text" information, so perhaps reporting the character offset (as we do with dynamic errors) would be useful even though it is in general inaccurate.

Actions #2

Updated by Michael Kay 6 months ago

  • Status changed from New to In Progress

Tracing through this case, I see that we catch the XPathException in StyleElement.typeCheck() line 1579 and at this point construct a new AttributeLocation which is actually losing information (the name and location of the attribute containing the XPath expression are already present in the exception's location data). The loss of information thus occurs before the call on public void compileError(XmlProcessingError error) which you identified.

What is needed is for StyleElement.typeCheck() to combine the XPath-level location information contained in the XPathException with the XSLT-level information (element and attribute) known to the StyleElement - unless that information is already there, in which case it needs to do nothing.

This gives us:

Error at char 5 in xsl:value-of/@select on line 9 column 47 of test.xsl:
  XPTY0004  Required cardinality of first operand of 'eq' is zero or one; supplied value has
  cardinality more than one

Another strategy might be to report the "short string" representation of the expression that we are type-checking. A number of error paths do collect this information, but it is currently ignored by the StandardErrorReporter (it is used in the old StandardErrorListener but for some reason that was not carried forward.) In fact the StandardErrorReporter contains the sad remains of an attempt to do this:

            Expression failingExpression = null;
            String extraContext = formatExtraContext(failingExpression, nearBy);

The reason it doesn't work is that the failingExpression isn't actually available in the XmlProcessingError. Fixed this, and we now get:

Error evaluating ((4, 4)) at char 5 in xsl:value-of/@select on line 9 column 47 of test.xsl:
  XPTY0004  Required cardinality of first operand of 'eq' is zero or one; supplied value has
  cardinality more than one
Errors were reported during stylesheet compilation

Digging deeper, why does StyleElement.compileError(XPathException) construct an XmlProcessingIncident and copy information across to it, rather than wrapping the XPathException in an XmlProcessingException? I'll ignore that for now.

Actions #3

Updated by Michael Kay 6 months ago

  • Category set to Diagnostics
  • Status changed from In Progress to Resolved
  • Assignee set to Michael Kay
  • Priority changed from Low to Normal
  • Applies to branch 12, trunk added
  • Fix Committed on Branch 12, 9.5, trunk added
  • Platforms .NET, Java added
Actions #4

Updated by O'Neil Delpratt 5 months ago

  • Status changed from Resolved to Closed
  • % Done changed from 0 to 100
  • Fixed in Maintenance Release 12.4 added

Bug fix applied in the Saxon 12.4 maintenance release

Actions #5

Updated by Debbie Lockett 5 months ago

  • Fix Committed on Branch deleted (9.5)

Please register to edit this issue

Also available in: Atom PDF