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

Fix unsound handling of catch parameters and throws clauses #436

Open
wants to merge 73 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 65 commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
0fd1c05
revert Aosen and set name as Aosen Xiong
Ao-senXiong Mar 20, 2023
bdc0984
add skip to see if contributor's issue status
Ao-senXiong Mar 20, 2023
15d3a06
Merge branch 'master' into add-tests-throw-initialization
wmdietl Mar 28, 2023
8d800e3
Merge branch 'master' into add-tests-throw-initialization
Ao-senXiong Mar 31, 2023
a36ccd8
commit for pull
Ao-senXiong May 4, 2023
be0944e
Merge branch 'master' of https://github.com/eisop/checker-framework i…
Ao-senXiong May 4, 2023
19d0e41
Merge branch 'master' into add-tests-throw-initialization
Ao-senXiong May 6, 2023
11fd903
add more modified code
Ao-senXiong May 7, 2023
7fca586
fix bugs in previous code
Ao-senXiong May 7, 2023
d411e71
fix bugs and apply spotlessJava
Ao-senXiong May 7, 2023
7552ae8
import concrete class rather wildcard
Ao-senXiong May 7, 2023
227b661
refine code
Ao-senXiong May 7, 2023
4fe7623
refine to support multiple throw
Ao-senXiong May 8, 2023
fd5d966
Merge branch 'master' into add-tests-throw-initialization
Ao-senXiong May 8, 2023
a09bb74
Merge pull request #2 from Ao-senXiong/fix-bug
Ao-senXiong May 8, 2023
d53d96c
Merge branch 'master' of https://github.com/Ao-senXiong/checker-frame…
Ao-senXiong May 8, 2023
814926d
Merge branch 'master' of https://github.com/eisop/checker-framework i…
Ao-senXiong May 8, 2023
bd13421
sync with master
Ao-senXiong May 8, 2023
d951cba
sync with master
Ao-senXiong May 8, 2023
eb1421f
Merge remote-tracking branch 'origin/add-tests-throw-initialization' …
Ao-senXiong May 8, 2023
6b61eb9
Merge branch 'master' of https://github.com/eisop/checker-framework i…
Ao-senXiong May 8, 2023
edd6b1f
add comment to new method
Ao-senXiong May 8, 2023
34af1c9
refine comment
Ao-senXiong May 8, 2023
f8fa5af
change method name convention for reading
Ao-senXiong May 9, 2023
fdf508c
change the behavior of basetypevisitor
Ao-senXiong May 26, 2023
78004bc
fix run time exception
Ao-senXiong May 26, 2023
b6ba932
change past testcase
Ao-senXiong May 27, 2023
ba2cca8
change expectation of past test case
Ao-senXiong May 27, 2023
afaf923
refine commented code
Ao-senXiong Jun 2, 2023
8b21d1c
change variable name to adopt java naming convention
Ao-senXiong Jun 2, 2023
b261c0c
Merge branch 'master' into add-tests-throw-initialization
Ao-senXiong Jun 2, 2023
820e9b6
Merge branch 'master' into add-tests-throw-initialization
Ao-senXiong Jun 9, 2023
8916dac
refine code
Ao-senXiong Jun 22, 2023
bf76d9c
refine code
Ao-senXiong Jun 22, 2023
51edde5
Merge branch 'add-tests-throw-initialization' of https://github.com/A…
Ao-senXiong Jun 22, 2023
8c913a6
Merge branch 'master' of https://github.com/eisop/checker-framework i…
Ao-senXiong Jun 22, 2023
53122d7
refine
Ao-senXiong Jun 22, 2023
f23d832
Merge branch 'master' into add-tests-throw-initialization
Ao-senXiong Sep 28, 2023
14d6d46
apply spotless
Ao-senXiong Sep 28, 2023
0fe31da
dummy commit to triger pipeline build
Ao-senXiong Sep 29, 2023
eeb0f52
Merge branch 'master' into add-tests-throw-initialization
wmdietl Sep 30, 2023
dafe559
address comments
Ao-senXiong Oct 1, 2023
41e5bd3
address comments
Ao-senXiong Oct 1, 2023
cc72ed7
Merge branch 'add-tests-throw-initialization' of https://github.com/A…
Ao-senXiong Oct 1, 2023
a7586e4
Merge remote-tracking branch 'origin/master' into add-tests-throw-ini…
Ao-senXiong Oct 1, 2023
3ca4ac1
add condition check if there is no annotation then get top
Ao-senXiong Oct 1, 2023
e832320
Merge remote-tracking branch 'origin' into add-tests-throw-initializa…
Ao-senXiong Oct 1, 2023
5beec9b
Merge branch 'master' into add-tests-throw-initialization
Ao-senXiong Oct 7, 2023
b54e4e3
Merge branch 'master' into add-tests-throw-initialization
Ao-senXiong Oct 10, 2023
593e77d
apply spotless
Ao-senXiong Oct 10, 2023
ef042d1
Merge branch 'master' into add-tests-throw-initialization
Ao-senXiong Oct 17, 2023
be00a27
adapte to typetool api change
Ao-senXiong Oct 17, 2023
0e42c58
Merge branch 'master' into add-tests-throw-initialization
Ao-senXiong Oct 17, 2023
dc0303e
Merge branch 'master' into add-tests-throw-initialization
Ao-senXiong Oct 21, 2023
0b5cea6
Merge branch 'master' into add-tests-throw-initialization
Ao-senXiong Oct 22, 2023
b422422
undo the hack for guava
Ao-senXiong Oct 22, 2023
e65a00a
Merge branch 'eisop:master' into add-tests-throw-initialization
Ao-senXiong Oct 23, 2023
33f822d
add testcase for generic exception and use getEffectiveAnnotations
Ao-senXiong Oct 23, 2023
2689059
Merge branch 'master' into add-tests-throw-initialization
Ao-senXiong Oct 23, 2023
f213fd7
Merge branch 'master' into add-tests-throw-initialization
Ao-senXiong Oct 24, 2023
bf039c3
Merge branch 'master' into add-tests-throw-initialization
Ao-senXiong Nov 9, 2023
b4af2b9
Merge branch 'master' into add-tests-throw-initialization
Ao-senXiong Nov 24, 2023
4f0f7a8
Merge branch 'eisop:master' into add-tests-throw-initialization
Ao-senXiong Dec 5, 2023
2cc9262
restructure test case
Ao-senXiong Dec 5, 2023
8565088
Merge branch 'master' into add-tests-throw-initialization
Ao-senXiong Dec 5, 2023
fdc8f56
Merge branch 'master' into add-tests-throw-initialization
Ao-senXiong Jul 4, 2024
770135b
Undo the renaming
Ao-senXiong Jul 4, 2024
3363ffd
Undo a few change
Ao-senXiong Jul 4, 2024
2ccc562
Merge test case into one and delete comment
Ao-senXiong Jul 4, 2024
4862beb
Empty commit for CI
Ao-senXiong Jul 4, 2024
8869606
Merge branch 'master' into add-tests-throw-initialization
Ao-senXiong Aug 27, 2024
7678ddc
Refine the test case and more comment
Ao-senXiong Aug 27, 2024
9a7140b
Do empty check instead of null check
Ao-senXiong Sep 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ private boolean isNewArrayInToArray(NewArrayTree tree) {
@Override
protected void checkThrownExpression(ThrowTree tree) {
checkForNullability(tree.getExpression(), THROWING_NULLABLE);
super.checkThrownExpression(tree);
}

/** Case 5: Check for synchronizing locks. */
Expand Down
10 changes: 5 additions & 5 deletions checker/tests/guieffect/ThrowCatchTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ class Inner {}
// Type var test
<E extends @UI PolyUIException> void throwTypeVarUI1(E ex1, @UI E ex2) throws PolyUIException {
if (flag) {
// :: error: (throw.type.invalid)
// :: error: (throw.type.incompatible)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing the error key also requires a change to this file:
https://github.com/eisop/checker-framework/blob/e604d8d10a38d09f7c3e94c4d614b2d729f48085/framework/src/main/java/org/checkerframework/common/basetype/messages.properties#L35C12-L35C19

incompatible is the better term, as there is a comparison between two types.
In a previous discussion we said we do this renaming in a separate PR. Was there a change of mind?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have opened a new PR here #618.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you should undo these changes in this PR, so that we can merge this PR independently of #618.

throw ex1;
}
// :: error: (throw.type.invalid)
// :: error: (throw.type.incompatible)
throw ex2;
}

<@UI E extends @UI PolyUIException> void throwTypeVarUI2(E ex1) throws PolyUIException {
// :: error: (throw.type.invalid)
// :: error: (throw.type.incompatible)
throw ex1;
}

Expand All @@ -48,7 +48,7 @@ class Inner {}
<@AlwaysSafe E extends @UI PolyUIException> void throwTypeVarMixed(E ex1, @AlwaysSafe E ex2)
throws PolyUIException {
if (flag) {
// :: error: (throw.type.invalid)
// :: error: (throw.type.incompatible)
throw ex1;
}
throw ex2;
Expand All @@ -75,7 +75,7 @@ void throwNull() {

void throwDeclared() {
try {
// :: error: (throw.type.invalid)
// :: error: (throw.type.incompatible)
throw ui;
} catch (@UI PolyUIException UIParam) {

Expand Down
154 changes: 154 additions & 0 deletions checker/tests/initialization/EISOPIssue363a.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// Test case for issue 363:
// https://github.com/eisop/checker-framework/issues/363

import org.checkerframework.checker.initialization.qual.NotOnlyInitialized;
import org.checkerframework.checker.initialization.qual.UnderInitialization;
import org.checkerframework.checker.initialization.qual.UnknownInitialization;

public class EISOPIssue363a {
class MyException extends Exception {
@NotOnlyInitialized EISOPIssue363a cause;

MyException(EISOPIssue363a cause) {
this.cause = cause;
}

MyException(@UnderInitialization EISOPIssue363a cause, String dummy) {
this.cause = cause;
}

MyException(@UnknownInitialization EISOPIssue363a cause, int dummy) {
this.cause = cause;
}
}

Object field;

EISOPIssue363a() throws MyException {
// :: error: (throw.type.incompatible)
// :: error: (argument.type.incompatible)
throw new MyException(this);
}

EISOPIssue363a(boolean dummy1, boolean dummy2) throws MyException {
// :: error: (throw.type.incompatible)
throw new MyException(this, 0);
}

EISOPIssue363a(boolean dummy1, boolean dummy2, boolean dummy3) throws MyException {
// :: error: (throw.type.incompatible)
throw new MyException(this, "UnderInitialization");
}

EISOPIssue363a(int dummy) throws @UnknownInitialization MyException {
throw new MyException(this, 0);
}

EISOPIssue363a(int dummy1, int dummy2) throws @UnknownInitialization MyException {
throw new MyException(this, "UnderInitialization");
}

EISOPIssue363a(int dummy1, int dummy2, int dummy3) throws @UnknownInitialization MyException {
// :: error: (argument.type.incompatible)
throw new MyException(this);
}

EISOPIssue363a(String dummy) throws @UnderInitialization MyException {
throw new MyException(this, "UnderInitialization");
}

EISOPIssue363a(String dummy1, String dummy2) throws @UnderInitialization MyException {
throw new MyException(this, 0);
}

EISOPIssue363a(String dummy1, String dummy2, String dummy3)
throws @UnderInitialization MyException {
// :: error: (argument.type.incompatible)
throw new MyException(this);
}

void test1() {
try {
EISOPIssue363a obj = new EISOPIssue363a(1);
} catch (MyException ex) {
ex.cause.field.toString();
}
}

void test2() {
try {
EISOPIssue363a obj = new EISOPIssue363a();
} catch (@UnknownInitialization MyException ex) {
// :: error: (dereference.of.nullable)
ex.cause.field.toString();
}
}

void test3() {
try {
EISOPIssue363a obj = new EISOPIssue363a();
} catch (@UnderInitialization MyException ex) {
// :: error: (dereference.of.nullable)
ex.cause.field.toString();
}
}

void test4() {
try {
EISOPIssue363a obj = new EISOPIssue363a(1);
} catch (MyException ex) {
ex.cause.field.toString();
}
}

void test5() {
try {
EISOPIssue363a obj = new EISOPIssue363a(1);
} catch (@UnknownInitialization MyException ex) {
// :: error: (dereference.of.nullable)
ex.cause.field.toString();
}
}

void test6() {
try {
EISOPIssue363a obj = new EISOPIssue363a(1);
} catch (@UnderInitialization MyException ex) {
// :: error: (dereference.of.nullable)
ex.cause.field.toString();
}
}

void test7() {
try {
EISOPIssue363a obj = new EISOPIssue363a("UnderInitialization");
} catch (MyException ex) {
ex.cause.field.toString();
}
}

void test8() {
try {
EISOPIssue363a obj = new EISOPIssue363a("UnderInitialization");
} catch (@UnknownInitialization MyException ex) {
// :: error: (dereference.of.nullable)
ex.cause.field.toString();
}
}

void test9() {
try {
EISOPIssue363a obj = new EISOPIssue363a("UnderInitialization");
} catch (@UnderInitialization MyException ex) {
// :: error: (dereference.of.nullable)
ex.cause.field.toString();
}
}

<X extends Throwable> void throwIfInstanceOf(Throwable throwable, Class<X> declaredType)
throws X {
if (declaredType.isInstance(throwable)) {
throw declaredType.cast(throwable);
}
}
}
94 changes: 94 additions & 0 deletions checker/tests/initialization/EISOPIssue363b.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Test multiple throw excetion in method signature for issue 363:
// https://github.com/eisop/checker-framework/issues/363

import org.checkerframework.checker.initialization.qual.NotOnlyInitialized;
import org.checkerframework.checker.initialization.qual.UnderInitialization;
import org.checkerframework.checker.initialization.qual.UnknownInitialization;

public class EISOPIssue363b {
class MyException1 extends Exception {
@NotOnlyInitialized EISOPIssue363b cause;

MyException1(EISOPIssue363b cause) {
this.cause = cause;
}

MyException1(@UnderInitialization EISOPIssue363b cause, String dummy) {
this.cause = cause;
}

MyException1(@UnknownInitialization EISOPIssue363b cause, int dummy) {
this.cause = cause;
}
}

class MyException2 extends Exception {
@NotOnlyInitialized EISOPIssue363b cause;

MyException2(EISOPIssue363b cause) {
this.cause = cause;
}

MyException2(@UnderInitialization EISOPIssue363b cause, String dummy) {
this.cause = cause;
}

MyException2(@UnknownInitialization EISOPIssue363b cause, int dummy) {
this.cause = cause;
}
}

Object field;

EISOPIssue363b(int dummy) throws MyException1, MyException2 {
// :: error: (throw.type.incompatible)
throw new MyException1(this, 0);
}

EISOPIssue363b(int dummy1, int dummy2) throws MyException1, MyException2 {
// :: error: (throw.type.incompatible)
throw new MyException2(this, 0);
}

EISOPIssue363b(boolean dummy) throws @UnknownInitialization MyException1, MyException2 {
throw new MyException1(this, 0);
}

EISOPIssue363b(boolean dummy1, boolean dummy2)
throws @UnknownInitialization MyException1, MyException2 {
// :: error: (throw.type.incompatible)
throw new MyException2(this, 0);
}

EISOPIssue363b(boolean dummy1, boolean dummy2, boolean dummy3)
throws MyException1, @UnknownInitialization MyException2 {
// :: error: (throw.type.incompatible)
throw new MyException1(this, 0);
}

EISOPIssue363b(boolean dummy1, boolean dummy2, boolean dummy3, boolean dummy4)
throws MyException1, @UnknownInitialization MyException2 {
throw new MyException2(this, 0);
}

EISOPIssue363b(String dummy) throws @UnderInitialization MyException1, MyException2 {
throw new MyException1(this, "UnderInitialization");
}

EISOPIssue363b(String dummy1, String dummy2)
throws @UnderInitialization MyException1, MyException2 {
// :: error: (throw.type.incompatible)
throw new MyException2(this, "UnderInitialization");
}

EISOPIssue363b(String dummy1, String dummy2, String dummy3)
throws MyException1, @UnderInitialization MyException2 {
// :: error: (throw.type.incompatible)
throw new MyException1(this, "UnderInitialization");
}

EISOPIssue363b(String dummy1, String dummy2, String dummy3, String dummy4)
throws MyException1, @UnderInitialization MyException2 {
throw new MyException2(this, "UnderInitialization");
}
}
2 changes: 1 addition & 1 deletion checker/tests/lock/TestTreeKinds.java
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ void testTreeTypes() {
// throwing a guarded object - when throwing an exception, it must be @GuardedBy({}). Even
// @GuardedByUnknown is not allowed.
try {
// :: error: (throw.type.invalid)
// :: error: (throw.type.incompatible)
throw exception;
} catch (Exception e) {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Types;

/* NO-AFU
import org.checkerframework.common.wholeprograminference.WholeProgramInference;
Expand Down Expand Up @@ -3052,11 +3053,23 @@ private AnnotationMirrorSet getExceptionParameterLowerBoundAnnotationsCached() {
* @param tree a CatchTree to check
*/
protected void checkExceptionParameter(CatchTree tree) {
AnnotationMirrorSet requiredAnnotations =
Set<? extends AnnotationMirror> requiredAnnotations =
getExceptionParameterLowerBoundAnnotationsCached();
VariableTree excParamTree = tree.getParameter();
AnnotatedTypeMirror excParamType = atypeFactory.getAnnotatedType(excParamTree);

// List<AnnotatedTypeMirror> exceptionList =
// atypeFactory.getAnnotatedType(methodTree).getThrownTypes();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wmdietl I try to compare the type of exception that method body throw and the type of catch parameter. Do you have any suggestion on how to get what exception the method body throws?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wmdietl I think we should allow the following code as the exception may not be catched like how Java handle this:

void foo() throw @UnknowInitialization myException {}

void test(){
    try {
        foo()
    } catch (MyException e) {}
}

// if (methodTree != null && exceptionList != null) {
// for (AnnotatedTypeMirror exception : exceptionList) {
// Types typesUtil = atypeFactory.getProcessingEnv().getTypeUtils();
// if (typesUtil.isSubtype(
// excParamType.getUnderlyingType(), exception.getUnderlyingType()))
// {
// requiredAnnotations = excParamType.getEffectiveAnnotations();
// break;
// }
// }
// }
for (AnnotationMirror required : requiredAnnotations) {
AnnotationMirror found = excParamType.getAnnotationInHierarchy(required);
assert found != null;
Expand Down Expand Up @@ -3116,15 +3129,35 @@ protected void checkThrownExpression(ThrowTree tree) {
AnnotatedTypeMirror throwType = atypeFactory.getAnnotatedType(tree.getExpression());
TypeMirror throwTM = throwType.getUnderlyingType();
Set<? extends AnnotationMirror> required = getThrowUpperBoundAnnotations();
if (methodTree != null) {
List<AnnotatedTypeMirror> exceptionList =
atypeFactory.getAnnotatedType(methodTree).getThrownTypes();
if (exceptionList != null) {
for (AnnotatedTypeMirror exception : exceptionList) {
Types typesUtil = atypeFactory.getProcessingEnv().getTypeUtils();
if (typesUtil.isSubtype(
exception.getUnderlyingType(), throwType.getUnderlyingType())) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the logic for this subtype test? Shouldn't it be the other way around?
Why is it only comparing the TypeMirrors and not considering annotations?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The purpose here is to update required variable, which is used to check subtyping relationship between actual throw type and required.

The logic is: we should only update require only if the exception throwed is a subtype of one of the decleared throw exception in method signature. For example for the following code, we should update required to @Initailized instead of use top annotation in the hierarchy. And since actual throw exception has @UnderInitialization type. so throw type invalid error should be issued

    EISOPIssue363(int dummy1, int dummy2, int dummy3) throws @Initailized MyException {
        // :: error: (throw.type.invalid)
        throw new MyException(this, "UnderInitialization");
    }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add test case for methodinvocation.

// use the type of the exception in the method signature if this exception
// is a subtype of it, otherwise use top to permit runtime exceptions not
// declared.
required = exception.getEffectiveAnnotations();
break;
}
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be a warning or error if no specific required type is found?
When should this use getThrowUpperBoundAnnotations(); ?
Could you make that initialization more explicit, to make clear it is intentional?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be a warning or error if no specific required type is found? When should this use getThrowUpperBoundAnnotations(); ? Could you make that initialization more explicit, to make clear it is intentional?

I am think about because there is not always a throw clause in the method signature. Consider the following example. If there is not match, there should not be warning or error.

void foo() {
    try {
         // code might throw an exception
    } catch (SomeCheckedException e) {

    }
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You didn't come back to this comment. Doesn't this mean that from a throw statement, you need to look at both the enclosing catch clauses and only finally at the throws clause on the enclosing method?

Copy link
Member Author

@Ao-senXiong Ao-senXiong Sep 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wmdietl Do you mean code like following shoule issue error for throw type and catch clause mismatch?

void foo() {
    try {
        throw new @Initalized SomeCheckedException();
    } catch (@UnderInitialization SomeCheckedException e) {

    }
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in that case we would need a visitry method in basetypevisitor to get the try block and compare it with possible multiple catch block.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure why it is not shown in the first page of this PR directly but I do reply the comment here yesterday.

switch (throwType.getKind()) {
case NULL:
case DECLARED:
case TYPEVAR:
case WILDCARD:
if (!typeHierarchy.isSubtypeShallowEffective(throwType, required)) {
AnnotationMirrorSet found = throwType.getEffectiveAnnotations();
AnnotationMirrorSet foundEffective = throwType.getEffectiveAnnotations();
checker.reportError(
tree.getExpression(), "throw.type.invalid", found, required);
tree.getExpression(),
"throw.type.incompatible",
foundEffective,
required);
}
break;

Expand All @@ -3133,15 +3166,18 @@ protected void checkThrownExpression(ThrowTree tree) {
AnnotationMirrorSet foundPrimary = unionType.getAnnotations();
if (!qualHierarchy.isSubtypeShallow(foundPrimary, required, throwTM)) {
checker.reportError(
tree.getExpression(), "throw.type.invalid", foundPrimary, required);
tree.getExpression(),
"throw.type.incompatible",
foundPrimary,
required);
}
for (AnnotatedTypeMirror altern : unionType.getAlternatives()) {
TypeMirror alternTM = altern.getUnderlyingType();
if (!qualHierarchy.isSubtypeShallow(
altern.getAnnotations(), required, alternTM)) {
checker.reportError(
tree.getExpression(),
"throw.type.invalid",
"throw.type.incompatible",
altern.getAnnotations(),
required);
}
Expand Down
Loading