diff --git a/infer/src/checkers/NullabilityCheck.ml b/infer/src/checkers/NullabilityCheck.ml index f4dc844f7..03add7bca 100644 --- a/infer/src/checkers/NullabilityCheck.ml +++ b/infer/src/checkers/NullabilityCheck.ml @@ -151,6 +151,15 @@ module TransferFunctions (CFG : ProcCfg.S) = struct | HilExp.BinaryOperator (Binop.Ne, exp, HilExp.AccessPath ap) ) , _ , _ + , _ ) + | Assume + ( HilExp.UnaryOperator + ( Unop.LNot + , ( HilExp.BinaryOperator (Binop.Eq, HilExp.AccessPath ap, exp) + | HilExp.BinaryOperator (Binop.Eq, exp, HilExp.AccessPath ap) ) + , _ ) + , _ + , _ , _ ) -> if HilExp.is_null_literal exp then assume_pnames_notnull ap astate else astate | _ -> diff --git a/infer/src/checkers/registerCheckers.ml b/infer/src/checkers/registerCheckers.ml index 20dddd164..f86a474eb 100644 --- a/infer/src/checkers/registerCheckers.ml +++ b/infer/src/checkers/registerCheckers.ml @@ -61,7 +61,9 @@ let all_checkers = ; callbacks= [(Procedure PrintfArgs.callback_printf_args, Config.Java)] } ; { name= "nullable checks" ; active= Config.check_nullable - ; callbacks= [(Procedure NullabilityCheck.checker, Config.Clang)] } + ; callbacks= + [ (Procedure NullabilityCheck.checker, Config.Clang) + ; (Procedure NullabilityCheck.checker, Config.Java) ] } ; { name= "nullable suggestion" ; active= Config.suggest_nullable ; callbacks= diff --git a/infer/tests/codetoanalyze/cpp/nullable/issues.exp b/infer/tests/codetoanalyze/cpp/nullable/issues.exp index 6c4c75713..58c89fc8f 100644 --- a/infer/tests/codetoanalyze/cpp/nullable/issues.exp +++ b/infer/tests/codetoanalyze/cpp/nullable/issues.exp @@ -18,6 +18,8 @@ codetoanalyze/cpp/nullable/method.cpp, callMethodOnNullableObjectBad, 1, NULL_DE codetoanalyze/cpp/nullable/method.cpp, callMethodOnNullableObjectOk, 2, NULL_TEST_AFTER_DEREFERENCE, [start of procedure callMethodOnNullableObjectOk(),start of procedure mayReturnNullObject,Condition is false,return from a call to T_mayReturnNullObject,Condition is false] codetoanalyze/cpp/nullable/method.cpp, dereferenceFieldOfNullableObjectBad, 2, NULLABLE_DEREFERENCE, [deference of &p,assignment of the nullable value,definition of mayReturnNullObject] codetoanalyze/cpp/nullable/method.cpp, dereferenceFieldOfNullableObjectBad, 2, NULL_DEREFERENCE, [start of procedure dereferenceFieldOfNullableObjectBad(),start of procedure mayReturnNullObject,Condition is true,return from a call to T_mayReturnNullObject] +codetoanalyze/cpp/nullable/method.cpp, methodAlwaysCheckedForNullOkay, 1, NULL_TEST_AFTER_DEREFERENCE, [start of procedure methodAlwaysCheckedForNullOkay(),Condition is true,start of procedure mayReturnNullObject,Condition is false,return from a call to T_mayReturnNullObject,Condition is false] +codetoanalyze/cpp/nullable/method.cpp, methodAlwaysCheckedForNullOkay, 2, NULL_DEREFERENCE, [start of procedure methodAlwaysCheckedForNullOkay(),Condition is true,start of procedure mayReturnNullObject,Condition is false,return from a call to T_mayReturnNullObject,Condition is true,start of procedure mayReturnNullObject,Condition is false,return from a call to T_mayReturnNullObject] codetoanalyze/cpp/nullable/method.cpp, methodCallOnFieldOfNullableObjectBad, 2, NULLABLE_DEREFERENCE, [deference of &p,assignment of the nullable value,definition of mayReturnNullObject] codetoanalyze/cpp/nullable/method.cpp, methodCallOnFieldOfNullableObjectBad, 2, NULL_DEREFERENCE, [start of procedure methodCallOnFieldOfNullableObjectBad(),start of procedure mayReturnNullObject,Condition is true,return from a call to T_mayReturnNullObject] codetoanalyze/cpp/nullable/method.cpp, methodCheckedForNullOkay, 1, NULL_TEST_AFTER_DEREFERENCE, [start of procedure methodCheckedForNullOkay(),start of procedure mayReturnNullObject,Condition is false,return from a call to T_mayReturnNullObject,Condition is false] @@ -25,8 +27,6 @@ codetoanalyze/cpp/nullable/method.cpp, methodCheckedForNullOkay, 2, NULL_DEREFER codetoanalyze/cpp/nullable/method.cpp, methodNotAlwaysCheckedForNullBad, 1, NULL_TEST_AFTER_DEREFERENCE, [start of procedure methodNotAlwaysCheckedForNullBad(),Condition is false,start of procedure mayReturnNullObject,Condition is false,return from a call to T_mayReturnNullObject,Condition is false] codetoanalyze/cpp/nullable/method.cpp, methodNotAlwaysCheckedForNullBad, 2, NULLABLE_DEREFERENCE, [deference of n$6,definition of mayReturnNullObject] codetoanalyze/cpp/nullable/method.cpp, methodNotAlwaysCheckedForNullBad, 2, NULL_DEREFERENCE, [start of procedure methodNotAlwaysCheckedForNullBad(),Condition is false,start of procedure mayReturnNullObject,Condition is false,return from a call to T_mayReturnNullObject,Condition is true,start of procedure mayReturnNullObject,Condition is false,return from a call to T_mayReturnNullObject] -codetoanalyze/cpp/nullable/method.cpp, methodNotAlwaysCheckedForNullOkay, 1, NULL_TEST_AFTER_DEREFERENCE, [start of procedure methodNotAlwaysCheckedForNullOkay(),Condition is true,start of procedure mayReturnNullObject,Condition is false,return from a call to T_mayReturnNullObject,Condition is false] -codetoanalyze/cpp/nullable/method.cpp, methodNotAlwaysCheckedForNullOkay, 2, NULL_DEREFERENCE, [start of procedure methodNotAlwaysCheckedForNullOkay(),Condition is true,start of procedure mayReturnNullObject,Condition is false,return from a call to T_mayReturnNullObject,Condition is true,start of procedure mayReturnNullObject,Condition is false,return from a call to T_mayReturnNullObject] codetoanalyze/cpp/nullable/method.cpp, nullableAssignmentInOneBranchBad, 7, NULLABLE_DEREFERENCE, [deference of &p,assignment of the nullable value,definition of mayReturnNullObject] codetoanalyze/cpp/nullable/method.cpp, nullableAssignmentInOneBranchBad, 7, NULL_DEREFERENCE, [start of procedure nullableAssignmentInOneBranchBad(),Condition is true,start of procedure mayReturnNullObject,Condition is true,return from a call to T_mayReturnNullObject] codetoanalyze/cpp/nullable/method.cpp, reportsViolationInNotNullElseBranchBad, 1, NULL_TEST_AFTER_DEREFERENCE, [start of procedure reportsViolationInNotNullElseBranchBad(),start of procedure mayReturnNullObject,Condition is false,return from a call to T_mayReturnNullObject,Condition is false] diff --git a/infer/tests/codetoanalyze/cpp/nullable/method.cpp b/infer/tests/codetoanalyze/cpp/nullable/method.cpp index e2d01d2ce..a5a1999d2 100644 --- a/infer/tests/codetoanalyze/cpp/nullable/method.cpp +++ b/infer/tests/codetoanalyze/cpp/nullable/method.cpp @@ -112,7 +112,7 @@ void reportsViolationInNotNullElseBranchBad(T* t) { } } -void methodNotAlwaysCheckedForNullOkay(T* t) { +void methodAlwaysCheckedForNullOkay(T* t) { if (star() && t->mayReturnNullObject() != nullptr) { t->mayReturnNullObject()->doSomething(); // does not report here } diff --git a/infer/tests/codetoanalyze/java/checkers/Makefile b/infer/tests/codetoanalyze/java/checkers/Makefile index 136308e36..72d4c9dfe 100644 --- a/infer/tests/codetoanalyze/java/checkers/Makefile +++ b/infer/tests/codetoanalyze/java/checkers/Makefile @@ -11,7 +11,7 @@ ANALYZER = checkers INFER_OPTIONS = \ --debug-exceptions --dynamic-dispatch none --no-default-checkers \ --annotation-reachability --fragment-retains-view --immutable-cast --printf-args --quandary \ - --suggest-nullable --racerd \ + --suggest-nullable --check-nullable --racerd \ INFERPRINT_OPTIONS = --issues-tests SOURCES = $(wildcard *.java) diff --git a/infer/tests/codetoanalyze/java/checkers/NullableViolation.java b/infer/tests/codetoanalyze/java/checkers/NullableViolation.java new file mode 100644 index 000000000..8716fdcb9 --- /dev/null +++ b/infer/tests/codetoanalyze/java/checkers/NullableViolation.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2017 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +package codetoanalyze.java.checkers; + +import javax.annotation.Nullable; + +public class NullableViolation { + + class T { + int x; + void doSomething() {} + } + + native static @Nullable T returnsNullable(); + + void dereferenceNullableReturnValueBad() { + T t = returnsNullable(); + t.x = 42; // reports here + } + + void dereferenceNullableReturnValueOkay() { + T t = returnsNullable(); + if (t != null) { + t.x = 42; // does not report here + } + } + + void dereferenceNullableMethodBad() { + returnsNullable().doSomething(); // reports here + } + + void dereferenceNullableMethodCheckedForNullOkay() { + if (returnsNullable() != null) { + returnsNullable().doSomething(); // does not report here + } + } + + void dereferenceNullableMethodIncorrectlyCheckedForNullBad() { + if (returnsNullable() == null) { + returnsNullable().doSomething(); // reports here + } + } + + void dereferenceNullableMethodInElseBranchBad() { + if (returnsNullable() != null) { + } else { + returnsNullable().doSomething(); // reports here + } + } + + native boolean star(); + + void dereferenceNullableMethodAlwaysCheckedForNullOkay() { + if (star() && returnsNullable() != null) { + returnsNullable().doSomething(); // does not report here + } + } + + void dereferenceNullableMethodNotAlwaysCheckedForNullBad() { + if (star() || returnsNullable() != null) { + returnsNullable().doSomething(); // reports here + } + } + +} diff --git a/infer/tests/codetoanalyze/java/checkers/issues.exp b/infer/tests/codetoanalyze/java/checkers/issues.exp index e58faa843..b558e5c8e 100644 --- a/infer/tests/codetoanalyze/java/checkers/issues.exp +++ b/infer/tests/codetoanalyze/java/checkers/issues.exp @@ -39,6 +39,11 @@ codetoanalyze/java/checkers/NullableSuggest.java, void NullableSuggest.assignNul codetoanalyze/java/checkers/NullableSuggest.java, void NullableSuggest.assignNullToFieldTransitiveLoopBad(int), 7, FIELD_SHOULD_BE_NULLABLE, [Field obj0 could be assigned here,Some array element could be assigned here,Variable is assigned null here] codetoanalyze/java/checkers/NullableSuggest.java, void NullableSuggest.compareNullToFieldBad(), 2, FIELD_SHOULD_BE_NULLABLE, [Field obj0 is compared to null here] codetoanalyze/java/checkers/NullableSuggest.java, void NullableSuggest.multipleChainsAlwaysSelectShortestBad(boolean), 6, FIELD_SHOULD_BE_NULLABLE, [Field obj0 could be assigned here,Variable is assigned null here] +codetoanalyze/java/checkers/NullableViolation.java, void NullableViolation.dereferenceNullableMethodBad(), 1, NULLABLE_DEREFERENCE, [deference of n$0,definition of returnsNullable] +codetoanalyze/java/checkers/NullableViolation.java, void NullableViolation.dereferenceNullableMethodInElseBranchBad(), 3, NULLABLE_DEREFERENCE, [deference of n$2,definition of returnsNullable] +codetoanalyze/java/checkers/NullableViolation.java, void NullableViolation.dereferenceNullableMethodIncorrectlyCheckedForNullBad(), 2, NULLABLE_DEREFERENCE, [deference of n$2,definition of returnsNullable] +codetoanalyze/java/checkers/NullableViolation.java, void NullableViolation.dereferenceNullableMethodNotAlwaysCheckedForNullBad(), 2, NULLABLE_DEREFERENCE, [deference of n$6,definition of returnsNullable] +codetoanalyze/java/checkers/NullableViolation.java, void NullableViolation.dereferenceNullableReturnValueBad(), 2, NULLABLE_DEREFERENCE, [deference of &t,assignment of the nullable value,definition of returnsNullable] codetoanalyze/java/checkers/PrintfArgsChecker.java, void PrintfArgsChecker.formatStringIsNotLiteral(PrintStream), 2, CHECKERS_PRINTF_ARGS, [Format string must be string literal] codetoanalyze/java/checkers/PrintfArgsChecker.java, void PrintfArgsChecker.stringInsteadOfInteger(PrintStream), 1, printf(...) at line 40: parameter 2 is expected to be of type java.lang.Integer but java.lang.String was given., [] codetoanalyze/java/checkers/PrintfArgsChecker.java, void PrintfArgsChecker.wrongNumberOfArguments(PrintStream), 1, format string arguments don't mach provided arguments in printf(...) at line 44, []