Skip to content

Commit

Permalink
fix: False-positive error on let-defined variable
Browse files Browse the repository at this point in the history
Fixes #993

Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr committed Nov 8, 2024
1 parent 29a810a commit 2e221e9
Show file tree
Hide file tree
Showing 16 changed files with 853 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
import com.redhat.qute.parser.expression.Parts.PartKind;
import com.redhat.qute.parser.parameter.ParameterParser;
import com.redhat.qute.parser.template.ASTVisitor;
import com.redhat.qute.parser.template.JavaTypeInfoProvider;
import com.redhat.qute.parser.template.Node;
import com.redhat.qute.parser.template.Parameter;
import com.redhat.qute.parser.template.ParametersContainer;
import com.redhat.qute.parser.template.Template;

/**
* Method part.
Expand Down Expand Up @@ -56,6 +58,17 @@ public int getEndName() {
public PartKind getPartKind() {
return PartKind.Method;
}

@Override
public JavaTypeInfoProvider resolveJavaType() {
Template template = super.getOwnerTemplate();
boolean hasNamespace = getNamespace() != null;
if (hasNamespace) {
// ex : {http:request()}
return template.findWithNamespace(this);
}
return null;
}

/**
* Set the open bracket offset.
Expand Down Expand Up @@ -244,7 +257,7 @@ public boolean isOperator() {
public boolean isOrOperator() {
return false;
}

@Override
public String getTemplateContent() {
return getOwnerTemplate().getText();
Expand All @@ -260,4 +273,5 @@ protected void accept0(ASTVisitor visitor) {
visitor.visit(this);
visitor.endVisit(this);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import com.redhat.qute.parser.expression.Parts.PartKind;
import com.redhat.qute.parser.template.ASTVisitor;
import com.redhat.qute.parser.template.JavaTypeInfoProvider;
import com.redhat.qute.parser.template.Parameter;
import com.redhat.qute.parser.template.Section;
import com.redhat.qute.parser.template.SectionKind;
Expand Down Expand Up @@ -42,6 +43,11 @@ public PartKind getPartKind() {
return PartKind.Namespace;
}

@Override
public JavaTypeInfoProvider resolveJavaType() {
return null;
}

@Override
public int getStartName() {
if (startName != -1) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public int getStartName() {
return startName;
}

@Override
public JavaTypeInfoProvider resolveJavaType() {
Template template = super.getOwnerTemplate();
String partName = getPartName();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import com.redhat.qute.parser.expression.Parts.PartKind;
import com.redhat.qute.parser.template.Expression;
import com.redhat.qute.parser.template.JavaTypeInfoProvider;
import com.redhat.qute.parser.template.Node;
import com.redhat.qute.parser.template.NodeKind;
import com.redhat.qute.parser.template.Parameter;
Expand Down Expand Up @@ -216,11 +217,13 @@ public Parameter getOwnerParameter() {
Expression expression = getParent().getParent();
return expression != null ? expression.getOwnerParameter() : null;
}

/**
* Returns the part kind.
*
* @return the part kind.
*/
public abstract PartKind getPartKind();

public abstract JavaTypeInfoProvider resolveJavaType();
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import com.redhat.qute.parser.expression.Parts.PartKind;
import com.redhat.qute.parser.template.ASTVisitor;
import com.redhat.qute.parser.template.JavaTypeInfoProvider;

/**
* Property part.
Expand All @@ -24,7 +25,7 @@
* <p>
* {item['name']}
* </p>
*
* @author Angelo ZERR
*
*/
Expand All @@ -42,6 +43,11 @@ public PartKind getPartKind() {
return PartKind.Property;
}

@Override
public JavaTypeInfoProvider resolveJavaType() {
return null;
}

