-
Notifications
You must be signed in to change notification settings - Fork 19
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
base: master
Are you sure you want to change the base?
Fix unsound handling of catch parameters and throws clauses #436
Conversation
0abca68
to
0fd1c05
Compare
…work into add-tests-throw-initialization
…nto add-tests-throw-initialization
…into add-tests-throw-initialization
…nto add-tests-throw-initialization
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for all the improvements to this PR! I have another long list of comments.
@@ -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) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
import org.checkerframework.checker.initialization.qual.NotOnlyInitialized; | ||
import org.checkerframework.checker.initialization.qual.UnknownInitialization; | ||
|
||
class MyException extends Exception { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Try having just one top-level class in tests or include something, like 363a
in all top-level class names.
Otherwise, it is very easy for two different tests to define a generic class name like MyException
and strange behavior can result.
this.cause = cause; | ||
} | ||
|
||
MyException(@Initialized EISOPIssue363a cause, String dummy) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This just makes the @Initialized
qualifier explicit. Is that the intention?
Should there be a separate constructor that takes an @UnderInitialization
parameter, to test all possibilities?
throw new MyException(this, 0); | ||
} | ||
|
||
EISOPIssue363a(char dummy) throws MyException { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's rather misleading that this constructor, with a char
parameter, invokes the MyException
constructor with a String argument. The following constructor with a String
parameter, invokes a different constructor. Maybe these can be aligned a bit better?
public static void test3(String[] args) { | ||
try { | ||
EISOPIssue363a obj = new EISOPIssue363a(); | ||
} catch (@Initialized MyException ex) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the same as test1, just with an explicit qualifier. Put them next to each other and add a comment?
framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java
Outdated
Show resolved
Hide resolved
framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java
Outdated
Show resolved
Hide resolved
framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java
Outdated
Show resolved
Hide resolved
framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java
Outdated
Show resolved
Hide resolved
framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java
Outdated
Show resolved
Hide resolved
getExceptionParameterLowerBoundAnnotationsCached(); | ||
VariableTree excParamTree = tree.getParameter(); | ||
AnnotatedTypeMirror excParamType = atypeFactory.getAnnotatedType(excParamTree); | ||
|
||
// List<AnnotatedTypeMirror> exceptionList = | ||
// atypeFactory.getAnnotatedType(methodTree).getThrownTypes(); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) {}
}
@Ao-senXiong Can you look into wrapping up this change, without the renaming that is done in #618? Let's first fix the soundness issue and then improve the naming. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for getting back to this.
if (methodTree != null) { | ||
List<AnnotatedTypeMirror> allowedExceptions = | ||
atypeFactory.getAnnotatedType(methodTree).getThrownTypes(); | ||
if (allowedExceptions != null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
allowedExceptions
should never be null, so this check is redundant.
for (AnnotatedTypeMirror exception : allowedExceptions) { | ||
Types typesUtil = atypeFactory.getProcessingEnv().getTypeUtils(); | ||
if (typesUtil.isSubtype( | ||
exception.getUnderlyingType(), throwType.getUnderlyingType())) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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");
}
There was a problem hiding this comment.
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.
break; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
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?
Fixes #363. This PR revealed the need for #494 and depends on it being merged first.