Skip to content

Commit

Permalink
Overly defensive short circuiting of lambda exception analysis result…
Browse files Browse the repository at this point in the history
…s in compilation failure (#1181)

* Fixes https://bugs.eclipse.org/bugs/show_bug.cgi?id=547231
* Fixes #2065
  • Loading branch information
srikanth-sankaran committed Mar 23, 2024
1 parent 696ceaf commit cd3c1b5
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 86 deletions.
Expand Up @@ -725,11 +725,6 @@ public void tagAsHavingErrors() {
this.ignoreFurtherInvestigation = true;
}

@Override
public void tagAsHavingIgnoredMandatoryErrors(int problemId) {
// Nothing to do for this context;
}

public void traverse(
ASTVisitor visitor,
ClassScope classScope) {
Expand Down
Expand Up @@ -778,11 +778,6 @@ public void tagAsHavingErrors() {
this.ignoreFurtherInvestigation = true;
}

@Override
public void tagAsHavingIgnoredMandatoryErrors(int problemId) {
// Nothing to do for this context;
}

public void traverse(ASTVisitor visitor, CompilationUnitScope unitScope) {
traverse(visitor, unitScope, true);
}
Expand Down
Expand Up @@ -56,7 +56,6 @@

import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.ClassFile;
import org.eclipse.jdt.internal.compiler.CompilationResult;
Expand Down Expand Up @@ -128,7 +127,6 @@ public class LambdaExpression extends FunctionalExpression implements IPolyExpre
public boolean hasOuterClassMemberReference = false;
private int outerLocalVariablesSlotSize = 0;
private boolean assistNode = false;
private boolean hasIgnoredMandatoryErrors = false;
private ReferenceBinding classType;
private Set thrownExceptions;
private static final SyntheticArgumentBinding [] NO_SYNTHETIC_ARGUMENTS = new SyntheticArgumentBinding[0];
Expand Down Expand Up @@ -808,17 +806,6 @@ public MethodScope getScope() {
return this.scope;
}

private boolean enclosingScopesHaveErrors() {
Scope skope = this.enclosingScope;
while (skope != null) {
ReferenceContext context = skope.referenceContext();
if (context != null && context.hasErrors())
return true;
skope = skope.parent;
}
return false;
}

private void analyzeShape() { // Simple minded analysis for code assist & potential compatibility.
class ShapeComputer extends ASTVisitor {
@Override
Expand Down Expand Up @@ -1022,8 +1009,7 @@ private LambdaExpression cachedResolvedCopy(TypeBinding targetType, boolean anyT
if (!requireExceptionAnalysis)
return copy;
if (copy.thrownExceptions == null)
if (!copy.hasIgnoredMandatoryErrors && !enclosingScopesHaveErrors())
copy.analyzeExceptions();
copy.analyzeExceptions();
return copy;
} finally {
this.enclosingScope.problemReporter().switchErrorHandlingPolicy(oldPolicy);
Expand Down Expand Up @@ -1231,42 +1217,6 @@ public void tagAsHavingErrors() {
}
}

@Override
public void tagAsHavingIgnoredMandatoryErrors(int problemId) {
switch (problemId) {
// 15.27.3 requires exception throw related errors to not influence congruence. Other errors should. Also don't abort shape analysis.
case IProblem.UnhandledExceptionOnAutoClose:
case IProblem.UnhandledExceptionInDefaultConstructor:
case IProblem.UnhandledException:
return;
/* The following structural problems can occur only because of target type imposition. Filter, so we can distinguish inherent errors
in explicit lambdas. This is to help decide whether to proceed with data/control flow analysis to discover shape. In case of inherent
errors, we will not call analyze code as it is not prepared to analyze broken programs.
*/
case IProblem.VoidMethodReturnsValue:
case IProblem.ShouldReturnValueHintMissingDefault:
case IProblem.ShouldReturnValue:
case IProblem.ReturnTypeMismatch:
case IProblem.IncompatibleLambdaParameterType:
case IProblem.lambdaParameterTypeMismatched:
case IProblem.lambdaSignatureMismatched:
case IProblem.LambdaDescriptorMentionsUnmentionable:
case IProblem.TargetTypeNotAFunctionalInterface:
case IProblem.illFormedParameterizationOfFunctionalInterface:
case IProblem.NoGenericLambda:
return;
default:
this.hasIgnoredMandatoryErrors = true;
MethodScope enclosingLambdaScope = this.scope == null ? null : this.scope.enclosingLambdaScope();
while (enclosingLambdaScope != null) {
LambdaExpression enclosingLambda = (LambdaExpression) enclosingLambdaScope.referenceContext;
enclosingLambda.hasIgnoredMandatoryErrors = true;
enclosingLambdaScope = enclosingLambdaScope.enclosingLambdaScope();
}
return;
}
}

public Set<TypeBinding> getThrownExceptions() {
if (this.thrownExceptions == null)
return Collections.emptySet();
Expand Down
Expand Up @@ -455,11 +455,6 @@ public void tagAsHavingErrors() {
this.ignoreFurtherInvestigation = true;
}

@Override
public void tagAsHavingIgnoredMandatoryErrors(int problemId) {
// Nothing to do for this context;
}

public String getModuleVersion() {
if (this.scope != null) {
LookupEnvironment env = this.scope.environment().root;
Expand Down
Expand Up @@ -1646,11 +1646,6 @@ public void tagAsHavingErrors() {
this.ignoreFurtherInvestigation = true;
}

@Override
public void tagAsHavingIgnoredMandatoryErrors(int problemId) {
// Nothing to do for this context;
}

/**
* Iteration for a package member type
*/
Expand Down
Expand Up @@ -34,7 +34,4 @@ public interface ReferenceContext {
boolean hasErrors();

void tagAsHavingErrors();

void tagAsHavingIgnoredMandatoryErrors(int problemId);

}
Expand Up @@ -138,8 +138,6 @@ public void handle(
return; // ignore non reportable warning
}
}
if (mandatory)
referenceContext.tagAsHavingIgnoredMandatoryErrors(problemId);
return;
}

Expand Down
Expand Up @@ -7972,8 +7972,8 @@ public void testGH1060() {
);
}

//https://bugs.eclipse.org/bugs/show_bug.cgi?id=546161
//LambdaConversionException due to invalid instantiated method type argument to LambdaMetafactory::metafactory
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=546161
// LambdaConversionException due to invalid instantiated method type argument to LambdaMetafactory::metafactory
public void testBug546161() {
this.runConformTest(
new String[] {
Expand All @@ -7994,9 +7994,77 @@ public void testBug546161() {
""
);
}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=547231
// Logging inside lambda fails with error
public void testBug547231() {
this.runConformTest(
new String[] {
"Problematic.java",
"import java.io.IOException;\n" +
"import java.nio.file.Path;\n" +
"import java.util.Collections;\n" +
"import java.util.Set;\n" +
"import java.util.function.Consumer;\n" +
"import java.util.logging.Level;\n" +
"import java.util.logging.Logger;\n" +
"\n" +
"public class Problematic {\n" +
"\n" +
" Logger LOGGER = Logger.getLogger(Problematic.class.getName());\n" +
"\n" +
" @FunctionalInterface\n" +
" private interface ThrowingConsumer<T, E extends Throwable> {\n" +
" void accept(T t) throws E;\n" +
" }\n" +
"\n" +
" private class FileAsset {\n" +
" public FileAsset move(String path) throws IOException {\n" +
" System.out.println(path);\n" +
" return null;\n" +
" }\n" +
"\n" +
" public Path getPath() {\n" +
" return null;\n" +
" }\n" +
" }\n" +
"\n" +
" static <T, E extends Exception> void process(Consumer<Consumer<T>> code, ThrowingConsumer<T, E> throwingConsumer)\n" +
" throws E {\n" +
" code.accept(t -> {\n" +
" try {\n" +
" throwingConsumer.accept(t);\n" +
" } catch (Exception e) {\n" +
" e.printStackTrace();\n" +
" }\n" +
" });\n" +
" }\n" +
"\n" +
" public void execute(String path) throws IOException {\n" +
" Set<FileAsset> set = Collections.singleton(new FileAsset());\n" +
" process(set::forEach, (a) -> {\n" +
" process(set::forEach, (b) -> {\n" +
" process(set::forEach, (c) -> {\n" +
" c.move(path);\n" +
"\n" +
" // produces error: Unhandled exception type IOException\n" +
" LOGGER.log(Level.FINE, () -> \"Moved \" + c.getPath() + \" to \" + a);\n" +
"\n" +
" // no error\n" +
" LOGGER.log(Level.FINE, \"Moved \" + c.getPath() + \" to \" + a);\n" +
" });\n" +
" });\n" +
" });\n" +
" }\n" +
" public static void main(String [] args) {\n" +
" System.out.println(\"OK\");\n" +
" }\n" +
"}\n"},
"OK"
);
}

//https://bugs.eclipse.org/bugs/show_bug.cgi?id=546161
//LambdaConversionException due to invalid instantiated method type argument to LambdaMetafactory::metafactory
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=546161
// LambdaConversionException due to invalid instantiated method type argument to LambdaMetafactory::metafactory
public void testBug546161_2() {
this.runConformTest(
new String[] {
Expand All @@ -8016,8 +8084,8 @@ public void testBug546161_2() {
);
}

//https://bugs.eclipse.org/bugs/show_bug.cgi?id=574269
//java.lang.invoke.LambdaConversionException: Invalid receiver type class java.lang.Object
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=574269
// java.lang.invoke.LambdaConversionException: Invalid receiver type class java.lang.Object
public void testBug574269() {
this.runConformTest(
new String[] {
Expand Down Expand Up @@ -8087,8 +8155,8 @@ public void testBug574269() {
);
}

//https://bugs.eclipse.org/bugs/show_bug.cgi?id=574269
//java.lang.invoke.LambdaConversionException: Invalid receiver type class java.lang.Object
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=574269
// java.lang.invoke.LambdaConversionException: Invalid receiver type class java.lang.Object
public void testBug574269_2() {
this.runConformTest(
new String[] {
Expand Down Expand Up @@ -8134,8 +8202,8 @@ public void testBug574269_2() {
);
}

//https://bugs.eclipse.org/bugs/show_bug.cgi?id=570511
//java.lang.BootstrapMethodError in Eclipse compiled code that doesn't happen with javac
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=570511
// java.lang.BootstrapMethodError in Eclipse compiled code that doesn't happen with javac
public void testBug570511() {
this.runConformTest(
new String[] {
Expand Down Expand Up @@ -8390,6 +8458,82 @@ public void testBug576252() {
"}\n"},
"class LambdaTest");
}

// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2065
// Eclipse compiler incorrectly reports unhandled exceptions on lamba code
public void testIssue2065() {

if (this.complianceLevel < ClassFileConstants.JDK10)
return;

this.runConformTest(
new String[] {
"Foobar.java",
"""
import java.nio.file.*;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Foobar {
@FunctionalInterface
public interface FailableSupplier<T, E extends Throwable> {
T get() throws E;
}
public static <T, E extends Throwable> T get(final FailableSupplier<T, E> supplier) {
try {
return supplier.get();
} catch (final Throwable t) {
throw new RuntimeException(t);
}
}
@FunctionalInterface
public interface FailableFunction<T, R, E extends Throwable> {
R apply(T input) throws E;
}
public static <T, R, E extends Throwable> R apply(final FailableFunction<T, R, E> function, final T input) {
return get(() -> function.apply(input));
}
public static <T, R> Function<T, R> asFunction(final FailableFunction<T, R, ?> function) {
return input -> apply(function, input);
}
public static Set<Class<?>> getFoobarClasses(final String packageName, final ClassLoader classLoader) {
return get(() -> {
final var resourcePath = packageName.replace('.', '/');
var resource = classLoader.getResource(resourcePath).toURI();
FileSystem fileSystem = null;
try {
if ("jar".equals(resource.getScheme())) {
fileSystem = FileSystems.newFileSystem(resource, Collections.emptyMap());
}
final var localPath = Paths.get(resource);
return Files.list(localPath)
.map(file -> file.getFileName().toString())
.filter(filename -> filename.endsWith(".class"))
.filter(filename -> !filename.contains("Foobar"))
.map(asFunction(fileName -> {
final var qualifiedClassName = packageName + '.' + fileName.substring(0, fileName.lastIndexOf(".class")).replace('/', '.');
return (Class<?>) Class.forName(qualifiedClassName, false, classLoader);
}))
.collect(Collectors.toUnmodifiableSet());
} finally {
if (fileSystem != null) {
fileSystem.close();
}
}
});
}
}
"""},
"");
}


public static Class testClass() {
return LambdaExpressionsTest.class;
}
Expand Down

0 comments on commit cd3c1b5

Please sign in to comment.