From 68591ffd5c3122f54b23dc74f17ce73e52fb01f9 Mon Sep 17 00:00:00 2001 From: Laszlo Kishalmi Date: Sun, 21 Apr 2024 14:40:44 -0700 Subject: [PATCH] HCL: fix possible NPE-s, in List.of() and asString() --- .../hcl/ast/HCLArithmeticOperation.java | 12 ++- .../languages/hcl/ast/HCLAttribute.java | 5 ++ .../modules/languages/hcl/ast/HCLBlock.java | 4 + .../languages/hcl/ast/HCLCollection.java | 12 ++- .../hcl/ast/HCLConditionalOperation.java | 9 ++- .../languages/hcl/ast/HCLContainer.java | 2 + .../modules/languages/hcl/ast/HCLElement.java | 2 +- .../languages/hcl/ast/HCLExpression.java | 9 ++- .../hcl/ast/HCLExpressionFactory.java | 10 ++- .../languages/hcl/ast/HCLForExpression.java | 22 ++++-- .../languages/hcl/ast/HCLFunction.java | 7 +- .../languages/hcl/ast/HCLIdentifier.java | 28 +++++-- .../hcl/ast/HCLResolveOperation.java | 29 ++++++- .../languages/hcl/ast/HCLVariable.java | 2 +- .../languages/hcl/ast/HCLCollectionTest.java | 43 ++++++++++ .../hcl/ast/HCLExpressionTestSupport.java | 49 ++++++++++++ .../hcl/ast/HCLForExpressionTest.java | 78 +++++++++++++++++++ .../languages/hcl/ast/HCLFunctionTest.java | 36 +++++++++ .../languages/hcl/ast/HCLLiteralsTest.java | 2 +- .../languages/hcl/ast/HCLOperationsTest.java | 45 ++++++++++- 20 files changed, 363 insertions(+), 43 deletions(-) create mode 100644 ide/languages.hcl/test/unit/src/org/netbeans/modules/languages/hcl/ast/HCLCollectionTest.java create mode 100644 ide/languages.hcl/test/unit/src/org/netbeans/modules/languages/hcl/ast/HCLExpressionTestSupport.java create mode 100644 ide/languages.hcl/test/unit/src/org/netbeans/modules/languages/hcl/ast/HCLForExpressionTest.java create mode 100644 ide/languages.hcl/test/unit/src/org/netbeans/modules/languages/hcl/ast/HCLFunctionTest.java diff --git a/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLArithmeticOperation.java b/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLArithmeticOperation.java index c8b5468beafb..fcf2b6a248be 100644 --- a/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLArithmeticOperation.java +++ b/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLArithmeticOperation.java @@ -19,6 +19,8 @@ package org.netbeans.modules.languages.hcl.ast; import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; /** * @@ -64,24 +66,26 @@ public String toString() { public record Binary(Operator op, HCLExpression left, HCLExpression right) implements HCLArithmeticOperation { @Override public String asString() { - return left.toString() + op.toString() + right.toString(); + return HCLExpression.asString(left) + op.toString() + HCLExpression.asString(right); } @Override public List elements() { - return List.of(left, right); + return Stream.of(left, right) + .filter(Objects::nonNull) + .toList(); } } public record Unary(Operator op, HCLExpression operand) implements HCLArithmeticOperation { @Override public String asString() { - return op.toString() + operand.toString(); + return op.toString() + HCLExpression.asString(operand); } @Override public List elements() { - return List.of(operand); + return operand != null ? List.of(operand) : List.of(); } } } diff --git a/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLAttribute.java b/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLAttribute.java index 0ad41a869a8f..729eb893ffc2 100644 --- a/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLAttribute.java +++ b/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLAttribute.java @@ -19,6 +19,7 @@ package org.netbeans.modules.languages.hcl.ast; import java.util.List; +import java.util.Objects; /** @@ -27,6 +28,10 @@ */ public record HCLAttribute(HCLIdentifier name, HCLExpression value) implements HCLElement { + public HCLAttribute { + Objects.requireNonNull(name, "name cannot be null"); + } + public String id() { return name.id(); } diff --git a/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLBlock.java b/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLBlock.java index e6478044acdb..a3f8b42072b0 100644 --- a/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLBlock.java +++ b/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLBlock.java @@ -34,6 +34,10 @@ public final class HCLBlock extends HCLContainer { public HCLBlock(List declaration, List elements) { super(elements); + Objects.requireNonNull(declaration, "declaration cannot be null"); + if (declaration.isEmpty()) { + throw new IllegalArgumentException("declaration cannot be empty"); + } this.declaration = List.copyOf(declaration); this.id = declaration.stream().map(d -> d.id()).collect(Collectors.joining(".")); } diff --git a/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLCollection.java b/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLCollection.java index 02acecad405b..15ad667b3d14 100644 --- a/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLCollection.java +++ b/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLCollection.java @@ -19,7 +19,9 @@ package org.netbeans.modules.languages.hcl.ast; import java.util.List; +import java.util.Objects; import java.util.StringJoiner; +import java.util.stream.Stream; /** * @@ -37,7 +39,7 @@ public record Tuple(List elements) implements HCLCollection elements() { - return List.of(key, value); + return Stream.of(key, value) + .filter(Objects::nonNull) + .toList(); } } @@ -66,7 +70,7 @@ public record Object(List elements) implements HCLCollection elements() { - return Arrays.asList(condition, trueValue, falseValue); + return Stream.of(condition, trueValue, falseValue) + .filter(Objects::nonNull) + .toList(); } diff --git a/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLContainer.java b/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLContainer.java index 9b0ca868f5b1..658d9cabb55d 100644 --- a/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLContainer.java +++ b/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLContainer.java @@ -19,6 +19,7 @@ package org.netbeans.modules.languages.hcl.ast; import java.util.List; +import java.util.Objects; /** * @@ -31,6 +32,7 @@ public sealed abstract class HCLContainer implements HCLElement permits HCLBlock private final List attributes; protected HCLContainer(List elements) { + Objects.requireNonNull(elements, "elements can be empty, but cannot be null"); this.elements = List.copyOf(elements); this.blocks = elements.stream().filter(HCLBlock.class::isInstance).map(HCLBlock.class::cast).toList(); this.attributes = elements.stream().filter(HCLAttribute.class::isInstance).map(HCLAttribute.class::cast).toList(); diff --git a/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLElement.java b/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLElement.java index 017efae28b4c..124067b49cbe 100644 --- a/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLElement.java +++ b/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLElement.java @@ -24,6 +24,6 @@ * * @author Laszlo Kishalmi */ -public sealed interface HCLElement permits HCLExpression, HCLIdentifier, HCLContainer, HCLAttribute { +public sealed interface HCLElement permits HCLExpression, HCLContainer, HCLAttribute { List elements(); } diff --git a/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLExpression.java b/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLExpression.java index 344f029dbaa3..9464c9d48907 100644 --- a/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLExpression.java +++ b/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLExpression.java @@ -36,17 +36,18 @@ public sealed interface HCLExpression extends HCLElement permits HCLConditionalOperation, HCLForExpression, HCLFunction, + HCLIdentifier, HCLLiteral, HCLResolveOperation, HCLTemplate, HCLVariable { - public static HCLExpression parse(String expr) { - HCLLexer lexer = new HCLLexer(CharStreams.fromString(expr)); - HCLParser parser = new HCLParser(new CommonTokenStream(lexer)); - return new HCLExpressionFactory().process(parser.expression()); + + public static String asString(HCLExpression expr) { + return expr != null ? expr.asString() : ""; } + @Override default List elements() { return Collections.emptyList(); } diff --git a/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLExpressionFactory.java b/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLExpressionFactory.java index cca7c870261f..235dc6bb00e9 100644 --- a/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLExpressionFactory.java +++ b/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLExpressionFactory.java @@ -18,16 +18,14 @@ */ package org.netbeans.modules.languages.hcl.ast; -import java.util.ArrayList; -import java.util.Collections; import java.util.LinkedList; import java.util.List; +import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import org.antlr.v4.runtime.NoViableAltException; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.tree.ParseTree; -import org.antlr.v4.runtime.tree.TerminalNode; import org.netbeans.modules.languages.hcl.grammar.HCLLexer; import static org.netbeans.modules.languages.hcl.grammar.HCLLexer.*; import org.netbeans.modules.languages.hcl.grammar.HCLParser; @@ -150,7 +148,10 @@ protected HCLExpression expr(HCLParser.CollectionValueContext ctx) throws Unsupp HCLParser.TupleContext tuple = ctx.tuple(); List elements = new LinkedList<>(); for (HCLParser.ExpressionContext ec : tuple.expression()) { - elements.add(expr(ec)); + HCLExpression e = expr(ec); + if (e != null) { + elements.add(created(e, ec)); + } } return new HCLCollection.Tuple(elements); } @@ -184,6 +185,7 @@ protected HCLExpression expr(HCLParser.FunctionCallContext ctx) { if (ctx.arguments() != null) { args = ctx.arguments().expression().stream() .map(this::expr) + .filter(Objects::nonNull) .toList(); expand = ctx.arguments().ELLIPSIS() != null; } diff --git a/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLForExpression.java b/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLForExpression.java index 43266854888e..13c6afaa8aa5 100644 --- a/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLForExpression.java +++ b/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLForExpression.java @@ -19,6 +19,8 @@ package org.netbeans.modules.languages.hcl.ast; import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; /** * @@ -39,10 +41,10 @@ public String asString() { StringBuilder sb = new StringBuilder(); sb.append("[for "); if (keyVar != null) { - sb.append(keyVar).append(','); + sb.append(keyVar.asString()).append(','); } - sb.append(valueVar).append(" in ").append(iterable.asString()).append(':'); - sb.append(result.asString()); + sb.append(HCLExpression.asString(valueVar)).append(" in ").append(HCLExpression.asString(iterable)).append(':'); + sb.append(HCLExpression.asString(result)); if (condition != null) { sb.append(" if ").append(condition.asString()); } @@ -52,7 +54,9 @@ public String asString() { @Override public List elements() { - return List.of(iterable, result, condition); + return Stream.of(iterable, result, condition) + .filter(Objects::nonNull) + .toList(); } } @@ -62,10 +66,10 @@ public String asString() { StringBuilder sb = new StringBuilder(); sb.append("{for "); if (keyVar != null) { - sb.append(keyVar).append(','); + sb.append(keyVar.asString()).append(','); } - sb.append(valueVar).append(" in ").append(iterable.asString()).append(':'); - sb.append(resultKey.asString()).append("=>").append(resultValue.asString()); + sb.append(HCLExpression.asString(valueVar)).append(" in ").append(HCLExpression.asString(iterable)).append(':'); + sb.append(HCLExpression.asString(resultKey)).append("=>").append(HCLExpression.asString(resultValue)); if (grouping) { sb.append("..."); } @@ -78,7 +82,9 @@ public String asString() { @Override public List elements() { - return List.of(iterable, resultKey, resultValue, condition); + return Stream.of(iterable, resultKey, resultValue, condition) + .filter(Objects::nonNull) + .toList(); } } diff --git a/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLFunction.java b/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLFunction.java index f2556a5384c1..1c91c9b1e278 100644 --- a/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLFunction.java +++ b/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLFunction.java @@ -19,6 +19,7 @@ package org.netbeans.modules.languages.hcl.ast; import java.util.List; +import java.util.Objects; import java.util.StringJoiner; /** @@ -28,14 +29,16 @@ public record HCLFunction(HCLIdentifier name, List args, boolean expand) implements HCLExpression { public HCLFunction { + Objects.requireNonNull(name, "name cannot be null"); + Objects.requireNonNull(args, "args can be empty, but cannot be null"); args = List.copyOf(args); } @Override public String asString() { StringJoiner sargs = new StringJoiner(",", "(", expand ? "...)" : ")"); - args.forEach((arg) -> sargs.add(arg.toString())); - return name + sargs.toString(); + args.forEach((arg) -> sargs.add(arg.asString())); + return name.asString() + sargs.toString(); } @Override diff --git a/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLIdentifier.java b/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLIdentifier.java index ad25ab30e01a..6782f8766b3d 100644 --- a/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLIdentifier.java +++ b/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLIdentifier.java @@ -24,18 +24,36 @@ * * @author Laszlo Kishalmi */ -public sealed interface HCLIdentifier extends HCLElement { +public sealed interface HCLIdentifier extends HCLExpression { String id(); @Override - default List elements() { + default List elements() { return List.of(); } - public record SimpleId(String id) implements HCLIdentifier {} + public record SimpleId(String id) implements HCLIdentifier { - public record StringId(String id) implements HCLIdentifier {} + @Override + public String asString() { + return id; + } + } + + public record StringId(String id) implements HCLIdentifier { + + @Override + public String asString() { + return id; + } + } + + public record ScopedId(HCLIdentifier parent, String id) implements HCLIdentifier { - public record ScopedId(HCLIdentifier parent, String id) implements HCLIdentifier {} + @Override + public String asString() { + return HCLExpression.asString(parent) + "::" + id; + } + } } diff --git a/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLResolveOperation.java b/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLResolveOperation.java index 4e721f89b353..21a381c3458f 100644 --- a/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLResolveOperation.java +++ b/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLResolveOperation.java @@ -19,6 +19,7 @@ package org.netbeans.modules.languages.hcl.ast; import java.util.List; +import java.util.Objects; /** * @@ -26,25 +27,37 @@ */ public sealed interface HCLResolveOperation extends HCLExpression { - HCLExpression base(); + default List elements() { return List.of(base()); } public record Attribute(HCLExpression base, HCLIdentifier attr) implements HCLResolveOperation { + + public Attribute { + Objects.requireNonNull(base, "base cannot be null"); + } + @Override public String asString() { - return base.asString() + "." + attr; + return base.asString() + "." + HCLExpression.asString(attr); } } public record Index(HCLExpression base, HCLExpression index, boolean legacy) implements HCLResolveOperation { + + public Index { + Objects.requireNonNull(base, "base cannot be null"); + Objects.requireNonNull(index, "index cannot be null"); + } + @Override public String asString() { - return base.asString() + (legacy ? "." + index : "[" + index + "]"); + return base.asString() + (legacy ? "." + index.asString() : "[" + index.asString() + "]"); } + @Override public List elements() { return List.of(base, index); @@ -52,6 +65,11 @@ public List elements() { } public record AttrSplat(HCLExpression base) implements HCLResolveOperation { + + public AttrSplat { + Objects.requireNonNull(base, "base cannot be null"); + } + @Override public String asString() { return base.asString() + ".*"; @@ -59,6 +77,11 @@ public String asString() { } public record FullSplat(HCLExpression base) implements HCLResolveOperation { + + public FullSplat { + Objects.requireNonNull(base, "base cannot be null"); + } + @Override public String asString() { return base.asString() + "[*]"; diff --git a/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLVariable.java b/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLVariable.java index 84f2779ec691..f1b8c7206882 100644 --- a/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLVariable.java +++ b/ide/languages.hcl/src/org/netbeans/modules/languages/hcl/ast/HCLVariable.java @@ -26,6 +26,6 @@ public record HCLVariable(HCLIdentifier name) implements HCLExpression { @Override public String asString() { - return name.id(); + return HCLExpression.asString(name); } } diff --git a/ide/languages.hcl/test/unit/src/org/netbeans/modules/languages/hcl/ast/HCLCollectionTest.java b/ide/languages.hcl/test/unit/src/org/netbeans/modules/languages/hcl/ast/HCLCollectionTest.java new file mode 100644 index 000000000000..3e9e9ac9b93c --- /dev/null +++ b/ide/languages.hcl/test/unit/src/org/netbeans/modules/languages/hcl/ast/HCLCollectionTest.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.languages.hcl.ast; + +import static org.netbeans.modules.languages.hcl.ast.HCLExpressionTestSupport.*; +import org.junit.Test; + +/** + * + * @author lkishalmi + */ +public class HCLCollectionTest { + + @Test + public void testTupleSelf() { + assertExpr("[]"); + assertExpr("[a]"); + assertExpr("[a,b]"); + } + + @Test + public void testObjectSelf() { + assertExpr("{}"); + assertExpr("{a=b}"); + assertExpr("{a=b,b=c}"); + } +} diff --git a/ide/languages.hcl/test/unit/src/org/netbeans/modules/languages/hcl/ast/HCLExpressionTestSupport.java b/ide/languages.hcl/test/unit/src/org/netbeans/modules/languages/hcl/ast/HCLExpressionTestSupport.java new file mode 100644 index 000000000000..9bdcbda465a1 --- /dev/null +++ b/ide/languages.hcl/test/unit/src/org/netbeans/modules/languages/hcl/ast/HCLExpressionTestSupport.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.languages.hcl.ast; + +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import org.netbeans.modules.languages.hcl.grammar.HCLLexer; +import org.netbeans.modules.languages.hcl.grammar.HCLParser; + +/** + * + * @author lkishalmi + */ +public class HCLExpressionTestSupport { + private HCLExpressionTestSupport() {} + + public static HCLExpression parse(String expr) { + HCLLexer lexer = new HCLLexer(CharStreams.fromString(expr)); + HCLParser parser = new HCLParser(new CommonTokenStream(lexer)); + return new HCLExpressionFactory().process(parser.expression()); + } + + public static void assertExpr(String expected, HCLExpression expr) { + assertNotNull(expr); + assertEquals(expected, expr.asString()); + } + + public static void assertExpr(String expected) { + assertExpr(expected, parse(expected)); + } +} diff --git a/ide/languages.hcl/test/unit/src/org/netbeans/modules/languages/hcl/ast/HCLForExpressionTest.java b/ide/languages.hcl/test/unit/src/org/netbeans/modules/languages/hcl/ast/HCLForExpressionTest.java new file mode 100644 index 000000000000..95feb2713635 --- /dev/null +++ b/ide/languages.hcl/test/unit/src/org/netbeans/modules/languages/hcl/ast/HCLForExpressionTest.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.languages.hcl.ast; + +import static org.junit.Assert.*; +import static org.netbeans.modules.languages.hcl.ast.HCLExpressionTestSupport.*; +import org.junit.Test; + +/** + * + * @author lkishalmi + */ +public class HCLForExpressionTest { + + @Test + public void testForTuple() { + HCLExpression expr = parse("[for i in local.t : i]"); + if (expr instanceof HCLForExpression.Tuple t) { + assertNull(t.keyVar()); + assertNull(t.condition()); + } else { + fail(); + } + } + + @Test + public void testForTupleSelf() { + assertExpr("[for i in l:i]"); + assertExpr("[for i,j in l:i]"); + assertExpr("[for i in l:i if i>0]"); + } + + @Test + public void testForObjectSelf() { + assertExpr("{for k,v in l:k=>v}"); + assertExpr("{for k,v in l:k=>v if v!=null}"); + assertExpr("{for k,v in l:k=>v... if v!=null}"); + } + + @Test + public void testForObject() { + HCLExpression expr = parse("{for k, v in local.m : k => v}"); + if (expr instanceof HCLForExpression.Object o) { + assertEquals("k", HCLExpression.asString(o.keyVar())); + assertNull(o.condition()); + } else { + fail(); + } + } + + @Test + public void testForObjectCondition() { + HCLExpression expr = parse("{for k, v in local.m : k => v if v > 0}"); + if (expr instanceof HCLForExpression.Object o) { + assertExpr("k", o.keyVar()); + assertExpr("v>0", o.condition()); + } else { + fail(); + } + } + +} diff --git a/ide/languages.hcl/test/unit/src/org/netbeans/modules/languages/hcl/ast/HCLFunctionTest.java b/ide/languages.hcl/test/unit/src/org/netbeans/modules/languages/hcl/ast/HCLFunctionTest.java new file mode 100644 index 000000000000..d86a6e138c2b --- /dev/null +++ b/ide/languages.hcl/test/unit/src/org/netbeans/modules/languages/hcl/ast/HCLFunctionTest.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.languages.hcl.ast; + +import static org.netbeans.modules.languages.hcl.ast.HCLExpressionTestSupport.*; +import org.junit.Test; + +/** + * + * @author lkishalmi + */ +public class HCLFunctionTest { + + @Test + public void testFunctionSelf() { + assertExpr("min()"); + assertExpr("min([1,b]...)"); + assertExpr("local::min()"); + } +} diff --git a/ide/languages.hcl/test/unit/src/org/netbeans/modules/languages/hcl/ast/HCLLiteralsTest.java b/ide/languages.hcl/test/unit/src/org/netbeans/modules/languages/hcl/ast/HCLLiteralsTest.java index d25afd1a87b1..77d3a9bf49f0 100644 --- a/ide/languages.hcl/test/unit/src/org/netbeans/modules/languages/hcl/ast/HCLLiteralsTest.java +++ b/ide/languages.hcl/test/unit/src/org/netbeans/modules/languages/hcl/ast/HCLLiteralsTest.java @@ -21,7 +21,7 @@ import org.junit.Test; import static org.junit.Assert.*; -import static org.netbeans.modules.languages.hcl.ast.HCLExpression.parse; +import static org.netbeans.modules.languages.hcl.ast.HCLExpressionTestSupport.*; /** * diff --git a/ide/languages.hcl/test/unit/src/org/netbeans/modules/languages/hcl/ast/HCLOperationsTest.java b/ide/languages.hcl/test/unit/src/org/netbeans/modules/languages/hcl/ast/HCLOperationsTest.java index 95d794667fba..5917a9e8ea01 100644 --- a/ide/languages.hcl/test/unit/src/org/netbeans/modules/languages/hcl/ast/HCLOperationsTest.java +++ b/ide/languages.hcl/test/unit/src/org/netbeans/modules/languages/hcl/ast/HCLOperationsTest.java @@ -21,7 +21,7 @@ import org.junit.Test; import static org.junit.Assert.*; -import static org.netbeans.modules.languages.hcl.ast.HCLExpression.parse; +import static org.netbeans.modules.languages.hcl.ast.HCLExpressionTestSupport.*; /** * @@ -39,6 +39,16 @@ public void testResolveVar() throws Exception { assertEquals("var", ((HCLVariable)resolve.base()).name().id()); } + @Test + public void testResolveIncomplete() throws Exception { + HCLExpression exp = parse("var."); + assertTrue(exp instanceof HCLResolveOperation.Attribute); + HCLResolveOperation.Attribute resolve = (HCLResolveOperation.Attribute) exp; + assertNull(resolve.attr()); + assertTrue(resolve.base() instanceof HCLVariable); + assertEquals("var", ((HCLVariable)resolve.base()).name().id()); + } + @Test public void testResolveIndex1() throws Exception { HCLExpression exp = parse("a[0]"); @@ -70,7 +80,36 @@ public void testConditional() throws Exception { assertTrue(cond.condition() instanceof HCLArithmeticOperation.Binary); assertTrue(cond.trueValue() instanceof HCLLiteral.NumericLit); assertTrue(cond.falseValue() instanceof HCLVariable); - - + } + + @Test + public void testConditionalSelf() throws Exception { + assertExpr("a?b:c"); + assertExpr("a>b?1:0"); + } + + @Test + public void testBinaryOperationSelf() throws Exception { + assertExpr("a+b"); + assertExpr("a-b"); + assertExpr("a*b"); + assertExpr("a/b"); + assertExpr("a%b"); + + assertExpr("a||b"); + assertExpr("a&&b"); + + assertExpr("ab"); + assertExpr("a<=b"); + assertExpr("a>=b"); + assertExpr("a==b"); + assertExpr("a!=b"); + } + + @Test + public void testUnaryOperationSelf() throws Exception { + assertExpr("!a"); + assertExpr("-a"); } }