diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/validator/QuteSyntaxErrorCode.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/validator/QuteSyntaxErrorCode.java index a6a2c09a9..84fcb1672 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/validator/QuteSyntaxErrorCode.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/validator/QuteSyntaxErrorCode.java @@ -48,8 +48,10 @@ public enum QuteSyntaxErrorCode implements IQuteErrorCode { /** * {#if test}Hello {name} */ - UNTERMINATED_SECTION("Parser error: unterminated section [{0}] detected", ParserError.UNTERMINATED_SECTION); + UNTERMINATED_SECTION("Parser error: unterminated section [{0}] detected", ParserError.UNTERMINATED_SECTION), + UNEXPECTED_TOKEN("Syntax error: `Unexpected ''{0}'' token`.", null); + private static List supportedErrorCodes; private final String rawMessage; diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/validator/QuteSyntaxValidator.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/validator/QuteSyntaxValidator.java index 7ab833695..2aa3ffc58 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/validator/QuteSyntaxValidator.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/validator/QuteSyntaxValidator.java @@ -16,9 +16,18 @@ import org.eclipse.lsp4j.Range; +import com.redhat.qute.parser.expression.MethodPart; +import com.redhat.qute.parser.expression.ObjectPart; +import com.redhat.qute.parser.expression.Part; +import com.redhat.qute.parser.expression.Parts.PartKind; +import com.redhat.qute.parser.expression.PropertyPart; import com.redhat.qute.parser.template.ASTVisitor; +import com.redhat.qute.parser.template.Expression; +import com.redhat.qute.parser.template.Node; +import com.redhat.qute.parser.template.NodeKind; +import com.redhat.qute.parser.template.Parameter; import com.redhat.qute.parser.template.Section; -import com.redhat.qute.parser.template.SectionKind; +import com.redhat.qute.parser.template.Template; import com.redhat.qute.parser.template.sections.CaseSection; import com.redhat.qute.parser.template.sections.CustomSection; import com.redhat.qute.parser.template.sections.EachSection; @@ -218,4 +227,62 @@ private void validateSectionSyntax(Section section) { } } + @Override + public boolean visit(ObjectPart node) { + validateEndWithDotSyntax(node); + return super.visit(node); + } + + public boolean visit(PropertyPart node) { + validateEndWithDotSyntax(node); + return super.visit(node); + } + + public boolean visit(MethodPart node) { + // Validate end dot syntax for parameters + for (Parameter parameter : node.getParameters()) { + Expression expression = parameter.getJavaTypeExpression(); + if (expression != null) { + expression.accept(this); + } + } + validateEndWithDotSyntax(node); + return super.visit(node); + } + + private void validateEndWithDotSyntax(Part node) { + if (!hasFollowingPart(node)) { + Template template = node.getOwnerTemplate(); + // It's the last part, check if it is not ended with '.' + int end = node.getPartKind() == PartKind.Method ? node.getEnd() + 1 : node.getEnd(); + String text = template.getText(); + if (end < text.length()) { + char c = text.charAt(end); + if (c == '.') { + Range range = QutePositionUtility.createRange(end, end + 1, template); + reporter.reportError(range, node, + QuteSyntaxErrorCode.UNEXPECTED_TOKEN, c); + } + } + } + } + + private boolean hasFollowingPart(Part node) { + Node next = node.getNextSibling(); + if (next == null) { + // - {name.} + // - {name.size().} + return false; + } + if (next.getKind() == NodeKind.ExpressionPart) { + Part nextPart = (Part) next; + if (nextPart.getPartKind() == PartKind.Method) { + // - {name.or(10)} <-- valid + // - {name. ?: 10} <-- invaid: infix notation + MethodPart methodPart = (MethodPart) nextPart; + return !methodPart.isInfixNotation(); + } + } + return true; + } } diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteDiagnostics.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteDiagnostics.java index ce32c8622..2c69573ff 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteDiagnostics.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteDiagnostics.java @@ -597,21 +597,6 @@ private ResolvedJavaTypeInfo validateExpressionParts(Parts parts, Section ownerS for (int i = 0; i < parts.getChildCount(); i++) { Part current = parts.getChild(i); - if (current.isLast()) { - // It's the last part, check if it is not ended with '.' - int end = current.getEnd(); - String text = template.getText(); - if (end < text.length()) { - char c = text.charAt(end); - if (c == '.') { - Range range = QutePositionUtility.createRange(end, end + 1, template); - Diagnostic diagnostic = createDiagnostic(range, DiagnosticSeverity.Error, - QuteErrorCode.SyntaxError, "Unexpected '.' token."); - diagnostics.add(diagnostic); - } - } - } - switch (current.getPartKind()) { case Namespace: { diff --git a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/QuteDiagnosticsInExpressionTest.java b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/QuteDiagnosticsInExpressionTest.java index 38e6a57ef..6f80914cc 100644 --- a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/QuteDiagnosticsInExpressionTest.java +++ b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/QuteDiagnosticsInExpressionTest.java @@ -365,22 +365,6 @@ public void undefineNamespace() throws Exception { d))); } - @Test - public void objectPartEndsWithDot() { - String template = "{@java.util.List items}\r\n" + // - "{items.}"; - testDiagnosticsFor(template, d(1, 6, 1, 7, QuteErrorCode.SyntaxError, // - "Syntax error: `Unexpected '.' token.`.", DiagnosticSeverity.Error)); - } - - @Test - public void propertyPartEndsWithDot() { - String template = "{@java.util.List items}\r\n" + // - "{items.size.}"; - testDiagnosticsFor(template, d(1, 11, 1, 12, QuteErrorCode.SyntaxError, // - "Syntax error: `Unexpected '.' token.`.", DiagnosticSeverity.Error)); - } - @Test public void invalidMethodVoid() { String template = "{@java.lang.String string}\r\n" + // diff --git a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/syntax/QuteDiagnosticsEndDotSyntaxErrorTest.java b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/syntax/QuteDiagnosticsEndDotSyntaxErrorTest.java new file mode 100644 index 000000000..0dd530829 --- /dev/null +++ b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/syntax/QuteDiagnosticsEndDotSyntaxErrorTest.java @@ -0,0 +1,86 @@ +/******************************************************************************* +* Copyright (c) 2023 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.qute.services.diagnostics.syntax; + +import static com.redhat.qute.QuteAssert.d; +import static com.redhat.qute.QuteAssert.testDiagnosticsFor; + +import org.eclipse.lsp4j.DiagnosticSeverity; +import org.junit.jupiter.api.Test; + +import com.redhat.qute.parser.validator.QuteSyntaxErrorCode; + +/** + * Syntax error with end dot. + * + * @author Angelo ZERR + * + */ +public class QuteDiagnosticsEndDotSyntaxErrorTest { + + @Test + public void objectPartEndsWithDot() { + String template = "{@java.util.List items}\r\n" + // + "{items.}"; + testDiagnosticsFor(template, d(1, 6, 1, 7, QuteSyntaxErrorCode.UNEXPECTED_TOKEN, // + "Syntax error: `Unexpected '.' token`.", DiagnosticSeverity.Error)); + } + + @Test + public void objectPartInInfixNotationEndsWithDot() { + String template = "{@java.lang.String name}\r\n" + // + "{name. ?: \"Qute\"}"; + testDiagnosticsFor(template, d(1, 5, 1, 6, QuteSyntaxErrorCode.UNEXPECTED_TOKEN, // + "Syntax error: `Unexpected '.' token`.", DiagnosticSeverity.Error)); + } + + @Test + public void objectPartInInfixNotationEndsWithDot2() { + String template = "{@java.lang.String name}\r\n" + // + "{name ?: name.}"; + testDiagnosticsFor(template, d(1, 13, 1, 14, QuteSyntaxErrorCode.UNEXPECTED_TOKEN, // + "Syntax error: `Unexpected '.' token`.", DiagnosticSeverity.Error)); + } + + @Test + public void objectPartInMethodParamEndsWithDot() { + String template = "{@java.util.List items}\r\n" + // + "{@int index}\r\n" + // + "{items.get(index.)}"; + testDiagnosticsFor(template, d(2, 16, 2, 17, QuteSyntaxErrorCode.UNEXPECTED_TOKEN, // + "Syntax error: `Unexpected '.' token`.", DiagnosticSeverity.Error)); + } + + @Test + public void propertyPartEndsWithDot() { + String template = "{@java.util.List items}\r\n" + // + "{items.size.}"; + testDiagnosticsFor(template, d(1, 11, 1, 12, QuteSyntaxErrorCode.UNEXPECTED_TOKEN, // + "Syntax error: `Unexpected '.' token`.", DiagnosticSeverity.Error)); + } + + @Test + public void propertyPartInMethodParamEndsWithDot() { + String template = "{@java.util.List items}\r\n" + // + "{items.get(items.size.)}"; + testDiagnosticsFor(template, d(1, 21, 1, 22, QuteSyntaxErrorCode.UNEXPECTED_TOKEN, // + "Syntax error: `Unexpected '.' token`.", DiagnosticSeverity.Error)); + } + + @Test + public void methodPartEndsWithDot() { + String template = "{@java.util.List items}\r\n" + // + "{items.size().}"; + testDiagnosticsFor(template, d(1, 13, 1, 14, QuteSyntaxErrorCode.UNEXPECTED_TOKEN, // + "Syntax error: `Unexpected '.' token`.", DiagnosticSeverity.Error)); + } +} diff --git a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/syntax/QuteDiagnosticsOverridedSyntaxErrorTest.java b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/syntax/overrided/QuteDiagnosticsSectionSyntaxErrorTest.java similarity index 94% rename from qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/syntax/QuteDiagnosticsOverridedSyntaxErrorTest.java rename to qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/syntax/overrided/QuteDiagnosticsSectionSyntaxErrorTest.java index 779e260eb..5caff9344 100644 --- a/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/syntax/QuteDiagnosticsOverridedSyntaxErrorTest.java +++ b/qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/diagnostics/syntax/overrided/QuteDiagnosticsSectionSyntaxErrorTest.java @@ -9,7 +9,7 @@ * Contributors: * Red Hat Inc. - initial API and implementation *******************************************************************************/ -package com.redhat.qute.services.diagnostics.syntax; +package com.redhat.qute.services.diagnostics.syntax.overrided; import static com.redhat.qute.QuteAssert.ca; import static com.redhat.qute.QuteAssert.d; @@ -25,12 +25,13 @@ import com.redhat.qute.services.diagnostics.QuteErrorCode; /** - * Syntax error which improves the error of the real Qute parser. + * Syntax error which improves the error with sections from the real Qute + * parser. * * @author Angelo ZERR * */ -public class QuteDiagnosticsOverridedSyntaxErrorTest { +public class QuteDiagnosticsSectionSyntaxErrorTest { @Test public void UNTERMINATED_SECTION_with_let() throws Exception {