Skip to content

Commit

Permalink
Merge pull request #4279 from jlerbsc/master
Browse files Browse the repository at this point in the history
fix: issue 4240 Calling resolve on catch block parameter throws exception
  • Loading branch information
jlerbsc committed Dec 30, 2023
2 parents 59e3785 + a6f2559 commit 6d50915
Show file tree
Hide file tree
Showing 2 changed files with 228 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,13 @@

import static com.github.javaparser.resolution.Navigator.demandParentNode;

import java.util.Optional;

import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.*;
import com.github.javaparser.ast.expr.*;
import com.github.javaparser.ast.stmt.CatchClause;
import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt;
import com.github.javaparser.ast.type.Type;
import com.github.javaparser.ast.type.TypeParameter;
Expand All @@ -36,9 +39,11 @@
import com.github.javaparser.resolution.UnsolvedSymbolException;
import com.github.javaparser.resolution.declarations.*;
import com.github.javaparser.resolution.model.SymbolReference;
import com.github.javaparser.resolution.model.Value;
import com.github.javaparser.resolution.types.ResolvedPrimitiveType;
import com.github.javaparser.resolution.types.ResolvedType;
import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
import com.github.javaparser.symbolsolver.javaparsermodel.declarations.*;

/**
Expand Down Expand Up @@ -241,23 +246,51 @@ public <T> T resolveDeclaration(Node node, Class<T> resultClass) {
throw new UnsolvedSymbolException("We are unable to find the constructor declaration corresponding to " + node);
}
}
if (node instanceof Parameter) {
if (ResolvedParameterDeclaration.class.equals(resultClass)) {
Parameter parameter = (Parameter) node;
CallableDeclaration callableDeclaration = node.findAncestor(CallableDeclaration.class).get();
ResolvedMethodLikeDeclaration resolvedMethodLikeDeclaration;
if (callableDeclaration.isConstructorDeclaration()) {
resolvedMethodLikeDeclaration = callableDeclaration.asConstructorDeclaration().resolve();
} else {
resolvedMethodLikeDeclaration = callableDeclaration.asMethodDeclaration().resolve();
}
for (int i = 0; i < resolvedMethodLikeDeclaration.getNumberOfParams(); i++) {
if (resolvedMethodLikeDeclaration.getParam(i).getName().equals(parameter.getNameAsString())) {
return resultClass.cast(resolvedMethodLikeDeclaration.getParam(i));
}
}
}
}
if (node instanceof Parameter) {
if (ResolvedParameterDeclaration.class.equals(resultClass)) {
Parameter parameter = (Parameter) node;
Optional<Node> parentNode = node.getParentNode();
if (!parentNode.isPresent()) {
throw new UnsolvedSymbolException(
"We are unable to resolve the parameter declaration corresponding to " + node);
}
Node parent = (Node) parentNode.get();
if (parent instanceof ConstructorDeclaration) {
Optional<ResolvedParameterDeclaration> resolvedParameterDeclaration = resolveParameterDeclaration(
((ConstructorDeclaration) parent).resolve(), parameter);
return resolvedParameterDeclaration.map(rpd -> resultClass.cast(rpd))
.orElseThrow(() -> new UnsolvedSymbolException(
"We are unable to resolve the parameter declaration corresponding to " + node));
} else if (parent instanceof MethodDeclaration) {
Optional<ResolvedParameterDeclaration> resolvedParameterDeclaration = resolveParameterDeclaration(
((MethodDeclaration) parent).resolve(), parameter);
return resolvedParameterDeclaration.map(rpd -> resultClass.cast(rpd))
.orElseThrow(() -> new UnsolvedSymbolException(
"We are unable to resolve the parameter declaration corresponding to " + node));
} else if (parent instanceof RecordDeclaration) {
Optional<ResolvedParameterDeclaration> resolvedParameterDeclaration = resolveParameterDeclaration(
((RecordDeclaration) parent).resolve(), parameter);
return resolvedParameterDeclaration.map(rpd -> resultClass.cast(rpd))
.orElseThrow(() -> new UnsolvedSymbolException(
"We are unable to resolve the parameter declaration corresponding to " + node));
} else if (parent instanceof LambdaExpr) {
Optional<ResolvedParameterDeclaration> resolvedParameterDeclaration = resolveParameterDeclaration(
parameter);
return resolvedParameterDeclaration.map(rpd -> resultClass.cast(rpd))
.orElseThrow(() -> new UnsolvedSymbolException(
"We are unable to resolve the parameter declaration corresponding to " + node));
} else if (parent instanceof CatchClause) {
Optional<ResolvedParameterDeclaration> resolvedParameterDeclaration = resolveParameterDeclaration(
parameter);
return resolvedParameterDeclaration.map(rpd -> resultClass.cast(rpd))
.orElseThrow(() -> new UnsolvedSymbolException(
"We are unable to resolve the parameter declaration corresponding to " + node));
} else {
throw new UnsolvedSymbolException(
"We are unable to resolve the parameter declaration corresponding to " + node);
}
}
}
if (node instanceof AnnotationExpr) {
SymbolReference<ResolvedAnnotationDeclaration> result = JavaParserFacade.get(typeSolver).solve((AnnotationExpr) node);
if (result.isSolved()) {
Expand All @@ -281,7 +314,80 @@ public <T> T resolveDeclaration(Node node, Class<T> resultClass) {
throw new UnsupportedOperationException("Unable to find the declaration of type " + resultClass.getSimpleName()
+ " from " + node.getClass().getSimpleName());
}

/*
* Resolves constructor or method parameter
*/
private Optional<ResolvedParameterDeclaration> resolveParameterDeclaration(
ResolvedMethodLikeDeclaration resolvedMethodLikeDeclaration, Parameter parameter) {
for (int i = 0; i < resolvedMethodLikeDeclaration.getNumberOfParams(); i++) {
if (resolvedMethodLikeDeclaration.getParam(i).getName().equals(parameter.getNameAsString())) {
return Optional.of(resolvedMethodLikeDeclaration.getParam(i));
}
}
return Optional.empty();
}

