Skip to content

Commit

Permalink
tests for missing method return type (positive / negative)
Browse files Browse the repository at this point in the history
+ ignore missing return type if not needed for further resolving
  - distinguish by expression context (vanilla -> not needed)
  - but for send in receiver position report despite vanilla context
+ avoid resolving MemberValuePair.value in expressionContext VANILLA
  • Loading branch information
stephan-herrmann committed Jun 22, 2024
1 parent f474a32 commit 23a6953
Show file tree
Hide file tree
Showing 9 changed files with 175 additions and 31 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2000, 2023 IBM Corporation and others.
* Copyright (c) 2000, 2024 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -4485,7 +4485,7 @@ public int generateMethodInfoAttributes(MethodBinding methodBinding) {
}
}
if ((methodBinding.tagBits & TagBits.HasMissingType) != 0) {
this.missingTypes = methodBinding.collectMissingTypes(this.missingTypes);
this.missingTypes = methodBinding.collectMissingTypes(this.missingTypes, true);
}
return attributesNumber;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
* Copyright (c) 2000, 2024 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -208,6 +208,9 @@ public TypeBinding resolveType(BlockScope scope) {
&& ((CastExpression)this.receiver).innermostCastedExpression() instanceof NullLiteral) {
this.receiver.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on
}
if (this.receiver instanceof MessageSend) {
((MessageSend) this.receiver).isReceiver = true;
}
TypeBinding arrayType = this.receiver.resolveType(scope);
if (arrayType != null) {
this.receiver.computeConversion(scope, arrayType, arrayType);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2000, 2018 IBM Corporation and others.
* Copyright (c) 2000, 2024 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -661,6 +661,9 @@ public TypeBinding resolveType(BlockScope scope) {
this.receiver.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on
receiverCast = true;
}
if (this.receiver instanceof MessageSend) {
((MessageSend) this.receiver).isReceiver = true;
}
this.actualReceiverType = this.receiver.resolveType(scope);
if (this.actualReceiverType == null) {
this.constant = Constant.NotAConstant;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2000, 2017 IBM Corporation and others.
* Copyright (c) 2000, 2024 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -69,6 +69,8 @@ public void resolveTypeExpecting(BlockScope scope, TypeBinding requiredType) {
this.compilerElementPair = new ElementValuePair(this.name, this.value, this.binding);
return;
}
// the following is not really useful, but let's humor the resolution even if value is not a constant
this.value.setExpressionContext(ExpressionContext.ASSIGNMENT_CONTEXT);
if (requiredType == null) {
// fault tolerance: keep resolving
if (this.value instanceof ArrayInitializer) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ public class MessageSend extends Expression implements IPolyExpression, Invocati
public TypeReference[] typeArguments;
public TypeBinding[] genericTypeArguments;
public ExpressionContext expressionContext = VANILLA_CONTEXT;
public boolean isReceiver = false;

// hold on to this context from invocation applicability inference until invocation type inference (per method candidate):
private SimpleLookupTable/*<PGMB,InferenceContext18>*/ inferenceContexts;
Expand Down Expand Up @@ -811,6 +812,9 @@ public TypeBinding resolveType(BlockScope scope) {
if (this.receiver instanceof CastExpression) {
this.receiver.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on
}
if (this.receiver instanceof MessageSend) {
((MessageSend) this.receiver).isReceiver = true;
}
this.actualReceiverType = this.receiver.resolveType(scope);
if (this.actualReceiverType instanceof InferenceVariable) {
return null; // not yet ready for resolving
Expand Down Expand Up @@ -1113,6 +1117,10 @@ protected boolean isUnnecessaryReceiverCast(BlockScope scope, TypeBinding uncast
}

protected boolean isMissingTypeRelevant() {
if (this.expressionContext == ExpressionContext.VANILLA_CONTEXT && !this.isReceiver) {
if (this.binding.collectMissingTypes(null, false) == null)
return false; // only irrelevant return type is missing
}
if ((this.binding.returnType.tagBits & TagBits.HasMissingType) == 0
&& this.binding.isVarargs()) {
if (this.arguments.length < this.binding.parameters.length) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -413,9 +413,11 @@ public final boolean canBeSeenBy(TypeBinding receiverType, InvocationSite invoca
return false;
}

public List<TypeBinding> collectMissingTypes(List<TypeBinding> missingTypes) {
public List<TypeBinding> collectMissingTypes(List<TypeBinding> missingTypes, boolean considerReturnType) {
if ((this.tagBits & TagBits.HasMissingType) != 0) {
missingTypes = this.returnType.collectMissingTypes(missingTypes);
if (considerReturnType) {
missingTypes = this.returnType.collectMissingTypes(missingTypes);
}
for (TypeBinding parameter : this.parameters) {
missingTypes = parameter.collectMissingTypes(missingTypes);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6951,7 +6951,7 @@ public void missingSynchronizedOnInheritedMethod(MethodBinding currentMethod, Me
currentMethod.sourceEnd());
}
public void missingTypeInConstructor(ASTNode location, MethodBinding constructor) {
List<TypeBinding> missingTypes = constructor.collectMissingTypes(null);
List<TypeBinding> missingTypes = constructor.collectMissingTypes(null, true);
if (missingTypes == null) {
System.err.println("The constructor " + constructor + " is wrongly tagged as containing missing types"); //$NON-NLS-1$ //$NON-NLS-2$
return;
Expand Down Expand Up @@ -6984,7 +6984,7 @@ public void missingTypeInConstructor(ASTNode location, MethodBinding constructor
public void missingTypeInLambda(LambdaExpression lambda, MethodBinding method) {
int nameSourceStart = lambda.sourceStart();
int nameSourceEnd = lambda.diagnosticsSourceEnd();
List<TypeBinding> missingTypes = method.collectMissingTypes(null);
List<TypeBinding> missingTypes = method.collectMissingTypes(null, true);
if (missingTypes == null) {
System.err.println("The lambda expression " + method + " is wrongly tagged as containing missing types"); //$NON-NLS-1$ //$NON-NLS-2$
return;
Expand All @@ -7011,7 +7011,7 @@ public void missingTypeInMethod(ASTNode astNode, MethodBinding method) {
nameSourceStart = astNode.sourceStart;
nameSourceEnd = astNode.sourceEnd;
}
List<TypeBinding> missingTypes = method.collectMissingTypes(null);
List<TypeBinding> missingTypes = method.collectMissingTypes(null, true);
if (missingTypes == null) {
System.err.println("The method " + method + " is wrongly tagged as containing missing types"); //$NON-NLS-1$ //$NON-NLS-2$
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1907,7 +1907,7 @@ public void test059() {
" Zork bb() {\n" +
" }\n" +
" void cc() {\n" +
" this.bb();\n" +
" Object o = this.bb();\n" +
" }\n" +
" public static void main(String[] args) {\n" +
" System.out.println(\"SUCCESS\");\n" +
Expand All @@ -1921,8 +1921,8 @@ public void test059() {
"Zork cannot be resolved to a type\n" +
"----------\n" +
"2. ERROR in X.java (at line 10)\n" +
" this.bb();\n" +
" ^^\n" +
" Object o = this.bb();\n" +
" ^^\n" +
"The method bb() from the type X refers to the missing type Zork\n" +
"----------\n");
}
Expand All @@ -1940,7 +1940,7 @@ public void test060() {
" Zork bb() {\n" +
" }\n" +
" void cc() {\n" +
" this.bb();\n" +
" Object o = this.bb();\n" +
" }\n" +
" public static void main(String[] args) {\n" +
" System.out.println(\"SUCCESS\");\n" +
Expand All @@ -1959,8 +1959,8 @@ public void test060() {
"Zork cannot be resolved to a type\n" +
"----------\n" +
"3. ERROR in X.java (at line 10)\n" +
" this.bb();\n" +
" ^^\n" +
" Object o = this.bb();\n" +
" ^^\n" +
"The method bb() from the type X refers to the missing type Zork\n" +
"----------\n");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -486,8 +486,9 @@ public void test005() {
"public class X {\n" +
" void foo() {\n" +
" p.OtherFoo ofoo = new p.OtherFoo();\n" +
" ofoo.bar();\n" +
" Object o = ofoo.bar();\n" +
" q1.q2.Zork z;\n" +
" ofoo.bar();\n" + // not a problem
" }\n" +
"} \n",
},
Expand All @@ -497,8 +498,8 @@ public void test005() {
// compiler results
"----------\n" + /* expected compiler log */
"1. ERROR in X.java (at line 4)\n" +
" ofoo.bar();\n" +
" ^^^\n" +
" Object o = ofoo.bar();\n" +
" ^^^\n" +
"The method bar() from the type OtherFoo refers to the missing type Zork\n" +
"----------\n" +
"2. ERROR in X.java (at line 5)\n" +
Expand Down Expand Up @@ -559,12 +560,7 @@ public void test006() {
" ^^\n" +
"The import q1 cannot be resolved\n" +
"----------\n" +
"2. ERROR in X.java (at line 5)\n" +
" ofoo.bar();\n" +
" ^^^\n" +
"The method bar() from the type OtherFoo refers to the missing type Zork\n" +
"----------\n" +
"3. ERROR in X.java (at line 6)\n" +
"2. ERROR in X.java (at line 6)\n" +
" Zork z;\n" +
" ^^^^\n" +
"Zork cannot be resolved to a type\n" +
Expand Down Expand Up @@ -607,7 +603,7 @@ public void test007() {
"public class X {\n" +
" void foo() {\n" +
" p.OtherFoo ofoo = new p.OtherFoo();\n" +
" ofoo.bar();\n" +
" Object o = ofoo.bar();\n" +
" Zork z;\n" +
" }\n" +
"} \n",
Expand All @@ -623,8 +619,8 @@ public void test007() {
"The import q1 cannot be resolved\n" +
"----------\n" +
"2. ERROR in X.java (at line 5)\n" +
" ofoo.bar();\n" +
" ^^^\n" +
" Object o = ofoo.bar();\n" +
" ^^^\n" +
"The method bar() from the type OtherFoo refers to the missing type Zork\n" +
"----------\n" +
"3. ERROR in X.java (at line 6)\n" +
Expand Down Expand Up @@ -3439,8 +3435,8 @@ public void test074() {
"The method bar1() from the type X refers to the missing type Zork\n" +
"----------\n" +
"2. ERROR in X.java (at line 5)\n" +
" bar2();\n" +
" ^^^^\n" +
" Object o = bar2();\n" +
" ^^^^\n" +
"The method bar2() from the type X refers to the missing type Zork\n" +
"----------\n" +
"3. ERROR in X.java (at line 6)\n" +
Expand Down Expand Up @@ -3481,7 +3477,7 @@ public void test074() {
"public class X {\n" +
" void foo() {\n" +
" bar1().foo();\n" +
" bar2();\n" +
" Object o = bar2();\n" +
" bar3(null);\n" +
" bar4(null,null);\n" +
" }\n" +
Expand Down Expand Up @@ -9319,4 +9315,134 @@ The method m(A, String...) from the type B refers to the missing type A
""";
runner.runNegativeTest();
}
public void testMissingClass_returnType_OK() {
if (this.complianceLevel < ClassFileConstants.JDK1_8) return; // ignore different outcome below 1.8 since PR 2543
Runner runner = new Runner();
runner.testFiles = new String[] {
"p1/A.java",
"""
package p1;
public class A {
public A() { System.out.print("new A()"); }
}
""",
"p1/B.java",
"""
package p1;
public class B {
public A m(Object o) {
System.out.print("B.m()");
return new A();
}
}
"""
};
runner.runConformTest();

// temporarily move A.class to A.moved (i.e. simulate removing it from classpath for the next compile)
File classFileA = new File(OUTPUT_DIR, "p1" + File.separator + "A.class");
File movedFileA = new File(OUTPUT_DIR, "p1" + File.separator + "A.moved");
classFileA.renameTo(movedFileA);
runner.shouldFlushOutputDirectory = false;

runner.testFiles = new String[] {
"p2/C.java",
"""
package p2;
import p1.B;
public class C extends B {
void test(B b) {
b.m(this);
}
}
"""
};
runner.expectedErrorString = "java.lang.NoClassDefFoundError: p1/A\n";
runner.runConformTest();

runner.testFiles = new String[] {
"p2/Main.java",
"""
package p2;
import p1.B;
public class Main {
public static void main(String... args) {
new C().test(new B());
System.out.print("SUCCESS");
}
}
"""
};
// restore A.class and expect execution of Main to succeed
movedFileA.renameTo(classFileA);
runner.expectedOutputString = "B.m()new A()SUCCESS";
runner.shouldFlushOutputDirectory = false;
runner.expectedErrorString = null;
runner.runConformTest();
}
public void testMissingClass_returnType_NOK() {
if (this.complianceLevel < ClassFileConstants.JDK1_8) return; // ignore different outcome below 1.8 since PR 2543
Runner runner = new Runner();
runner.testFiles = new String[] {
"p1/A.java",
"""
package p1;
public class A {}
""",
"p1/B.java",
"""
package p1;
public class B {
public A m(Object o) { return new A(); }
public void n(A a) {}
}
"""
};
runner.runConformTest();

// delete binary file A (i.e. simulate removing it from classpath for subsequent compile)
Util.delete(new File(OUTPUT_DIR, "p1" + File.separator + "A.class"));

runner.shouldFlushOutputDirectory = false;

runner.testFiles = new String[] {
"p2/C.java",
"""
package p2;
import p1.B;
public class C extends B {
void test(B b) {
B b2 = b.m(this);
b.n(b.m(this));
}
@Override
public B m(Object o) { return super.m(o); }
}
"""
};
runner.expectedCompilerLog = """
----------
1. ERROR in p2\\C.java (at line 5)
B b2 = b.m(this);
^
The method m(Object) from the type B refers to the missing type A
----------
2. ERROR in p2\\C.java (at line 6)
b.n(b.m(this));
^
The method m(Object) from the type B refers to the missing type A
----------
3. ERROR in p2\\C.java (at line 9)
public B m(Object o) { return super.m(o); }
^
The return type is incompatible with B.m(Object)
----------
4. ERROR in p2\\C.java (at line 9)
public B m(Object o) { return super.m(o); }
^
The method m(Object) from the type B refers to the missing type A
----------
""";
runner.runNegativeTest();
}
}

0 comments on commit 23a6953

Please sign in to comment.