[nullsafe] Fix a bug and specify behavior for multiparam version for @TrueOnNull and @FalseOnNull annotations

Summary:
Yay, the previous refactoring finally makes it possible to do some actual
changes to the code in `TypeCheck.ml`!

Changes in this diff:
1. Fixes the bug: TrueOnNull and FalseOnNull were working only for
static methods. Surpsingly nobody noticed that. It is because the first
argument for non-static method was `this`.
2. Behavior change: TrueOnNull/FalseOnNull were not working correctly
where there are several argumens. See the task attached for the example
of the legit usecase. Now the behavior is the following: if there are
several Nullable arguments infer nullability for all of them.

Reviewed By: skcho

Differential Revision: D19770219

fbshipit-source-id: 7dffe42cd
master
Mitya Lyubarskiy 5 years ago committed by Facebook Github Bot
parent 5bee0858c6
commit 067a545c0e

@ -656,30 +656,40 @@ let normalize_cond_for_sil_prune idenv ~node cond =
let rec check_condition_for_sil_prune tenv idenv calls_this find_canonical_duplicate loc curr_pname let rec check_condition_for_sil_prune tenv idenv calls_this find_canonical_duplicate loc curr_pname
curr_pdesc curr_annotated_signature linereader typestate checks true_branch instr_ref curr_pdesc curr_annotated_signature linereader typestate checks true_branch instr_ref
~nullsafe_mode ~original_node ~node c : TypeState.t = ~nullsafe_mode ~original_node ~node c : TypeState.t =
(* check if the expression is coming from a call, and return the argument *) (* check if the expression is coming from a call, and return the arguments *)
let from_call filter_callee e : Exp.t option = let extract_arguments_from_call filter_callee expr =
match e with match expr with
| Exp.Var id -> ( | Exp.Var id -> (
match Errdesc.find_normal_variable_funcall node id with match Errdesc.find_normal_variable_funcall node id with
| Some (Exp.Const (Const.Cfun pn), e1 :: _, _, _) when filter_callee pn -> | Some (Exp.Const (Const.Cfun pn), arguments, _, _) when filter_callee pn ->
Some e1 Some arguments
| _ -> | _ ->
None ) None )
| _ -> | _ ->
None None
in in
(* check if the expression is coming from instanceof *) (* check if the expression is coming from (`a` instanceof `b`), and returns `b`, if it is the case *)
let from_instanceof e : Exp.t option = from_call ComplexExpressions.procname_instanceof e in let extract_argument_from_instanceof expr =
match extract_arguments_from_call ComplexExpressions.procname_instanceof expr with
| Some [argument; _] ->
Some argument
| Some _ ->
Logging.die Logging.InternalError "expected exactly two arguments in instanceOf expression"
| None ->
None
in
(* check if the expression is coming from a procedure returning false on null *) (* check if the expression is coming from a procedure returning false on null *)
let from_is_false_on_null e : Exp.t option = let extract_arguments_from_call_to_false_on_null_func e =
from_call (ComplexExpressions.procname_is_false_on_null tenv) e extract_arguments_from_call (ComplexExpressions.procname_is_false_on_null tenv) e
in in
(* check if the expression is coming from a procedure returning true on null *) (* check if the expression is coming from a procedure returning true on null *)
let from_is_true_on_null e : Exp.t option = let extract_arguments_from_call_to_true_on_null_func e =
from_call (ComplexExpressions.procname_is_true_on_null tenv) e extract_arguments_from_call (ComplexExpressions.procname_is_true_on_null tenv) e
in in
(* check if the expression is coming from Map.containsKey *) (* check if the expression is coming from Map.containsKey *)
let from_containsKey e : Exp.t option = from_call ComplexExpressions.procname_containsKey e in let is_from_containsKey expr =
extract_arguments_from_call ComplexExpressions.procname_containsKey expr |> Option.is_some
in
(* Call to x.containsKey(e) returned `true`. (* Call to x.containsKey(e) returned `true`.
It means that subsequent calls to `x.get(e)` should be inferred as non-nullables. It means that subsequent calls to `x.get(e)` should be inferred as non-nullables.
We achieve this behavior by adding the result of a call to `x.get(e)` (in form of corresponding pvar) We achieve this behavior by adding the result of a call to `x.get(e)` (in form of corresponding pvar)
@ -766,14 +776,18 @@ let rec check_condition_for_sil_prune tenv idenv calls_this find_canonical_dupli
We've just ensured that [expr] == false. We've just ensured that [expr] == false.
Update the typestate accordingly. Update the typestate accordingly.
*) *)
let handle_boolean_equal_false expr = let handle_boolean_equal_false expr typestate =
match from_is_true_on_null expr with match extract_arguments_from_call_to_true_on_null_func expr with
| Some argument -> | Some arguments ->
(* [expr] is false hence, according to true-on-null contract, [argument] can not not be null. (* [expr] is false hence, according to true-on-null contract, neither of [arguments] can be null.
Hence we can infer its nullability as a non-null. (otherwise the result would have been true)
Hence we can infer their nullability as non-null.
*) *)
List.fold ~init:typestate
~f:(fun accumulated_typestate argument ->
set_original_pvar_to_nonnull_in_typestate ~with_cond_redundant_check:false argument set_original_pvar_to_nonnull_in_typestate ~with_cond_redundant_check:false argument
typestate accumulated_typestate )
arguments
| None -> | None ->
typestate typestate
in in
@ -782,22 +796,25 @@ let rec check_condition_for_sil_prune tenv idenv calls_this find_canonical_dupli
Update the typestate accordingly. Update the typestate accordingly.
*) *)
let handle_boolean_equal_true expr typestate = let handle_boolean_equal_true expr typestate =
match from_is_false_on_null expr with match extract_arguments_from_call_to_false_on_null_func expr with
| Some argument -> | Some arguments ->
(* [expr] is true hence, according to false-on-null contract, [argument] can not not be null. (* [expr] is true hence, according to false-on-null contract, neither of [arguments] can be null.
Hence we can infer its nullability as a non-null. (otherwise the result would have been false).
Hence we can infer their nullability as non-null.
*) *)
List.fold ~init:typestate
~f:(fun accumulated_typestate argument ->
set_original_pvar_to_nonnull_in_typestate ~with_cond_redundant_check:false argument set_original_pvar_to_nonnull_in_typestate ~with_cond_redundant_check:false argument
typestate accumulated_typestate )
arguments
| None -> ( | None -> (
match from_instanceof expr with match extract_argument_from_instanceof expr with
| Some argument -> | Some argument ->
(* ([argument] instanceof [expr] == true) implies (expr != null) *) (* ([argument] instanceof [expr] == true) implies (expr != null) *)
set_original_pvar_to_nonnull_in_typestate ~with_cond_redundant_check:false argument set_original_pvar_to_nonnull_in_typestate ~with_cond_redundant_check:false argument
typestate typestate
| None -> | None ->
if Option.is_some (from_containsKey expr) then if is_from_containsKey expr then handle_containsKey_returned_true expr typestate
handle_containsKey_returned_true expr typestate
else typestate ) else typestate )
in in
(* Assuming [expr] is a non-primitive, this is the branch where, according to PRUNE semantics, (* Assuming [expr] is a non-primitive, this is the branch where, according to PRUNE semantics,
@ -815,7 +832,7 @@ let rec check_condition_for_sil_prune tenv idenv calls_this find_canonical_dupli
`null` and `false`, hence the expression means either "some_bool == false" or "some_object == null" `null` and `false`, hence the expression means either "some_bool == false" or "some_object == null"
We don't currently have a logic for the latter case, but we do support the former We don't currently have a logic for the latter case, but we do support the former
*) *)
handle_boolean_equal_false expr handle_boolean_equal_false expr typestate
| Exp.BinOp (Binop.Ne, Exp.Const (Const.Cint i), expr) | Exp.BinOp (Binop.Ne, Exp.Const (Const.Cint i), expr)
| Exp.BinOp (Binop.Ne, expr, Exp.Const (Const.Cint i)) | Exp.BinOp (Binop.Ne, expr, Exp.Const (Const.Cint i))
when IntLit.iszero i -> when IntLit.iszero i ->