/*
* Resolves record parameter
*/
private Optional<ResolvedParameterDeclaration> resolveParameterDeclaration(
ResolvedReferenceTypeDeclaration resolvedReferenceTypeDeclaration, Parameter parameter) {
ResolvedFieldDeclaration rfd = resolvedReferenceTypeDeclaration.getField(parameter.getNameAsString());
if (rfd == null) return Optional.empty();
ResolvedParameterDeclaration resolvedParameterDeclaration = new ResolvedParameterDeclaration() {

@Override
public ResolvedType getType() {
return rfd.getType();
}

@Override
public String getName() {
return parameter.getNameAsString();
}

@Override
public boolean isVariadic() {
return parameter.isVarArgs();
}

};
return Optional.of(resolvedParameterDeclaration);
}

/*
* Resolves lambda expression parameters and catch clause parameters
*/
private Optional<ResolvedParameterDeclaration> resolveParameterDeclaration(Parameter parameter) {
ResolvedParameterDeclaration resolvedParameterDeclaration = new ResolvedParameterDeclaration() {

@Override
public ResolvedType getType() {
Node parentNode = parameter.getParentNode().get();
if (parameter.getType().isUnknownType() && parentNode instanceof LambdaExpr) {
Optional<Value> value = JavaParserFactory.getContext(parentNode, typeSolver)
.solveSymbolAsValue(parameter.getNameAsString());
return value.map(v -> v.getType()).orElseThrow(() -> new UnsolvedSymbolException(
"We are unable to resolve the parameter declaration corresponding to " + parameter));
}
return JavaParserFacade.get(typeSolver).convertToUsage(parameter.getType());
}

@Override
public String getName() {
return parameter.getNameAsString();
}

@Override
public boolean isVariadic() {
return parameter.isVarArgs();
}

};
return Optional.of(resolvedParameterDeclaration);
}

