From faea3b253a7bce2219a8ae4e37998745875c7dd1 Mon Sep 17 00:00:00 2001 From: Akash Yadav Date: Tue, 7 Nov 2023 19:09:12 +0530 Subject: [PATCH] fix(lsp/java): gracefully handle non-existing methods while looking for method signatures --- .../lsp/java/providers/SignatureProvider.java | 27 +++--- .../androidide/lsp/java/utils/FindHelper.java | 83 +++++++++++++------ 2 files changed, 75 insertions(+), 35 deletions(-) diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/SignatureProvider.java b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/SignatureProvider.java index 75ef902d50..40bbe3a155 100644 --- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/SignatureProvider.java +++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/providers/SignatureProvider.java @@ -240,21 +240,28 @@ private ParameterInformation parameter(@NonNull VariableElement p) { } private void addSourceInfo( - CompileTask task, @NonNull ExecutableElement method, SignatureInformation info) { - TypeElement type = (TypeElement) method.getEnclosingElement(); - String className = type.getQualifiedName().toString(); - String methodName = method.getSimpleName().toString(); - String[] erasedParameterTypes = FindHelper.erasedParameterTypes(task, method); - Optional file = compiler.findAnywhere(className); + @NonNull CompileTask task, + @NonNull ExecutableElement method, + @NonNull SignatureInformation info + ) { + final var type = (TypeElement) method.getEnclosingElement(); + final var className = type.getQualifiedName().toString(); + final var methodName = method.getSimpleName().toString(); + final var erasedParameterTypes = FindHelper.erasedParameterTypes(task, method); + final var file = compiler.findAnywhere(className); if (!file.isPresent()) { return; } - ParseTask parse = compiler.parse(file.get()); - MethodTree source = FindHelper.findMethod(parse, className, methodName, erasedParameterTypes); - TreePath path = Trees.instance(task.task).getPath(parse.root, source); - DocCommentTree docTree = DocTrees.instance(task.task).getDocCommentTree(path); + final var parse = compiler.parse(file.get()); + final var source = FindHelper.findMethod(parse, className, methodName, erasedParameterTypes); + if (source == null) { + return; + } + + final var path = Trees.instance(task.task).getPath(parse.root, source); + final var docTree = DocTrees.instance(task.task).getDocCommentTree(path); if (docTree != null) { info.setDocumentation(MarkdownHelper.asMarkupContent(docTree)); diff --git a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/utils/FindHelper.java b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/utils/FindHelper.java index 03872ffab8..9323d3ade0 100644 --- a/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/utils/FindHelper.java +++ b/lsp/java/src/main/java/com/itsaky/androidide/lsp/java/utils/FindHelper.java @@ -17,12 +17,24 @@ package com.itsaky.androidide.lsp.java.utils; +import androidx.annotation.Nullable; import com.itsaky.androidide.lsp.java.compiler.CompileTask; import com.itsaky.androidide.lsp.java.parser.ParseTask; import com.itsaky.androidide.lsp.java.visitors.FindTypeDeclarationNamed; import com.itsaky.androidide.models.Location; import com.itsaky.androidide.models.Position; import com.itsaky.androidide.models.Range; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Paths; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import jdkx.lang.model.element.Element; +import jdkx.lang.model.element.ElementKind; +import jdkx.lang.model.element.ExecutableElement; +import jdkx.lang.model.element.TypeElement; +import jdkx.lang.model.type.TypeMirror; +import jdkx.lang.model.util.Types; import openjdk.source.tree.ArrayTypeTree; import openjdk.source.tree.ClassTree; import openjdk.source.tree.CompilationUnitTree; @@ -38,19 +50,6 @@ import openjdk.source.util.TreePath; import openjdk.source.util.Trees; -import java.io.IOException; -import java.net.URI; -import java.nio.file.Paths; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import jdkx.lang.model.element.Element; -import jdkx.lang.model.element.ElementKind; -import jdkx.lang.model.element.ExecutableElement; -import jdkx.lang.model.element.TypeElement; -import jdkx.lang.model.type.TypeMirror; -import jdkx.lang.model.util.Types; - public class FindHelper { public static String[] erasedParameterTypes(CompileTask task, ExecutableElement method) { @@ -63,25 +62,47 @@ public static String[] erasedParameterTypes(CompileTask task, ExecutableElement return erasedParameterTypes; } + /** + * Find the method with name methodName in class clasName with the given + * parameter types. + * + * @param task The parse task. + * @param className The fully qualified class name. + * @param methodName The name of method in class className. + * @param erasedParameterTypes The parameter types of the method (fully qualified class names). + * @return The {@link MethodTree} for the method, or null if method was not found. + */ + @Nullable public static MethodTree findMethod( ParseTask task, String className, String methodName, String[] erasedParameterTypes) { ClassTree classTree = findType(task, className); for (Tree member : classTree.getMembers()) { - if (member.getKind() != Tree.Kind.METHOD) continue; + if (member.getKind() != Tree.Kind.METHOD) { + continue; + } MethodTree method = (MethodTree) member; - if (!method.getName().contentEquals(methodName)) continue; - if (!isSameMethodType(method, erasedParameterTypes)) continue; + if (!method.getName().contentEquals(methodName)) { + continue; + } + if (!isSameMethodType(method, erasedParameterTypes)) { + continue; + } return method; } - throw new RuntimeException("no method"); + + return null; } public static VariableTree findField(ParseTask task, String className, String memberName) { ClassTree classTree = findType(task, className); for (Tree member : classTree.getMembers()) { - if (member.getKind() != Tree.Kind.VARIABLE) continue; + if (member.getKind() != Tree.Kind.VARIABLE) { + continue; + } VariableTree variable = (VariableTree) member; - if (!variable.getName().contentEquals(memberName)) continue; + if (!variable.getName().contentEquals(memberName)) { + continue; + } return variable; } throw new RuntimeException("no variable"); @@ -98,7 +119,9 @@ public static ExecutableElement findMethod( return null; } for (Element member : type.getEnclosedElements()) { - if (member.getKind() != ElementKind.METHOD) continue; + if (member.getKind() != ElementKind.METHOD) { + continue; + } ExecutableElement method = (ExecutableElement) member; if (isSameMethod(task, method, className, methodName, erasedParameterTypes)) { return method; @@ -115,13 +138,21 @@ private static boolean isSameMethod( String[] erasedParameterTypes) { Types types = task.task.getTypes(); TypeElement parent = (TypeElement) method.getEnclosingElement(); - if (!parent.getQualifiedName().contentEquals(className)) return false; - if (!method.getSimpleName().contentEquals(methodName)) return false; - if (method.getParameters().size() != erasedParameterTypes.length) return false; + if (!parent.getQualifiedName().contentEquals(className)) { + return false; + } + if (!method.getSimpleName().contentEquals(methodName)) { + return false; + } + if (method.getParameters().size() != erasedParameterTypes.length) { + return false; + } for (int i = 0; i < erasedParameterTypes.length; i++) { TypeMirror erasure = types.erasure(method.getParameters().get(i).asType()); boolean same = erasure.toString().equals(erasedParameterTypes[i]); - if (!same) return false; + if (!same) { + return false; + } } return true; } @@ -197,7 +228,9 @@ private static boolean typeMatches(Tree candidate, String erasedType) { } if (candidate instanceof ArrayTypeTree) { ArrayTypeTree array = (ArrayTypeTree) candidate; - if (!erasedType.endsWith("[]")) return false; + if (!erasedType.endsWith("[]")) { + return false; + } String erasedElement = erasedType.substring(0, erasedType.length() - "[]".length()); return typeMatches(array.getType(), erasedElement); }