@ -16,65 +16,202 @@ import javax.annotation.Nullable;
/** Testing functionality related to @TrueOnNull and @FalseOnNull methods */ /** Testing functionality related to @TrueOnNull and @FalseOnNull methods */
public class TrueFalseOnNull { public class TrueFalseOnNull {
// Example of API that benefits from annotating with @TrueOnNull and @FalseOnNull static class StaticOneParam {
static class AnnotatedTextUtils { @TrueOnNull
static boolean trueOnNull(@Nullable Object o) {
return o == null ? true : o.toString() == "something";
}
@FalseOnNull
static boolean falseOnNull(@Nullable Object o) {
return o == null ? false : o.toString() == "something";
}
static boolean notAnnotated(@Nullable Object o) {
return o == null ? false : o.toString() == "something";
}
}
static class NonStaticOneParam {
private String compareTo = "something";
@TrueOnNull @TrueOnNull
static boolean isEmpty(@Nullable CharSequence s) { boolean trueOnNull(@Nullable Object o) {
return s == null || s.equals(""); return o == null ? true : o.toString() == compareTo;
} }
@FalseOnNull @FalseOnNull
static boolean isNotEmpty(@Nullable CharSequence s) { boolean falseOnNull(@Nullable Object o) {
return s != null && s.length() > 0; return o == null ? false : o.toString() == compareTo;
}
boolean notAnnotated(@Nullable Object o) {
return o == null ? false : o.toString() == compareTo;
} }
} }
// The same API, but not annotated // @TrueOnNull and @FalseOnNull should expect true/false will be returned if ANY of input objects
static class NotAnnotatedTextUtils { // is null.
static boolean isEmpty(@Nullable CharSequence s) { // In other words, they should infer that all input nullable objects are non-null in the
return s == null || s.equals(""); // corresponding branch.
static class NonStaticSeveralParams {
private String compareTo = "something";
@TrueOnNull
boolean trueOnNull(
@Nullable Object nullable1, int primitive, Object nonnull, @Nullable Object nullable2) {
if (nullable1 == null || nullable2 == null) {
return true;
}
return nonnull == compareTo;
} }
static boolean isNotEmpty(@Nullable CharSequence s) { @FalseOnNull
return s != null && s.length() > 0; boolean falseOnNull(
@Nullable Object nullable1, int primitive, Object nonnull, @Nullable Object nullable2) {
if (nullable1 == null || nullable2 == null) {
return false;
} }
return nonnull == compareTo;
} }
class Test { boolean notAnnotated(
void testTrueOnNull(@Nullable CharSequence s) { @Nullable Object nullable1, int primitive, Object nonnull, @Nullable Object nullable2) {
// Explicitly annotated if (nullable1 == null || nullable2 == null) {
if (!AnnotatedTextUtils.isEmpty(s)) { return false;
s.toString(); // OK: if we are here, we know that s is not null }
return nonnull == compareTo;
} }
}
class TestStaticOneParam {
// Not annotated void trueOnNullPositiveBranchIsBAD(@Nullable String s) {
if (!NotAnnotatedTextUtils.isEmpty(s)) { if (StaticOneParam.trueOnNull(s)) {
s.toString(); // BAD: the typecker does not know s can not be null s.toString();
}
} }
if (AnnotatedTextUtils.isEmpty(s)) { void trueOnNullNegativeBranchIsOK(@Nullable String s) {
s.toString(); // BAD: s can be null or an empty string if (!StaticOneParam.trueOnNull(s)) {
s.toString();
} }
} }
void testFalseOnNull(@Nullable CharSequence s) { void falseOnNullPositiveBranchIsOK(@Nullable String s) {
// Explicitly annotated if (StaticOneParam.falseOnNull(s)) {
if (AnnotatedTextUtils.isNotEmpty(s)) { s.toString();
s.toString(); // OK: if we are here, we know that `s` is not null }
} }
// Not annotated void falseOnNullNegativeBranchIsBAD(@Nullable String s) {
if (NotAnnotatedTextUtils.isNotEmpty(s)) { if (!StaticOneParam.falseOnNull(s)) {
s.toString(); // BAD: the typecker does not know `s` can not be null s.toString();
}
} }
if (!AnnotatedTextUtils.isNotEmpty(s)) { void notAnnotatedPositiveBranchIsBAD(@Nullable String s) {
s.toString(); // BAD: `s` can be null or an empty string if (StaticOneParam.notAnnotated(s)) {
s.toString();
} }
} }
void testModelledTrueOnNull(String s) { void notAnnotatedNegativeBranchIsBAD(@Nullable String s) {
if (!StaticOneParam.notAnnotated(s)) {
s.toString();
}
}
}
class TestNonStaticOneParam {
private NonStaticOneParam object = new NonStaticOneParam();
void trueOnNullPositiveBranchIsBAD(@Nullable String s) {
if (object.trueOnNull(s)) {
s.toString();
}
}
void trueOnNullNegativeBranchIsOK(@Nullable String s) {
if (!object.trueOnNull(s)) {
s.toString();
}
}
void falseOnNullPositiveBranchIsOK(@Nullable String s) {
if (object.falseOnNull(s)) {
s.toString();
}
}
void falseOnNullNegativeBranchIsBAD(@Nullable String s) {
if (!object.falseOnNull(s)) {
s.toString();
}
}
void notAnnotatedPositiveBranchIsBAD(@Nullable String s) {
if (object.notAnnotated(s)) {
s.toString();
}
}
void notAnnotatedNegativeBranchIsBAD(@Nullable String s) {
if (!object.notAnnotated(s)) {
s.toString();
}
}
}
class TestNonStaticSeveralParams {
private NonStaticSeveralParams object = new NonStaticSeveralParams();
void trueOnNullPositiveBranchIsBAD(@Nullable String s1, @Nullable String s2) {
if (object.trueOnNull(s1, 1, "123", s2)) {
s1.toString();
s2.toString();
}
}
void trueOnNullNegativeBranchIsOK(@Nullable String s1, @Nullable String s2) {
if (!object.trueOnNull(s1, 1, "123", s2)) {
s1.toString();
s2.toString();
}
}
void falseOnNullPositiveBranchIsOK(@Nullable String s1, @Nullable String s2) {
if (object.falseOnNull(s1, 1, "123", s2)) {
s1.toString();
s2.toString();
}
}
void falseOnNullNegativeBranchIsBAD(@Nullable String s1, @Nullable String s2) {
if (!object.falseOnNull(s1, 1, "123", s2)) {
s1.toString();
s2.toString();
}
}
void notAnnotatedPositiveBranchIsBAD(@Nullable String s1, @Nullable String s2) {
if (object.notAnnotated(s1, 1, "123", s2)) {
s1.toString();
s2.toString();
}
}
void notAnnotatedNegativeBranchIsBAD(@Nullable String s1, @Nullable String s2) {
if (!object.notAnnotated(s1, 1, "123", s2)) {
s1.toString();
s2.toString();
}
}
}
class TestModels {
void testModelledTrueOnNull(@Nullable String s) {
// TextUtils.isEmpty is modelled as TrueOnNull // TextUtils.isEmpty is modelled as TrueOnNull
if (!TextUtils.isEmpty(s)) { if (!TextUtils.isEmpty(s)) {
s.toString(); // OK: if we are here, we know that `s` is not null s.toString(); // OK: if we are here, we know that `s` is not null
@ -84,19 +221,19 @@ public class TrueFalseOnNull {
if (!Strings.isNullOrEmpty(s)) { if (!Strings.isNullOrEmpty(s)) {
s.toString(); // OK: if we are here, we know that `s` is not null s.toString(); // OK: if we are here, we know that `s` is not null
} }
if (!NotAnnotatedTextUtils.isEmpty(s)) {
s.toString(); // BAD: the typechecker can not figure this out for not modelled class
} }
} }
// this is a common enough pattern to be tested separately
class EarlyReturn {
void testEarlyReturn(@Nullable CharSequence s1, @Nullable CharSequence s2) { void testEarlyReturn(@Nullable CharSequence s1, @Nullable CharSequence s2) {
if (AnnotatedTextUtils.isEmpty(s1) || NotAnnotatedTextUtils.isEmpty(s2)) { if (StaticOneParam.trueOnNull(s1) || StaticOneParam.notAnnotated(s2)) {
return; return;
} }
s1.toString(); // OK: if `s1` was null, we would no rech this point s1.toString(); // OK: if `s1` was null, we would no rech this point
s2.toString(); // BAD: typechecker can not figure this for not annotated class s2.toString(); // BAD: no reason for `s2` to become non-nullable
} }
} }
} }