@Override
public int getStartName() {
computeBracketNotationIfNeeded();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ public TemplateConfiguration getConfiguration() {
*
* @return the class name found from the namespace and null otherwise.
*/
public JavaTypeInfoProvider findWithNamespace(ObjectPart objectPart) {
public JavaTypeInfoProvider findWithNamespace(Part objectPart) {
String namespace = objectPart.getNamespace();
if (namespace == null) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@
import com.redhat.qute.commons.QuteResolvedJavaTypeParams;
import com.redhat.qute.commons.ResolvedJavaTypeInfo;
import com.redhat.qute.commons.datamodel.resolvers.ValueResolverKind;
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;
import com.redhat.qute.parser.expression.Parts.PartKind;
import com.redhat.qute.parser.template.Expression;
import com.redhat.qute.parser.template.ExpressionParameter;
import com.redhat.qute.parser.template.JavaTypeInfoProvider;
Expand Down Expand Up @@ -364,7 +366,9 @@ private CompletableFuture<ResolvedJavaTypeInfo> resolveJavaType(Parts parts, int
break;
case Property:
case Method:
if (future != null) {
if (future == null && current.getPartKind() == PartKind.Method) {
future = resolveJavaType((MethodPart) current);
} else if (future != null) {
ResolvedJavaTypeInfo actualResolvedType = future.getNow(null);
if (actualResolvedType != null) {
future = resolveJavaType(current, actualResolvedType);
Expand Down Expand Up @@ -465,6 +469,18 @@ private CompletableFuture<ResolvedJavaTypeInfo> resolveJavaType(Part part,
return resolveJavaType(memberType);
}

private CompletableFuture<ResolvedJavaTypeInfo> resolveJavaType(MethodPart objectPart) {
JavaTypeInfoProvider javaTypeInfo = objectPart.resolveJavaType();
if (javaTypeInfo == null) {
return RESOLVED_JAVA_TYPE_INFO_NULL_FUTURE;
}
String javaType = javaTypeInfo.getJavaType();
if (StringUtils.isEmpty(javaType)) {
return RESOLVED_JAVA_TYPE_INFO_NULL_FUTURE;
}
return resolveJavaType(javaType, javaTypeInfo);
}

private CompletableFuture<ResolvedJavaTypeInfo> resolveJavaType(ObjectPart objectPart) {
CompletableFuture<ResolvedJavaTypeInfo> future = null;
JavaTypeInfoProvider javaTypeInfo = objectPart.resolveJavaType();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -969,7 +969,9 @@ private boolean findMethodResolver(ResolvedJavaTypeInfo baseType, String namespa
// Check if the current resolver matches the parameters.
boolean matchParameters = isMatchParameters(resolver, parameterTypes);
result.setMatchParameters(matchParameters);
return true;
if (matchParameters) {
return true;
}
}
} else {
if (baseType == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,7 @@ public static void testInlayHintFor(String value, QuteInlayHintSettings inlayHin
testInlayHintFor(value, FILE_URI, null, PROJECT_URI, TEMPLATE_BASE_DIR, inlayHintSettings, expected);
}

private static void testInlayHintFor(String value, String fileUri, String templateId, String projectUri,
public static void testInlayHintFor(String value, String fileUri, String templateId, String projectUri,
String templateBaseDir, QuteInlayHintSettings inlayHintSettings, InlayHint... expected) throws Exception {
QuteProjectRegistry projectRegistry = new MockQuteProjectRegistry();
Template template = createTemplate(value, fileUri, projectUri, templateBaseDir, projectRegistry);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import com.redhat.qute.commons.usertags.UserTagInfo;
import com.redhat.qute.project.multiple.QuteProjectA;
import com.redhat.qute.project.multiple.QuteProjectB;
import com.redhat.qute.project.qute_web.QuteWebProject;
import com.redhat.qute.project.roq.RoqProject;

public class MockQuteProjectRegistry extends QuteProjectRegistry {
Expand Down Expand Up @@ -68,6 +69,9 @@ protected QuteProject createProject(ProjectInfo projectInfo) {
if (RoqProject.PROJECT_URI.equals(projectInfo.getUri())) {
return new RoqProject(projectInfo, this);
}
if (QuteWebProject.PROJECT_URI.equals(projectInfo.getUri())) {
return new QuteWebProject(projectInfo, this);
}
return super.createProject(projectInfo);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*******************************************************************************
* Copyright (c) 2024 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.project.qute_web;

import java.util.List;
import java.util.Map;

import com.redhat.qute.commons.ProjectInfo;
import com.redhat.qute.commons.ResolvedJavaTypeInfo;
import com.redhat.qute.commons.datamodel.DataModelParameter;
import com.redhat.qute.commons.datamodel.DataModelProject;
import com.redhat.qute.commons.datamodel.DataModelTemplate;
import com.redhat.qute.commons.datamodel.resolvers.NamespaceResolverInfo;
import com.redhat.qute.commons.datamodel.resolvers.ValueResolverInfo;
import com.redhat.qute.project.BaseQuteProject;
import com.redhat.qute.project.QuteProjectRegistry;

/**
* Qute Web project.
*/
public class QuteWebProject extends BaseQuteProject {

public static final String PROJECT_URI = "qute-web";

private DataModelProject<DataModelTemplate<?>> dataModel;

public QuteWebProject(ProjectInfo projectInfo, QuteProjectRegistry projectRegistry) {
super(projectInfo, projectRegistry);
}

@Override
protected void fillResolvedJavaTypes(List<ResolvedJavaTypeInfo> resolvedJavaTypes) {
super.fillResolvedJavaTypes(resolvedJavaTypes);
loadResolvedJavaType("HttpServletRequest.json", resolvedJavaTypes, QuteWebProject.class);
}

@Override
protected void fillTemplates(List<DataModelTemplate<DataModelParameter>> templates) {

}

@Override
protected void fillValueResolvers(List<ValueResolverInfo> valueResolvers) {
valueResolvers.addAll(getDataModel().getValueResolvers());
}

private DataModelProject<DataModelTemplate<?>> getDataModel() {
if (dataModel == null) {
dataModel = loadDataModel("QuteWebDataModel.json", QuteWebProject.class);
}
return dataModel;
}

@Override
protected void fillNamespaceResolverInfos(Map<String, NamespaceResolverInfo> namespaces) {

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*******************************************************************************
* Copyright (c) 2024 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.completions.qute_web;

import static com.redhat.qute.QuteAssert.c;
import static com.redhat.qute.QuteAssert.r;

import org.eclipse.lsp4j.CompletionItem;
import org.junit.jupiter.api.Test;

import com.redhat.qute.QuteAssert;
import com.redhat.qute.project.qute_web.QuteWebProject;

/**
* Test completion with Qute Web Quarkus extension.
*
* @author Angelo ZERR
*
*/
public class QuteWebCompletionsTest {

@Test
public void http_param() throws Exception {
String template = "{#let name=http:param('name', 'Qute')}\r\n" + //
"{name.|}";
testCompletionFor(template, //
// - resolvers
c("orEmpty(base : T) : List<T>", "orEmpty", r(1, 6, 1, 6)),
c("ifTruthy(base : T, arg : Object) : T", "ifTruthy(${1:arg})$0", r(1, 6, 1, 6)),
c("or(base : T, arg : Object) : T", "or(${1:arg})$0", r(1, 6, 1, 6)),
// - String Java fields
c("UTF16 : byte", "UTF16", r(1, 6, 1, 6)),
// - String Java methods
c("getBytes() : byte[]", "getBytes", r(1, 6, 1, 6)),
c("getBytes(charsetName : String) : byte[]", "getBytes(${1:charsetName})$0", r(1, 6, 1, 6)),
c("charAt(index : int) : char", "charAt(${1:index})$0", r(1, 6, 1, 6)));
}

public static void testCompletionFor(String value,
CompletionItem... expectedItems) throws Exception {
QuteAssert.testCompletionFor(value, true, QuteAssert.FILE_URI, null, QuteWebProject.PROJECT_URI,
QuteAssert.TEMPLATE_BASE_DIR,
null,
expectedItems);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*******************************************************************************
* Copyright (c) 2024 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.qute_web;

import org.eclipse.lsp4j.Diagnostic;
import org.junit.jupiter.api.Test;

import com.redhat.qute.QuteAssert;
import com.redhat.qute.project.qute_web.QuteWebProject;

/**
* Test diagnostics with Qute Web Quarkus extension.
*
* @author Angelo ZERR
*
*/
public class QuteWebDiagnosticsTest {

@Test
public void noError() throws Exception {
String template = "{#let name=http:param('name', 'Qute')}\r\n" + //
"<!DOCTYPE html>\r\n" + //
"<html>\r\n" + //
"<body>\r\n" + //
"<h1>Hello {name}</h1>\r\n" + //
"</body>\r\n" + //
"</html>";
testDiagnosticsFor(template);
}

private static void testDiagnosticsFor(String value, Diagnostic... expected) {
QuteAssert.testDiagnosticsFor(value, QuteAssert.FILE_URI, null, QuteWebProject.PROJECT_URI,
QuteAssert.TEMPLATE_BASE_DIR, true, null, expected);
}

}
Loading

0 comments on commit 2e221e9

Please sign in to comment.