Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions src/main/resources/org/eolang/lints/misc/roll-bases.xsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
* SPDX-FileCopyrightText: Copyright (c) 2016-2026 Objectionary.com
* SPDX-License-Identifier: MIT
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:eo="https://www.eolang.org" version="2.0" id="roll-bases">
<xsl:import href="/org/eolang/parser/_funcs.xsl"/>
<xsl:import href="/org/eolang/funcs/lineno.xsl"/>
<xsl:import href="/org/eolang/funcs/escape.xsl"/>
<xsl:import href="/org/eolang/funcs/defect-context.xsl"/>
<xsl:output encoding="UTF-8" method="xml"/>
<xsl:template match="/">
<defects>
<xsl:for-each select="//o[starts-with(@base, '.') and o[1][@base and not(starts-with(@base, '.')) and not(@name) and not(o) and not(eo:has-data(.))]]">
<defect>
<xsl:variable name="line" select="eo:lineno(@line)"/>
<xsl:attribute name="line">
<xsl:value-of select="$line"/>
</xsl:attribute>
<xsl:if test="$line = '0'">
<xsl:attribute name="context">
<xsl:value-of select="eo:defect-context(.)"/>
</xsl:attribute>
</xsl:if>
<xsl:attribute name="severity">warning</xsl:attribute>
<xsl:text>The base "</xsl:text>
<xsl:value-of select="eo:escape(concat(o[1]/@base, @base))"/>
<xsl:text>" must be written instead of the nested bases "</xsl:text>
<xsl:value-of select="eo:escape(@base)"/>
<xsl:text>" and "</xsl:text>
<xsl:value-of select="eo:escape(o[1]/@base)"/>
<xsl:text>"</xsl:text>
</defect>
</xsl:for-each>
</defects>
</xsl:template>
</xsl:stylesheet>
24 changes: 24 additions & 0 deletions src/main/resources/org/eolang/motives/misc/roll-bases.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Roll bases

A composite `@base` attribute, written as nested objects, must be
collapsed into a single short form when possible. For example:

```xml
<o base=".foo">
<o base=".bar">
<o base="x"/>
</o>
</o>
```

must be written as:

```xml
<o base="x.bar.foo"/>
```

The collapse is possible when the outer object has a `@base` starting
with `.`, and its single child has a `@base` that does not start with
`.`, has no inner objects, no data, and no name. Such unrolled bases
are usually a result of hand-written XMIR or of XMIR produced by tools
other than `eo-parser`, which already rolls bases during parsing.
70 changes: 70 additions & 0 deletions src/test/java/org/eolang/lints/LtByXslTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,76 @@ void validatesEoPacksForErrors() throws IOException {
);
}

@Test
void catchesUnrolledBases() throws ImpossibleModificationException {
MatcherAssert.assertThat(
"An unrolled composite base must be reported",
new LtByXsl("misc/roll-bases").defects(
new XMLDocument(
new Xembler(
new Directives().add("object")
.add("o").attr("base", ".foo")
.add("o").attr("base", "x").up()
.up().up()
).xml()
)
),
Matchers.iterableWithSize(1)
);
}

@Test
void allowsRolledBase() throws ImpossibleModificationException {
MatcherAssert.assertThat(
"A rolled composite base must not be reported",
new LtByXsl("misc/roll-bases").defects(
new XMLDocument(
new Xembler(
new Directives().add("object")
.add("o").attr("base", "x.foo").up().up()
).xml()
)
),
Matchers.emptyIterable()
);
}

@Test
void allowsNestedBaseWithData() throws ImpossibleModificationException {
MatcherAssert.assertThat(
"A child with data must not be rolled",
new LtByXsl("misc/roll-bases").defects(
new XMLDocument(
new Xembler(
new Directives().add("object")
.add("o").attr("base", ".foo")
.add("o").attr("base", "x").set("data").up()
.up().up()
).xml()
)
),
Matchers.emptyIterable()
);
}

@Test
void allowsDoubleDotBases() throws ImpossibleModificationException {
MatcherAssert.assertThat(
"Two dot-prefixed bases must not be rolled",
new LtByXsl("misc/roll-bases").defects(
new XMLDocument(
new Xembler(
new Directives().add("object")
.add("o").attr("base", ".foo")
.add("o").attr("base", ".bar").up()
.up().up()
).xml()
)
),
Matchers.emptyIterable()
);
}

/**
* Whether a pack is eligible for validation?
* @param pack Pack
Expand Down
Loading