Skip to content

Commit

Permalink
Merge pull request #4462 from jlerbsc/master
Browse files Browse the repository at this point in the history
Fix: issue 4399 MethodCallExpr inside lambda in assignment expression cannot be resolved
  • Loading branch information
jlerbsc committed Jun 6, 2024
2 parents 95c938b + d5b5d4b commit 13487dd
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.Parameter;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.expr.AssignExpr;
import com.github.javaparser.ast.expr.CastExpr;
import com.github.javaparser.ast.expr.LambdaExpr;
import com.github.javaparser.ast.expr.MethodCallExpr;
Expand Down Expand Up @@ -55,7 +56,7 @@
* @author Federico Tomassetti
*/
public class LambdaExprContext extends AbstractJavaParserContext<LambdaExpr> {

public LambdaExprContext(LambdaExpr wrappedNode, TypeSolver typeSolver) {
super(wrappedNode, typeSolver);
}
Expand Down Expand Up @@ -115,86 +116,38 @@ public Optional<Value> solveSymbolAsValue(String name) {
}
return Optional.empty();
}
if (parentNode instanceof VariableDeclarator) {
if (parentNode instanceof VariableDeclarator) {
VariableDeclarator variableDeclarator = (VariableDeclarator) parentNode;
ResolvedType t = JavaParserFacade.get(typeSolver).convertToUsage(variableDeclarator.getType());
Optional<MethodUsage> functionalMethod = FunctionalInterfaceLogic.getFunctionalMethod(t);
if (functionalMethod.isPresent()) {
ResolvedType lambdaType = functionalMethod.get().getParamType(index);

// Replace parameter from declarator
Map<ResolvedTypeParameterDeclaration, ResolvedType> inferredTypes = new HashMap<>();
if (lambdaType.isReferenceType()) {
for (com.github.javaparser.utils.Pair<ResolvedTypeParameterDeclaration, ResolvedType> entry : lambdaType.asReferenceType().getTypeParametersMap()) {
if (entry.b.isTypeVariable() && entry.b.asTypeParameter().declaredOnType()) {
ResolvedType ot = t.asReferenceType().typeParametersMap().getValue(entry.a);
lambdaType = lambdaType.replaceTypeVariables(entry.a, ot, inferredTypes);
}
}
} else if (lambdaType.isTypeVariable() && lambdaType.asTypeParameter().declaredOnType()) {
lambdaType = t.asReferenceType().typeParametersMap().getValue(lambdaType.asTypeParameter());
}

Value value = new Value(lambdaType, name);
return Optional.of(value);
}
throw new UnsupportedOperationException("functional method is not present in variable declarator");
return solveLambdaParameter(t, index)
.map(resolvedLamdbaTypeParametre -> Optional.of(new Value(resolvedLamdbaTypeParametre, name)))
.orElseThrow(() -> new UnsupportedOperationException("functional method is not present in variable declarator"));
}
if (parentNode instanceof ReturnStmt) {
ReturnStmt returnStmt = (ReturnStmt) parentNode;
Optional<MethodDeclaration> optDeclaration = returnStmt.findAncestor(MethodDeclaration.class);
if (optDeclaration.isPresent()) {
ResolvedType t = JavaParserFacade.get(typeSolver).convertToUsage(optDeclaration.get().asMethodDeclaration().getType());
Optional<MethodUsage> functionalMethod = FunctionalInterfaceLogic.getFunctionalMethod(t);

if (functionalMethod.isPresent()) {
ResolvedType lambdaType = functionalMethod.get().getParamType(index);

// Replace parameter from declarator
Map<ResolvedTypeParameterDeclaration, ResolvedType> inferredTypes = new HashMap<>();
if (lambdaType.isReferenceType()) {
for (com.github.javaparser.utils.Pair<ResolvedTypeParameterDeclaration, ResolvedType> entry : lambdaType.asReferenceType().getTypeParametersMap()) {
if (entry.b.isTypeVariable() && entry.b.asTypeParameter().declaredOnType()) {
ResolvedType ot = t.asReferenceType().typeParametersMap().getValue(entry.a);
lambdaType = lambdaType.replaceTypeVariables(entry.a, ot, inferredTypes);
}
}
} else if (lambdaType.isTypeVariable() && lambdaType.asTypeParameter().declaredOnType()) {
lambdaType = t.asReferenceType().typeParametersMap().getValue(lambdaType.asTypeParameter());
}

Value value = new Value(lambdaType, name);
return Optional.of(value);
}
throw new UnsupportedOperationException("functional method is not present in return statement");
return solveLambdaParameter(t, index)
.map(resolvedLamdbaTypeParametre -> Optional.of(new Value(resolvedLamdbaTypeParametre, name)))
.orElseThrow(() -> new UnsupportedOperationException("functional method is not present in return expression"));
}
}
if (parentNode instanceof CastExpr) {
CastExpr castExpr = (CastExpr) parentNode;
ResolvedType t = JavaParserFacade.get(typeSolver).convertToUsage(castExpr.getType());
Optional<MethodUsage> functionalMethod = FunctionalInterfaceLogic.getFunctionalMethod(t);

if (functionalMethod.isPresent()) {
ResolvedType lambdaType = functionalMethod.get().getParamType(index);

// Replace parameter from declarator
Map<ResolvedTypeParameterDeclaration, ResolvedType> inferredTypes = new HashMap<>();
if (lambdaType.isReferenceType()) {
for (com.github.javaparser.utils.Pair<ResolvedTypeParameterDeclaration, ResolvedType> entry : lambdaType.asReferenceType().getTypeParametersMap()) {
if (entry.b.isTypeVariable() && entry.b.asTypeParameter().declaredOnType()) {
ResolvedType ot = t.asReferenceType().typeParametersMap().getValue(entry.a);
lambdaType = lambdaType.replaceTypeVariables(entry.a, ot, inferredTypes);
}
}
} else if (lambdaType.isTypeVariable() && lambdaType.asTypeParameter().declaredOnType()) {
lambdaType = t.asReferenceType().typeParametersMap().getValue(lambdaType.asTypeParameter());
}

Value value = new Value(lambdaType, name);
return Optional.of(value);
}
throw new UnsupportedOperationException("functional method is not present in cast expression");
return solveLambdaParameter(t, index)
.map(resolvedLamdbaTypeParametre -> Optional.of(new Value(resolvedLamdbaTypeParametre, name)))
.orElseThrow(() -> new UnsupportedOperationException("functional method is not present in cast expression"));
}
if (parentNode instanceof AssignExpr) {
AssignExpr expr = (AssignExpr) parentNode;
ResolvedType t = expr.calculateResolvedType();
return solveLambdaParameter(t, index)
.map(resolvedLamdbaTypeParametre -> Optional.of(new Value(resolvedLamdbaTypeParametre, name)))
.orElseThrow(() -> new UnsupportedOperationException(
"Unknown node type: " + parentNode.getClass().getSimpleName()));
}
throw new UnsupportedOperationException("Unknown node type: " + parentNode.getClass().getSimpleName());
}
}
Expand All @@ -204,6 +157,33 @@ public Optional<Value> solveSymbolAsValue(String name) {
return solveSymbolAsValueInParentContext(name);
}

