From f117a74a07bbf2d8d059acbfdf28858fb9bcf11b Mon Sep 17 00:00:00 2001 From: Ivan Kniazkov Date: Thu, 13 Oct 2022 09:25:36 +0300 Subject: [PATCH 1/3] Fix qulice error in generated code (typed holes) --- .../codegen/java/MatcherClassFiller.java | 7 +- .../org/cqfn/astranaut/rules/Descriptor.java | 49 +++++++++-- .../codegen/java/MatcherGeneratorTest.java | 12 +++ ...matcher_generator_only_holes_with_type.txt | 86 +++++++++++++++++++ 4 files changed, 144 insertions(+), 10 deletions(-) create mode 100644 src/test/resources/codegen/java/matcher_generator_only_holes_with_type.txt diff --git a/src/main/java/org/cqfn/astranaut/codegen/java/MatcherClassFiller.java b/src/main/java/org/cqfn/astranaut/codegen/java/MatcherClassFiller.java index ba4f755..c867415 100644 --- a/src/main/java/org/cqfn/astranaut/codegen/java/MatcherClassFiller.java +++ b/src/main/java/org/cqfn/astranaut/codegen/java/MatcherClassFiller.java @@ -269,8 +269,13 @@ private void createMatchMethod() { final String condition = this.createCondition(); if (this.descriptor.hasTypedHole()) { final String extractor = this.createExtractorWithTypedHoles(); + String modifier = ""; + if (this.descriptor.hasOnlyEllipsisOrTypedHole()) { + modifier = "final "; + } final String code = String.format( - "boolean result = %s;\n%s\nreturn result;", + "%sboolean result = %s;\n%s\nreturn result;", + modifier, condition, extractor ); diff --git a/src/main/java/org/cqfn/astranaut/rules/Descriptor.java b/src/main/java/org/cqfn/astranaut/rules/Descriptor.java index 38d0a6c..3566ecf 100644 --- a/src/main/java/org/cqfn/astranaut/rules/Descriptor.java +++ b/src/main/java/org/cqfn/astranaut/rules/Descriptor.java @@ -130,7 +130,7 @@ public boolean hasHole() { result = true; } if (!result) { - result = this.hasHoleWithAttribute(attribute -> true); + result = this.hasHoleWithAttribute(attribute -> true, false); } return result; } @@ -140,7 +140,10 @@ public boolean hasHole() { * @return Checking result, {@code true} if the descriptor has a typed hole */ public boolean hasTypedHole() { - return this.hasHoleWithAttribute(attribute -> attribute == HoleAttribute.TYPED); + return this.hasHoleWithAttribute( + attribute -> attribute == HoleAttribute.TYPED, + false + ); } /** @@ -149,7 +152,19 @@ public boolean hasTypedHole() { */ public boolean hasEllipsisOrTypedHole() { return this.hasHoleWithAttribute( - attribute -> attribute == HoleAttribute.ELLIPSIS || attribute == HoleAttribute.TYPED + attribute -> attribute == HoleAttribute.ELLIPSIS || attribute == HoleAttribute.TYPED, + false + ); + } + + /** + * Checks whether the descriptor has only holes with ellipsis or a node type. + * @return Checking result, {@code true} if the descriptor has a hole with ellipsis + */ + public boolean hasOnlyEllipsisOrTypedHole() { + return this.hasHoleWithAttribute( + attribute -> attribute == HoleAttribute.ELLIPSIS || attribute == HoleAttribute.TYPED, + true ); } @@ -209,17 +224,33 @@ private void parametersToString(final StringBuilder builder) { /** * Checks whether the descriptor has a hole with specified attribute. * @param checker Checker that checks the attribute matches some criteria + * @param exceptional The checker returns {@code true} only if the parameter list contains + * exclusively holes with these attributes * @return Checking result, {@code true} if the descriptor has a hole with specified attribute */ - private boolean hasHoleWithAttribute(final AttributeChecker checker) { + private boolean hasHoleWithAttribute( + final AttributeChecker checker, + final boolean exceptional) { boolean result = false; final List parameters = this.getParameters(); final ListIterator iterator = parameters.listIterator(parameters.size()); - while (iterator.hasPrevious()) { - final Parameter parameter = iterator.previous(); - if (parameter instanceof Hole && checker.check(((Hole) parameter).getAttribute())) { - result = true; - break; + if (exceptional) { + while (iterator.hasPrevious()) { + final Parameter parameter = iterator.previous(); + if (parameter instanceof Hole && checker.check(((Hole) parameter).getAttribute())) { + result = true; + } else { + result = false; + break; + } + } + } else { + while (iterator.hasPrevious()) { + final Parameter parameter = iterator.previous(); + if (parameter instanceof Hole && checker.check(((Hole) parameter).getAttribute())) { + result = true; + break; + } } } return result; diff --git a/src/test/java/org/cqfn/astranaut/codegen/java/MatcherGeneratorTest.java b/src/test/java/org/cqfn/astranaut/codegen/java/MatcherGeneratorTest.java index d7031c0..acba9c7 100644 --- a/src/test/java/org/cqfn/astranaut/codegen/java/MatcherGeneratorTest.java +++ b/src/test/java/org/cqfn/astranaut/codegen/java/MatcherGeneratorTest.java @@ -143,6 +143,18 @@ void testUnknownNumberOfChildNodesWithSameType() { Assertions.assertTrue(result > 0); } + /** + * Test case: node contains only typed holes. + */ + @Test + void testNodeContainsOnlyTypedHoles() { + final int result = this.testing( + "AAA(BBB#1)", + "matcher_generator_only_holes_with_type.txt" + ); + Assertions.assertTrue(result > 0); + } + /** * Performs a test. * @param code Source code of descriptor diff --git a/src/test/resources/codegen/java/matcher_generator_only_holes_with_type.txt b/src/test/resources/codegen/java/matcher_generator_only_holes_with_type.txt new file mode 100644 index 0000000..6d5493c --- /dev/null +++ b/src/test/resources/codegen/java/matcher_generator_only_holes_with_type.txt @@ -0,0 +1,86 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Ivan Kniazkov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.uast; + +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import org.cqfn.astranaut.core.Matcher; +import org.cqfn.astranaut.core.Node; + +/** + * Checks if the node matches some structure, and extracts the data and children. + * + * @since 1.0 + */ +public final class Matcher0 implements Matcher { + /** + * The instance. + */ + public static final Matcher INSTANCE = new Matcher0(); + + /** + * Expected node type. + */ + private static final String EXPECTED_TYPE = "AAA"; + + /** + * The number of the first hole. + */ + private static final int FIRST_HOLE_ID = 1; + + /** + * The type of the first hole. + */ + private static final String FIRST_HOLE_TYPE = "BBB"; + + /** + * Constructor. + */ + private Matcher0() { + } + + @Override + public boolean match(final Node node, + final Map> children, + final Map data) { + final boolean result = node.belongsToGroup(Matcher0.EXPECTED_TYPE); + final LinkedList batch = new LinkedList<>(node.getChildrenList()); + if (result) { + final List list = new LinkedList<>(); + while (!batch.isEmpty()) { + final Node child = batch.pollFirst(); + if (Matcher0.FIRST_HOLE_TYPE.equals(child.getTypeName())) { + list.add(child); + } else { + batch.addFirst(child); + break; + } + } + children.put(Matcher0.FIRST_HOLE_ID, list); + } + return result; + } +} From 93718eb06f49fbab92a91ed34f14befeea57c1e2 Mon Sep 17 00:00:00 2001 From: Ivan Kniazkov Date: Thu, 13 Oct 2022 09:39:31 +0300 Subject: [PATCH 2/3] Improved test --- .../astranaut/codegen/java/MatcherGeneratorTest.java | 4 ++-- .../java/matcher_generator_only_holes_with_type.txt | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/cqfn/astranaut/codegen/java/MatcherGeneratorTest.java b/src/test/java/org/cqfn/astranaut/codegen/java/MatcherGeneratorTest.java index acba9c7..776e0a8 100644 --- a/src/test/java/org/cqfn/astranaut/codegen/java/MatcherGeneratorTest.java +++ b/src/test/java/org/cqfn/astranaut/codegen/java/MatcherGeneratorTest.java @@ -147,9 +147,9 @@ void testUnknownNumberOfChildNodesWithSameType() { * Test case: node contains only typed holes. */ @Test - void testNodeContainsOnlyTypedHoles() { + void testNodeContainsOnlyEllipsisAndTypedHoles() { final int result = this.testing( - "AAA(BBB#1)", + "AAA(BBB#1, #2...)", "matcher_generator_only_holes_with_type.txt" ); Assertions.assertTrue(result > 0); diff --git a/src/test/resources/codegen/java/matcher_generator_only_holes_with_type.txt b/src/test/resources/codegen/java/matcher_generator_only_holes_with_type.txt index 6d5493c..8023f6b 100644 --- a/src/test/resources/codegen/java/matcher_generator_only_holes_with_type.txt +++ b/src/test/resources/codegen/java/matcher_generator_only_holes_with_type.txt @@ -56,6 +56,11 @@ public final class Matcher0 implements Matcher { */ private static final String FIRST_HOLE_TYPE = "BBB"; + /** + * The number of the second hole. + */ + private static final int SECOND_HOLE_ID = 2; + /** * Constructor. */ @@ -81,6 +86,13 @@ public final class Matcher0 implements Matcher { } children.put(Matcher0.FIRST_HOLE_ID, list); } + if (result) { + final List list = new LinkedList<>(); + while (!batch.isEmpty()) { + list.add(batch.pollFirst()); + } + children.put(Matcher0.SECOND_HOLE_ID, list); + } return result; } } From 9d2288045dc6a246a9e227872e9574df5a8580be Mon Sep 17 00:00:00 2001 From: Ivan Kniazkov Date: Thu, 13 Oct 2022 10:49:56 +0300 Subject: [PATCH 3/3] Remove dead code --- .../codegen/java/MatcherClassFiller.java | 33 +++---------------- 1 file changed, 4 insertions(+), 29 deletions(-) diff --git a/src/main/java/org/cqfn/astranaut/codegen/java/MatcherClassFiller.java b/src/main/java/org/cqfn/astranaut/codegen/java/MatcherClassFiller.java index c867415..2a06c9e 100644 --- a/src/main/java/org/cqfn/astranaut/codegen/java/MatcherClassFiller.java +++ b/src/main/java/org/cqfn/astranaut/codegen/java/MatcherClassFiller.java @@ -496,14 +496,16 @@ private String formatHoleExtractor(final Hole hole, final int index) { destination, hole.getValue() ); - if (hole.getAttribute() == HoleAttribute.ELLIPSIS && index == 0) { + final HoleAttribute attribute = hole.getAttribute(); + assert attribute != HoleAttribute.TYPED; + if (attribute == HoleAttribute.ELLIPSIS && index == 0) { result = String.format( "children.put(%s.%s, node.getChildrenList());\n", this.klass.getName(), destination ); - } else if (hole.getAttribute() == HoleAttribute.ELLIPSIS) { + } else if (attribute == HoleAttribute.ELLIPSIS) { this.alist = true; final List code = Arrays.asList( "final int count = node.getChildCount();", @@ -521,33 +523,6 @@ private String formatHoleExtractor(final Hole hole, final int index) { ) ); result = String.join("\n", code); - } else if (hole.getAttribute() == HoleAttribute.TYPED) { - this.alist = true; - final String capacity; - if (index == 0) { - capacity = "count"; - } else { - capacity = String.format("count - %d", index); - } - final List code = Arrays.asList( - "final int count = node.getChildCount();", - String.format("final List list = new ArrayList<>(%s);", capacity), - String.format( - "for (int index = %d; index < count; index = index + 1) {", - index - ), - "final Node child = node.getChild(index);", - String.format("if (\"%s\".equals(child.getTypeName())) {", hole.getType()), - "list.add(child);", - "}", - "}", - String.format( - "children.put(%s.%s, list);\n", - this.klass.getName(), - destination - ) - ); - result = String.join("\n", code); } else { this.collections = true; final String srclbl = this.children.getLabel();