@Override
public <T> T toResolvedType(Type javaparserType, Class<T> resultClass) {
ResolvedType resolvedType = JavaParserFacade.get(typeSolver).convertToUsage(javaparserType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@
package com.github.javaparser.symbolsolver;

import com.github.javaparser.JavaParser;
import com.github.javaparser.JavaParserAdapter;
import com.github.javaparser.ParseStart;
import com.github.javaparser.ParserConfiguration;
import com.github.javaparser.ParserConfiguration.LanguageLevel;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.*;
import com.github.javaparser.resolution.TypeSolver;
Expand All @@ -44,6 +46,8 @@

import static com.github.javaparser.Providers.provider;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

class JavaParserAPIIntegrationTest extends AbstractSymbolResolutionTest {

Expand Down Expand Up @@ -175,5 +179,105 @@ void parameterDeclarationResolve() throws IOException {
Parameter declaration = methodDeclaration.getParameter(0);
ResolvedParameterDeclaration resolvedDeclaration = declaration.resolve();
}


@Test
void resolveParameterDeclarationOnConstructor() throws IOException {
String code =
"class Foo {\n"
+ " String baz;\n"
+ " Foo(String baz){\n"
+ " this.baz = baz;\n"
+ " }"
+ "}";
ParserConfiguration parserConfiguration = new ParserConfiguration();
parserConfiguration.setSymbolResolver(new JavaSymbolSolver(typeSolver));
JavaParserAdapter parser = JavaParserAdapter.of(new JavaParser(parserConfiguration));
CompilationUnit cu = parser.parse(code);
Parameter parameter = cu.findFirst(Parameter.class).get();
ResolvedParameterDeclaration resolvedParameterDeclaration = parameter.resolve();
assertEquals("java.lang.String",resolvedParameterDeclaration.describeType());
assertTrue(resolvedParameterDeclaration.isParameter());
}

@Test
void resolveParameterDeclarationOnMethodDeclaration() throws IOException {
String code =
"class Foo {\n"
+ " void m(String bar) {}\n"
+ "}";
ParserConfiguration parserConfiguration = new ParserConfiguration();
parserConfiguration.setSymbolResolver(new JavaSymbolSolver(typeSolver));
JavaParserAdapter parser = JavaParserAdapter.of(new JavaParser(parserConfiguration));
CompilationUnit cu = parser.parse(code);
Parameter parameter = cu.findFirst(Parameter.class).get();
ResolvedParameterDeclaration resolvedParameterDeclaration = parameter.resolve();
assertEquals("java.lang.String",resolvedParameterDeclaration.describeType());
assertTrue(resolvedParameterDeclaration.isParameter());
}

@Test()
void resolveParameterDeclarationOnRecordDeclaration() throws IOException {
String code = "record Point(Integer x) { }";
ParserConfiguration parserConfiguration = new ParserConfiguration().setLanguageLevel(LanguageLevel.JAVA_16);
parserConfiguration.setSymbolResolver(new JavaSymbolSolver(typeSolver));
JavaParserAdapter parser = JavaParserAdapter.of(new JavaParser(parserConfiguration));
CompilationUnit cu = parser.parse(code);
Parameter parameter = cu.findFirst(Parameter.class).get();
// TODO Fixme when the record declarations are resolved.
assertThrows(UnsupportedOperationException.class, () -> parameter.resolve());
// assertEquals("java.lang.Integer",parameter.resolve().describeType());
}

@Test()
void resolveParameterDeclarationOnCatchClauseExpr() throws IOException {
String code =
"class Foo {\n"
+ " void m() {\n"
+ " try {\n"
+ " throw new java.io.FileNotFoundException();\n"
+ " } catch (java.io.IOException ioe) {}\n"
+ " }\n"
+ "}";
ParserConfiguration parserConfiguration = new ParserConfiguration();
parserConfiguration.setSymbolResolver(new JavaSymbolSolver(typeSolver));
JavaParserAdapter parser = JavaParserAdapter.of(new JavaParser(parserConfiguration));
CompilationUnit cu = parser.parse(code);
Parameter parameter = cu.findFirst(Parameter.class).get();
ResolvedParameterDeclaration resolvedParameterDeclaration = parameter.resolve();
assertEquals("java.io.IOException",resolvedParameterDeclaration.describeType());
assertTrue(resolvedParameterDeclaration.isParameter());
}

@Test()
void resolveParameterDeclarationOnLambdaExprWithTypeInference() throws IOException {
String code =
"class Foo {\n"
+ " java.util.function.Consumer<Integer> consumer = item -> {};\n"
+ "}";
ParserConfiguration parserConfiguration = new ParserConfiguration();
parserConfiguration.setSymbolResolver(new JavaSymbolSolver(typeSolver));
JavaParserAdapter parser = JavaParserAdapter.of(new JavaParser(parserConfiguration));
CompilationUnit cu = parser.parse(code);
Parameter parameter = cu.findFirst(Parameter.class).get();
ResolvedParameterDeclaration resolvedParameterDeclaration = parameter.resolve();
assertEquals("java.lang.Integer",resolvedParameterDeclaration.describeType());
assertTrue(resolvedParameterDeclaration.isParameter());
}

@Test()
void resolveParameterDeclarationOnLambdaExprWithoutTypeInference() throws IOException {
String code =
"class Foo {\n"
+ " java.util.function.Consumer<Long> consumer = (Long a) -> { System.out.println(a); };\n"
+ "}";
ParserConfiguration parserConfiguration = new ParserConfiguration();
parserConfiguration.setSymbolResolver(new JavaSymbolSolver(typeSolver));
JavaParserAdapter parser = JavaParserAdapter.of(new JavaParser(parserConfiguration));
CompilationUnit cu = parser.parse(code);
Parameter parameter = cu.findFirst(Parameter.class).get();
ResolvedParameterDeclaration resolvedParameterDeclaration = parameter.resolve();
assertEquals("java.lang.Long",resolvedParameterDeclaration.describeType());
assertTrue(resolvedParameterDeclaration.isParameter());
}

}

0 comments on commit 6d50915

Please sign in to comment.