Summary: Some (all?) of this is already tested in other tests, but this feature is important enough (and the implementation is scattered accross the whole code), so I found it useful to have a small test that ensures the very basic things are working as expected. See `NestedFieldAccess.java` that tests far more advances things, but here we focus only very basic things: conditions, local variable assignments, and explicit assignments. Reviewed By: jvillard Differential Revision: D20339056 fbshipit-source-id: a6cfd0043master
parent
10afd021a1
commit
087a5a9787
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package codetoanalyze.java.nullsafe_default;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nullsafe has a feature: field nullability is memoized within a method. In other words, nullsafe
|
||||||
|
* assumes:
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>a) There will be no tricky multithreading.
|
||||||
|
* <li>b) If a field is set to non-null, the method won't call another method that will nullify it
|
||||||
|
* back.
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* <p>NOTE: This feature is unsound, but assumptions a) and b) mostly hold for real codebases, so
|
||||||
|
* here nullsafe tradeoffs theoretical unsoundness for practical usability.
|
||||||
|
*
|
||||||
|
* <p>This class tests basic properties of this feature.
|
||||||
|
*/
|
||||||
|
public class FieldNullabilityMemoization {
|
||||||
|
private @Nullable Object nullable;
|
||||||
|
|
||||||
|
void dereferenceIsBAD() {
|
||||||
|
nullable.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void dereferenceViaLocalVarIsBAD() {
|
||||||
|
Object a = nullable;
|
||||||
|
a.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void dereferenceAfterCheckIsOK() {
|
||||||
|
if (nullable != null) {
|
||||||
|
// Theoretically, a different thread could modify the field right here.
|
||||||
|
// But practically, if such things can happen, we have much bigger problems than nullability.
|
||||||
|
nullable.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dereferenceAfterCheckViaLocalVarIsOK() {
|
||||||
|
if (nullable != null) {
|
||||||
|
// Theoretically, a different thread could modify the field right here.
|
||||||
|
// But practically, if such things can happen, we have much bigger problems than nullability.
|
||||||
|
Object a = nullable;
|
||||||
|
a.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dereferenceAfterNonnullAssignmentIsOK() {
|
||||||
|
nullable = "";
|
||||||
|
// Theoretically, a different thread could modify the field right here.
|
||||||
|
// But practically, if such things can happen, we have much bigger problems than nullability.
|
||||||
|
nullable.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void dereferenceAfterNonnullAssignmentViaLocalVarIsOK() {
|
||||||
|
nullable = "";
|
||||||
|
|
||||||
|
// Theoretically, a different thread could modify the field right here.
|
||||||
|
// But practically, if such things can happen, we have much bigger problems than nullability.
|
||||||
|
|
||||||
|
Object a = nullable;
|
||||||
|
a.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FN_nullabilityIsPreservedEvenOnMethodCalls() {
|
||||||
|
nullable = "";
|
||||||
|
|
||||||
|
// Calling methods does not invalidate nullability of fields,
|
||||||
|
// even if they theoritically can nullify the field.
|
||||||
|
// In practice, this happens extremely rarely, but in this synthetic example
|
||||||
|
// this will lead to an NPE.
|
||||||
|
nullify();
|
||||||
|
|
||||||
|
// Uncaught NPE
|
||||||
|
nullable.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void nullify() {
|
||||||
|
nullable = null;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue