diff --git a/iac-extensions/arm/src/main/java/org/sonar/iac/arm/parser/bicep/BicepGrammar.java b/iac-extensions/arm/src/main/java/org/sonar/iac/arm/parser/bicep/BicepGrammar.java index 4f60d7df95..5b4855d530 100644 --- a/iac-extensions/arm/src/main/java/org/sonar/iac/arm/parser/bicep/BicepGrammar.java +++ b/iac-extensions/arm/src/main/java/org/sonar/iac/arm/parser/bicep/BicepGrammar.java @@ -37,6 +37,7 @@ import org.sonar.iac.arm.tree.api.Variable; import org.sonar.iac.arm.tree.api.VariableDeclaration; import org.sonar.iac.arm.tree.api.bicep.AmbientTypeReference; +import org.sonar.iac.arm.tree.api.bicep.ArrayTypeSuffix; import org.sonar.iac.arm.tree.api.bicep.CompileTimeImportDeclaration; import org.sonar.iac.arm.tree.api.bicep.Decorator; import org.sonar.iac.arm.tree.api.bicep.ForExpression; @@ -61,6 +62,7 @@ import org.sonar.iac.arm.tree.api.bicep.TupleType; import org.sonar.iac.arm.tree.api.bicep.TypeDeclaration; import org.sonar.iac.arm.tree.api.bicep.TypeExpressionAble; +import org.sonar.iac.arm.tree.api.bicep.TypeReferenceSuffix; import org.sonar.iac.arm.tree.api.bicep.TypedLambdaExpression; import org.sonar.iac.arm.tree.api.bicep.UnaryOperator; import org.sonar.iac.arm.tree.api.bicep.expression.UnaryExpression; @@ -427,13 +429,35 @@ public SingularTypeExpression SINGULAR_TYPE_EXPRESSION() { b.token(Punctuator.QUERY))))); } + public TypeExpressionAble TYPE_REFERENCE() { + return b.nonterminal(BicepLexicalGrammar.TYPE_REFERENCE).is( + f.typeReference( + b.firstOf( + AMBIENT_TYPE_REFERENCE(), + IDENTIFIER()), + b.zeroOrMore(TYPE_REFERENCE_SUFFIX()))); + } + + public TypeReferenceSuffix TYPE_REFERENCE_SUFFIX() { + return b.nonterminal(BicepLexicalGrammar.TYPE_REFERENCE_SUFFIX).is( + b.firstOf( + ARRAY_TYPE_SUFFIX())); + } + + public ArrayTypeSuffix ARRAY_TYPE_SUFFIX() { + return b.nonterminal(BicepLexicalGrammar.ARRAY_TYPE_SUFFIX).is( + f.arrayTypeSuffix( + b.token(Punctuator.LBRACKET), + b.optional(NUMERIC_LITERAL()), + b.token(Punctuator.RBRACKET))); + } + public TypeExpressionAble PRIMARY_TYPE_EXPRESSION() { return b.nonterminal(BicepLexicalGrammar.PRIMARY_TYPE_EXPRESSION).is( b.firstOf( - AMBIENT_TYPE_REFERENCE(), - // The literal value needs to be before identifier + // The literal value needs to be before identifier, which is in type reference LITERAL_VALUE_AS_TYPE_EXPRESSION_ABLE(), - IDENTIFIER(), + TYPE_REFERENCE(), UNARY_OPERATOR_LITERAL_VALUE(), MULTILINE_STRING(), STRING_LITERAL(), diff --git a/iac-extensions/arm/src/main/java/org/sonar/iac/arm/parser/bicep/BicepLexicalConstant.java b/iac-extensions/arm/src/main/java/org/sonar/iac/arm/parser/bicep/BicepLexicalConstant.java index 6b5f7c2042..c35deaa951 100644 --- a/iac-extensions/arm/src/main/java/org/sonar/iac/arm/parser/bicep/BicepLexicalConstant.java +++ b/iac-extensions/arm/src/main/java/org/sonar/iac/arm/parser/bicep/BicepLexicalConstant.java @@ -40,7 +40,7 @@ public class BicepLexicalConstant { public static final String NULL = "null"; public static final String SINGLE_QUOTED_STRING_CONTENT = "(?:(?!\\$\\{)(?:\\\\[\\S]|[^']))*+"; public static final String IDENTIFIER_LITERAL = "[a-zA-Z_][a-zA-Z_0-9]*+"; - public static final String AMBIENT_TYPE = "(?:array|bool|int|object|string)(?=\\s|\\)|,|\\z)"; + public static final String AMBIENT_TYPE = "(?:array|bool|int|object|string)(?=\\s|\\)|,|\\[|\\z)"; public static final String UNARY_OPERATOR = "!|-|\\+"; public static final String MULTILINE_STRING = "(?:(?!''')(?:[\\s\\S])?+)*+"; public static final String EXCLAMATION_SIGN_ALONE = "!(?!=)"; diff --git a/iac-extensions/arm/src/main/java/org/sonar/iac/arm/parser/bicep/BicepLexicalGrammar.java b/iac-extensions/arm/src/main/java/org/sonar/iac/arm/parser/bicep/BicepLexicalGrammar.java index aae29033d6..488206ec90 100644 --- a/iac-extensions/arm/src/main/java/org/sonar/iac/arm/parser/bicep/BicepLexicalGrammar.java +++ b/iac-extensions/arm/src/main/java/org/sonar/iac/arm/parser/bicep/BicepLexicalGrammar.java @@ -75,7 +75,10 @@ public enum BicepLexicalGrammar implements GrammarRuleKey { PARENTHESIZED_TYPE_EXPRESSION, OBJECT_TYPE, OBJECT_TYPE_PROPERTY, + TYPE_REFERENCE, + TYPE_REFERENCE_SUFFIX, AMBIENT_TYPE_REFERENCE, + ARRAY_TYPE_SUFFIX, IF_CONDITION, EXPRESSION, BINARY_EXPRESSION, diff --git a/iac-extensions/arm/src/main/java/org/sonar/iac/arm/parser/bicep/TreeFactory.java b/iac-extensions/arm/src/main/java/org/sonar/iac/arm/parser/bicep/TreeFactory.java index 8734500ec0..a0c8d5c3f6 100644 --- a/iac-extensions/arm/src/main/java/org/sonar/iac/arm/parser/bicep/TreeFactory.java +++ b/iac-extensions/arm/src/main/java/org/sonar/iac/arm/parser/bicep/TreeFactory.java @@ -20,6 +20,7 @@ package org.sonar.iac.arm.parser.bicep; import com.sonar.sslr.api.typed.Optional; +import java.util.Collections; import java.util.List; import org.sonar.iac.arm.tree.api.ArmTree; import org.sonar.iac.arm.tree.api.ArrayExpression; @@ -40,6 +41,7 @@ import org.sonar.iac.arm.tree.api.Variable; import org.sonar.iac.arm.tree.api.VariableDeclaration; import org.sonar.iac.arm.tree.api.bicep.AmbientTypeReference; +import org.sonar.iac.arm.tree.api.bicep.ArrayTypeSuffix; import org.sonar.iac.arm.tree.api.bicep.CompileTimeImportDeclaration; import org.sonar.iac.arm.tree.api.bicep.Decorator; import org.sonar.iac.arm.tree.api.bicep.ForExpression; @@ -64,6 +66,7 @@ import org.sonar.iac.arm.tree.api.bicep.TupleType; import org.sonar.iac.arm.tree.api.bicep.TypeDeclaration; import org.sonar.iac.arm.tree.api.bicep.TypeExpressionAble; +import org.sonar.iac.arm.tree.api.bicep.TypeReferenceSuffix; import org.sonar.iac.arm.tree.api.bicep.TypedLambdaExpression; import org.sonar.iac.arm.tree.api.bicep.UnaryOperator; import org.sonar.iac.arm.tree.api.bicep.expression.AdditiveExpression; @@ -90,6 +93,7 @@ import org.sonar.iac.arm.tree.impl.VariableImpl; import org.sonar.iac.arm.tree.impl.bicep.AmbientTypeReferenceImpl; import org.sonar.iac.arm.tree.impl.bicep.ArrayExpressionImpl; +import org.sonar.iac.arm.tree.impl.bicep.ArrayTypeSuffixImpl; import org.sonar.iac.arm.tree.impl.bicep.BooleanLiteralImpl; import org.sonar.iac.arm.tree.impl.bicep.CompileTimeImportDeclarationImpl; import org.sonar.iac.arm.tree.impl.bicep.DecoratorImpl; @@ -476,6 +480,18 @@ public AmbientTypeReference ambientTypeReference(SyntaxToken token) { return new AmbientTypeReferenceImpl(token); } + public TypeExpressionAble typeReference(TypeExpressionAble type, Optional> optionalSuffixes) { + List suffixes = optionalSuffixes.or(Collections.emptyList()); + for (TypeReferenceSuffix suffix : suffixes) { + type = suffix.applyTo(type); + } + return type; + } + + public ArrayTypeSuffix arrayTypeSuffix(SyntaxToken lBracket, Optional length, SyntaxToken rBracket) { + return new ArrayTypeSuffixImpl(lBracket, length.orNull(), rBracket); + } + public UnaryOperator unaryOperator(SyntaxToken token) { return new UnaryOperatorImpl(token); } diff --git a/iac-extensions/arm/src/main/java/org/sonar/iac/arm/tree/api/ArmTree.java b/iac-extensions/arm/src/main/java/org/sonar/iac/arm/tree/api/ArmTree.java index 5e639d59e6..865dad6dec 100644 --- a/iac-extensions/arm/src/main/java/org/sonar/iac/arm/tree/api/ArmTree.java +++ b/iac-extensions/arm/src/main/java/org/sonar/iac/arm/tree/api/ArmTree.java @@ -21,6 +21,7 @@ import javax.annotation.CheckForNull; import org.sonar.iac.arm.tree.api.bicep.AmbientTypeReference; +import org.sonar.iac.arm.tree.api.bicep.ArrayTypeReference; import org.sonar.iac.arm.tree.api.bicep.CompileTimeImportDeclaration; import org.sonar.iac.arm.tree.api.bicep.Decorator; import org.sonar.iac.arm.tree.api.bicep.ForExpression; @@ -126,6 +127,7 @@ enum Kind implements GrammarRuleKey { TERNARY_EXPRESSION(TernaryExpression.class), PARENTHESIZED_EXPRESSION(ParenthesizedExpression.class), AMBIENT_TYPE_REFERENCE(AmbientTypeReference.class), + ARRAY_TYPE_REFERENCE(ArrayTypeReference.class), UNARY_OPERATOR(UnaryOperator.class), TUPLE_TYPE(TupleType.class), TUPLE_ITEM(TupleItem.class), diff --git a/iac-extensions/arm/src/main/java/org/sonar/iac/arm/tree/api/bicep/ArrayTypeReference.java b/iac-extensions/arm/src/main/java/org/sonar/iac/arm/tree/api/bicep/ArrayTypeReference.java new file mode 100644 index 0000000000..9628638cb9 --- /dev/null +++ b/iac-extensions/arm/src/main/java/org/sonar/iac/arm/tree/api/bicep/ArrayTypeReference.java @@ -0,0 +1,33 @@ +/* + * SonarQube IaC Plugin + * Copyright (C) 2021-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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 3 of the License, or (at your option) any later version. + * + * This program 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 program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.iac.arm.tree.api.bicep; + +import javax.annotation.CheckForNull; +import org.sonar.iac.arm.tree.api.NumericLiteral; + +/** + * Marker interface for array types (e.g. {@code string[]}) + */ +public interface ArrayTypeReference extends TypeExpressionAble { + TypeExpressionAble getType(); + + @CheckForNull + NumericLiteral getLength(); +} diff --git a/iac-extensions/arm/src/main/java/org/sonar/iac/arm/tree/api/bicep/ArrayTypeSuffix.java b/iac-extensions/arm/src/main/java/org/sonar/iac/arm/tree/api/bicep/ArrayTypeSuffix.java new file mode 100644 index 0000000000..191f260442 --- /dev/null +++ b/iac-extensions/arm/src/main/java/org/sonar/iac/arm/tree/api/bicep/ArrayTypeSuffix.java @@ -0,0 +1,26 @@ +/* + * SonarQube IaC Plugin + * Copyright (C) 2021-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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 3 of the License, or (at your option) any later version. + * + * This program 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 program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.iac.arm.tree.api.bicep; + +/** + * Interface for array type suffix that build into {@link ArrayTypeReference}. + */ +public interface ArrayTypeSuffix extends TypeReferenceSuffix { +} diff --git a/iac-extensions/arm/src/main/java/org/sonar/iac/arm/tree/api/bicep/TypeReferenceSuffix.java b/iac-extensions/arm/src/main/java/org/sonar/iac/arm/tree/api/bicep/TypeReferenceSuffix.java new file mode 100644 index 0000000000..798f3b2fde --- /dev/null +++ b/iac-extensions/arm/src/main/java/org/sonar/iac/arm/tree/api/bicep/TypeReferenceSuffix.java @@ -0,0 +1,34 @@ +/* + * SonarQube IaC Plugin + * Copyright (C) 2021-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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 3 of the License, or (at your option) any later version. + * + * This program 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 program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.iac.arm.tree.api.bicep; + +/** + * Interface for type suffix that get applied to {@link TypeExpressionAble} types. + */ +public interface TypeReferenceSuffix { + + /** + * Apply the suffix to another type. + * + * @param baseType the base type to which the suffix will be applied + * @return the base type with the suffix + */ + TypeExpressionAble applyTo(TypeExpressionAble baseType); +} diff --git a/iac-extensions/arm/src/main/java/org/sonar/iac/arm/tree/impl/bicep/AmbientTypeReferenceImpl.java b/iac-extensions/arm/src/main/java/org/sonar/iac/arm/tree/impl/bicep/AmbientTypeReferenceImpl.java index 584aa9b115..cdc9e89319 100644 --- a/iac-extensions/arm/src/main/java/org/sonar/iac/arm/tree/impl/bicep/AmbientTypeReferenceImpl.java +++ b/iac-extensions/arm/src/main/java/org/sonar/iac/arm/tree/impl/bicep/AmbientTypeReferenceImpl.java @@ -46,4 +46,9 @@ public List children() { public Kind getKind() { return Kind.AMBIENT_TYPE_REFERENCE; } + + @Override + public String toString() { + return token.toString(); + } } diff --git a/iac-extensions/arm/src/main/java/org/sonar/iac/arm/tree/impl/bicep/ArrayTypeReferenceImpl.java b/iac-extensions/arm/src/main/java/org/sonar/iac/arm/tree/impl/bicep/ArrayTypeReferenceImpl.java new file mode 100644 index 0000000000..a71f158c4a --- /dev/null +++ b/iac-extensions/arm/src/main/java/org/sonar/iac/arm/tree/impl/bicep/ArrayTypeReferenceImpl.java @@ -0,0 +1,84 @@ +/* + * SonarQube IaC Plugin + * Copyright (C) 2021-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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 3 of the License, or (at your option) any later version. + * + * This program 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 program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.iac.arm.tree.impl.bicep; + +import java.util.ArrayList; +import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.sonar.iac.arm.tree.api.NumericLiteral; +import org.sonar.iac.arm.tree.api.bicep.ArrayTypeReference; +import org.sonar.iac.arm.tree.api.bicep.SyntaxToken; +import org.sonar.iac.arm.tree.api.bicep.TypeExpressionAble; +import org.sonar.iac.arm.tree.impl.AbstractArmTreeImpl; +import org.sonar.iac.common.api.tree.Tree; + +public class ArrayTypeReferenceImpl extends AbstractArmTreeImpl implements ArrayTypeReference { + private final TypeExpressionAble type; + private final SyntaxToken lBracket; + @CheckForNull + private final NumericLiteral length; + private final SyntaxToken rBracket; + + public ArrayTypeReferenceImpl(TypeExpressionAble type, SyntaxToken lBracket, @Nullable NumericLiteral length, SyntaxToken rBracket) { + this.type = type; + this.lBracket = lBracket; + this.length = length; + this.rBracket = rBracket; + } + + @Override + public TypeExpressionAble getType() { + return type; + } + + @CheckForNull + @Override + public NumericLiteral getLength() { + return length; + } + + @Override + public List children() { + List children = new ArrayList<>(type.children()); + children.add(lBracket); + if (length != null) { + children.add(length); + } + children.add(rBracket); + return children; + } + + @Override + public Kind getKind() { + return Kind.ARRAY_TYPE_REFERENCE; + } + + @Override + public String toString() { + var result = type.toString(); + result += lBracket.toString(); + if (length != null) { + result += length.toString(); + } + result += rBracket.toString(); + return result; + } +} diff --git a/iac-extensions/arm/src/main/java/org/sonar/iac/arm/tree/impl/bicep/ArrayTypeSuffixImpl.java b/iac-extensions/arm/src/main/java/org/sonar/iac/arm/tree/impl/bicep/ArrayTypeSuffixImpl.java new file mode 100644 index 0000000000..43ae9f28aa --- /dev/null +++ b/iac-extensions/arm/src/main/java/org/sonar/iac/arm/tree/impl/bicep/ArrayTypeSuffixImpl.java @@ -0,0 +1,45 @@ +/* + * SonarQube IaC Plugin + * Copyright (C) 2021-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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 3 of the License, or (at your option) any later version. + * + * This program 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 program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.iac.arm.tree.impl.bicep; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.sonar.iac.arm.tree.api.NumericLiteral; +import org.sonar.iac.arm.tree.api.bicep.ArrayTypeSuffix; +import org.sonar.iac.arm.tree.api.bicep.SyntaxToken; +import org.sonar.iac.arm.tree.api.bicep.TypeExpressionAble; + +public class ArrayTypeSuffixImpl implements ArrayTypeSuffix { + private final SyntaxToken lBracket; + @CheckForNull + private final NumericLiteral length; + private final SyntaxToken rBracket; + + public ArrayTypeSuffixImpl(SyntaxToken lBracket, @Nullable NumericLiteral length, SyntaxToken rBracket) { + this.lBracket = lBracket; + this.length = length; + this.rBracket = rBracket; + } + + @Override + public TypeExpressionAble applyTo(TypeExpressionAble baseType) { + return new ArrayTypeReferenceImpl(baseType, lBracket, length, rBracket); + } +} diff --git a/iac-extensions/arm/src/test/java/org/sonar/iac/arm/tree/impl/bicep/ArrayTypeReferenceImplTest.java b/iac-extensions/arm/src/test/java/org/sonar/iac/arm/tree/impl/bicep/ArrayTypeReferenceImplTest.java new file mode 100644 index 0000000000..7bc6b13f1b --- /dev/null +++ b/iac-extensions/arm/src/test/java/org/sonar/iac/arm/tree/impl/bicep/ArrayTypeReferenceImplTest.java @@ -0,0 +1,103 @@ +/* + * SonarQube IaC Plugin + * Copyright (C) 2021-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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 3 of the License, or (at your option) any later version. + * + * This program 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 program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.iac.arm.tree.impl.bicep; + +import org.junit.jupiter.api.Test; +import org.sonar.iac.arm.parser.bicep.BicepLexicalGrammar; +import org.sonar.iac.arm.tree.api.ArmTree; +import org.sonar.iac.arm.tree.api.Identifier; +import org.sonar.iac.arm.tree.api.bicep.AmbientTypeReference; +import org.sonar.iac.arm.tree.api.bicep.ArrayTypeReference; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.iac.arm.ArmTestUtils.recursiveTransformationOfTreeChildrenToStrings; + +class ArrayTypeReferenceImplTest extends BicepTreeModelTest { + + @Test + void shouldParseArrayType() { + String code = "string[3]"; + ArrayTypeReference tree = parse(code, BicepLexicalGrammar.TYPE_REFERENCE); + assertThat(tree.is(ArmTree.Kind.ARRAY_TYPE_REFERENCE)).isTrue(); + assertThat(tree.getType().is(ArmTree.Kind.AMBIENT_TYPE_REFERENCE)).isTrue(); + assertThat(((AmbientTypeReference) tree.getType()).value()).isEqualTo("string"); + assertThat(tree.getLength()).isNotNull(); + assertThat(tree.getLength().is(ArmTree.Kind.NUMERIC_LITERAL)).isTrue(); + assertThat(tree.getLength().value()).isEqualTo("3"); + assertThat(recursiveTransformationOfTreeChildrenToStrings(tree)).containsExactly("string", "[", "3", "]"); + } + + @Test + void shouldParseArrayTypeWithoutLength() { + String code = "bool[]"; + ArrayTypeReference tree = parse(code, BicepLexicalGrammar.TYPE_REFERENCE); + assertThat(tree.is(ArmTree.Kind.ARRAY_TYPE_REFERENCE)).isTrue(); + assertThat(tree.getType().is(ArmTree.Kind.AMBIENT_TYPE_REFERENCE)).isTrue(); + assertThat(((AmbientTypeReference) tree.getType()).value()).isEqualTo("bool"); + assertThat(tree.getLength()).isNull(); + assertThat(recursiveTransformationOfTreeChildrenToStrings(tree)).containsExactly("bool", "[", "]"); + } + + @Test + void shouldParseMultiDimensionalArrayType() { + String code = "int[3][5][7]"; + ArrayTypeReference tree = parse(code, BicepLexicalGrammar.TYPE_REFERENCE); + + assertThat(tree.is(ArmTree.Kind.ARRAY_TYPE_REFERENCE)).isTrue(); + assertThat(recursiveTransformationOfTreeChildrenToStrings(tree)) + .containsExactly("int", "[", "3", "]", "[", "5", "]", "[", "7", "]"); + + ArrayTypeReference innerType1 = (ArrayTypeReference) tree.getType(); + assertThat(recursiveTransformationOfTreeChildrenToStrings(innerType1)) + .containsExactly("int", "[", "3", "]", "[", "5", "]"); + + ArrayTypeReference innerType2 = (ArrayTypeReference) innerType1.getType(); + assertThat(recursiveTransformationOfTreeChildrenToStrings(innerType2)) + .containsExactly("int", "[", "3", "]"); + + AmbientTypeReference innerType3 = (AmbientTypeReference) innerType2.getType(); + assertThat(recursiveTransformationOfTreeChildrenToStrings(innerType3)) + .containsExactly("int"); + } + + @Test + void shouldParseMultiDimensionalArrayTypeWithMixedLength() { + String code = "object[][15][0]"; + ArrayTypeReference tree = parse(code, BicepLexicalGrammar.TYPE_REFERENCE); + assertThat(tree.is(ArmTree.Kind.ARRAY_TYPE_REFERENCE)).isTrue(); + assertThat(recursiveTransformationOfTreeChildrenToStrings(tree)).containsExactly("object", "[", "]", "[", "15", "]", "[", "0", "]"); + } + + @Test + void shouldParseArrayTypeWithIdentifier() { + String code = "typeIdentifier[3]"; + ArrayTypeReference tree = parse(code, BicepLexicalGrammar.TYPE_REFERENCE); + assertThat(tree.is(ArmTree.Kind.ARRAY_TYPE_REFERENCE)).isTrue(); + assertThat(tree.getType().is(ArmTree.Kind.IDENTIFIER)).isTrue(); + assertThat(((Identifier) tree.getType()).value()).isEqualTo("typeIdentifier"); + assertThat(recursiveTransformationOfTreeChildrenToStrings(tree)).containsExactly("typeIdentifier", "[", "3", "]"); + } + + @Test + void shouldConvertToString() { + ArrayTypeReference tree = parse("string[5][15][]", BicepLexicalGrammar.TYPE_REFERENCE); + assertThat(tree).hasToString("string[5][15][]"); + } +} diff --git a/iac-extensions/arm/src/test/java/org/sonar/iac/arm/tree/impl/bicep/FunctionDeclarationImplTest.java b/iac-extensions/arm/src/test/java/org/sonar/iac/arm/tree/impl/bicep/FunctionDeclarationImplTest.java index 738bd536a4..e307017c52 100644 --- a/iac-extensions/arm/src/test/java/org/sonar/iac/arm/tree/impl/bicep/FunctionDeclarationImplTest.java +++ b/iac-extensions/arm/src/test/java/org/sonar/iac/arm/tree/impl/bicep/FunctionDeclarationImplTest.java @@ -27,7 +27,6 @@ import org.sonar.iac.arm.tree.api.bicep.FunctionDeclaration; import static org.assertj.core.api.Assertions.assertThat; -import static org.sonar.iac.common.testing.IacTestUtils.code; class FunctionDeclarationImplTest extends BicepTreeModelTest { @@ -38,6 +37,7 @@ void shouldParseFunctionDeclaration() { .matches("func myFunction () string => 'result'") .matches("func myFunction(foo int) string => '${foo}'") .matches("func myFunction(foo int, bar object) int => 0") + .matches("func typedArg(input string[]) int => length(input)") // defining a function of name the same as keyword is possible .matches("func func() string => 'result'") .matches("func if() string => 'result'") @@ -45,10 +45,10 @@ void shouldParseFunctionDeclaration() { .matches("func param() string => 'result'") .matches("@description('comment') func myFunction(foo int, bar object) int => 0") .matches("@sys.description('comment') func myFunction(foo int, bar object) int => 0") - .matches(code( - "@description('comment')", - "@allowed([42])", - "func myFunction(foo int, bar object) int => 0")) + .matches(""" + @description('comment') + @allowed([42]) + func myFunction(foo int, bar object) int => 0""") .notMatches("func myFunction() => 'result'") .notMatches("func myFunction") @@ -58,7 +58,7 @@ void shouldParseFunctionDeclaration() { @Test void shouldParseSimpleFunctionDeclaration() { - String code = code("@description('comment') func myFunction() string => 'result'"); + String code = "@description('comment') func myFunction() string => 'result'"; FunctionDeclaration tree = parse(code, BicepLexicalGrammar.FUNCTION_DECLARATION); assertThat(tree.is(ArmTree.Kind.FUNCTION_DECLARATION)).isTrue(); assertThat(tree.lambdaExpression().is(ArmTree.Kind.TYPED_LAMBDA_EXPRESSION)).isTrue(); diff --git a/iac-extensions/arm/src/test/java/org/sonar/iac/arm/tree/impl/bicep/PrimaryTypeExpressionTest.java b/iac-extensions/arm/src/test/java/org/sonar/iac/arm/tree/impl/bicep/PrimaryTypeExpressionTest.java index 66a5d2b553..9fe5b59d45 100644 --- a/iac-extensions/arm/src/test/java/org/sonar/iac/arm/tree/impl/bicep/PrimaryTypeExpressionTest.java +++ b/iac-extensions/arm/src/test/java/org/sonar/iac/arm/tree/impl/bicep/PrimaryTypeExpressionTest.java @@ -38,13 +38,12 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.sonar.iac.arm.ArmTestUtils.recursiveTransformationOfTreeChildrenToStrings; -import static org.sonar.iac.common.testing.IacTestUtils.code; class PrimaryTypeExpressionTest extends BicepTreeModelTest { @Test void shouldParseUnaryExpression() { ArmAssertions.assertThat(BicepLexicalGrammar.PRIMARY_TYPE_EXPRESSION) - // ambient Type Reference + // ambient type reference .matches("array") .matches(" array") .matches("bool") @@ -55,6 +54,21 @@ void shouldParseUnaryExpression() { .matches(" object") .matches("string") .matches(" string") + // array type reference + .matches("array[]") + .matches("bool[]") + .matches("int[]") + .matches("object[]") + .matches("string[]") + .matches(" string[]") + .matches("abc[]") + .matches("string[0]") + .matches("string[3]") + .matches("string[][]") + .matches("string[][3]") + .matches("string[3][]") + .matches("string[3][7]") + .matches("string[3][7][10]") // identifier .matches("abc") .matches("A") @@ -100,29 +114,35 @@ void shouldParseUnaryExpression() { .matches("''''''") .matches("'''python main.py'''") .matches("'''python main.py --abc ${{input.abc}} --def ${xyz}'''") - .matches(code("'''", - "first line", - "second line", - "'''")) - .matches(code("'''", - "first line", - "// inline comment", - "'''")) - .matches(code("'''", - "first line", - "/* inline comment */", - "'''")) - .matches(code("'''", - "first line", - "/* inline", - "comment */", - "'''")) - .matches(code("'''", - "it's awesome", - "'''")) - .matches(code("'''", - "it''s awesome", - "'''")) + .matches(""" + ''' + first line + second line + '''""") + .matches(""" + ''' + first line + // inline comment + '''""") + .matches(""" + ''' + first line + /* inline comment */ + '''""") + .matches(""" + ''' + first line + /* inline + comment */ + '''""") + .matches(""" + ''' + it's awesome + '''""") + .matches(""" + ''' + it''s awesome + '''""") // object type .matches("{}") .matches("{ }") @@ -179,7 +199,12 @@ void shouldParseUnaryExpression() { .notMatches("identifier = {}") .notMatches("[]typeExpr") .notMatches("[\ntypeExpr") + .notMatches("typeExpr[") .notMatches("typeExpr]") + .notMatches("typeExpr[][") + .notMatches("typeExpr[]]") + .notMatches("typeExpr[[]]") + .notMatches("typeExpr[-3]") .notMatches("{typeExpr}"); } @@ -237,11 +262,12 @@ void shouldParseSimpleStringLiteral() { @Test void shouldParseSimpleMultilineString() { - String code = code("'''", - "a", - "123", - "BBB", - "'''"); + String code = """ + ''' + a + 123 + BBB + '''"""; MultilineString tree = parse(code, BicepLexicalGrammar.PRIMARY_TYPE_EXPRESSION); @@ -265,9 +291,10 @@ void shouldParseSimpleObjectType() { @Test void shouldParseSimpleTupleType() { - String code = code("[", - "@functionName123() typeExpr", - "]"); + String code = """ + [ + @functionName123() typeExpr + ]"""; TupleType tree = parse(code, BicepLexicalGrammar.TUPLE_TYPE); assertThat(tree.is(ArmTree.Kind.TUPLE_TYPE)).isTrue(); Assertions.assertThat(recursiveTransformationOfTreeChildrenToStrings(tree))