Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid crashing when encountering method references #290

Merged
merged 11 commits into from
Jun 5, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.github.javaparser.ast.expr.InstanceOfExpr;
import com.github.javaparser.ast.expr.LambdaExpr;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.MethodReferenceExpr;
import com.github.javaparser.ast.expr.NameExpr;
import com.github.javaparser.ast.expr.ObjectCreationExpr;
import com.github.javaparser.ast.expr.PatternExpr;
Expand Down Expand Up @@ -1069,6 +1070,36 @@ public Visitable visit(FieldAccessExpr node, Void p) {
return super.visit(node, p);
}

@Override
public Visitable visit(MethodReferenceExpr node, Void p) {
if (insideTargetMember) {
// TODO: handle all of the possible forms listed in JLS 15.13, not just the simplest
Expression scope = node.getScope();
if (scope.isTypeExpr()) {
Type scopeAsType = scope.asTypeExpr().getType();
String scopeAsTypeFQN = scopeAsType.asString();
if (!isAClassPath(scopeAsTypeFQN) && scopeAsType.isClassOrInterfaceType()) {
scopeAsTypeFQN =
getQualifiedNameForClassOrInterfaceType(scopeAsType.asClassOrInterfaceType());
}
if (classfileIsInOriginalCodebase(scopeAsTypeFQN)) {
addedTargetFiles.add(qualifiedNameToFilePath(scopeAsTypeFQN));
} else {
// TODO: create a synthetic class?
}
}
String identifier = node.getIdentifier();
// can be either the name of a method or "new"
if ("new".equals(identifier)) {
// TODO: figure out how to handle this case
System.err.println("Specimin warning: new in method references is not supported: " + node);
return super.visit(node, p);
}
potentialUsedMembers.add(identifier);
}
return super.visit(node, p);
}

@Override
public Visitable visit(MethodCallExpr method, Void p) {
/*
Expand Down Expand Up @@ -2169,8 +2200,8 @@ public static boolean canSolveArguments(NodeList<Expression> argList) {
return true;
}
for (Expression arg : argList) {
if (arg.isLambdaExpr()) {
// Skip lambdas here and treat them specially later.
if (arg.isLambdaExpr() || arg.isMethodReferenceExpr()) {
// Skip lambdas and method refs here and treat them specially later.
continue;
}
if (!canBeSolved(arg)) {
Expand Down Expand Up @@ -2263,6 +2294,12 @@ private List<String> getArgumentTypesImpl(
LambdaExpr lambda = arg.asLambdaExpr();
parametersList.add(resolveLambdaType(lambda, pkgName));
continue;
} else if (arg.isMethodReferenceExpr()) {
// TODO: is there a better way to handle this? How should we know
// what the type is? The method ref is sometimes not solvable here.
// Maybe we will need to handle this in JavaTypeCorrect?
parametersList.add("java.util.function.Supplier<?>");
continue;
}

ResolvedType type = arg.calculateResolvedType();
Expand Down
18 changes: 18 additions & 0 deletions src/test/java/org/checkerframework/specimin/MethodRef2Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.checkerframework.specimin;

import java.io.IOException;
import org.junit.Test;

/**
* A test for a crash related to method signatures that occurred in Randoop:
* https://github.com/njit-jerse/specimin/issues/154
*/
public class MethodRef2Test {
@Test
public void runTest() throws IOException {
SpeciminTestExecutor.runTestWithoutJarPaths(
"methodref2",
new String[] {"com/example/Simple.java"},
new String[] {"com.example.Simple#bar()"});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.example;

public class MethodSignature {

public String toString() {
throw new Error();
}
}
15 changes: 15 additions & 0 deletions src/test/resources/methodref2/expected/com/example/Simple.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.example;

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

import org.plumelib.util.CollectionsPlume;

class Simple {

Map<MethodSignature, MethodSignature> replacementMap;

void bar() {
List<String> signatureList = CollectionsPlume.mapList(MethodSignature::toString, replacementMap.keySet());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.plumelib.util;

public class CollectionsPlume {

public static OrgPlumelibUtilCollectionsPlumeMapListReturnType mapList(java.util.function.Supplier<?> parameter0, java.util.Set<?> parameter1) {
throw new Error();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package org.plumelib.util;

public class OrgPlumelibUtilCollectionsPlumeMapListReturnType {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.example;

public class MethodSignature {
@Override
public String toString() {
return "a string";
}
}
17 changes: 17 additions & 0 deletions src/test/resources/methodref2/input/com/example/Simple.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.example;

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

import org.plumelib.util.CollectionsPlume;

class Simple {

Map<MethodSignature, MethodSignature> replacementMap;

// Target method.
void bar() {
List<String> signatureList =
CollectionsPlume.mapList(MethodSignature::toString, replacementMap.keySet());
}
}
3 changes: 3 additions & 0 deletions typecheck_test_outputs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ for testcase in * ; do
if [ "${testcase}" = "shared" ]; then continue; fi
# https://bugs.openjdk.org/browse/JDK-8319461 wasn't actually fixed (this test is based on that bug)
if [ "${testcase}" = "superinterfaceextends" ]; then continue; fi
# incomplete handling of method references: https://github.com/njit-jerse/specimin/issues/291
# this test exists to check that no crash occurs, not that Specimin produces the correct output
if [ "${testcase}" = "methodref2" ]; then continue; fi
cd "${testcase}/expected/" || exit 1
# javac relies on word splitting
# shellcheck disable=SC2046
Expand Down
Loading