diff --git a/src/main/resources/org/eolang/lints/misc/roll-bases.xsl b/src/main/resources/org/eolang/lints/misc/roll-bases.xsl new file mode 100644 index 000000000..a32456d3c --- /dev/null +++ b/src/main/resources/org/eolang/lints/misc/roll-bases.xsl @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + warning + The base " + + " must be written instead of the nested bases " + + " and " + + " + + + + + diff --git a/src/main/resources/org/eolang/motives/misc/roll-bases.md b/src/main/resources/org/eolang/motives/misc/roll-bases.md new file mode 100644 index 000000000..4262a6123 --- /dev/null +++ b/src/main/resources/org/eolang/motives/misc/roll-bases.md @@ -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 + + + + + +``` + +must be written as: + +```xml + +``` + +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. diff --git a/src/test/java/org/eolang/lints/LtByXslTest.java b/src/test/java/org/eolang/lints/LtByXslTest.java index 144bef92c..031f3fb45 100644 --- a/src/test/java/org/eolang/lints/LtByXslTest.java +++ b/src/test/java/org/eolang/lints/LtByXslTest.java @@ -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