/*
* Infers the type of a parameter of a lambda expression
*/
private Optional<ResolvedType> solveLambdaParameter(ResolvedType t, int parameterIndex) {
ResolvedType lambdaType = null;
Optional<MethodUsage> functionalMethod = FunctionalInterfaceLogic.getFunctionalMethod(t);
if (functionalMethod.isPresent()) {
lambdaType = functionalMethod.get().getParamType(parameterIndex);

// Replace parameter from declarator
Map<ResolvedTypeParameterDeclaration, ResolvedType> inferredTypes = new HashMap<>();
if (lambdaType.isReferenceType()) {
for (com.github.javaparser.utils.Pair<ResolvedTypeParameterDeclaration, ResolvedType> entry : lambdaType
.asReferenceType().getTypeParametersMap()) {
if (entry.b.isTypeVariable() && entry.b.asTypeParameter().declaredOnType()) {
ResolvedType ot = t.asReferenceType().typeParametersMap().getValue(entry.a);
lambdaType = lambdaType.replaceTypeVariables(entry.a, ot, inferredTypes);
}
}
} else if (lambdaType.isTypeVariable() && lambdaType.asTypeParameter().declaredOnType()) {
lambdaType = t.asReferenceType().typeParametersMap()
.getValue(lambdaType.asTypeParameter());
}
}
return Optional.ofNullable(lambdaType);
}

@Override
public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name) {
for (Parameter parameter : wrappedNode.getParameters()) {
Expand Down Expand Up @@ -237,7 +217,8 @@ public List<Parameter> parametersExposedToChild(Node child) {
/// Protected methods
///

protected final Optional<Value> solveWithAsValue(SymbolDeclarator symbolDeclarator, String name) {
@Override
protected final Optional<Value> solveWithAsValue(SymbolDeclarator symbolDeclarator, String name) {
for (ResolvedValueDeclaration decl : symbolDeclarator.getSymbolDeclarations()) {
if (decl.getName().equals(name)) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.expr.AssignExpr;
import com.github.javaparser.ast.expr.CastExpr;
import com.github.javaparser.ast.expr.LambdaExpr;
import com.github.javaparser.ast.expr.MethodCallExpr;
Expand Down Expand Up @@ -138,4 +139,27 @@ void solveParameterOfLambdaInCast() {
assertTrue(ref.isPresent());
assertEquals("java.lang.String", ref.get().getType().describe());
}

@Test
// see https://github.com/javaparser/javaparser/issues/4399
void solveParameterOfLambdaInAssignExpr() {

Path src = adaptPath("src/test/resources");
CombinedTypeSolver combinedTypeSolver = new CombinedTypeSolver();
combinedTypeSolver.add(new ReflectionTypeSolver());
combinedTypeSolver.add(new JavaParserTypeSolver(src, new LeanParserConfiguration()));

CompilationUnit cu = parseSample("Lambda", combinedTypeSolver);

com.github.javaparser.ast.body.ClassOrInterfaceDeclaration clazz = Navigator.demandClass(cu, "Agenda");
MethodDeclaration method = Navigator.demandMethod(clazz, "testInAssignExpr");
AssignExpr expr = Navigator.demandNodeOfGivenClass(method, AssignExpr.class);
LambdaExpr lambdaExpr = expr.getValue().asLambdaExpr();

Context context = new LambdaExprContext(lambdaExpr, combinedTypeSolver);

Optional<Value> ref = context.solveSymbolAsValue("p");
assertTrue(ref.isPresent());
assertEquals("java.lang.String", ref.get().getType().describe());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,10 @@ public class Agenda {
public void testCast(){
Object a = (Lambda) p -> p.toLowerCase();
}

public void testInAssignExpr() {
Lambda a;
a = p -> p.toLowerCase();
}

}

0 comments on commit 13487dd

Please sign in to comment.