Closed GoogleCodeExporter closed 5 years ago
The Checker Framework currently does type refinement on a final variable based
on the initializer expression, but not based on subsequent conditional
expressions.
I have committed a test case as
checker-framework/checkers/tests/nullness/FinalVar2.java
Original comment by michael.ernst@gmail.com
on 16 Oct 2013 at 9:59
I find comment #1 misleading.
The following example:
static Object method1(@Nullable Object arg) {
final Object tmp = arg;
if (tmp == null) {
return "hello";
}
tmp.hashCode();
return "bye";
}
passes the Nullness Checker without an error.
I think we correctly type refine the final variable in the outer class, but
when we initialize the store for the anonymous inner class, we only use the
declared types of the final variables. We should take the refined values of the
final variables at the point where the anonymous class is instantiated.
Stefan recently worked on anonymous inner classes, maybe he can have a quick
look.
Original comment by wdi...@gmail.com
on 17 Oct 2013 at 3:23
We do remember the refined value of final variables and use it to initialize
the store in the anonymous inner class. However, the refinement is only based
on the initializer of the final variable; future refinements based on
comparing the final variable with other values are not used. This has the
advantage, that only a single value per final variable needs to be remembered,
rather than (potentially) a value for every program point.
I'm sure it would be possible to remember more information, but it also does
not seem like a high-priority issue. It can easily be worked around by doing
the check what value the final variable holds earlier. For the example given,
one could change it as follows:
if (arg == null) {
return null;
}
final Object tmp = arg;
return new Inner() {
String getThing() {
return tmp.toString();
}
};
I changed the priority to low for now.
Original comment by stefanheule@gmail.com
on 17 Oct 2013 at 5:16
Original comment by michael.ernst@gmail.com
on 25 May 2014 at 4:25
Original comment by michael.ernst@gmail.com
on 29 May 2014 at 4:04
Issue 411 has been merged into this issue.
Original comment by wdi...@gmail.com
on 16 Mar 2015 at 8:54
Issue 411 has been merged into this issue.
Original comment by wdi...@gmail.com
on 16 Mar 2015 at 8:54
While the workaround seems reasonable, it would be nice to not require to change valid code to meet the requirements of a static analysis tool.
This issue was reported by another user in #811.
I added a test case checker/tests/nullness/Issue811java.
org.checkerframework.dataflow.cfg.CFGBuilder.CFGTranslationPhaseOne.visitNewClass(NewClassTree, Void)
contains the comment "We ignore any class body because its methods should be visited separately.".
This is likely the location where the current store should be propagated to that anonymous class.
Handling of inner/anonymous classes probably needs a solution similar to how lambda expressions are handled. Note how rewriting an anonymous class to an equivalent lambda expression gives the correct refinement behavior. See
org.checkerframework.dataflow.cfg.CFGBuilder.declaredLambdas
org.checkerframework.framework.type.GenericAnnotatedTypeFactory.analyze(Queue<ClassTree>, Queue<Pair<LambdaExpressionTree, Store>>, UnderlyingAST, List<Pair<VariableElement, Value>>, ClassTree, boolean, boolean, boolean, Store)
org.checkerframework.framework.flow.CFAbstractTransfer.initialStore(UnderlyingAST, List<LocalVariableNode>)
Is there a cleaner annotation based workaround rather the local variable copy? (I'm thinking something like @MonotonicNonNull - although this one specifically doesn't work).
I don't think an approach with an annotation on the field is possible. Annotating the field would not help you with determining whether the type was refined or not. Flow-sensitivity needs to be implemented correctly, which is a considerable engineering effort. I wrote up a few comments last week, but won't get to work on this for a while.
Issue #903 was closed as a dupe of this. Given that, can you add the following as a test when fixing this issue? Thanks!
class Foo {
private final Object mBar;
public Foo() {
mBar = "test";
Runnable runnable = new Runnable() {
@Override
public void run() {
// unexpected [dereference.of.nullable] error here
mBar.toString();
}
};
runnable.run();
}
}
Thanks for the test case. In commit ce3465d, I have added two test cases as files checker/tests/nullness/Issue266.java and checker/tests/nullness/Issue266a.java.
List of test cases: checker/tests/nullness/FinalVar2.java checker/tests/nullness/Issue411.java checker/tests/nullness/Issue811java checker/tests/nullness/Issue266.java checker/tests/nullness/Issue266a.java
Original issue reported on code.google.com by
cus...@google.com
on 16 Oct 2013 at 4:45Attachments: