Skip to content

Commit

Permalink
Functional interfaces should not include a method if it's not used (#334
Browse files Browse the repository at this point in the history
)

This PR fixes an issue present with na-791a where, even though a
functional interface's method is not used, it is still being included in
the output, causing compilation errors. Since the `@FunctionalInterface`
annotation is removed, we should treat all interfaces equally.
  • Loading branch information
theron-wang authored Jul 26, 2024
1 parent 3e836d3 commit ac79474
Show file tree
Hide file tree
Showing 10 changed files with 86 additions and 8 deletions.
16 changes: 8 additions & 8 deletions src/main/java/org/checkerframework/specimin/PrunerVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,14 @@ public Visitable visit(MethodDeclaration methodDecl, Void p) {
return super.visit(methodDecl, p);
}

if (insideFunctionalInterface && usedMembers.contains(signature)) {
if (methodDecl.getBody().isPresent()) {
// avoid introducing unsolved symbols into the final output.
methodDecl.setBody(StaticJavaParser.parseBlock("{ throw new Error(); }"));
}
return methodDecl;
}

if (usedMembers.contains(signature) || isAResolvedYetStuckMethod(methodDecl)) {
boolean isMethodInsideInterface = isInsideInterface(methodDecl);
// do nothing if methodDecl is just a method signature in a class.
Expand All @@ -348,14 +356,6 @@ public Visitable visit(MethodDeclaration methodDecl, Void p) {
return methodDecl;
}

if (insideFunctionalInterface) {
if (methodDecl.getBody().isPresent()) {
// avoid introducing unsolved symbols into the final output.
methodDecl.setBody(StaticJavaParser.parseBlock("{ throw new Error(); }"));
}
return methodDecl;
}

// if insideTargetMethod is true, this current method declaration belongs to an anonnymous
// class inside the target method.
if (!insideTargetMember) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.github.javaparser.ast.type.Type;
import com.github.javaparser.ast.type.UnionType;
import com.github.javaparser.ast.visitor.Visitable;
import com.github.javaparser.resolution.MethodUsage;
import com.github.javaparser.resolution.UnsolvedSymbolException;
import com.github.javaparser.resolution.declarations.ResolvedConstructorDeclaration;
import com.github.javaparser.resolution.declarations.ResolvedEnumConstantDeclaration;
Expand Down Expand Up @@ -470,6 +471,18 @@ public Visitable visit(MethodCallExpr call, Void p) {
Expression arg = call.getArgument(i);
if (arg.isLambdaExpr()) {
updateUsedClassBasedOnType(decl.getParam(i).getType());
// We should mark the abstract method for preservation as well
if (decl.getParam(i).getType().isReferenceType()) {
ResolvedReferenceType functionalInterface =
decl.getParam(i).getType().asReferenceType();
for (MethodUsage method : functionalInterface.getDeclaredMethods()) {
if (method.getDeclaration().isAbstract()) {
preserveMethodDecl(method.getDeclaration());
// Only one abstract method per functional interface
break;
}
}
}
}
}
}
Expand Down
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;

/**
* This test checks that if a functional interface has an unused method, it is removed, and if a
* synthetic functional interface method is generated, it is preserved.
*/
public class UnusedFuncInterfaceMethodTest {
@Test
public void runTest() throws IOException {
SpeciminTestExecutor.runTestWithoutJarPaths(
"unusedfuncinterfacemethod",
new String[] {"com/example/Simple.java"},
new String[] {"com.example.Simple#foo()"});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.example;

public class Bar {

public static ComExampleBarUseReturnType use(SyntheticFunction4<?, ?, ?, ?, ?> parameter0) {
throw new Error();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.example;

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

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

public class Simple implements FuncInterfaceUnused {

public void foo() {
Bar.use((a, b, c, d) -> 0);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.example;

public interface SyntheticFunction4<T, T1, T2, T3, T4> {

public T4 apply(T parameter0, T1 parameter1, T2 parameter2, T3 parameter3);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.example;

@FunctionalInterface
public interface FuncInterfaceUnused {
void shouldBeRemoved();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example;

public class Simple implements FuncInterfaceUnused {
public void foo() {
Bar.use((a, b, c, d) -> 0);
}

public void shouldBeRemoved() {

}
}

0 comments on commit ac79474

Please sign in to comment.