@ -247,9 +247,22 @@ codetoanalyze/java/nullsafe-default/StrictModeForThirdParty.java, codetoanalyze.
codetoanalyze/java/nullsafe-default/SwitchCase.java, codetoanalyze.java.nullsafe_default.SwitchCase.getNullableColor():codetoanalyze.java.nullsafe_default.Color, 0, ERADICATE_RETURN_OVER_ANNOTATED, no_bucket, WARNING, [Method `getNullableColor()` is annotated with `@Nullable` but never returns null.] codetoanalyze/java/nullsafe-default/SwitchCase.java, codetoanalyze.java.nullsafe_default.SwitchCase.getNullableColor():codetoanalyze.java.nullsafe_default.Color, 0, ERADICATE_RETURN_OVER_ANNOTATED, no_bucket, WARNING, [Method `getNullableColor()` is annotated with `@Nullable` but never returns null.]
codetoanalyze/java/nullsafe-default/SwitchCase.java, codetoanalyze.java.nullsafe_default.SwitchCase.switchOnNullIsBad():java.lang.String, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [NullPointerException will be thrown at this line! `color` is `null` and is dereferenced via calling `ordinal()`: null constant at line 14] codetoanalyze/java/nullsafe-default/SwitchCase.java, codetoanalyze.java.nullsafe_default.SwitchCase.switchOnNullIsBad():java.lang.String, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [NullPointerException will be thrown at this line! `color` is `null` and is dereferenced via calling `ordinal()`: null constant at line 14]
codetoanalyze/java/nullsafe-default/SwitchCase.java, codetoanalyze.java.nullsafe_default.SwitchCase.switchOnNullableIsBad():java.lang.String, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`color` is nullable and is not locally checked for null when calling `ordinal()`: call to getNullableColor() at line 28] codetoanalyze/java/nullsafe-default/SwitchCase.java, codetoanalyze.java.nullsafe_default.SwitchCase.switchOnNullableIsBad():java.lang.String, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`color` is nullable and is not locally checked for null when calling `ordinal()`: call to getNullableColor() at line 28]
codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, codetoanalyze.java.nullsafe_default.TrueFalseOnNull$Test.testEarlyReturn(java.lang.CharSequence,java.lang.CharSequence):void, 5, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s2` is nullable and is not locally checked for null when calling `toString()`.] codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, codetoanalyze.java.nullsafe_default.TrueFalseOnNull$EarlyReturn.testEarlyReturn(java.lang.CharSequence,java.lang.CharSequence):void, 5, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s2` is nullable and is not locally checked for null when calling `toString()`.]
codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, codetoanalyze.java.nullsafe_default.TrueFalseOnNull$Test.testFalseOnNull(java.lang.CharSequence):void, 6, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `toString()`.] codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, codetoanalyze.java.nullsafe_default.TrueFalseOnNull$TestModels.testModelledTrueOnNull(java.lang.String):void, 0, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`TextUtils.isEmpty(...)`: parameter #1 is declared non-nullable but the argument `s` is nullable.]
codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, codetoanalyze.java.nullsafe_default.TrueFalseOnNull$Test.testFalseOnNull(java.lang.CharSequence):void, 10, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `toString()`.] codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, codetoanalyze.java.nullsafe_default.TrueFalseOnNull$TestNonStaticOneParam.falseOnNullNegativeBranchIsBAD(java.lang.String):void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `toString()`.]
codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, codetoanalyze.java.nullsafe_default.TrueFalseOnNull$Test.testTrueOnNull(java.lang.CharSequence):void, 6, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `toString()`.] codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, codetoanalyze.java.nullsafe_default.TrueFalseOnNull$TestNonStaticOneParam.notAnnotatedNegativeBranchIsBAD(java.lang.String):void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `toString()`.]
codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, codetoanalyze.java.nullsafe_default.TrueFalseOnNull$Test.testTrueOnNull(java.lang.CharSequence):void, 10, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `toString()`.] codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, codetoanalyze.java.nullsafe_default.TrueFalseOnNull$TestNonStaticOneParam.notAnnotatedPositiveBranchIsBAD(java.lang.String):void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `toString()`.]
codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, codetoanalyze.java.nullsafe_default.TrueFalseOnNull$TestNonStaticOneParam.trueOnNullPositiveBranchIsBAD(java.lang.String):void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `toString()`.]
codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, codetoanalyze.java.nullsafe_default.TrueFalseOnNull$TestNonStaticSeveralParams.falseOnNullNegativeBranchIsBAD(java.lang.String,java.lang.String):void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s1` is nullable and is not locally checked for null when calling `toString()`.]
codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, codetoanalyze.java.nullsafe_default.TrueFalseOnNull$TestNonStaticSeveralParams.falseOnNullNegativeBranchIsBAD(java.lang.String,java.lang.String):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s2` is nullable and is not locally checked for null when calling `toString()`.]
codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, codetoanalyze.java.nullsafe_default.TrueFalseOnNull$TestNonStaticSeveralParams.notAnnotatedNegativeBranchIsBAD(java.lang.String,java.lang.String):void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s1` is nullable and is not locally checked for null when calling `toString()`.]
codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, codetoanalyze.java.nullsafe_default.TrueFalseOnNull$TestNonStaticSeveralParams.notAnnotatedNegativeBranchIsBAD(java.lang.String,java.lang.String):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s2` is nullable and is not locally checked for null when calling `toString()`.]
codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, codetoanalyze.java.nullsafe_default.TrueFalseOnNull$TestNonStaticSeveralParams.notAnnotatedPositiveBranchIsBAD(java.lang.String,java.lang.String):void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s1` is nullable and is not locally checked for null when calling `toString()`.]
codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, codetoanalyze.java.nullsafe_default.TrueFalseOnNull$TestNonStaticSeveralParams.notAnnotatedPositiveBranchIsBAD(java.lang.String,java.lang.String):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s2` is nullable and is not locally checked for null when calling `toString()`.]
codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, codetoanalyze.java.nullsafe_default.TrueFalseOnNull$TestNonStaticSeveralParams.trueOnNullPositiveBranchIsBAD(java.lang.String,java.lang.String):void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s1` is nullable and is not locally checked for null when calling `toString()`.]
codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, codetoanalyze.java.nullsafe_default.TrueFalseOnNull$TestNonStaticSeveralParams.trueOnNullPositiveBranchIsBAD(java.lang.String,java.lang.String):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s2` is nullable and is not locally checked for null when calling `toString()`.]
codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, codetoanalyze.java.nullsafe_default.TrueFalseOnNull$TestStaticOneParam.falseOnNullNegativeBranchIsBAD(java.lang.String):void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `toString()`.]
codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, codetoanalyze.java.nullsafe_default.TrueFalseOnNull$TestStaticOneParam.notAnnotatedNegativeBranchIsBAD(java.lang.String):void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `toString()`.]
codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, codetoanalyze.java.nullsafe_default.TrueFalseOnNull$TestStaticOneParam.notAnnotatedPositiveBranchIsBAD(java.lang.String):void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `toString()`.]
codetoanalyze/java/nullsafe-default/TrueFalseOnNull.java, codetoanalyze.java.nullsafe_default.TrueFalseOnNull$TestStaticOneParam.trueOnNullPositiveBranchIsBAD(java.lang.String):void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `toString()`.]
codetoanalyze/java/nullsafe-default/third-party-test-code/some/test/pckg/ThirdPartyTestClass.java, some.test.pckg.ThirdPartyTestClass.returnSpecifiedAsNullable():java.lang.String, 0, ERADICATE_RETURN_OVER_ANNOTATED, no_bucket, WARNING, [Method `returnSpecifiedAsNullable()` is annotated with `@Nullable` but never returns null.] codetoanalyze/java/nullsafe-default/third-party-test-code/some/test/pckg/ThirdPartyTestClass.java, some.test.pckg.ThirdPartyTestClass.returnSpecifiedAsNullable():java.lang.String, 0, ERADICATE_RETURN_OVER_ANNOTATED, no_bucket, WARNING, [Method `returnSpecifiedAsNullable()` is annotated with `@Nullable` but never returns null.]

Loading…
Cancel
Save