Skip to content

Commit

Permalink
False type mismatch on array initializer with null annotations
Browse files Browse the repository at this point in the history
(regression 2023-03)

fixes #1007
  • Loading branch information
stephan-herrmann committed May 16, 2023
1 parent 354897d commit c5bf8ba
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -198,14 +198,23 @@ public TypeBinding resolveTypeExpecting(BlockScope scope, TypeBinding expectedTy
if (TypeBinding.notEquals(elementType, expressionType)) // must call before computeConversion() and typeMismatchError()
scope.compilationUnitScope().recordTypeConversion(elementType, expressionType);

if (expression.isConstantValueOfTypeAssignableToType(expressionType, elementType)
|| expressionType.isCompatibleWith(elementType)) {
expression.computeConversion(scope, elementType, expressionType);
} else if (isBoxingCompatible(expressionType, elementType, expression, scope)) {
expression.computeConversion(scope, elementType, expressionType);
} else {
scope.problemReporter().typeMismatchError(expressionType, elementType, expression, null);
}
// type checking MemberValuePairs of an annotation may need deferring if a relevant type has not yet connected its hierarchy:
DeferrableCheck typeChecking = new DeferrableCheck(
// enablement:
() -> (!expressionType.hasUnconnectedSourceHierarchy() && !elementType.hasUnconnectedSourceHierarchy()),
// task:
() -> {
if (expression.isConstantValueOfTypeAssignableToType(expressionType, elementType)
|| expressionType.isCompatibleWith(elementType)) {
expression.computeConversion(scope, elementType, expressionType);
} else if (isBoxingCompatible(expressionType, elementType, expression, scope)) {
expression.computeConversion(scope, elementType, expressionType);
} else {
scope.problemReporter().typeMismatchError(expressionType, elementType, expression, null);
}
});
if (!typeChecking.tryRun())
scope.environment().addDeferredCheck(typeChecking);
}
return this.binding;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1466,6 +1466,7 @@ void connectTypeHierarchy() {
compilationUnitScopeLocal.connectingHierarchy = wasAlreadyConnecting;
}
LookupEnvironment env = environment();
env.runDeferredChecks();
try {
env.missingClassFileLocation = this.referenceContext;
checkForInheritedMemberTypes(sourceType);
Expand Down Expand Up @@ -1519,6 +1520,7 @@ private void connectTypeHierarchyWithoutMembers() {
sourceType.tagBits |= TagBits.TypeVariablesAreConnected;
if (noProblems && sourceType.isHierarchyInconsistent())
problemReporter().hierarchyHasProblems(sourceType);
environment().runDeferredChecks();
} finally {
compilationUnitScopeLocal.connectingHierarchy = wasAlreadyConnecting;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*******************************************************************************
* Copyright (c) 2023 GK Software SE and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Stephan Herrmann - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;

import java.util.function.Supplier;

public class DeferrableCheck {
final Supplier<Boolean> enablement;
final Runnable task;
public DeferrableCheck(Supplier<Boolean> enablement, Runnable checkTask) {
this.enablement = enablement;
this.task = checkTask;
}
public boolean tryRun() {
if (this.enablement.get()) {
this.task.run();
return true;
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -148,6 +149,8 @@ public class LookupEnvironment implements ProblemReasons, TypeConstants {

public String moduleVersion; // ROOT_ONLY

List<DeferrableCheck> deferredChecks = new ArrayList<>(); // SHARED

final static int BUILD_FIELDS_AND_METHODS = 4;
final static int BUILD_TYPE_HIERARCHY = 1;
final static int CHECK_AND_SET_IMPORTS = 2;
Expand Down Expand Up @@ -204,6 +207,7 @@ public LookupEnvironment(ITypeRequestor typeRequestor, CompilerOptions globalOpt
this.typeSystem = rootEnv.typeSystem;
// knownModules is unused in specific LookupEnvironments
this.useModuleSystem = rootEnv.useModuleSystem;
this.deferredChecks = rootEnv.deferredChecks;
}

// NOTE: only for resolving!
Expand Down Expand Up @@ -2323,4 +2327,15 @@ public Binding getInaccessibleBinding(char[][] compoundName, ModuleBinding clien
}
return null;
}

public void addDeferredCheck(DeferrableCheck check) {
this.deferredChecks.add(check);
}
public void runDeferredChecks() {
for (Iterator<DeferrableCheck> iterator = this.deferredChecks.iterator(); iterator.hasNext();) {
DeferrableCheck deferrableCheck = iterator.next();
if (deferrableCheck.tryRun())
iterator.remove();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1062,6 +1062,24 @@ public boolean isHierarchyConnected() {
return this.superclass != null && this.superInterfaces != null;
}

@Override
public boolean hasUnconnectedSourceHierarchy() {
if (this.type.hasUnconnectedSourceHierarchy())
return true;
if (this.arguments != null) {
enterRecursiveFunction();
try {
for (TypeBinding argument : this.arguments) {
if (argument == null || argument.hasUnconnectedSourceHierarchy())
return true;
}
} finally {
exitRecursiveFunction();
}
}
return false;
}

@Override
public boolean isProperType(boolean admitCapture18) {
if (this.arguments != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@
import org.eclipse.jdt.internal.compiler.ast.LambdaExpression;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.NullAnnotationMatching;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.impl.JavaFeature;
Expand Down Expand Up @@ -1356,27 +1355,6 @@ public boolean implementsInterface(ReferenceBinding anInterface, boolean searchH
}
}
}
// see https://github.com/eclipse-jdt/eclipse.jdt.core/issues/629
if (nextPosition == 0 && this instanceof SourceTypeBinding) {
SourceTypeBinding sourceType = (SourceTypeBinding) this;
if (sourceType.scope != null && sourceType.scope.referenceContext != null && sourceType.scope.compilerOptions().isAnnotationBasedNullAnalysisEnabled) {
TypeReference[] references = sourceType.scope.referenceContext.superInterfaces;
if (references == null || references.length == 0) {
return false;
}
for (TypeReference reference: references) {
if (!(reference.resolvedType instanceof ReferenceBinding)) {
reference.resolveType(sourceType.scope);
}
if (reference.resolvedType instanceof ReferenceBinding) {
ReferenceBinding binding = (ReferenceBinding) reference.resolvedType;
if (binding.isEquivalentTo(anInterface)) {
return true;
}
}
}
}
}
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2163,6 +2163,10 @@ public boolean isHierarchyConnected() {
return (this.tagBits & TagBits.EndHierarchyCheck) != 0;
}
@Override
public boolean hasUnconnectedSourceHierarchy() {
return !isHierarchyConnected();
}
@Override
public boolean isRepeatableAnnotationType() {
if (!isPrototype()) throw new IllegalStateException();
return this.containerAnnotationType != null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1765,6 +1765,14 @@ public void exitRecursiveFunction() {
public boolean isFunctionalType() {
return false;
}

/**
* Returns true if the type or one of its constituents is a SourceTypeBinding that has not yet connected its hierarchy
*/
public boolean hasUnconnectedSourceHierarchy() {
return false;
}

/**
* Refresh some tagBits from details into the main type.
* Currently handled: TagBits.HasNullTypeAnnotation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18748,4 +18748,29 @@ public void testRedundantNonNull_field() {
runner.classLibraries = this.LIBS;
runner.runWarningTest();
}
public void testGH1007_srikanth() {
Runner runner = new Runner();
runner.testFiles =
new String[] {
"SubClass.java",
"// ECJ error in next line: Type mismatch: cannot convert from Class<SubClass> to Class<? extends SuperClass>[]\n" +
"@AnnotationWithArrayInitializer(annotationArgument = SubClass.class)\n" +
"class AnnotatedClass2 extends AnnotatedSuperClass {}\n" +
"\n" +
"//ECJ error in next line: Type mismatch: cannot convert from Class<SubClass> to Class<? extends SuperClass>\n" +
"@AnnotationWithArrayInitializer(annotationArgument = {SubClass.class})\n" +
"class AnnotatedClass extends AnnotatedSuperClass {}\n" +
"\n" +
"\n" +
"class AnnotatedSuperClass {}\n" +
"\n" +
"@interface AnnotationWithArrayInitializer {\n" +
" Class<? extends SuperClass>[] annotationArgument();\n" +
"}\n" +
"\n" +
"class SubClass extends SuperClass {}\n" +
"abstract class SuperClass {}"
};
runner.runConformTest();
}
}

0 comments on commit c5bf8ba

Please sign in to comment.