diff --git a/.gitignore b/.gitignore index 0c020b4c282..d7998a8499a 100644 --- a/.gitignore +++ b/.gitignore @@ -200,7 +200,6 @@ rebar3.crashdump /infer/src/clang/dune /infer/src/integration/dune /infer/src/java/dune -/infer/src/nullsafe/unit/dune /infer/src/opensource/dune /infer/src/python/unit/dune /infer/src/unit/dune diff --git a/Makefile b/Makefile index c38cf11f327..a6ae679ada4 100644 --- a/Makefile +++ b/Makefile @@ -246,8 +246,6 @@ DIRECT_TESTS += \ java_immutability \ java_inefficientKeysetIterator \ java_litho-required-props \ - java_nullsafe \ - java_nullsafe-annotation-graph \ java_performance \ java_performance-exclusive \ java_pulse \ diff --git a/examples/demo/demo.sh b/examples/demo/demo.sh index 6e972744e15..aa3e462fea7 100755 --- a/examples/demo/demo.sh +++ b/examples/demo/demo.sh @@ -13,4 +13,3 @@ infer -- make -C 02 clean all infer explore --no-source-preview --select 0 infer -- make -C 03 clean all infer explore --no-source-preview --select 1 -infer --eradicate-only -- make -C 04 clean all diff --git a/infer/annotations/README.md b/infer/annotations/README.md index 297192c5aec..4a27320dddf 100644 --- a/infer/annotations/README.md +++ b/infer/annotations/README.md @@ -1,6 +1,6 @@ # Infer annotations -This project provides extra annotations for Java checkers, such as starvation or nullsafe. +This project provides extra annotations for Java checkers, such as starvation. ## How to build JARs (suitable for release) diff --git a/infer/annotations/src/main/java/com/facebook/infer/annotation/Assertions.java b/infer/annotations/src/main/java/com/facebook/infer/annotation/Assertions.java index a77d74df29a..49298481e73 100644 --- a/infer/annotations/src/main/java/com/facebook/infer/annotation/Assertions.java +++ b/infer/annotations/src/main/java/com/facebook/infer/annotation/Assertions.java @@ -55,9 +55,10 @@ public static T assumeNotNull(@Nullable T object) { } /** - * Makes Nullsafe stop complaining when {@code object} is dereferenced or converted to a - * non-nullable. In contrast with {@code #assumeNotNull()}, indicates cases when the proper fix - * needs to be committed, but for some reason it is hard or impossible to do it staight away. + * Makes Nullsafe (a nullability type checker) stop complaining when {@code object} is + * dereferenced or converted to a non-nullable. In contrast with {@code #assumeNotNull()}, + * indicates cases when the proper fix needs to be committed, but for some reason it is hard or + * impossible to do it staight away. */ public static T nullsafeFIXME(@Nullable T object, String explanationOrTask) { return object; diff --git a/infer/documentation/checkers/Eradicate.md b/infer/documentation/checkers/Eradicate.md deleted file mode 100644 index fda2978e9c9..00000000000 --- a/infer/documentation/checkers/Eradicate.md +++ /dev/null @@ -1,75 +0,0 @@ -> "I call it my billion-dollar mistake. It was the invention of the null -> reference in 1965." -> -> [Tony Hoare](http://en.wikipedia.org/wiki/Tony_Hoare) - -### What is Infer:Eradicate? - -Infer:Eradicate is a type checker for `@Nullable` annotations for Java. It is part -of the Infer static analysis suite of tools. The goal is to eradicate null -pointer exceptions. - -@Nullable -annotations denote that a parameter, field or the return value of a method can -be null. When decorating a parameter, this denotes that the parameter can -legitimately be null and the method will need to deal with it. When decorating a -method, this denotes the method might legitimately return null. - -Starting from @Nullable-annotated programs, the checker performs a flow -sensitive analysis to propagate the nullability through assignments and calls, -and flags errors for unprotected accesses to nullable values or -inconsistent/missing annotations. It can also be used to add annotations to a -previously un-annotated program. - -### What is the @Nullable convention? - -If you say nothing, you're saying that the value cannot be null. This is the -recommended option when possible: - -Program safely, annotate nothing! - -When this cannot be done, add a @Nullable annotation before the type to indicate -that the value can be null. - -### What is annotated? - -Annotations are placed at the interface of method calls and field accesses: - -- Parameters and return type of a method declaration. -- Field declarations. - -Local variable declarations are not annotated: their nullability is inferred. - -### How is Infer:Eradicate invoked? - -Eradicate can be invoked by adding the option `--eradicate` to the checkers mode -as in this example: - -```bash -infer run -a checkers --eradicate -- javac Test.java -``` - -The checker will report an error on the following program that accesses a -nullable value without null check: - -```java -class C { - int getLength(@Nullable String s) { - return s.length(); - } -} -``` - -But it will not report an error on this guarded dereference: - -```java -class C { - int getLength(@Nullable String s) { - if (s != null) { - return s.length(); - } else { - return -1; - } - } -} -``` diff --git a/infer/documentation/checkers/Pulse.md b/infer/documentation/checkers/Pulse.md index 00e03c640ac..13aae741936 100644 --- a/infer/documentation/checkers/Pulse.md +++ b/infer/documentation/checkers/Pulse.md @@ -69,7 +69,7 @@ void false_negative() { int* x = (int*) malloc(sizeof(int)); if (x) { // unknown call to x makes Pulse forget that x was allocated, in case it frees x - unknown(x); + unknown(x); } } // no memory leak reported: false negative! @@ -94,7 +94,7 @@ $ infer debug --procedures --procedures-filter 'false_negative' --procedures-sum ## Pulse x Nullsafe -[Nullsafe](/docs/next/checker-eradicate) is a type checker for `@Nullable` annotations for Java. Classes following the Nullsafe discipline are annotated with `@Nullsafe`. +Nullsafe is a type checker for `@Nullable` annotations for Java. Classes following the Nullsafe discipline are annotated with `@Nullsafe`. Consider the classes `Person` and `Registry` from the previous example. Assuming that class `Person` is annotated with `@Nullsafe`. In this case, we also annotate `getEmergencyContact()` with `@Nullable`, to make explicit that this method can return the `null` value. There is still the risk that classes depending on `Person` have Null dereferences. In this case, Pulse would report a Null dereference on `Registry`. It could also be the case that class `Registry` is annotated with `@Nullsafe`. By default Pulse reports on `@Nullsafe` files too, see the `--pulse-nullsafe-report-npe` option (Facebook-specific: Pulse does not report on `@Nullsafe` files). diff --git a/infer/documentation/issues/CHECKERS_IMMUTABLE_CAST.md b/infer/documentation/issues/CHECKERS_IMMUTABLE_CAST.md deleted file mode 100644 index e21684fb2be..00000000000 --- a/infer/documentation/issues/CHECKERS_IMMUTABLE_CAST.md +++ /dev/null @@ -1,15 +0,0 @@ -This error type is reported in Java. It fires when an immutable collection is -returned from a method whose type is mutable. - -```java - public List getSomeList() { - ImmutableList l = foo(...); - return l; - } -``` - -This can lead to a runtime error if users of `getSomeList` try to modify the -list e.g. by adding elements. - -Action: you can change the return type to be immutable, or make a copy of the -collection so that it can be modified. diff --git a/infer/documentation/issues/ERADICATE_CONDITION_REDUNDANT.md b/infer/documentation/issues/ERADICATE_CONDITION_REDUNDANT.md deleted file mode 100644 index 2b39866e015..00000000000 --- a/infer/documentation/issues/ERADICATE_CONDITION_REDUNDANT.md +++ /dev/null @@ -1,22 +0,0 @@ -This report is inactive by default. Condition (x != null) or (x == null) when x -cannot be null: the first condition is always true and the second is always -false - -Example: - -```java -class C { - void m() { - String s = new String("abc"); - if (s != null) { - int n = s.length(); - } - } -} -``` - -Action: Make sure that the annotations are correct, as the condition is -considered redundant based on the existing annotations. In particular, check the -annotation of any input parameters and fields of the current method, as well as -the annotations of any method called directly by the current method, if -relevant. If the annotations are correct, you can remove the redundant case. diff --git a/infer/documentation/issues/ERADICATE_FIELD_NOT_INITIALIZED.md b/infer/documentation/issues/ERADICATE_FIELD_NOT_INITIALIZED.md deleted file mode 100644 index 09c95b1fa99..00000000000 --- a/infer/documentation/issues/ERADICATE_FIELD_NOT_INITIALIZED.md +++ /dev/null @@ -1,17 +0,0 @@ -The constructor does not initialize a field f which is not annotated with -@Nullable - -Example: - -```java -class C { - String f; - - C () { // field f not initialized and not annotated @Nullable - } -} -``` - -Action: The preferred action is to initialize the field with a value that is not -null. If, by design, null is a valid value for the field, then it should be -annotated with @Nullable. diff --git a/infer/documentation/issues/ERADICATE_FIELD_NOT_NULLABLE.md b/infer/documentation/issues/ERADICATE_FIELD_NOT_NULLABLE.md deleted file mode 100644 index 657108c9d96..00000000000 --- a/infer/documentation/issues/ERADICATE_FIELD_NOT_NULLABLE.md +++ /dev/null @@ -1,20 +0,0 @@ -An assignment x.f = v where v could be null and field f is not annotated with -@Nullable. - -Example: - -```java -class C { - String f; - - void foo(@Nullable String s) { - f = s; - } -} -``` - -Action: The preferred action is to ensure that a null value is never stored in -the field, by changing the code or changing annotations. If this cannot be done, -add a @Nullable annotation to the field. This annotation might trigger more -warnings in other code that uses the field, as that code must now deal with null -values. diff --git a/infer/documentation/issues/ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION.md b/infer/documentation/issues/ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION.md deleted file mode 100644 index 399927c354a..00000000000 --- a/infer/documentation/issues/ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION.md +++ /dev/null @@ -1,40 +0,0 @@ -A parameter of the overridden method is missing a @Nullable annotation present in the superclass. - -Action: choose a consistent annotation based on the desired invariant. - -Example: - -```java -class A { - - int len(@Nullable String s) { - if (s != null) { - return s.length(); - } else { - return 0; - } - } -} - -class B extends A { - - int len(String s) { // @Nullable missing. - return s.length(); - } -} -``` - -A consistent use of @Nullable on parameters across subtyping should prevent runtime issue like in: - -```java -public class Main { - - String s; - - int foo() { - A a = new B(); - return a.len(s); - } -} -``` - diff --git a/infer/documentation/issues/ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION.md b/infer/documentation/issues/ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION.md deleted file mode 100644 index 001126dfa49..00000000000 --- a/infer/documentation/issues/ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION.md +++ /dev/null @@ -1,39 +0,0 @@ -The return type of the overridden method is annotated @Nullable, but the -corresponding method in the superclass is not. - -Action: choose a consistent annotation based on the desired invariant. - -Example: - -```java -class A { - String create() { - return new String("abc"); - } -} - -class B extends A { - @Nullable String create() { // Inconsistent @Nullable annotation. - return null; - } -} -``` - -A consistent use of `@Nullable` on the return type across subtyping should prevent -runtime issue like in: - -```java -class Main { - - int foo(A a) { - String s = a.create(); - return s.length(); - } - - void main(String[] args) { - A a = new B(); - foo(a); - } - -} -``` diff --git a/infer/documentation/issues/ERADICATE_META_CLASS_NEEDS_IMPROVEMENT.md b/infer/documentation/issues/ERADICATE_META_CLASS_NEEDS_IMPROVEMENT.md deleted file mode 100644 index 707a48504b5..00000000000 --- a/infer/documentation/issues/ERADICATE_META_CLASS_NEEDS_IMPROVEMENT.md +++ /dev/null @@ -1,3 +0,0 @@ -Reported when the class either: -- has at least one nullability issue, or -- has at least one (currently possibly hidden) issue preventing it from being marked `@Nullsafe`. diff --git a/infer/documentation/issues/ERADICATE_PARAMETER_NOT_NULLABLE.md b/infer/documentation/issues/ERADICATE_PARAMETER_NOT_NULLABLE.md deleted file mode 100644 index 610bbda0174..00000000000 --- a/infer/documentation/issues/ERADICATE_PARAMETER_NOT_NULLABLE.md +++ /dev/null @@ -1,22 +0,0 @@ -Method call x.m(..., v, ...) where v can be null and the corresponding parameter -in method m is not annotated with @Nullable - -Example: - -```java -class C { - void m(C x) { - String s = x.toString() - } - - void test(@Nullable C x) { - m(x); - } -} -``` - -Action: The preferred action is to ensure that a null value is never passed to -the method, by changing the code or changing annotations. If this cannot be -done, add a @Nullable annotation to the relevant parameter in the method -declaration. This annotation might trigger more warnings in the implementation -of method m, as that code must now deal with null values. diff --git a/infer/documentation/issues/ERADICATE_RETURN_NOT_NULLABLE.md b/infer/documentation/issues/ERADICATE_RETURN_NOT_NULLABLE.md deleted file mode 100644 index 931bc715008..00000000000 --- a/infer/documentation/issues/ERADICATE_RETURN_NOT_NULLABLE.md +++ /dev/null @@ -1,18 +0,0 @@ -Method m can return null, but the method's return type is not annotated with -@Nullable - -Example: - -```java -class C { - String m() { - return null; - } -} -``` - -Action: The preferred action is to ensure that a null value is never returned by -the method, by changing the code or changing annotations. If this cannot be -done, add a @Nullable annotation to the method declaration. This annotation -might trigger more warnings in the callers of method m, as the callers must now -deal with null values. diff --git a/infer/documentation/issues/ERADICATE_RETURN_OVER_ANNOTATED.md b/infer/documentation/issues/ERADICATE_RETURN_OVER_ANNOTATED.md deleted file mode 100644 index 0f0311fb655..00000000000 --- a/infer/documentation/issues/ERADICATE_RETURN_OVER_ANNOTATED.md +++ /dev/null @@ -1,20 +0,0 @@ -This report is inactive by default. Method m is annotated with @Nullable but the -method cannot return null - -Example: - -```java -class C { - @Nullable String m() { - String s = new String("abc"); - return s; - } -} -``` - -Action: Make sure that the annotations are correct, as the return annotation is -considered redundant based on the existing annotations. In particular, check the -annotation of any input parameters and fields of the current method, as well as -the annotations of any method called directly by the current method, if -relevant. If the annotations are correct, you can remove the @Nullable -annotation. diff --git a/infer/man/man1/infer-analyze.txt b/infer/man/man1/infer-analyze.txt index 7b0ac3de90a..f264c5f6f03 100644 --- a/infer/man/man1/infer-analyze.txt +++ b/infer/man/man1/infer-analyze.txt @@ -118,14 +118,6 @@ OPTIONS --liveness, --parameter-not-null-checked, --racerd, --siof, --self-in-block, --starvation (Conversely: --default-checkers) - --eradicate - Activates: checker eradicate: The eradicate `@Nullable` checker - for Java annotations. (Conversely: --no-eradicate) - - --eradicate-only - Activates: Enable eradicate and disable all other checkers - (Conversely: --no-eradicate-only) - --files-to-analyze-index file File containing a list of source files where analysis should start from. When used, the set of files given to this argument must be a @@ -153,16 +145,6 @@ OPTIONS Show this manual with all internal options in the INTERNAL OPTIONS section - --immutable-cast - Activates: checker immutable-cast: Detection of object cast from - immutable types to mutable types. For instance, it will detect - casts from `ImmutableList` to `List`, `ImmutableMap` to `Map`, and - `ImmutableSet` to `Set`. (Conversely: --no-immutable-cast) - - --immutable-cast-only - Activates: Enable immutable-cast and disable all other checkers - (Conversely: --no-immutable-cast-only) - --impurity Activates: checker impurity: Detects functions with potential side-effects. Same as "purity", but implemented on top of Pulse. diff --git a/infer/man/man1/infer-full.txt b/infer/man/man1/infer-full.txt index 75dd24a2a44..284c4678d08 100644 --- a/infer/man/man1/infer-full.txt +++ b/infer/man/man1/infer-full.txt @@ -516,7 +516,6 @@ OPTIONS CHECKERS_CALLS_EXPENSIVE_METHOD (enabled by default), CHECKERS_EXPENSIVE_OVERRIDES_UNANNOTATED (enabled by default), CHECKERS_FRAGMENT_RETAINS_VIEW (enabled by default), - CHECKERS_IMMUTABLE_CAST (enabled by default), CHECKERS_PRINTF_ARGS (enabled by default), CLASS_CAST_EXCEPTION (disabled by default), CONDITION_ALWAYS_FALSE (disabled by default), @@ -538,28 +537,6 @@ OPTIONS DIVIDE_BY_ZERO (disabled by default), DO_NOT_REPORT (enabled by default), EMPTY_VECTOR_ACCESS (enabled by default), - ERADICATE_ANNOTATION_GRAPH (enabled by default), - ERADICATE_BAD_NESTED_CLASS_ANNOTATION (enabled by default), - ERADICATE_CONDITION_REDUNDANT (enabled by default), - ERADICATE_FIELD_NOT_INITIALIZED (enabled by default), - ERADICATE_FIELD_NOT_NULLABLE (enabled by default), - ERADICATE_FIELD_OVER_ANNOTATED (enabled by default), - ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION (enabled - by default), - ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION (enabled by - default), - ERADICATE_META_CLASS_CAN_BE_NULLSAFE (disabled by default), - ERADICATE_META_CLASS_IS_NULLSAFE (disabled by default), - ERADICATE_META_CLASS_NEEDS_IMPROVEMENT (disabled by default), - ERADICATE_NULLABLE_DEREFERENCE (enabled by default), - ERADICATE_PARAMETER_NOT_NULLABLE (enabled by default), - ERADICATE_REDUNDANT_NESTED_CLASS_ANNOTATION (enabled by - default), - ERADICATE_RETURN_NOT_NULLABLE (enabled by default), - ERADICATE_RETURN_OVER_ANNOTATED (enabled by default), - ERADICATE_UNCHECKED_USAGE_IN_NULLSAFE (enabled by default), - ERADICATE_UNVETTED_THIRD_PARTY_IN_NULLSAFE (enabled by - default), EXECUTION_TIME_COMPLEXITY_INCREASE (enabled by default), EXECUTION_TIME_COMPLEXITY_INCREASE_UI_THREAD (enabled by default), @@ -716,14 +693,6 @@ OPTIONS checker run; see individual checker options to turn them on or off. See also infer-report(1). - --eradicate - Activates: checker eradicate: The eradicate `@Nullable` checker - for Java annotations. (Conversely: --no-eradicate) See also infer-analyze(1). - - --eradicate-only - Activates: Enable eradicate and disable all other checkers - (Conversely: --no-eradicate-only) See also infer-analyze(1). - --erlang-ast-dir dir Also load AST from all .json files in the given path. These .json files usually come from a previous run with --debug. @@ -873,17 +842,6 @@ OPTIONS Activates: Generate an html report of issues found. (Conversely: --no-html) See also infer-explore(1). - --immutable-cast - Activates: checker immutable-cast: Detection of object cast from - immutable types to mutable types. For instance, it will detect - casts from `ImmutableList` to `List`, `ImmutableMap` to `Map`, and - `ImmutableSet` to `Set`. (Conversely: --no-immutable-cast) - See also infer-analyze(1). - - --immutable-cast-only - Activates: Enable immutable-cast and disable all other checkers - (Conversely: --no-immutable-cast-only) See also infer-analyze(1). - --impurity Activates: checker impurity: Detects functions with potential side-effects. Same as "purity", but implemented on top of Pulse. @@ -2403,22 +2361,6 @@ INTERNAL OPTIONS --enable-issue-type-reset Set --enable-issue-type to the empty list. - --eradicate-condition-redundant - Activates: Condition redundant warnings (Conversely: - --no-eradicate-condition-redundant) - - --eradicate-field-over-annotated - Activates: Field over-annotated warnings (Conversely: - --no-eradicate-field-over-annotated) - - --eradicate-return-over-annotated - Activates: Return over-annotated warning (Conversely: - --no-eradicate-return-over-annotated) - - --eradicate-verbose - Activates: Print initial and final typestates (Conversely: - --no-eradicate-verbose) - --erlang-ast-dir-reset Cancel the effect of --erlang-ast-dir. @@ -2633,48 +2575,6 @@ INTERNAL OPTIONS --nullable-annotation-name-reset Cancel the effect of --nullable-annotation-name. - --nullsafe-annotation-graph - Activates: Nullsafe: an experimental mode for calculating the - dependency graph between potential annotations to add in the - source code. (Conversely: --no-nullsafe-annotation-graph) - - --nullsafe-disable-field-not-initialized-in-nonstrict-classes - Activates: Nullsafe: In this mode field not initialized issues - won't be reported unless the class is marked as @NullsafeStrict. - This feature is needed for compatibility reasons. (Conversely: - --no-nullsafe-disable-field-not-initialized-in-nonstrict-classes) - - --no-nullsafe-optimistic-third-party-in-default-mode - Deactivates: Nullsafe: Unless @Nullsafe annotation is used, treat - not annotated third party method params as if they were annotated - as nullable, and return values as if they were annotated as - non-null (Conversely: - --nullsafe-optimistic-third-party-in-default-mode) - - --nullsafe-strict-containers - Activates: Warn when containers are used with nullable keys or - values (Conversely: --no-nullsafe-strict-containers) - - --nullsafe-third-party-location-for-messaging-only string - Path to a folder with annotated signatures to include into error - message. If not specified, path will be fetched from - nullsafe-third-party-signatures. This param is only needed for the - case when the real repository is located in the different place, - and nullsafe-third-party-signatures contains only its copy (which - can happen e.g. in case of caching by the build system) - - --nullsafe-third-party-location-for-messaging-only-reset - Cancel the effect of - --nullsafe-third-party-location-for-messaging-only. - - --nullsafe-third-party-signatures string - Path to a folder with annotated signatures of third-party methods - to be taken into account by nullsafe. Path is either relative to - .inferconfig folder or absolute - - --nullsafe-third-party-signatures-reset - Cancel the effect of --nullsafe-third-party-signatures. - --no-only-cheap-debug Deactivates: Disable expensive debugging output (Conversely: --only-cheap-debug) diff --git a/infer/man/man1/infer-report.txt b/infer/man/man1/infer-report.txt index 3ecc39b0c04..9dd2292748d 100644 --- a/infer/man/man1/infer-report.txt +++ b/infer/man/man1/infer-report.txt @@ -128,7 +128,6 @@ OPTIONS CHECKERS_CALLS_EXPENSIVE_METHOD (enabled by default), CHECKERS_EXPENSIVE_OVERRIDES_UNANNOTATED (enabled by default), CHECKERS_FRAGMENT_RETAINS_VIEW (enabled by default), - CHECKERS_IMMUTABLE_CAST (enabled by default), CHECKERS_PRINTF_ARGS (enabled by default), CLASS_CAST_EXCEPTION (disabled by default), CONDITION_ALWAYS_FALSE (disabled by default), @@ -150,28 +149,6 @@ OPTIONS DIVIDE_BY_ZERO (disabled by default), DO_NOT_REPORT (enabled by default), EMPTY_VECTOR_ACCESS (enabled by default), - ERADICATE_ANNOTATION_GRAPH (enabled by default), - ERADICATE_BAD_NESTED_CLASS_ANNOTATION (enabled by default), - ERADICATE_CONDITION_REDUNDANT (enabled by default), - ERADICATE_FIELD_NOT_INITIALIZED (enabled by default), - ERADICATE_FIELD_NOT_NULLABLE (enabled by default), - ERADICATE_FIELD_OVER_ANNOTATED (enabled by default), - ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION (enabled - by default), - ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION (enabled by - default), - ERADICATE_META_CLASS_CAN_BE_NULLSAFE (disabled by default), - ERADICATE_META_CLASS_IS_NULLSAFE (disabled by default), - ERADICATE_META_CLASS_NEEDS_IMPROVEMENT (disabled by default), - ERADICATE_NULLABLE_DEREFERENCE (enabled by default), - ERADICATE_PARAMETER_NOT_NULLABLE (enabled by default), - ERADICATE_REDUNDANT_NESTED_CLASS_ANNOTATION (enabled by - default), - ERADICATE_RETURN_NOT_NULLABLE (enabled by default), - ERADICATE_RETURN_OVER_ANNOTATED (enabled by default), - ERADICATE_UNCHECKED_USAGE_IN_NULLSAFE (enabled by default), - ERADICATE_UNVETTED_THIRD_PARTY_IN_NULLSAFE (enabled by - default), EXECUTION_TIME_COMPLEXITY_INCREASE (enabled by default), EXECUTION_TIME_COMPLEXITY_INCREASE_UI_THREAD (enabled by default), diff --git a/infer/man/man1/infer.txt b/infer/man/man1/infer.txt index f3d64c36dae..4b93e7ef607 100644 --- a/infer/man/man1/infer.txt +++ b/infer/man/man1/infer.txt @@ -516,7 +516,6 @@ OPTIONS CHECKERS_CALLS_EXPENSIVE_METHOD (enabled by default), CHECKERS_EXPENSIVE_OVERRIDES_UNANNOTATED (enabled by default), CHECKERS_FRAGMENT_RETAINS_VIEW (enabled by default), - CHECKERS_IMMUTABLE_CAST (enabled by default), CHECKERS_PRINTF_ARGS (enabled by default), CLASS_CAST_EXCEPTION (disabled by default), CONDITION_ALWAYS_FALSE (disabled by default), @@ -538,28 +537,6 @@ OPTIONS DIVIDE_BY_ZERO (disabled by default), DO_NOT_REPORT (enabled by default), EMPTY_VECTOR_ACCESS (enabled by default), - ERADICATE_ANNOTATION_GRAPH (enabled by default), - ERADICATE_BAD_NESTED_CLASS_ANNOTATION (enabled by default), - ERADICATE_CONDITION_REDUNDANT (enabled by default), - ERADICATE_FIELD_NOT_INITIALIZED (enabled by default), - ERADICATE_FIELD_NOT_NULLABLE (enabled by default), - ERADICATE_FIELD_OVER_ANNOTATED (enabled by default), - ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION (enabled - by default), - ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION (enabled by - default), - ERADICATE_META_CLASS_CAN_BE_NULLSAFE (disabled by default), - ERADICATE_META_CLASS_IS_NULLSAFE (disabled by default), - ERADICATE_META_CLASS_NEEDS_IMPROVEMENT (disabled by default), - ERADICATE_NULLABLE_DEREFERENCE (enabled by default), - ERADICATE_PARAMETER_NOT_NULLABLE (enabled by default), - ERADICATE_REDUNDANT_NESTED_CLASS_ANNOTATION (enabled by - default), - ERADICATE_RETURN_NOT_NULLABLE (enabled by default), - ERADICATE_RETURN_OVER_ANNOTATED (enabled by default), - ERADICATE_UNCHECKED_USAGE_IN_NULLSAFE (enabled by default), - ERADICATE_UNVETTED_THIRD_PARTY_IN_NULLSAFE (enabled by - default), EXECUTION_TIME_COMPLEXITY_INCREASE (enabled by default), EXECUTION_TIME_COMPLEXITY_INCREASE_UI_THREAD (enabled by default), @@ -716,14 +693,6 @@ OPTIONS checker run; see individual checker options to turn them on or off. See also infer-report(1). - --eradicate - Activates: checker eradicate: The eradicate `@Nullable` checker - for Java annotations. (Conversely: --no-eradicate) See also infer-analyze(1). - - --eradicate-only - Activates: Enable eradicate and disable all other checkers - (Conversely: --no-eradicate-only) See also infer-analyze(1). - --erlang-ast-dir dir Also load AST from all .json files in the given path. These .json files usually come from a previous run with --debug. @@ -873,17 +842,6 @@ OPTIONS Activates: Generate an html report of issues found. (Conversely: --no-html) See also infer-explore(1). - --immutable-cast - Activates: checker immutable-cast: Detection of object cast from - immutable types to mutable types. For instance, it will detect - casts from `ImmutableList` to `List`, `ImmutableMap` to `Map`, and - `ImmutableSet` to `Set`. (Conversely: --no-immutable-cast) - See also infer-analyze(1). - - --immutable-cast-only - Activates: Enable immutable-cast and disable all other checkers - (Conversely: --no-immutable-cast-only) See also infer-analyze(1). - --impurity Activates: checker impurity: Detects functions with potential side-effects. Same as "purity", but implemented on top of Pulse. diff --git a/infer/src/IR/DecompiledExp.ml b/infer/src/IR/DecompiledExp.ml index 2d25f87747b..b147b790368 100644 --- a/infer/src/IR/DecompiledExp.ml +++ b/infer/src/IR/DecompiledExp.ml @@ -31,8 +31,6 @@ type t = path, with Dpvar being the simplest one *) type vpath = t option -let eradicate_java () = Config.is_checker_enabled Eradicate && Language.curr_language_is Java - let split_var_clang var_name = match String.rsplit2 ~on:'.' var_name with Some (_, name) -> name | _ -> var_name @@ -67,10 +65,7 @@ let rec pp fmt = function | Dderef de -> F.fprintf fmt "*%a" pp de | Dfcall (fun_dexp, args, _, {cf_virtual= isvirtual}) -> - let pp_args fmt des = - if eradicate_java () then (if not (List.is_empty des) then F.pp_print_string fmt "...") - else Pp.comma_seq pp fmt des - in + let pp_args fmt des = Pp.comma_seq pp fmt des in let pp_fun fmt = function | Dconst (Cfun pname) -> let s = @@ -102,9 +97,6 @@ let rec pp fmt = function if Language.curr_language_is Java then F.fprintf fmt "%a.%s" pp de (Fieldname.get_field_name f) else F.fprintf fmt "%a->%s" pp de (Fieldname.to_string f) - | Ddot (Dpvar _, fe) when eradicate_java () -> - (* static field access *) - F.pp_print_string fmt (Fieldname.to_simplified_string fe) | Ddot (de, f) -> let field_text = if Language.curr_language_is Java then Fieldname.get_field_name f else Fieldname.to_string f @@ -117,11 +109,10 @@ let rec pp fmt = function | Dpvaraddr pv -> let var_name = Mangled.to_string (Pvar.get_name pv) in let s = - if eradicate_java () then Pvar.get_simplified_name pv - else if Language.curr_language_is Clang then split_var_clang var_name + if Language.curr_language_is Clang then split_var_clang var_name else Mangled.to_string (Pvar.get_name pv) in - let pp_ampersand fmt = if not (eradicate_java ()) then F.pp_print_string fmt "&" in + let pp_ampersand fmt = F.pp_print_string fmt "&" in F.fprintf fmt "%t%s" pp_ampersand s | Dunop (op, de) -> F.fprintf fmt "%s%a" (Unop.to_string op) pp de diff --git a/infer/src/IR/Fieldname.mli b/infer/src/IR/Fieldname.mli index fb6eb494f41..7c15dfbc46b 100644 --- a/infer/src/IR/Fieldname.mli +++ b/infer/src/IR/Fieldname.mli @@ -38,6 +38,7 @@ val get_capture_field_position : t -> int option val is_java : t -> bool val is_java_synthetic : t -> bool + [@@warning "-unused-value-declaration"] (** Check if the field is autogenerated/synthetic **) val is_internal : t -> bool diff --git a/infer/src/IR/Ident.mli b/infer/src/IR/Ident.mli index 0dab691fa2b..ef98b8f8356 100644 --- a/infer/src/IR/Ident.mli +++ b/infer/src/IR/Ident.mli @@ -31,9 +31,6 @@ val equal_kind : kind -> kind -> bool (** Set for identifiers. *) module Set : Caml.Set.S with type elt = t -(** Hash table with ident as key. *) -module Hash : Caml.Hashtbl.S with type key = t - (** Map with ident as key. *) module Map : Caml.Map.S with type key = t diff --git a/infer/src/IR/JavaClassName.ml b/infer/src/IR/JavaClassName.ml index ba578ea9add..8778eaa0ed4 100644 --- a/infer/src/IR/JavaClassName.ml +++ b/infer/src/IR/JavaClassName.ml @@ -14,14 +14,6 @@ module L = Logging type t = {classname: string; package: string option} [@@deriving compare, equal, yojson_of, sexp, hash, normalize] -module Map = Caml.Map.Make (struct - type nonrec t = t [@@deriving compare] -end) - -module Set = Caml.Set.Make (struct - type nonrec t = t [@@deriving compare] -end) - let make ~package ~classname = match package with Some "" -> {package= None; classname} | _ -> {package; classname} @@ -102,10 +94,5 @@ let get_user_defined_class_if_anonymous_inner {package; classname} = let is_anonymous_inner_class_name t = get_user_defined_class_if_anonymous_inner t |> is_some -let is_external_via_config t = - let package = package t in - Option.exists ~f:Config.java_package_is_external package - - let pp_with_verbosity ~verbose fmt t = if verbose then pp fmt t else F.pp_print_string fmt (classname t) diff --git a/infer/src/IR/JavaClassName.mli b/infer/src/IR/JavaClassName.mli index 8ba30e4939c..f99b1dfacb5 100644 --- a/infer/src/IR/JavaClassName.mli +++ b/infer/src/IR/JavaClassName.mli @@ -9,10 +9,6 @@ open! IStd type t [@@deriving compare, equal, yojson_of, sexp, hash, normalize] -module Map : Caml.Map.S with type key = t - -module Set : Caml.Set.S with type elt = t - val make : package:string option -> classname:string -> t (** [make ~package:(Some "java.lang") "Object"] creates a value representing [java.lang.Object] *) @@ -32,9 +28,6 @@ val package : t -> string option val classname : t -> string -val is_external_via_config : t -> bool -(** Considered external based on config flags. *) - val get_outer_class_name : t -> t option (** If this is an inner class, return the closest outer, e.g. A$B for A$B$C. None if the class is outermost *) diff --git a/infer/src/IR/Procdesc.ml b/infer/src/IR/Procdesc.ml index 918a7f70f01..fc5f87e6b51 100644 --- a/infer/src/IR/Procdesc.ml +++ b/infer/src/IR/Procdesc.ml @@ -662,20 +662,6 @@ let replace_instrs_by_using_context pdesc ~f ~update_context ~context_at_node = update_nodes pdesc ~update -(** fold between two nodes or until we reach a branching structure *) -let fold_slope_range = - let rec aux node visited acc ~f = - let visited = NodeSet.add node visited in - let acc = f acc node in - match Node.get_succs node with - | [n] when not (NodeSet.mem n visited) -> - aux n visited acc ~f - | _ -> - acc - in - fun src_node dst_node ~init ~f -> aux src_node (NodeSet.singleton dst_node) init ~f - - (** Set the exit node of the proc desc *) let set_exit_node pdesc node = pdesc.exit_node <- node @@ -912,12 +898,6 @@ let is_specialized pdesc = attributes.ProcAttributes.is_specialized -let is_kotlin pdesc = - let attributes = get_attributes pdesc in - let source = attributes.ProcAttributes.translation_unit in - SourceFile.has_extension ~ext:Config.kotlin_source_extension source - - (* true if pvar is a captured variable of a cpp lambda or objc block *) let is_captured_pvar procdesc pvar = let procname = get_proc_name procdesc in diff --git a/infer/src/IR/Procdesc.mli b/infer/src/IR/Procdesc.mli index 2219b05b6e2..4799f80b1f6 100644 --- a/infer/src/IR/Procdesc.mli +++ b/infer/src/IR/Procdesc.mli @@ -335,9 +335,6 @@ val iter_nodes : (Node.t -> unit) -> t -> unit val fold_nodes : t -> init:'accum -> f:('accum -> Node.t -> 'accum) -> 'accum (** fold over all the nodes of a procedure *) -val fold_slope_range : Node.t -> Node.t -> init:'accum -> f:('accum -> Node.t -> 'accum) -> 'accum -(** fold between two nodes or until we reach a branching structure *) - val set_succs : Node.t -> normal:Node.t list option -> exn:Node.t list option -> unit (** Set the successor nodes and exception nodes, if given, and update predecessor links *) @@ -364,8 +361,6 @@ val pp_with_instrs : ?print_types:bool -> Format.formatter -> t -> unit val is_specialized : t -> bool -val is_kotlin : t -> bool - val is_captured_pvar : t -> Pvar.t -> bool (** true if pvar is a captured variable of a cpp lambda or obcj block *) diff --git a/infer/src/IR/Procname.ml b/infer/src/IR/Procname.ml index 18fe066b78c..6724caccef2 100644 --- a/infer/src/IR/Procname.ml +++ b/infer/src/IR/Procname.ml @@ -159,12 +159,8 @@ module Java = struct let get_method j = j.method_name - let replace_method_name method_name j = {j with method_name} - let replace_parameters parameters j = {j with parameters} - let replace_return_type ret_type j = {j with return_type= Some ret_type} - let get_parameters j = j.parameters (** Prints a string of a java procname with the given level of verbosity *) @@ -205,8 +201,6 @@ module Java = struct F.fprintf fmt "%a%s" pp_class_name_dot j j.method_name - let to_simplified_string ?(withclass = false) = Pp.string_of_pp (pp ~withclass Simple) - let get_return_typ pname_java = Option.value ~default:StdTyp.void pname_java.return_type let is_close {method_name} = String.equal method_name "close" @@ -223,10 +217,6 @@ module Java = struct let is_constructor {method_name} = String.equal method_name constructor_method_name - let is_anonymous_inner_class_constructor_exn {class_name} = - Typ.Name.Java.is_anonymous_inner_class_name_exn class_name - - let is_anonymous_inner_class_method {class_name} = Option.value ~default:false (Typ.Name.Java.is_anonymous_inner_class_name_opt class_name) @@ -235,8 +225,6 @@ module Java = struct let is_instance x = not (is_static x) - let is_lambda {method_name} = String.is_prefix ~prefix:"lambda$" method_name - let is_generated {method_name} = String.is_prefix ~prefix:"$" method_name let is_access_method {method_name} = diff --git a/infer/src/IR/Procname.mli b/infer/src/IR/Procname.mli index 559b7298b8b..82ebead7e41 100644 --- a/infer/src/IR/Procname.mli +++ b/infer/src/IR/Procname.mli @@ -49,30 +49,16 @@ module Java : sig type t [@@deriving compare, equal] - val to_simplified_string : ?withclass:bool -> t -> string - val constructor_method_name : string val class_initializer_method_name : string - val replace_method_name : string -> t -> t - (** Replace the method name of an existing java procname. *) - - val replace_parameters : Typ.t list -> t -> t - (** Replace the parameters of a java procname. *) - - val replace_return_type : Typ.t -> t -> t - (** Replace the method of a java procname. *) - val get_class_name : t -> string (** Return the fully qualified class name of a java procedure name (package + class name) *) val get_class_type_name : t -> Typ.Name.t (** Return the class name as a typename of a java procedure name. *) - val get_simple_class_name : t -> string - (** Return the simple class name of a java procedure name (i.e. name without the package info). *) - val get_package : t -> string option (** Return the package name of a java procedure name. *) @@ -95,10 +81,6 @@ module Java : sig val is_autogen_method : t -> bool (** Check if the procedure name is of an auto-generated/synthetic method. *) - val is_anonymous_inner_class_constructor_exn : t -> bool - (** Check if the procedure name is an anonymous inner class constructor. Throws if it is not a - Java type *) - val is_close : t -> bool (** Check if the method name is "close". *) @@ -109,9 +91,6 @@ module Java : sig (** Check if the proc name has the type of a java vararg. Note: currently only checks that the last argument has type Object[]. *) - val is_lambda : t -> bool - (** Check if the proc name comes from a lambda expression *) - val is_generated : t -> bool (** Check if the proc name comes from generated code *) diff --git a/infer/src/IR/Typ.ml b/infer/src/IR/Typ.ml index f43bc438041..4c90c39573a 100644 --- a/infer/src/IR/Typ.ml +++ b/infer/src/IR/Typ.ml @@ -595,11 +595,6 @@ module Name = struct L.die InternalError "Tried to split a non-java class name into a java split type@." - let is_anonymous_inner_class_name_exn class_name = - let java_class_name = get_java_class_name_exn class_name in - JavaClassName.is_anonymous_inner_class_name java_class_name - - let is_anonymous_inner_class_name_opt class_name = get_java_class_name_opt class_name |> Option.map ~f:JavaClassName.is_anonymous_inner_class_name diff --git a/infer/src/IR/Typ.mli b/infer/src/IR/Typ.mli index 5a0fb797857..122c063fb03 100644 --- a/infer/src/IR/Typ.mli +++ b/infer/src/IR/Typ.mli @@ -226,9 +226,6 @@ module Name : sig val is_external : t -> bool (** return true if the typename is in the .inferconfig list of external classes *) - val is_anonymous_inner_class_name_exn : t -> bool - (** Throws if it is not a Java class *) - val is_anonymous_inner_class_name_opt : t -> bool option (** return None if it is not a Java class *) end diff --git a/infer/src/absint/AndroidFramework.ml b/infer/src/absint/AndroidFramework.ml index 75fca959265..9f9bd824f52 100644 --- a/infer/src/absint/AndroidFramework.ml +++ b/infer/src/absint/AndroidFramework.ml @@ -9,22 +9,8 @@ open! IStd (** Android lifecycle types and their lifecycle methods that are called by the framework *) -let on_destroy = "onDestroy" - -let on_destroy_view = "onDestroyView" - let drawable_prefix = "R$drawable" -(** return true if [pname] is a special lifecycle cleanup method *) -let is_destroy_method pname = - match pname with - | Procname.Java pname_java -> - let method_name = Procname.Java.get_method pname_java in - String.equal method_name on_destroy || String.equal method_name on_destroy_view - | _ -> - false - - let is_autocloseable tenv tname = PatternMatch.is_subtype_of_str tenv tname "java.lang.AutoCloseable" diff --git a/infer/src/absint/AndroidFramework.mli b/infer/src/absint/AndroidFramework.mli index a454401ea69..61cc05fa445 100644 --- a/infer/src/absint/AndroidFramework.mli +++ b/infer/src/absint/AndroidFramework.mli @@ -18,6 +18,3 @@ val is_view : Tenv.t -> Typ.Name.t -> bool (** return true if [typename] <: android.view.View *) val is_fragment : Tenv.t -> Typ.Name.t -> bool - -val is_destroy_method : Procname.t -> bool -(** return true if [procname] is a special lifecycle cleanup method *) diff --git a/infer/src/absint/DataFlow.ml b/infer/src/absint/DataFlow.ml deleted file mode 100644 index 8207e014825..00000000000 --- a/infer/src/absint/DataFlow.ml +++ /dev/null @@ -1,192 +0,0 @@ -(* - * 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. - *) - -open! IStd -module L = Logging - -type throws = - | DontKnow (** May or may not throw an exception. *) - | Throws (** Definitely throws an exception. *) - | DoesNotThrow (** Does not throw an exception. *) - -(** Module type used to define the state component for a dataflow algorithm. *) -module type DFStateType = sig - (** Type for state. *) - type t - - val equal : t -> t -> bool - (** Equality between states. *) - - val join : t -> t -> t - (** Join two states (the old one is the first parameter). *) - - val do_node : Procdesc.Node.t -> t -> t list * t list - (** Perform a state transition on a node. *) - - val proc_throws : Procname.t -> throws - (** Can proc throw an exception? *) -end - -(** Type for the dataflow API. *) -module type DF = sig - type t - - type state - - type transition = Dead_state | Transition of state * state list * state list - - val join : state list -> state -> state - - val run : Procdesc.t -> state -> Procdesc.Node.t -> transition -end - -(** Determine if the node can throw an exception. *) -let node_throws pdesc node (proc_throws : Procname.t -> throws) : throws = - let instr_throws instr = - let is_return pvar = - let ret_pvar = Procdesc.get_ret_var pdesc in - Pvar.equal pvar ret_pvar - in - match instr with - | Sil.Store {e1= Exp.Lvar pvar; e2= Exp.Exn _} when is_return pvar -> - (* assignment to return variable is an artifact of a throw instruction *) - Throws - | Sil.Call (_, Exp.Const (Const.Cfun callee_pn), _, _, _) when BuiltinDecl.is_declared callee_pn - -> - if Procname.equal callee_pn BuiltinDecl.__cast then DontKnow else DoesNotThrow - | Sil.Call (_, Exp.Const (Const.Cfun callee_pn), _, _, _) -> - proc_throws callee_pn - | _ -> - DoesNotThrow - in - let res = ref DoesNotThrow in - let update_res throws = - match (!res, throws) with - | DontKnow, DontKnow -> - res := DontKnow - | Throws, _ | _, Throws -> - res := Throws - | DoesNotThrow, t | t, DoesNotThrow -> - res := t - in - let do_instr instr = update_res (instr_throws instr) in - Instrs.iter ~f:do_instr (Procdesc.Node.get_instrs node) ; - !res - - -(** Create an instance of the dataflow algorithm given a state parameter. *) -module MakeDF (St : DFStateType) : DF with type state = St.t = struct - module S = Procdesc.NodeSet - module H = Procdesc.NodeHash - - type worklist = S.t - - type statemap = St.t H.t - - type statelistmap = St.t list H.t - - type t = - { mutable worklist: worklist - ; pre_states: statemap - ; post_states: statelistmap - ; exn_states: statelistmap - ; proc_desc: Procdesc.t } - - type state = St.t - - type transition = Dead_state | Transition of state * state list * state list - - let join states initial_state = List.fold ~f:St.join ~init:initial_state states - - (** Propagate [new_state] to all the nodes immediately reachable. *) - let propagate t node states_succ states_exn (throws : throws) = - let propagate_to_dest new_state dest_node = - let push_state s = - H.replace t.pre_states dest_node s ; - t.worklist <- S.add dest_node t.worklist - in - try - let dest_state = H.find t.pre_states dest_node in - let dest_joined = St.join dest_state new_state in - if not (St.equal dest_state dest_joined) then push_state dest_joined - with Caml.Not_found -> push_state new_state - in - let succ_nodes = Procdesc.Node.get_succs node in - let exn_nodes = Procdesc.Node.get_exn node in - ( match throws with - | DoesNotThrow | DontKnow -> - List.iter ~f:(fun s -> List.iter ~f:(propagate_to_dest s) succ_nodes) states_succ - | Throws -> - () ) ; - ( match throws with - | Throws | DontKnow -> - List.iter ~f:(fun s -> List.iter ~f:(propagate_to_dest s) exn_nodes) states_exn - | DoesNotThrow -> - () ) ; - H.replace t.post_states node states_succ ; - H.replace t.exn_states node states_exn - - - (** Run the worklist-based dataflow algorithm. *) - let run proc_desc state = - let t = - let start_node = Procdesc.get_start_node proc_desc in - let init_set = S.singleton start_node in - let init_statemap = - let m = H.create 1 in - H.replace m start_node state ; - m - in - { worklist= init_set - ; pre_states= init_statemap - ; post_states= H.create 0 - ; exn_states= H.create 0 - ; proc_desc } - in - let () = - while not (S.is_empty t.worklist) do - let node = S.min_elt t.worklist in - t.worklist <- S.remove node t.worklist ; - try - let state = H.find t.pre_states node in - let states_succ, states_exn = St.do_node node state in - propagate t node states_succ states_exn (node_throws proc_desc node St.proc_throws) - with Caml.Not_found -> () - done - in - let transitions node = - try Transition (H.find t.pre_states node, H.find t.post_states node, H.find t.exn_states node) - with Caml.Not_found -> Dead_state - in - transitions -end - -(* MakeDF *) - -(** Example dataflow analysis: compute the distance from a node to the start node. *) -let _test_dataflow proc_desc = - let verbose = false in - let module DFCount = MakeDF (struct - type t = int - - let equal = Int.equal - - let join n m = if Int.equal n 0 then m else n - - let do_node n s = - if verbose then - L.(debug Analysis Verbose) "visiting node %a with state %d@." Procdesc.Node.pp n s ; - ([s + 1], [s + 1]) - - - let proc_throws _ = DoesNotThrow - end) in - let transitions = DFCount.run proc_desc 0 in - let do_node node = - match transitions node with DFCount.Transition _ -> () | DFCount.Dead_state -> () - in - List.iter ~f:do_node (Procdesc.get_nodes proc_desc) diff --git a/infer/src/absint/DataFlow.mli b/infer/src/absint/DataFlow.mli deleted file mode 100644 index 3a5dc0e3dac..00000000000 --- a/infer/src/absint/DataFlow.mli +++ /dev/null @@ -1,49 +0,0 @@ -(* - * 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. - *) - -open! IStd - -type throws = - | DontKnow (** May or may not throw an exception. *) - | Throws (** Definitely throws an exception. *) - | DoesNotThrow (** Does not throw an exception. *) - -(** Module type used to define the state component for a dataflow algorithm. *) -module type DFStateType = sig - (** Type for state. *) - type t - - val equal : t -> t -> bool - (** Equality between states. *) - - val join : t -> t -> t - (** Join two states (the old one is the first parameter). *) - - val do_node : Procdesc.Node.t -> t -> t list * t list - (** Perform a state transition on a node. *) - - val proc_throws : Procname.t -> throws - (** Can proc throw an exception? *) -end - -(** Type for the dataflow API. *) -module type DF = sig - type t - - type state - - type transition = Dead_state | Transition of state * state list * state list - - val join : state list -> state -> state - - val run : Procdesc.t -> state -> Procdesc.Node.t -> transition - (** Run the dataflow analysis on a procedure starting from the given state. Returns a function to - lookup the results of the analysis on every node *) -end - -(** Functor to create an instance of a dataflow analysis. *) -module MakeDF (St : DFStateType) : DF with type state = St.t diff --git a/infer/src/absint/Decompile.ml b/infer/src/absint/Decompile.ml index 23e54427ae3..8eab0538c23 100644 --- a/infer/src/absint/Decompile.ml +++ b/infer/src/absint/Decompile.ml @@ -8,27 +8,6 @@ open! IStd module L = Logging -(** Find a boolean assignment to a temporary variable holding a boolean condition. The boolean - parameter indicates whether the true or false branch is required. *) -let rec find_boolean_assignment node pvar true_branch : Procdesc.Node.t option = - let find_instr n = - let filter = function - | Sil.Store {e1= Exp.Lvar pvar_; e2= Exp.Const (Const.Cint i)} when Pvar.equal pvar pvar_ -> - Bool.(IntLit.iszero i <> true_branch) - | _ -> - false - in - Instrs.exists ~f:filter (Procdesc.Node.get_instrs n) - in - match Procdesc.Node.get_preds node with - | [pred_node] -> - find_boolean_assignment pred_node pvar true_branch - | [n1; n2] -> - if find_instr n1 then Some n1 else if find_instr n2 then Some n2 else None - | _ -> - None - - (** Find the function call instruction used to initialize normal variable [id], and return the function name and arguments *) let find_normal_variable_funcall (node : Procdesc.Node.t) (id : Ident.t) : diff --git a/infer/src/absint/Decompile.mli b/infer/src/absint/Decompile.mli index 9d75e8290ad..013caeefda7 100644 --- a/infer/src/absint/Decompile.mli +++ b/infer/src/absint/Decompile.mli @@ -7,10 +7,6 @@ open! IStd -val find_boolean_assignment : Procdesc.Node.t -> Pvar.t -> bool -> Procdesc.Node.t option -(** Find a boolean assignment to a temporary variable holding a boolean condition. The boolean - parameter indicates whether the true or false branch is required. *) - val find_normal_variable_funcall : Procdesc.Node.t -> Ident.t -> (Exp.t * Exp.t list * Location.t * CallFlags.t) option (** Find the function call instruction used to initialize normal variable [id], and return the diff --git a/infer/src/absint/NullsafeMode.ml b/infer/src/absint/NullsafeMode.ml new file mode 100644 index 00000000000..9d67a13f2e1 --- /dev/null +++ b/infer/src/absint/NullsafeMode.ml @@ -0,0 +1,74 @@ +(* + * 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. + *) +open! IStd + +type t = Default | Local | Strict + +let of_annot annot = + let open IOption.Let_syntax in + let* mode = Annot.find_parameter annot ~name:"value" in + match mode with + | Annot.Enum {value= "STRICT"} -> + return Strict + | Annot.Enum {value= "LOCAL"} -> ( + match Annot.find_parameter annot ~name:"trustOnly" with + | None | Some (Annot.Annot _) -> + return Local + | Some _ -> + None ) + | _ -> + None + + +let extract_mode_from_explicit_class_annotation tenv classname = + match PatternMatch.type_name_get_annotation tenv (Typ.JavaClass classname) with + | Some annots -> ( + if Annotations.ia_is_nullsafe_strict annots then Strict + else + match Annotations.ia_find_nullsafe annots with + | Some nullsafe_annot -> + Option.value (of_annot nullsafe_annot) ~default:Default + | _ -> + Default ) + | None -> + Default + + +let extract_user_defined_class_name java_class_name = + (* Anonymous inner classes are not proper classes and can not be annotated. Refer to underlying user class *) + JavaClassName.get_user_defined_class_if_anonymous_inner java_class_name + |> Option.value ~default:java_class_name + + +(** Get the minimal mode that is stricter or equal than both of given modes *) +let intersect mode1 mode2 = + match (mode1, mode2) with + | Strict, _ | _, Strict -> + Strict + | Local, _ | _, Local -> + Local + | Default, Default -> + Default + + +let of_class tenv class_name = + (* The mode of the class is the strictest over this class's mode annotation and its outer classes *) + let rec of_class_and_outer_classes class_name = + let curr_class_mode = extract_mode_from_explicit_class_annotation tenv class_name in + match JavaClassName.get_outer_class_name class_name with + | Some outer_name -> + intersect curr_class_mode (of_class_and_outer_classes outer_name) + | None -> + curr_class_mode + in + let user_defined_class = extract_user_defined_class_name class_name in + of_class_and_outer_classes user_defined_class + + +let of_java_procname tenv pname = + let class_name = Procname.Java.get_class_type_name pname in + of_class tenv (Typ.Name.Java.get_java_class_name_exn class_name) diff --git a/infer/src/nullsafe/immutableChecker.mli b/infer/src/absint/NullsafeMode.mli similarity index 69% rename from infer/src/nullsafe/immutableChecker.mli rename to infer/src/absint/NullsafeMode.mli index abe337129d8..4b109cb7352 100644 --- a/infer/src/nullsafe/immutableChecker.mli +++ b/infer/src/absint/NullsafeMode.mli @@ -4,7 +4,8 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. *) - open! IStd -val analyze : IntraproceduralAnalysis.t -> NullsafeSummary.t option +type t = Default | Local | Strict + +val of_java_procname : Tenv.t -> Procname.Java.t -> t diff --git a/infer/src/absint/PatternMatch.ml b/infer/src/absint/PatternMatch.ml index f3cca816d2f..0e7124c7362 100644 --- a/infer/src/absint/PatternMatch.ml +++ b/infer/src/absint/PatternMatch.ml @@ -52,8 +52,6 @@ let type_get_direct_supertypes tenv (typ : Typ.t) = [] -let type_get_class_name {Typ.desc} = match desc with Typ.Tptr (typ, _) -> Typ.name typ | _ -> None - let type_name_get_annotation tenv (name : Typ.name) : Annot.Item.t option = match Tenv.lookup tenv name with Some {annots} -> Some annots | None -> None @@ -201,49 +199,6 @@ module Java = struct let implements_kotlin_intrinsics = implements "kotlin.jvm.internal.Intrinsics" - let initializer_classes = - List.map ~f:Typ.Name.Java.from_string - [ "android.app.Activity" - ; "android.app.Application" - ; "android.app.Fragment" - ; "android.app.Service" - ; "android.support.v4.app.Fragment" - ; "androidx.fragment.app.Fragment" - ; "junit.framework.TestCase" ] - - - let initializer_methods = - ["onActivityCreated"; "onAttach"; "onCreate"; "onCreateView"; "setUp"; "onViewCreated"] - - - (** Check if the type has in its supertypes from the initializer_classes list. *) - let type_has_initializer (tenv : Tenv.t) (t : Typ.t) : bool = - let is_initializer_class typename _ = - List.mem ~equal:Typ.Name.equal initializer_classes typename - in - match t.desc with - | Typ.Tstruct name | Tptr ({desc= Tstruct name}, _) -> - supertype_exists tenv is_initializer_class name - | _ -> - false - - - (** Check if the method is one of the known initializer methods. *) - let method_is_initializer (tenv : Tenv.t) (proc_attributes : ProcAttributes.t) : bool = - match get_this_type_nonstatic_methods_only proc_attributes with - | Some this_type -> - if type_has_initializer tenv this_type then - match proc_attributes.ProcAttributes.proc_name with - | Procname.Java pname_java -> - let mname = Procname.Java.get_method pname_java in - List.exists ~f:(String.equal mname) initializer_methods - | _ -> - false - else false - | None -> - false - - let get_const_type_name (const : Const.t) : string = match const with | Const.Cstr _ -> @@ -290,18 +245,6 @@ module Java = struct Option.fold struct_opt ~init:acc ~f:(fun acc {Struct.annots} -> if check annots then name :: acc else acc ) ) |> List.rev - - - let is_override_of_lang_object_equals curr_pname = - let is_only_param_of_object_type = function - | [Procname.Parameter.JavaParameter param_type] - when Typ.equal param_type StdTyp.Java.pointer_to_java_lang_object -> - true - | _ -> - false - in - String.equal (Procname.get_method curr_pname) "equals" - && is_only_param_of_object_type (Procname.get_parameters curr_pname) end module ObjectiveC = struct @@ -433,28 +376,6 @@ let type_is_class typ = false -let proc_calls resolve_attributes pdesc filter : (Procname.t * ProcAttributes.t) list = - let res = ref [] in - let do_instruction _ instr = - match instr with - | Sil.Call (_, Exp.Const (Const.Cfun callee_pn), _, _, _) -> ( - match resolve_attributes callee_pn with - | Some callee_attributes -> - if filter callee_pn callee_attributes then res := (callee_pn, callee_attributes) :: !res - | None -> - () ) - | _ -> - () - in - let do_node node = - let instrs = Procdesc.Node.get_instrs node in - Instrs.iter ~f:(do_instruction node) instrs - in - let nodes = Procdesc.get_nodes pdesc in - List.iter ~f:do_node nodes ; - List.rev !res - - let has_same_signature proc_name = let method_name = Procname.get_method proc_name in let params = Procname.get_parameters proc_name in @@ -508,28 +429,6 @@ let override_iter f tenv proc_name = tenv proc_name ) -let lookup_attributes tenv proc_name = - let found_attributes = ref None in - let f pname = - match Attributes.load pname with - | None -> - false - | Some _ as attributes -> - found_attributes := attributes ; - true - in - ignore (override_find ~check_current_type:true f tenv proc_name) ; - !found_attributes - - -let lookup_attributes_exn tenv proc_name = - match lookup_attributes tenv proc_name with - | Some result -> - result - | None -> - Logging.die InternalError "Did not find attributes for %a" Procname.pp proc_name - - (** return the set of instance fields that are assigned to a null literal in [procdesc] *) let get_fields_nullified procdesc = (* walk through the instructions and look for instance fields that are assigned to null *) diff --git a/infer/src/absint/PatternMatch.mli b/infer/src/absint/PatternMatch.mli index 8145cb96052..1482338e4da 100644 --- a/infer/src/absint/PatternMatch.mli +++ b/infer/src/absint/PatternMatch.mli @@ -160,13 +160,6 @@ module Java : sig val find_superclasses_with_attributes : (Annot.Item.t -> bool) -> Tenv.t -> Typ.Name.t -> Typ.Name.t list (** find superclasss with attributes (e.g., [@ThreadSafe]), including current class*) - - val is_override_of_lang_object_equals : Procname.t -> bool - (** Whether the method is an override of `java.lang.Object.equals(Object)` or - `java.lang.Object.equals(Object)` itself *) - - val method_is_initializer : Tenv.t -> ProcAttributes.t -> bool - (** Check if the method is one of the known initializer methods. *) end val supertype_exists : Tenv.t -> (Typ.Name.t -> Struct.t -> bool) -> Typ.Name.t -> bool @@ -176,13 +169,6 @@ val supertype_find_map_opt : Tenv.t -> (Typ.Name.t -> 'a option) -> Typ.Name.t - (** Return the first non-None result found when applying the given function to supertypes of the named type, including the type itself *) -val proc_calls : - (Procname.t -> ProcAttributes.t option) - -> Procdesc.t - -> (Procname.t -> ProcAttributes.t -> bool) - -> (Procname.t * ProcAttributes.t) list -(** Return the callees that satisfy [filter]. *) - val override_exists : ?check_current_type:bool -> (Procname.t -> bool) -> Tenv.t -> Procname.t -> bool (** Return true if applying the given predicate to an override of [procname] (including [procname] @@ -192,17 +178,10 @@ val override_iter : (Procname.t -> unit) -> Tenv.t -> Procname.t -> unit (** Apply the given predicate to procname and each override of [procname]. For the moment, this only works for Java *) -val lookup_attributes : Tenv.t -> Procname.t -> ProcAttributes.t option - -val lookup_attributes_exn : Tenv.t -> Procname.t -> ProcAttributes.t - val type_name_get_annotation : Tenv.t -> Typ.name -> Annot.Item.t option val type_get_annotation : Tenv.t -> Typ.t -> Annot.Item.t option -val type_get_class_name : Typ.t -> Typ.Name.t option -(** Get the class name of the type *) - val type_is_class : Typ.t -> bool (** Is the type a class type *) diff --git a/infer/src/absint/annotations.ml b/infer/src/absint/annotations.ml index 1c460ae4800..e2f45bbd862 100644 --- a/infer/src/absint/annotations.ml +++ b/infer/src/absint/annotations.ml @@ -13,26 +13,10 @@ let any_thread = "AnyThread" let auto_cleanup = "AutoCleanup" -let bind = "Bind" - -let bind_view = "BindView" - -let bind_array = "BindArray" - -let bind_bitmap = "BindBitmap" - -let bind_drawable = "BindDrawable" - -let bind_string = "BindString" - let camel_nonnull = "NonNull" -let cleanup = "Cleanup" - let expensive = "Expensive" -let false_on_null = "FalseOnNull" - let for_ui_thread = "ForUiThread" let for_non_ui_thread = "ForNonUiThread" @@ -51,10 +35,6 @@ let inject = "Inject" let inject_prop = "InjectProp" -let inject_view = "InjectView" - -let json_field = "JsonField" - let lockless = "Lockless" let nonnull = "Nonnull" @@ -99,8 +79,6 @@ let synchronized_collection = "SynchronizedCollection" let suppress_lint = "SuppressLint" -let suppress_view_nullability = "SuppressViewNullability" - let recently_nonnull = "RecentlyNonNull" let recently_nullable = "RecentlyNullable" @@ -111,8 +89,6 @@ let thread_safe = "ThreadSafe" let thrift_service = "ThriftService" -let true_on_null = "TrueOnNull" - let ui_thread = "UiThread" let visibleForTesting = "VisibleForTesting" @@ -179,8 +155,6 @@ let struct_typ_has_annot (struct_typ : Struct.t) predicate = predicate struct_ty let ia_is_not_thread_safe ia = ia_ends_with ia not_thread_safe -let ia_is_propagates_nullable ia = ia_ends_with ia propagates_nullable - let ia_is_nullable ia = List.exists ~f:(ia_ends_with ia) [ nullable @@ -217,8 +191,6 @@ let ia_is_nullsafe_strict ia = ia_ends_with ia nullsafe_strict let ia_find_nullsafe ia = find_ia_ends_with ia nullsafe -let ia_is_false_on_null ia = ia_ends_with ia false_on_null - let ia_is_returns_ownership ia = ia_ends_with ia returns_ownership let ia_is_synchronized_collection ia = ia_ends_with ia synchronized_collection @@ -227,39 +199,12 @@ let ia_is_thread_safe ia = ia_ends_with ia thread_safe let ia_is_thrift_service ia = ia_ends_with ia thrift_service -let ia_is_true_on_null ia = ia_ends_with ia true_on_null - let ia_is_nonblocking ia = ia_ends_with ia nonblocking let ia_is_initializer ia = ia_ends_with ia initializer_ -let ia_is_cleanup ia = ia_ends_with ia cleanup - let ia_is_volatile ia = ia_contains ia volatile -let field_injector_readwrite_list = - [ inject_view - ; bind - ; bind_view - ; bind_array - ; bind_bitmap - ; bind_drawable - ; bind_string - ; suppress_view_nullability ] - - -let field_injector_readonly_list = inject :: field_injector_readwrite_list - -(** Annotations for readonly injectors. The injector framework initializes the field but does not - write null into it. *) -let ia_is_field_injector_readonly ia = List.exists ~f:(ia_ends_with ia) field_injector_readonly_list - -(** Annotations for read-write injectors. The injector framework initializes the field and can write - null into it. *) -let ia_is_field_injector_readwrite ia = - List.exists ~f:(ia_ends_with ia) field_injector_readwrite_list - - let ia_is_expensive ia = ia_ends_with ia expensive let ia_is_functional ia = ia_ends_with ia functional @@ -268,8 +213,6 @@ let ia_is_ignore_allocations ia = ia_ends_with ia ignore_allocations let ia_is_inject ia = ia_ends_with ia inject -let ia_is_json_field ia = ia_ends_with ia json_field - let ia_is_suppress_lint ia = ia_ends_with ia suppress_lint let ia_is_thread_confined ia = ia_ends_with ia thread_confined diff --git a/infer/src/absint/annotations.mli b/infer/src/absint/annotations.mli index aae6afe1025..5147f1934fc 100644 --- a/infer/src/absint/annotations.mli +++ b/infer/src/absint/annotations.mli @@ -58,20 +58,8 @@ val ia_ends_with : Annot.Item.t -> string -> bool val ia_has_annotation_with : Annot.Item.t -> (Annot.t -> bool) -> bool -val ia_is_false_on_null : Annot.Item.t -> bool - val ia_is_initializer : Annot.Item.t -> bool -val ia_is_cleanup : Annot.Item.t -> bool - -val ia_is_field_injector_readonly : Annot.Item.t -> bool -(** Annotations for readonly injectors. The injector framework initializes the field but does not - write null into it. *) - -val ia_is_field_injector_readwrite : Annot.Item.t -> bool -(** Annotations for read-write injectors. The injector framework initializes the field and can write - null into it. *) - val ia_is_nonnull : Annot.Item.t -> bool val ia_is_jetbrains_notnull : Annot.Item.t -> bool @@ -82,20 +70,14 @@ val ia_is_nullsafe_strict : Annot.Item.t -> bool val ia_find_nullsafe : Annot.Item.t -> Annot.t option -val ia_is_true_on_null : Annot.Item.t -> bool - val ia_is_expensive : Annot.Item.t -> bool val ia_is_functional : Annot.Item.t -> bool -val ia_is_propagates_nullable : Annot.Item.t -> bool - val ia_is_ignore_allocations : Annot.Item.t -> bool val ia_is_inject : Annot.Item.t -> bool -val ia_is_json_field : Annot.Item.t -> bool - val ia_is_suppress_lint : Annot.Item.t -> bool val ia_is_not_thread_safe : Annot.Item.t -> bool diff --git a/infer/src/backend/CallbackOfChecker.ml b/infer/src/backend/CallbackOfChecker.ml index a5cbb87fdb1..73781882b70 100644 --- a/infer/src/backend/CallbackOfChecker.ml +++ b/infer/src/backend/CallbackOfChecker.ml @@ -99,8 +99,3 @@ let intraprocedural checker ({Callbacks.summary} as callbacks) = let intraprocedural_with_field_dependency payload_field checker ({Callbacks.summary} as callbacks) = checker (to_intraprocedural_t callbacks) (Field.get payload_field summary.payloads |> Lazy.force) ; summary - - -let intraprocedural_with_field payload_field checker ({Callbacks.summary} as callbacks) = - let result = checker (to_intraprocedural_t callbacks) |> Lazy.from_val in - {summary with payloads= Field.fset payload_field summary.payloads result} diff --git a/infer/src/backend/CallbackOfChecker.mli b/infer/src/backend/CallbackOfChecker.mli index 8a60162d39b..1eca096096f 100644 --- a/infer/src/backend/CallbackOfChecker.mli +++ b/infer/src/backend/CallbackOfChecker.mli @@ -74,9 +74,3 @@ val intraprocedural_with_field_dependency : -> (IntraproceduralAnalysis.t -> 'payload -> unit) -> Callbacks.proc_callback_t (** an intra-procedural analysis that depends on the summary payload found by another *) - -val intraprocedural_with_field : - (Payloads.t, 'payload option Lazy.t) Field.t - -> (IntraproceduralAnalysis.t -> 'payload option) - -> Callbacks.proc_callback_t -(** runs an intra-procedural analysis that nonetheless produces a payload *) diff --git a/infer/src/backend/Payloads.ml b/infer/src/backend/Payloads.ml index 180eca0e61b..76b3699001d 100644 --- a/infer/src/backend/Payloads.ml +++ b/infer/src/backend/Payloads.ml @@ -27,7 +27,6 @@ type t = ; lineage: Lineage.Summary.t option Lazy.t ; lineage_shape: LineageShape.Summary.t option Lazy.t ; starvation: StarvationDomain.summary option Lazy.t - ; nullsafe: NullsafeSummary.t option Lazy.t ; uninit: UninitDomain.Summary.t option Lazy.t } [@@deriving fields] @@ -62,7 +61,6 @@ let all_fields = ~lineage:(fun f -> mk f Lineage Lineage.Summary.pp) ~lineage_shape:(fun f -> mk f LineageShape LineageShape.Summary.pp) ~starvation:(fun f -> mk f Starvation StarvationDomain.pp_summary) - ~nullsafe:(fun f -> mk f Nullsafe NullsafeSummary.pp) ~uninit:(fun f -> mk f Uninit UninitDomain.Summary.pp) (* sorted to help serialization, see {!SQLite.serialize} below *) |> List.sort ~compare:(fun (F {payload_id= payload_id1}) (F {payload_id= payload_id2}) -> @@ -98,7 +96,6 @@ let empty = ; lineage= no_payload ; lineage_shape= no_payload ; starvation= no_payload - ; nullsafe= no_payload ; uninit= no_payload } @@ -147,8 +144,7 @@ module SQLite = struct ~racerd:data_of_sqlite_column ~lab_resource_leaks:data_of_sqlite_column ~scope_leakage:data_of_sqlite_column ~siof:data_of_sqlite_column ~lineage:data_of_sqlite_column ~lineage_shape:data_of_sqlite_column - ~starvation:data_of_sqlite_column ~nullsafe:data_of_sqlite_column - ~uninit:data_of_sqlite_column + ~starvation:data_of_sqlite_column ~uninit:data_of_sqlite_column let eager_load stmt ~first_column = (make_eager first_column |> fst) stmt @@ -206,6 +202,5 @@ module SQLite = struct ; lineage= lazy (load table ~proc_uid Lineage) ; lineage_shape= lazy (load table ~proc_uid LineageShape) ; starvation= lazy (load table ~proc_uid Starvation) - ; nullsafe= lazy (load table ~proc_uid Nullsafe) ; uninit= lazy (load table ~proc_uid Uninit) } end diff --git a/infer/src/backend/Payloads.mli b/infer/src/backend/Payloads.mli index 5a13e6ab7b5..8008eb121b0 100644 --- a/infer/src/backend/Payloads.mli +++ b/infer/src/backend/Payloads.mli @@ -37,7 +37,6 @@ include sig ; lineage: Lineage.Summary.t option Lazy.t ; lineage_shape: LineageShape.Summary.t option Lazy.t ; starvation: StarvationDomain.summary option Lazy.t - ; nullsafe: NullsafeSummary.t option Lazy.t ; uninit: UninitDomain.Summary.t option Lazy.t } [@@deriving fields, yojson_of] end diff --git a/infer/src/backend/dune b/infer/src/backend/dune index 5302b06abb2..7ed79e0fa43 100644 --- a/infer/src/backend/dune +++ b/infer/src/backend/dune @@ -31,8 +31,6 @@ -open BO -open - Nullsafe - -open Pulselib -open Checkers @@ -58,7 +56,6 @@ IR Absint Biabduction - Nullsafe BO Checkers Costlib diff --git a/infer/src/backend/registerCheckers.ml b/infer/src/backend/registerCheckers.ml index de61d4fbb3b..1f1f474d32d 100644 --- a/infer/src/backend/registerCheckers.ml +++ b/infer/src/backend/registerCheckers.ml @@ -67,10 +67,6 @@ let file payload_field checker = File (CallbackOfChecker.interprocedural_file pa let intraprocedural checker = Procedure (CallbackOfChecker.intraprocedural checker) -let intraprocedural_with_payload payload_field checker = - Procedure (CallbackOfChecker.intraprocedural_with_field payload_field checker) - - let intraprocedural_with_field_dependency payload_field checker = Procedure (CallbackOfChecker.intraprocedural_with_field_dependency payload_field checker) @@ -190,16 +186,9 @@ let all_checkers = ; {checker= Liveness; callbacks= [(intraprocedural Liveness.checker, Clang)]} ; { checker= InefficientKeysetIterator ; callbacks= [(intraprocedural InefficientKeysetIterator.checker, Java)] } - ; { checker= ImmutableCast - ; callbacks= - [(intraprocedural_with_payload Payloads.Fields.nullsafe ImmutableChecker.analyze, Java)] } ; { checker= FragmentRetainsView ; callbacks= [(intraprocedural FragmentRetainsViewChecker.callback_fragment_retains_view, Java)] } - ; { checker= Eradicate - ; callbacks= - [ (intraprocedural_with_payload Payloads.Fields.nullsafe Eradicate.analyze_procedure, Java) - ; (file Payloads.Fields.nullsafe FileLevelAnalysis.analyze_file, Java) ] } ; { checker= Biabduction ; callbacks= (let biabduction = diff --git a/infer/src/base/Checker.ml b/infer/src/base/Checker.ml index 79ae907bf9a..ee300a9e179 100644 --- a/infer/src/base/Checker.ml +++ b/infer/src/base/Checker.ml @@ -17,9 +17,7 @@ type t = | Cost | Datalog | DisjunctiveDemo - | Eradicate | FragmentRetainsView - | ImmutableCast | Impurity | InefficientKeysetIterator | LithoRequiredProps @@ -182,20 +180,6 @@ let config_unsafe checker = ; cli_flags= Some {deprecated= []; show_in_help= false} ; enabled_by_default= false ; activates= [] } - | Eradicate -> - { id= "eradicate" - ; kind= - UserFacingDeprecated - { title= "Eradicate" - ; markdown_body= [%blob "./documentation/checkers/Eradicate.md"] - ; deprecation_message= - "Unmaintained and will be removed in the future. Consider using \ - [NullAway](https://github.com/uber/NullAway) as an alternative to Eradicate." } - ; support= mk_support_func ~java:Support () - ; short_documentation= "The eradicate `@Nullable` checker for Java annotations." - ; cli_flags= Some {deprecated= []; show_in_help= true} - ; enabled_by_default= false - ; activates= [] } | FragmentRetainsView -> { id= "fragment-retains-view" ; kind= @@ -209,23 +193,6 @@ let config_unsafe checker = ; cli_flags= Some {deprecated= []; show_in_help= true} ; enabled_by_default= true ; activates= [] } - | ImmutableCast -> - { id= "immutable-cast" - ; kind= - UserFacingDeprecated - { title= "Immutable Cast" - ; markdown_body= - "Casts flagged by this checker are unsafe because calling mutation operations on \ - the cast objects will fail at runtime." - ; deprecation_message= "Unmaintained due to poor actionability of the reports." } - ; support= mk_support_func ~java:Support () - ; short_documentation= - "Detection of object cast from immutable types to mutable types. For instance, it will \ - detect casts from `ImmutableList` to `List`, `ImmutableMap` to `Map`, and \ - `ImmutableSet` to `Set`." - ; cli_flags= Some {deprecated= []; show_in_help= true} - ; enabled_by_default= false - ; activates= [] } | Impurity -> { id= "impurity" ; kind= diff --git a/infer/src/base/Checker.mli b/infer/src/base/Checker.mli index e74f6fcf801..c5f80f652fc 100644 --- a/infer/src/base/Checker.mli +++ b/infer/src/base/Checker.mli @@ -16,9 +16,7 @@ type t = | Cost | Datalog | DisjunctiveDemo - | Eradicate | FragmentRetainsView - | ImmutableCast | Impurity | InefficientKeysetIterator | LithoRequiredProps diff --git a/infer/src/base/Config.ml b/infer/src/base/Config.ml index a4c2115861e..a1051cbb33b 100644 --- a/infer/src/base/Config.ml +++ b/infer/src/base/Config.ml @@ -1579,20 +1579,6 @@ and dynamic_dispatch_json_file_path = "Dynamic dispatch file path to get the JSON used for method name substitution" -and eradicate_condition_redundant = - CLOpt.mk_bool ~long:"eradicate-condition-redundant" "Condition redundant warnings" - - -and eradicate_field_over_annotated = - CLOpt.mk_bool ~long:"eradicate-field-over-annotated" "Field over-annotated warnings" - - -and eradicate_return_over_annotated = - CLOpt.mk_bool ~long:"eradicate-return-over-annotated" "Return over-annotated warning" - - -and eradicate_verbose = CLOpt.mk_bool ~long:"eradicate-verbose" "Print initial and final typestates" - and erlang_ast_dir = CLOpt.mk_path_opt ~long:"erlang-ast-dir" ~in_help:InferCommand.[(Capture, manual_erlang)] @@ -2115,49 +2101,6 @@ and nullable_annotation = CLOpt.mk_string_opt ~long:"nullable-annotation-name" "Specify a custom nullable annotation name." -and nullsafe_annotation_graph = - CLOpt.mk_bool ~long:"nullsafe-annotation-graph" - "Nullsafe: an experimental mode for calculating the dependency graph between potential \ - annotations to add in the source code." - - -and nullsafe_disable_field_not_initialized_in_nonstrict_classes = - CLOpt.mk_bool ~long:"nullsafe-disable-field-not-initialized-in-nonstrict-classes" ~default:false - "Nullsafe: In this mode field not initialized issues won't be reported unless the class is \ - marked as @NullsafeStrict. This feature is needed for compatibility reasons." - - -and nullsafe_optimistic_third_party_in_default_mode = - CLOpt.mk_bool - ~long:"nullsafe-optimistic-third-party-in-default-mode" - (* Turned on for compatibility reasons - *) - ~default:true - "Nullsafe: Unless @Nullsafe annotation is used, treat not annotated third party method params \ - as if they were annotated as nullable, and return values as if they were annotated as \ - non-null" - - -and nullsafe_third_party_signatures = - CLOpt.mk_string_opt ~long:"nullsafe-third-party-signatures" - "Path to a folder with annotated signatures of third-party methods to be taken into account by \ - nullsafe. Path is either relative to .inferconfig folder or absolute" - - -and nullsafe_third_party_location_for_messaging_only = - CLOpt.mk_string_opt ~long:"nullsafe-third-party-location-for-messaging-only" - "Path to a folder with annotated signatures to include into error message. If not specified, \ - path will be fetched from nullsafe-third-party-signatures. This param is only needed for the \ - case when the real repository is located in the different place, and \ - nullsafe-third-party-signatures contains only its copy (which can happen e.g. in case of \ - caching by the build system)" - - -and nullsafe_strict_containers = - CLOpt.mk_bool ~long:"nullsafe-strict-containers" ~default:false - "Warn when containers are used with nullable keys or values" - - and oom_threshold = CLOpt.mk_int_opt ~long:"oom-threshold" "Available memory threshold (in MB) below which multi-worker scheduling throttles back work. \ @@ -3991,14 +3934,6 @@ and dump_textual = !dump_textual and abstract_pulse_models_for_erlang = !abstract_pulse_models_for_erlang -and eradicate_condition_redundant = !eradicate_condition_redundant - -and eradicate_field_over_annotated = !eradicate_field_over_annotated - -and eradicate_return_over_annotated = !eradicate_return_over_annotated - -and eradicate_verbose = !eradicate_verbose - and erlang_ast_dir = !erlang_ast_dir and erlang_check_return = !erlang_check_return @@ -4176,24 +4111,6 @@ and no_translate_libs = not !headers and nullable_annotation = !nullable_annotation -and nullsafe_annotation_graph = !nullsafe_annotation_graph - -and nullsafe_disable_field_not_initialized_in_nonstrict_classes = - !nullsafe_disable_field_not_initialized_in_nonstrict_classes - - -and nullsafe_optimistic_third_party_in_default_mode = - !nullsafe_optimistic_third_party_in_default_mode - - -and nullsafe_third_party_signatures = !nullsafe_third_party_signatures - -and nullsafe_third_party_location_for_messaging_only = - !nullsafe_third_party_location_for_messaging_only - - -and nullsafe_strict_containers = !nullsafe_strict_containers - and oom_threshold = !oom_threshold and only_cheap_debug = !only_cheap_debug diff --git a/infer/src/base/Config.mli b/infer/src/base/Config.mli index f420e72c675..b9032d8ac85 100644 --- a/infer/src/base/Config.mli +++ b/infer/src/base/Config.mli @@ -359,14 +359,6 @@ val dump_textual : bool val dynamic_dispatch_json_file_path : string option -val eradicate_condition_redundant : bool - -val eradicate_field_over_annotated : bool - -val eradicate_return_over_annotated : bool - -val eradicate_verbose : bool - val erlang_ast_dir : string option val erlang_check_return : bool @@ -456,8 +448,6 @@ val inclusive_cost : bool val inferconfig_file : string option -val inferconfig_dir : string option - val is_checker_enabled : Checker.t -> bool val issues_tests : string option @@ -528,18 +518,6 @@ val no_translate_libs : bool val nullable_annotation : string option -val nullsafe_annotation_graph : bool - -val nullsafe_disable_field_not_initialized_in_nonstrict_classes : bool - -val nullsafe_optimistic_third_party_in_default_mode : bool - -val nullsafe_third_party_signatures : string option - -val nullsafe_third_party_location_for_messaging_only : string option - -val nullsafe_strict_containers : bool - val oom_threshold : int option val only_cheap_debug : bool diff --git a/infer/src/base/IssueType.ml b/infer/src/base/IssueType.ml index 842c535c073..322dda598c6 100644 --- a/infer/src/base/IssueType.ml +++ b/infer/src/base/IssueType.ml @@ -388,11 +388,6 @@ let checkers_fragment_retain_view = ~user_documentation:[%blob "./documentation/issues/CHECKERS_FRAGMENT_RETAINS_VIEW.md"] -let checkers_immutable_cast = - register ~id:"CHECKERS_IMMUTABLE_CAST" Warning ImmutableCast - ~user_documentation:[%blob "./documentation/issues/CHECKERS_IMMUTABLE_CAST.md"] - - let checkers_printf_args = register ~id:"CHECKERS_PRINTF_ARGS" Error PrintfArgs ~user_documentation:[%blob "./documentation/issues/CHECKERS_PRINTF_ARGS.md"] @@ -477,127 +472,6 @@ let empty_vector_access = ~user_documentation:[%blob "./documentation/issues/EMPTY_VECTOR_ACCESS.md"] -(* A technical issue needed to output the annotation graph for the class - not intended to be surfaces to the end user *) -let eradicate_annotation_graph = - (* Enabled by default since this requires a special mode anyway *) - register ~id:"ERADICATE_ANNOTATION_GRAPH" ~hum:"Annotation Graph" Info Eradicate - ~user_documentation:"" - - -(* Condition redundant is a very non-precise issue. Depending on the origin of what is compared with - null, this can have a lot of reasons to be actually nullable. - - Until it is made non-precise, it is recommended to not turn this warning on. But even when it is - on, this should not be more than advice. *) -let eradicate_condition_redundant = - register ~id:"ERADICATE_CONDITION_REDUNDANT" ~hum:"Condition Redundant" Advice Eradicate - ~user_documentation:[%blob "./documentation/issues/ERADICATE_CONDITION_REDUNDANT.md"] - - -let eradicate_field_not_initialized = - register ~id:"ERADICATE_FIELD_NOT_INITIALIZED" ~hum:"Field Not Initialized" Warning Eradicate - ~user_documentation:[%blob "./documentation/issues/ERADICATE_FIELD_NOT_INITIALIZED.md"] - - -let eradicate_field_not_nullable = - register ~id:"ERADICATE_FIELD_NOT_NULLABLE" ~hum:"Field Not Nullable" Warning Eradicate - ~user_documentation:[%blob "./documentation/issues/ERADICATE_FIELD_NOT_NULLABLE.md"] - - -(* Very non-precise issue. Should be actually turned off unless for experimental purposes. *) -let eradicate_field_over_annotated = - register ~id:"ERADICATE_FIELD_OVER_ANNOTATED" ~hum:"Field Over Annotated" Advice - Eradicate (* TODO *) - ~user_documentation:"" - - -let eradicate_inconsistent_subclass_parameter_annotation = - register ~id:"ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION" - ~hum:"Inconsistent Subclass Parameter Annotation" Warning Eradicate - ~user_documentation: - [%blob "./documentation/issues/ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION.md"] - - -let eradicate_inconsistent_subclass_return_annotation = - register ~id:"ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION" - ~hum:"Inconsistent Subclass Return Annotation" Warning Eradicate - ~user_documentation: - [%blob "./documentation/issues/ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION.md"] - - -let eradicate_redundant_nested_class_annotation = - register ~id:"ERADICATE_REDUNDANT_NESTED_CLASS_ANNOTATION" - ~hum:"@Nullsafe annotation is redundant" Advice Eradicate (* TODO *) - ~user_documentation:"" - - -let eradicate_bad_nested_class_annotation = - register ~id:"ERADICATE_BAD_NESTED_CLASS_ANNOTATION" - ~hum:"@Nullsafe annotation is inconsistent with outer class" Warning Eradicate (* TODO *) - ~user_documentation:"" - - -let eradicate_nullable_dereference = - register ~id:"ERADICATE_NULLABLE_DEREFERENCE" ~hum:"Nullable Dereference" Warning - Eradicate (* TODO *) - ~user_documentation:"" - - -let eradicate_parameter_not_nullable = - register ~id:"ERADICATE_PARAMETER_NOT_NULLABLE" ~hum:"Parameter Not Nullable" Warning Eradicate - ~user_documentation:[%blob "./documentation/issues/ERADICATE_PARAMETER_NOT_NULLABLE.md"] - - -let eradicate_return_not_nullable = - register ~id:"ERADICATE_RETURN_NOT_NULLABLE" ~hum:"Return Not Nullable" Warning Eradicate - ~user_documentation:[%blob "./documentation/issues/ERADICATE_RETURN_NOT_NULLABLE.md"] - - -(* Very non-precise issue. Should be actually turned off unless for experimental purposes. *) -let eradicate_return_over_annotated = - register ~id:"ERADICATE_RETURN_OVER_ANNOTATED" ~hum:"Return Over Annotated" Advice Eradicate - ~user_documentation:[%blob "./documentation/issues/ERADICATE_RETURN_OVER_ANNOTATED.md"] - - -let eradicate_unchecked_usage_in_nullsafe = - register ~id:"ERADICATE_UNCHECKED_USAGE_IN_NULLSAFE" - ~hum:"Nullsafe mode: unchecked usage of a value" Warning Eradicate (* TODO *) - ~user_documentation:"" - - -let eradicate_unvetted_third_party_in_nullsafe = - register ~id:"ERADICATE_UNVETTED_THIRD_PARTY_IN_NULLSAFE" - ~hum:"Nullsafe mode: unchecked usage of unvetted third-party" Warning Eradicate (* TODO *) - ~user_documentation:"" - - -(* Meta issues in eradicate are technical issues reflecting null-safety state of classes in general, - in contrast with concrete nullability type violations *) - -let eradicate_meta_class_is_nullsafe = - register ~id:"ERADICATE_META_CLASS_IS_NULLSAFE" - ~hum: - "Class is marked @Nullsafe and has 0 issues" (* Should be enabled for special integrations *) - ~enabled:false Info Eradicate (* TODO *) - ~user_documentation:"" - - -let eradicate_meta_class_needs_improvement = - register ~id:"ERADICATE_META_CLASS_NEEDS_IMPROVEMENT" - ~hum: - "Class needs improvement to become @Nullsafe" (* Should be enabled for special integrations *) - ~enabled:false Info Eradicate - ~user_documentation:[%blob "./documentation/issues/ERADICATE_META_CLASS_NEEDS_IMPROVEMENT.md"] - - -let eradicate_meta_class_can_be_nullsafe = - register ~id:"ERADICATE_META_CLASS_CAN_BE_NULLSAFE" - ~hum:"Class has 0 issues and can be marked @Nullsafe" - (* Should be enabled for special integrations *) - ~enabled:false Advice Eradicate (* TODO *) - ~user_documentation:"" - - let exposed_insecure_intent_handling = register ~id:"EXPOSED_INSECURE_INTENT_HANDLING" Error Quandary ~user_documentation:"Undocumented." diff --git a/infer/src/base/IssueType.mli b/infer/src/base/IssueType.mli index afbd6f0423a..e298bd70b47 100644 --- a/infer/src/base/IssueType.mli +++ b/infer/src/base/IssueType.mli @@ -129,8 +129,6 @@ val checkers_expensive_overrides_unexpensive : t val checkers_fragment_retain_view : t -val checkers_immutable_cast : t - val checkers_printf_args : t val class_cast_exception : t @@ -174,42 +172,6 @@ val do_not_report : t val empty_vector_access : t -val eradicate_annotation_graph : t - -val eradicate_condition_redundant : t - -val eradicate_field_not_initialized : t - -val eradicate_field_not_nullable : t - -val eradicate_field_over_annotated : t - -val eradicate_inconsistent_subclass_parameter_annotation : t - -val eradicate_inconsistent_subclass_return_annotation : t - -val eradicate_redundant_nested_class_annotation : t - -val eradicate_bad_nested_class_annotation : t - -val eradicate_nullable_dereference : t - -val eradicate_parameter_not_nullable : t - -val eradicate_return_not_nullable : t - -val eradicate_return_over_annotated : t - -val eradicate_unvetted_third_party_in_nullsafe : t - -val eradicate_unchecked_usage_in_nullsafe : t - -val eradicate_meta_class_can_be_nullsafe : t - -val eradicate_meta_class_needs_improvement : t - -val eradicate_meta_class_is_nullsafe : t - val exposed_insecure_intent_handling : t val expensive_cost_call : kind:CostKind.t -> t diff --git a/infer/src/biabduction/Predicates.ml b/infer/src/biabduction/Predicates.ml index e1dd60db7ed..62370fc65f8 100644 --- a/infer/src/biabduction/Predicates.ml +++ b/infer/src/biabduction/Predicates.ml @@ -959,69 +959,6 @@ let apply_sub subst : subst_fun = let exp_sub (subst : subst) e = exp_sub_ids (apply_sub subst) e -(** apply [f] to id's in [instr]. if [sub_id_binders] is false, [f] is only applied to bound id's *) -let instr_sub_ids ~sub_id_binders f (instr : Sil.instr) : Sil.instr = - let sub_id id = - match exp_sub_ids f (Var id) with Var id' when not (Ident.equal id id') -> id' | _ -> id - in - match instr with - | Load {id; e= rhs_exp; typ; loc} -> - let id' = if sub_id_binders then sub_id id else id in - let rhs_exp' = exp_sub_ids f rhs_exp in - if phys_equal id' id && phys_equal rhs_exp' rhs_exp then instr - else Load {id= id'; e= rhs_exp'; typ; loc} - | Store {e1= lhs_exp; typ; e2= rhs_exp; loc} -> - let lhs_exp' = exp_sub_ids f lhs_exp in - let rhs_exp' = exp_sub_ids f rhs_exp in - if phys_equal lhs_exp' lhs_exp && phys_equal rhs_exp' rhs_exp then instr - else Store {e1= lhs_exp'; typ; e2= rhs_exp'; loc} - | Call (((id, typ) as ret_id_typ), fun_exp, actuals, call_flags, loc) -> - let ret_id' = - if sub_id_binders then - let id' = sub_id id in - if Ident.equal id id' then ret_id_typ else (id', typ) - else ret_id_typ - in - let fun_exp' = exp_sub_ids f fun_exp in - let actuals' = - IList.map_changed ~equal:[%equal: Exp.t * Typ.t] - ~f:(fun ((actual, typ) as actual_pair) -> - let actual' = exp_sub_ids f actual in - if phys_equal actual' actual then actual_pair else (actual', typ) ) - actuals - in - if phys_equal ret_id' ret_id_typ && phys_equal fun_exp' fun_exp && phys_equal actuals' actuals - then instr - else Call (ret_id', fun_exp', actuals', call_flags, loc) - | Prune (exp, loc, true_branch, if_kind) -> - let exp' = exp_sub_ids f exp in - if phys_equal exp' exp then instr else Prune (exp', loc, true_branch, if_kind) - | Metadata (ExitScope (vars, loc)) -> - let sub_var var = - match var with - | Var.ProgramVar _ -> - var - | Var.LogicalVar ident -> - let ident' = sub_id ident in - if phys_equal ident ident' then var else Var.of_id ident' - in - let vars' = IList.map_changed ~equal:phys_equal ~f:sub_var vars in - if phys_equal vars vars' then instr else Metadata (ExitScope (vars', loc)) - | Metadata - ( Abstract _ - | CatchEntry _ - | EndBranches - | Nullify _ - | Skip - | TryEntry _ - | TryExit _ - | VariableLifetimeBegins _ ) -> - instr - - -(** apply [subst] to all id's in [instr], including binder id's *) -let instr_sub (subst : subst) instr = instr_sub_ids ~sub_id_binders:true (apply_sub subst) instr - let atom_sub subst = atom_expmap (exp_sub subst) let hpred_sub subst = diff --git a/infer/src/biabduction/Predicates.mli b/infer/src/biabduction/Predicates.mli index c70644a6235..b2f5b3d9ad2 100644 --- a/infer/src/biabduction/Predicates.mli +++ b/infer/src/biabduction/Predicates.mli @@ -344,9 +344,6 @@ val exp_sub : subst -> Exp.t -> Exp.t val atom_sub : subst -> atom -> atom -val instr_sub : subst -> Sil.instr -> Sil.instr -(** apply [subst] to all id's in [instr], including LHS id's *) - val hpred_sub : subst -> hpred -> hpred (** {2 Functions for replacing occurrences of expressions.} *) diff --git a/infer/src/biabduction/State.ml b/infer/src/biabduction/State.ml index 7a561fd6923..ca6d9dce944 100644 --- a/infer/src/biabduction/State.ml +++ b/infer/src/biabduction/State.ml @@ -69,73 +69,6 @@ let get_diverging_states_node () = !gs.diverging_states_node let get_diverging_states_proc () = !gs.diverging_states_proc -(** normalize the list of instructions by renaming let-bound ids *) -let instrs_normalize instrs = - let bound_ids = - let do_instr = function Sil.Load {id} -> Some id | _ -> None in - IContainer.rev_filter_map_to_list ~fold:Instrs.fold ~f:do_instr instrs - in - let subst = - let count = ref Int.min_value in - let gensym id = - incr count ; - Ident.set_stamp id !count - in - Predicates.subst_of_list (List.rev_map ~f:(fun id -> (id, Exp.Var (gensym id))) bound_ids) - in - let subst_and_add acc instr = Predicates.instr_sub subst instr :: acc in - Instrs.fold instrs ~init:[] ~f:subst_and_add - - -(** Create a function to find duplicate nodes. A node is a duplicate of another one if they have the - same kind and location and normalized (w.r.t. renaming of let - bound ids) list of instructions. *) -let mk_find_duplicate_nodes : Procdesc.t -> Procdesc.Node.t -> Procdesc.NodeSet.t = - let module M = (* map from (loc,kind) *) - Caml.Map.Make (struct - type t = Location.t * Procdesc.Node.nodekind [@@deriving compare] - end) in - let module E = struct - (** Threshold: do not build the map if too many nodes are duplicates. *) - let threshold = 100 - - exception Threshold - end in - let get_key node = - (* map key *) - let loc = Procdesc.Node.get_loc node in - let kind = Procdesc.Node.get_kind node in - (loc, kind) - in - fun proc_desc -> - let map = - (* map from (loc, kind) to (node -> instructions) map *) - let do_node m node = - let normalized_instrs = instrs_normalize (Procdesc.Node.get_instrs node) in - let key = get_key node in - M.update key - (fun s_opt -> - let s = Option.value s_opt ~default:Procdesc.NodeMap.empty in - if Procdesc.NodeMap.cardinal s > E.threshold then raise E.Threshold ; - Some (Procdesc.NodeMap.add node normalized_instrs s) ) - m - in - try Procdesc.fold_nodes proc_desc ~init:M.empty ~f:do_node with E.Threshold -> M.empty - in - let find_duplicate_nodes node = - try - let s = M.find (get_key node) map in - let node_normalized_instrs = Procdesc.NodeMap.find node s in - let collect_duplicates node' normalized_instrs' nset = - if List.equal Sil.equal_instr node_normalized_instrs normalized_instrs' then - Procdesc.NodeSet.add node' nset - else nset - in - Procdesc.NodeMap.fold collect_duplicates s Procdesc.NodeSet.empty - with Caml.Not_found -> Procdesc.NodeSet.singleton node - in - find_duplicate_nodes - - let get_inst_update pos = let loc = AnalysisState.get_loc_exn () in Predicates.inst_update loc pos diff --git a/infer/src/biabduction/State.mli b/infer/src/biabduction/State.mli index 7289d682b1a..2f874eafac9 100644 --- a/infer/src/biabduction/State.mli +++ b/infer/src/biabduction/State.mli @@ -51,10 +51,6 @@ val mark_instr_fail : exn -> unit val mark_instr_ok : unit -> unit (** Mark that the execution of the current instruction was OK *) -val mk_find_duplicate_nodes : Procdesc.t -> Procdesc.Node.t -> Procdesc.NodeSet.t -(** Create a function to find duplicate nodes. A node is a duplicate of another one if they have the - same kind and location and normalized (w.r.t. renaming of let - bound ids) list of instructions. *) - type log_issue = ?node:Procdesc.Node.t -> ?loc:Location.t -> ?ltr:Errlog.loc_trace -> exn -> unit val process_execution_failures : log_issue -> unit diff --git a/infer/src/biabduction/errdesc.ml b/infer/src/biabduction/errdesc.ml index 2f737ca75f2..e349a546e75 100644 --- a/infer/src/biabduction/errdesc.ml +++ b/infer/src/biabduction/errdesc.ml @@ -121,7 +121,7 @@ let rec find_normal_variable_load_ tenv (seen : Exp.Set.t) node id : DExp.t opti propagation. previously, we would have code like: n1 = foo(); x = n1; n2 = x; n2.toString(), but copy-propagation will optimize this to: n1 = foo(); x = n1; n1.toString(). This case allows us to recognize the association - between n1 and x. Eradicate/checkers don't use copy-prop, so they don't need this. *) + between n1 and x. Checkers don't use copy-prop, so they don't need this. *) Some (DExp.Dpvar pvar) | _ -> None diff --git a/infer/src/biabduction/errdesc.mli b/infer/src/biabduction/errdesc.mli index c0cd746ef23..be723bddc4c 100644 --- a/infer/src/biabduction/errdesc.mli +++ b/infer/src/biabduction/errdesc.mli @@ -17,9 +17,6 @@ val vpath_find : Tenv.t -> 'a Prop.t -> Exp.t -> DecompiledExp.vpath * Typ.t opt val hpred_is_open_resource : Tenv.t -> 'a Prop.t -> Predicates.hpred -> PredSymb.resource option (** Check whether the hpred is a |-> representing a resource in the Racquire state *) -val exp_rv_dexp : Tenv.t -> Procdesc.Node.t -> Exp.t -> DecompiledExp.t option -(** describe rvalue [e] as a dexp *) - val explain_array_access : Procname.t -> Tenv.t -> Localise.deref_str -> 'a Prop.t -> Location.t -> Localise.error_desc (** Produce a description of the array access performed in the current instruction, if any. *) diff --git a/infer/src/deadcode/Makefile b/infer/src/deadcode/Makefile index cbf7d871374..848f2de2d36 100644 --- a/infer/src/deadcode/Makefile +++ b/infer/src/deadcode/Makefile @@ -50,8 +50,8 @@ endif # Note that we run find under _build directory. Since we copy some # sources from subfolders to src/ folder to avoid duplicates we use # $(DEPTH_ONE) and iteration over main and library folders. -LIBRARY_FOLDERS = . ./IR ./textual ./python ./absint ./atd ./backend ./base ./biabduction ./bufferoverrun ./c_stubs ./checkers ./clang ./clang/unit ./concurrency ./cost ./datalog ./erlang ./integration ./istd ./java ./labs ./dotnet ./nullsafe ./nullsafe/unit ./pulse ./quandary ./scripts ./test_determinator ./topl ./unit -INCLUDE_FOLDERS = -I IR -I textual -I textual/unit -I python -I python/unit -I absint -I atd -I backend -I base -I biabduction -I bufferoverrun -I c_stubs -I checkers -I clang -I clang/unit -I concurrency -I cost -I datalog -I erlang -I integration -I istd -I java -I labs -I nullsafe -I nullsafe/unit -I pulse -I quandary -I scripts -I test_determinator -I topl -I unit +LIBRARY_FOLDERS = . ./IR ./textual ./python ./absint ./atd ./backend ./base ./biabduction ./bufferoverrun ./c_stubs ./checkers ./clang ./clang/unit ./concurrency ./cost ./datalog ./erlang ./integration ./istd ./java ./labs ./dotnet ./pulse ./quandary ./scripts ./test_determinator ./topl ./unit +INCLUDE_FOLDERS = -I IR -I textual -I textual/unit -I python -I python/unit -I absint -I atd -I backend -I base -I biabduction -I bufferoverrun -I c_stubs -I checkers -I clang -I clang/unit -I concurrency -I cost -I datalog -I erlang -I integration -I istd -I java -I labs -I pulse -I quandary -I scripts -I test_determinator -I topl -I unit ml_src_files = $(shell \ cd $(INFER_BUILD_DIR); \ diff --git a/infer/src/dune.in b/infer/src/dune.in index cff995c31ff..ce486f88ea2 100644 --- a/infer/src/dune.in +++ b/infer/src/dune.in @@ -101,8 +101,8 @@ let inferunit_stanza = (modules Inferunit) (flags (:standard -open Core -open IStdlib -open IStd -open IBase -open Backend -open Integration -open Textuallib - -open UnitTests -open NullsafeUnitTests -open ClangFrontend %s %s)) - (libraries %s oUnit core IStdlib IBase Backend Integration UnitTests NullsafeUnitTests Textuallib %s) + -open UnitTests -open ClangFrontend %s %s)) + (libraries %s oUnit core IStdlib IBase Backend Integration UnitTests Textuallib %s) (link_flags %s) (preprocess (pps ppx_compare)) (promote (until-clean) (into ../bin)) @@ -120,7 +120,7 @@ let infertop_stanza = (modes byte_complete) (modules Infertop) (flags (:standard -open Core -open IStdlib -open IStd -open Textuallib %s)) - (libraries %s utop Absint ATDGenerated Backend IBase Biabduction BO Checkers Concurrency Costlib CStubs IR Textuallib IStdlib Labs Nullsafe Pulselib PythonFrontend Quandary Integration TestDeterminators Topllib UnitTests) + (libraries %s utop Absint ATDGenerated Backend IBase Biabduction BO Checkers Concurrency Costlib CStubs IR Textuallib IStdlib Labs Pulselib PythonFrontend Quandary Integration TestDeterminators Topllib UnitTests) (link_flags (-linkall -warn-error -31)) (preprocess (pps ppx_compare)) (promote (until-clean) (into ../bin)) diff --git a/infer/src/inferunit.ml b/infer/src/inferunit.ml index 8ea29b02218..b722ba213c3 100644 --- a/infer/src/inferunit.ml +++ b/infer/src/inferunit.ml @@ -46,7 +46,7 @@ let () = ; TaintTests.tests ; TraceTests.tests ; WeakTopologicalOrderTests.tests ] - @ ClangTests.tests @ AllNullsafeTests.tests ) + @ ClangTests.tests ) in let test_suite = "all" >::: tests in run_test_tt_main test_suite ; diff --git a/infer/src/nullsafe/AggregatedSummaries.ml b/infer/src/nullsafe/AggregatedSummaries.ml deleted file mode 100644 index cc7a9157c42..00000000000 --- a/infer/src/nullsafe/AggregatedSummaries.ml +++ /dev/null @@ -1,153 +0,0 @@ -(* - * 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. - *) - -open! IStd -module F = Format - -module ClassInfo = struct - type t = - { class_name: JavaClassName.t - ; summaries: NullsafeSummary.t list - ; mutable nested_classes_info: t list - ; nested_anonymous_classes: anonymous_class_to_summaries } - - and anonymous_class_to_summaries = NullsafeSummary.t list JavaClassName.Map.t - - let get_class_name {class_name} = class_name - - let get_summaries {summaries} = summaries - - let get_nested_anonymous_summaries {nested_anonymous_classes} = nested_anonymous_classes - - let get_nested_classes_info {nested_classes_info} = nested_classes_info - - (** Add a link between this class and a nested class info *) - let add_nested_class_info ~nested t = t.nested_classes_info <- nested :: t.nested_classes_info - - let make_empty class_name = - { class_name - ; summaries= [] - ; nested_classes_info= [] - ; nested_anonymous_classes= JavaClassName.Map.empty } - - - (** Add top level (not anonymous) summary belonging to this class *) - let add_class_summary summary t = {t with summaries= summary :: t.summaries} - - (** Add summary for the nested anonymous class belonging to this class *) - let add_anonymous_summary anonymous_class_name summary t = - let updated_anonymous_classes = - JavaClassName.Map.update anonymous_class_name - (function None -> Some [summary] | Some summaries -> Some (summary :: summaries)) - t.nested_anonymous_classes - in - {t with nested_anonymous_classes= updated_anonymous_classes} - - - let rec get_recursive_summaries t = - let this_summaries = List.map t.summaries ~f:(fun summary -> (t.class_name, summary)) in - let anonymous_summaries = - JavaClassName.Map.bindings t.nested_anonymous_classes - |> List.map ~f:(fun (class_name, summaries) -> - List.map summaries ~f:(fun summary -> (class_name, summary)) ) - |> List.concat - in - let nested_summaries = - List.map t.nested_classes_info ~f:get_recursive_summaries |> List.concat - in - this_summaries @ nested_summaries @ anonymous_summaries - - - let rec pp fmt class_info = - let pp_anonymous fmt (class_name, summaries) = - F.fprintf fmt "Class name: %a, Summaries: %d" JavaClassName.pp class_name - (List.length summaries) - in - let pp_list fmt items ~pp = - if List.is_empty items then F.fprintf fmt "" - else List.iter items ~f:(F.fprintf fmt "%a@\n" pp) - in - let pp_content fmt {summaries; nested_anonymous_classes; nested_classes_info} = - F.fprintf fmt - "Summaries: %d@\nNested anonymous classes:@\n @[%a@]@\nNested classes:@\n @[%a@]" - (List.length summaries) (pp_list ~pp:pp_anonymous) - (JavaClassName.Map.bindings nested_anonymous_classes) - (pp_list ~pp) nested_classes_info - in - F.fprintf fmt "Class name: %a@\n @[%a@]" JavaClassName.pp class_info.class_name pp_content - class_info -end - -(* If key (class_name) was not in the map yet, add it, otherwise modify the existing value. - [update] is a function that modifies value. -*) -let update_in_map class_name ~update t = - JavaClassName.Map.update class_name - (fun class_info -> - let info_to_update = Option.value class_info ~default:(ClassInfo.make_empty class_name) in - Some (update info_to_update) ) - t - - -(* Add (empty) class info for all outer classes for this class recursively, - if it did not exist yet *) -let rec register_ancestors user_defined_class map = - match JavaClassName.get_outer_class_name user_defined_class with - | Some outer_class -> - (* add to the map if not added and to the same for parent *) - update_in_map outer_class ~update:Fn.id map |> register_ancestors outer_class - | None -> - map - - -(* Register this class and all it ancestor classes, if not registered yet, - and update list of summaries for this class. -*) -let register_classes_and_add_summary class_name summary map = - match JavaClassName.get_user_defined_class_if_anonymous_inner class_name with - | Some outer_class_name -> - (* That was a nested anonymous class. - We don't register it at top level, registed outer class instead. *) - update_in_map outer_class_name - ~update:(ClassInfo.add_anonymous_summary class_name summary) - map - |> register_ancestors outer_class_name - | None -> - (* This is not an anonymous class, register it as is *) - update_in_map class_name ~update:(ClassInfo.add_class_summary summary) map - |> register_ancestors class_name - - -(* the result is the map from all class names (including all ancestors) to corresponding info with summaries, - without links to nested classes *) -let create_initial_map all_summaries = - List.fold all_summaries ~init:JavaClassName.Map.empty ~f:(fun map (class_name, summary) -> - register_classes_and_add_summary class_name summary map ) - - -(* given a map containing all nested and parent class names (excluding anonymous classes), - update [nested_classes_info] links for all outer classes -*) -let set_links map = - JavaClassName.Map.iter - (fun class_name class_info -> - Option.iter (JavaClassName.get_outer_class_name class_name) ~f:(fun outer_class_name -> - let outer_info = JavaClassName.Map.find outer_class_name map in - ClassInfo.add_nested_class_info ~nested:class_info outer_info ) ) - map - - -let aggregate all_summaries = - let class_name_to_info = create_initial_map all_summaries in - set_links class_name_to_info ; - (* Return only list of top level classes that are not nested *) - JavaClassName.Map.bindings class_name_to_info - |> List.filter_map ~f:(fun (class_name, class_info) -> - if Option.is_none (JavaClassName.get_outer_class_name class_name) then - (* This class is the outermost, leave it *) - Some class_info - else None ) diff --git a/infer/src/nullsafe/AggregatedSummaries.mli b/infer/src/nullsafe/AggregatedSummaries.mli deleted file mode 100644 index 40fb7bbd5a8..00000000000 --- a/infer/src/nullsafe/AggregatedSummaries.mli +++ /dev/null @@ -1,36 +0,0 @@ -(* - * 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. - *) - -open! IStd - -(** Results of proc-level analysis, grouped by Java class they belong to. Useful for class-level - analysis, when summaries for all methods are calculated and need to be combined together. *) - -(** Aggregated information for each user defined (not anonymous) Java class *) -module ClassInfo : sig - type t - - val get_class_name : t -> JavaClassName.t - - val get_summaries : t -> NullsafeSummary.t list - - val get_nested_classes_info : t -> t list - - val get_nested_anonymous_summaries : t -> NullsafeSummary.t list JavaClassName.Map.t - (** List of all anonymous class summaries belonging to this class, together with name of anonymous - class. This is a flattenned list, so we don't care if one anonymous class is nested inside the - other. *) - - val get_recursive_summaries : t -> (JavaClassName.t * NullsafeSummary.t) list - (** A flattened list of all summaries, user-level, nested, and anonymous, combined together *) - - val pp : Format.formatter -> t -> unit -end - -val aggregate : (JavaClassName.t * NullsafeSummary.t) list -> ClassInfo.t list -(** Given a list of all summaries and their classes, group them by names and aggregate in a list of - top-level classes. *) diff --git a/infer/src/nullsafe/AnnotatedField.ml b/infer/src/nullsafe/AnnotatedField.ml deleted file mode 100644 index 0c107df1f77..00000000000 --- a/infer/src/nullsafe/AnnotatedField.ml +++ /dev/null @@ -1,111 +0,0 @@ -(* - * 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. - *) - -open! IStd - -(* TODO(T54088319) get rid of annotation_deprecated: - Introduce "field flags" and move all other usages to this dedicated datatype -*) -type t = {annotation_deprecated: Annot.Item.t; annotated_type: AnnotatedType.t} - -let rec get_type_name {Typ.desc} = - match desc with Typ.Tstruct name -> Some name | Typ.Tptr (t, _) -> get_type_name t | _ -> None - - -(* A heuristic to guess if the field is actually a Java enum value. *) -let is_enum_value tenv ~class_typ (field_info : Struct.field_info) = - (* It is tricky to get this information with 100% precision, but this works in most of - practical cases. - In Java, enums are special classes, and enum values are (implicitly generated) static fields in these classes, - which are initialized statically via calling the class constructor. - Though there can be OTHER (user-defined) static fields in the same enum class, - this is a rare case.*) - if not field_info.is_static then false - else - match (get_type_name class_typ, get_type_name field_info.typ) with - (* enums values are fields which type is the same as the type of the enum class *) - | Some class_name, Some field_type_name - when Typ.equal_name class_name field_type_name && PatternMatch.Java.is_enum tenv class_name -> - true - (* Could not fetch one of the class names, or they are different. Should not happen for enum values. *) - | _ -> - false - - -(* For the special mode, return the provisionally nullable annotation, otherwise return the unchaged nullability *) -let maybe_provisionally_nullable field_name ~field_class ~class_under_analysis nullability = - if - Config.nullsafe_annotation_graph - (* Provisionally nullable mode distinct "internal" fields in the class and all the fields outside *) - && Typ.Name.equal field_class class_under_analysis - && AnnotatedNullability.can_be_considered_for_provisional_annotation nullability - then AnnotatedNullability.ProvisionallyNullable (ProvisionalAnnotation.Field {field_name}) - else nullability - - -let get tenv field_name ~class_typ ~class_under_analysis = - let open IOption.Let_syntax in - let lookup = Tenv.lookup tenv in - (* We currently don't support field-level strict mode annotation, so fetch it from class *) - let nullsafe_mode = - Typ.name class_typ - |> Option.value_map - ~f:(fun class_name -> - Typ.Name.Java.get_java_class_name_exn class_name |> NullsafeMode.of_class tenv ) - ~default:NullsafeMode.Default - in - let is_third_party = - ThirdPartyAnnotationInfo.is_third_party_typ - (ThirdPartyAnnotationGlobalRepo.get_repo ()) - class_typ - in - let+ (Struct.{typ= field_typ; annotations} as field_info) = - Struct.get_field_info ~lookup field_name class_typ - in - let is_enum_value = is_enum_value tenv ~class_typ field_info in - let nullability = - (* TODO(T62825735): support trusted callees for fields *) - AnnotatedNullability.of_type_and_annotation ~is_callee_in_trust_list:false ~nullsafe_mode - ~is_third_party field_typ annotations - in - let special_case_nullability = - if - Nullability.is_subtype - ~subtype:(AnnotatedNullability.get_nullability nullability) - ~supertype:Nullability.ThirdPartyNonnull - then - if - is_enum_value - (* Enum values are the special case - they can not be null. So we can strengten nullability. - Note that if it is nullable, we do NOT change nullability: in this case this is probably - not an enum value, but just a static field annotated as nullable. - *) - then AnnotatedNullability.StrictNonnull EnumValue - else if Fieldname.is_java_synthetic field_name then - (* This field is artifact of codegen and is not visible to the user. - Surfacing it as non-strict is non-actionable for the user *) - AnnotatedNullability.StrictNonnull SyntheticField - else - match Models.is_field_nonnullable field_name with - | Some true -> - AnnotatedNullability.StrictNonnull ModelledNonnull - | Some false -> - AnnotatedNullability.Nullable ModelledNullable - | _ -> - nullability - else nullability - in - let field_class = - Option.value_exn (get_type_name class_typ) - ~message:"otherwise we would not have fetched field info above" - in - let final_nullability = - maybe_provisionally_nullable field_name ~field_class ~class_under_analysis - special_case_nullability - in - let annotated_type = AnnotatedType.{nullability= final_nullability; typ= field_typ} in - {annotation_deprecated= annotations; annotated_type} diff --git a/infer/src/nullsafe/AnnotatedField.mli b/infer/src/nullsafe/AnnotatedField.mli deleted file mode 100644 index 0370be6c3bd..00000000000 --- a/infer/src/nullsafe/AnnotatedField.mli +++ /dev/null @@ -1,15 +0,0 @@ -(* - * 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. - *) - -open! IStd - -(** Representation of a declared class field with nullsafe-specific data *) - -type t = {annotation_deprecated: Annot.Item.t; annotated_type: AnnotatedType.t} - -val get : Tenv.t -> Fieldname.t -> class_typ:Typ.t -> class_under_analysis:Typ.name -> t option -(** Looks up for a field declaration and, in case of success, converts it to [t] *) diff --git a/infer/src/nullsafe/AnnotatedNullability.ml b/infer/src/nullsafe/AnnotatedNullability.ml deleted file mode 100644 index cff8862932e..00000000000 --- a/infer/src/nullsafe/AnnotatedNullability.ml +++ /dev/null @@ -1,175 +0,0 @@ -(* - * 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. - *) - -open! IStd -module F = Format - -(** Representation of a (non local) type in Java program, with added information about its - nullability, according to the source code. Nullability information might come either from - explicit annotations, or from other sources, including conventions about defaults. Note that - nullsafe omits Nullability information in types used for local variable declarations: this - information is inferred according to flow-sensitive inferrence rule. *) - -(** See {!Nullability.t} for explanation *) -type t = - | Nullable of nullable_origin - | ProvisionallyNullable of ProvisionalAnnotation.t - | ThirdPartyNonnull - | UncheckedNonnull of unchecked_nonnull_origin - | LocallyTrustedNonnull - | LocallyCheckedNonnull - | StrictNonnull of strict_nonnull_origin -[@@deriving compare, equal] - -and nullable_origin = - | AnnotatedNullable - | AnnotatedPropagatesNullable - | HasPropagatesNullableInParam - | ModelledNullable -[@@deriving compare] - -and unchecked_nonnull_origin = AnnotatedNonnull | ImplicitlyNonnull - -and strict_nonnull_origin = - | ExplicitNonnullThirdParty - | ModelledNonnull - | StrictMode - | PrimitiveType - | ImplicitThis - | EnumValue - | SyntheticField -[@@deriving compare] - -let get_nullability = function - | Nullable _ -> - Nullability.Nullable - | ProvisionallyNullable _ -> - Nullability.ProvisionallyNullable - | ThirdPartyNonnull -> - Nullability.ThirdPartyNonnull - | UncheckedNonnull _ -> - Nullability.UncheckedNonnull - | LocallyTrustedNonnull -> - Nullability.LocallyTrustedNonnull - | LocallyCheckedNonnull -> - Nullability.LocallyCheckedNonnull - | StrictNonnull _ -> - Nullability.StrictNonnull - - -let pp fmt t = - let string_of_nullable_origin nullable_origin = - match nullable_origin with - | AnnotatedNullable -> - "@" - | AnnotatedPropagatesNullable -> - "propagates" - | HasPropagatesNullableInParam -> - "<-propagates" - | ModelledNullable -> - "model" - in - let string_of_declared_nonnull_origin origin = - match origin with AnnotatedNonnull -> "@" | ImplicitlyNonnull -> "implicit" - in - let string_of_nonnull_origin nonnull_origin = - match nonnull_origin with - | ExplicitNonnullThirdParty -> - "explicit3p" - | ModelledNonnull -> - "model" - | StrictMode -> - "strict" - | PrimitiveType -> - "primitive" - | ImplicitThis -> - "implicit_this" - | EnumValue -> - "enum" - | SyntheticField -> - "synthetic_field" - in - match t with - | Nullable origin -> - F.fprintf fmt "Nullable[%s]" (string_of_nullable_origin origin) - | ProvisionallyNullable _ -> - F.fprintf fmt "ProvisionallyNullable" - | ThirdPartyNonnull -> - F.fprintf fmt "ThirdPartyNonnull" - | UncheckedNonnull origin -> - F.fprintf fmt "UncheckedNonnull[%s]" (string_of_declared_nonnull_origin origin) - | LocallyTrustedNonnull -> - F.fprintf fmt "LocallyTrustedNonnull" - | LocallyCheckedNonnull -> - F.fprintf fmt "LocallyCheckedNonnull" - | StrictNonnull origin -> - F.fprintf fmt "StrictNonnull[%s]" (string_of_nonnull_origin origin) - - -let of_type_and_annotation ~is_callee_in_trust_list ~nullsafe_mode ~is_third_party typ annotations = - if not (PatternMatch.type_is_class typ) then StrictNonnull PrimitiveType - else if Annotations.ia_is_nullable annotations then - (* Explicitly nullable always means Nullable *) - let nullable_origin = - if Annotations.ia_is_propagates_nullable annotations then AnnotatedPropagatesNullable - else AnnotatedNullable - in - Nullable nullable_origin - else - (* Lack of nullable annotation means non-nullish case, lets specify which exactly. *) - match nullsafe_mode with - | NullsafeMode.Strict -> - (* In strict mode, not annotated with nullable means non-nullable *) - StrictNonnull StrictMode - | NullsafeMode.Local _ -> - (* In local mode, not annotated with nullable means non-nullable *) - LocallyCheckedNonnull - | NullsafeMode.Default -> - (* In default mode, agreements for "not [@Nullable]" depend on where code comes from *) - if is_third_party then - if Annotations.ia_is_nonnull annotations then - (* Third party method explicitly marked as [@Nonnull]. - This is considered strict - see documentation to [ExplicitNonnullThirdParty] - **) - StrictNonnull ExplicitNonnullThirdParty - else - (* Third party might not obey "not annotated hence not nullable" convention. - Hence by default we treat is with low level of trust. - *) - ThirdPartyNonnull - else - (* For non third party code, the agreement is "not annotated with [@Nullable] hence not null" *) - let preliminary_nullability = - if Annotations.ia_is_nonnull annotations then UncheckedNonnull AnnotatedNonnull - else UncheckedNonnull ImplicitlyNonnull - in - if is_callee_in_trust_list then LocallyTrustedNonnull else preliminary_nullability - - -let can_be_considered_for_provisional_annotation = function - | Nullable _ -> - (* already nullable *) false - | ProvisionallyNullable _ -> - (* already provisionally nullable *) true - | ThirdPartyNonnull -> - (* third party code is considered beyond control *) false - | UncheckedNonnull _ | LocallyTrustedNonnull | LocallyCheckedNonnull -> - (* legit non-primitive non-nullable type *) true - | StrictNonnull ExplicitNonnullThirdParty -> - (* third party code is considered beyond control *) false - | StrictNonnull ModelledNonnull -> - (* models correspond to code beyond control *) false - | StrictNonnull PrimitiveType -> - (* primitive type can not be annotated *) false - | StrictNonnull ImplicitThis -> - (* a synthetic param *) false - | StrictNonnull EnumValue -> - (* by design non-nullable *) false - | StrictNonnull SyntheticField -> - (* not present in source code *) false - | StrictNonnull StrictMode -> - (* legit non-nullable non-primitive type *) true diff --git a/infer/src/nullsafe/AnnotatedNullability.mli b/infer/src/nullsafe/AnnotatedNullability.mli deleted file mode 100644 index 1f323a4d489..00000000000 --- a/infer/src/nullsafe/AnnotatedNullability.mli +++ /dev/null @@ -1,93 +0,0 @@ -(* - * 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. - *) - -open! IStd - -(** Nullability of a type in Java program (e.g. in a function or field declaration). It might come - from explicit annotations (or lack of annotation), or from other sources, including conventions - about defaults, models, or the mode nullsafe runs in. NOTE: This is complementary to - {!InferredNullability.t}. {!InferredNullability} contains info about _actual_ nullability (what - did nullsafe infer according to its flow-sensitive rules.). In contrast, AnnotatedNullability - represents _formal_ type as it appears in the program code. NOTE: Nullsafe disregards - user-provided annotations for local types, so annotated nullability applies only for types - declared at methods and field level. *) - -(** See {!Nullability.t} for explanation *) -type t = - | Nullable of nullable_origin - | ProvisionallyNullable of ProvisionalAnnotation.t (** Exist only for specical run modes *) - | ThirdPartyNonnull - | UncheckedNonnull of unchecked_nonnull_origin - | LocallyTrustedNonnull - | LocallyCheckedNonnull - | StrictNonnull of strict_nonnull_origin -[@@deriving compare, equal] - -and nullable_origin = - | AnnotatedNullable (** The type is expicitly annotated with [@Nullable] in the code *) - | AnnotatedPropagatesNullable - (** If a function param is annotated as [@PropagatesNullable], this param is automatically - nullable *) - | HasPropagatesNullableInParam - (** If a method has at least one param marked as [@PropagatesNullable], return value is - automatically nullable *) - | ModelledNullable (** nullsafe knows it is nullable via its internal models *) -[@@deriving compare] - -and unchecked_nonnull_origin = - | AnnotatedNonnull - (** The type is explicitly annotated as non nullable via one of nonnull annotations Nullsafe - recognizes *) - | ImplicitlyNonnull - (** Infer was run in mode where all not annotated (non local) types are treated as non - nullable *) - -and strict_nonnull_origin = - | ExplicitNonnullThirdParty - (** Third party annotated as [@Nonnull] is considered strict. This is a controversial choice - and might be an unsoundness issue. The reason is practical. The best we can do for third - party is to register it in third party signature repository. Though this typically - requires human review, in practice errors are inevitable. On the other hand, if the - library owner explicitly annotated a function as nonnull, we assume this was made for - reason. In practice, requiring such methods to be registered in third party folder, will - introduce user friction but will not significantly increase safety. So our approach here - is optimistic. If some particular method or library is known to contain wrong [@Nonnull] - annotations, third party repository is a way to override this. *) - | ModelledNonnull (** nullsafe knows it is non-nullable via its internal models *) - | StrictMode (** under strict mode we consider non-null declarations to be trusted *) - | PrimitiveType (** Primitive types are non-nullable by language design *) - | ImplicitThis (** Implicit `this` param for virtual non-static methods *) - | EnumValue - (** Java enum value are statically initialized with non-nulls according to language semantics *) - | SyntheticField - (** Fake field that is not part of user codebase, but rather artifact of codegen/annotation - processor *) -[@@deriving compare] - -val get_nullability : t -> Nullability.t - -val of_type_and_annotation : - is_callee_in_trust_list:bool - -> nullsafe_mode:NullsafeMode.t - -> is_third_party:bool - -> Typ.t - -> Annot.Item.t - -> t -(** Given the type and its annotations, returns its nullability. NOTE: it does not take into account - models etc., so this is intended to be used as a helper function for more high-level annotation - processing. [is_callee_in_trust_list] defines whether the callee class is in the caller's - explicitly provided trust list and therefore whether its nullability should be refined. *) - -val can_be_considered_for_provisional_annotation : t -> bool -(** A method for the special mode where imaginary (provisional) [@Nullable] annotations are added to - the code: see also [ProvisionalAnnotation.t]. This is a helper method useful for preliminary - filtration of types that: - - - can be semantically annotated as [@Nullable] in the source code e.g. non-primitive types - - makes logical sense to annotate - e.g. the source code is under control. *) - -val pp : Format.formatter -> t -> unit diff --git a/infer/src/nullsafe/AnnotatedSignature.ml b/infer/src/nullsafe/AnnotatedSignature.ml deleted file mode 100644 index 9c2809c5756..00000000000 --- a/infer/src/nullsafe/AnnotatedSignature.ml +++ /dev/null @@ -1,254 +0,0 @@ -(* - * 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. - *) -open! IStd -module F = Format -module L = Logging - -(* TODO(T54088319) remove Annot.Item.t from t: - For all helper annotations guiding Nullsafe behavior, introduce corresponding datatypes: - a. Known ret value annotations (if any) - b. Known param annotations - c. Known method-level annotations. -*) - -type t = - {nullsafe_mode: NullsafeMode.t; kind: kind; ret: ret_signature; params: param_signature list} -[@@deriving compare] - -and ret_signature = {ret_annotation_deprecated: Annot.Item.t; ret_annotated_type: AnnotatedType.t} -[@@deriving compare] - -and param_signature = - { param_annotation_deprecated: Annot.Item.t - ; mangled: Mangled.t - ; param_annotated_type: AnnotatedType.t } -[@@deriving compare, equal] - -and kind = FirstParty | ThirdParty of third_party_model_source [@deriving compare] - -and third_party_model_source = - | Unregistered - | ModelledInternally - | InThirdPartyRepo of {filename: string; line_number: int} -[@@deriving compare] - -let get_non_virtual_params {params} = - match params with x :: tail when Mangled.is_this x.mangled -> tail | _ -> params - - -(* get nullability of method's return type given its annotations and information about its params *) -let nullability_for_return ~proc_name ~is_callee_in_trust_list ~nullsafe_mode ~is_third_party - ~is_provisional_annotation_mode ret_type ret_annotations ~has_propagates_nullable_in_param = - let nullability = - AnnotatedNullability.of_type_and_annotation ~is_callee_in_trust_list ~nullsafe_mode - ~is_third_party ret_type ret_annotations - in - (* if any param is annotated with propagates nullable, the return nullability is also nullable *) - let nullability = - match nullability with - | AnnotatedNullability.Nullable _ -> - nullability (* We already know it is nullable - lets not overwrite the origin *) - | _ when has_propagates_nullable_in_param -> - (* if any params is propagates nullable, the return type can be only nullable *) - AnnotatedNullability.Nullable AnnotatedNullability.HasPropagatesNullableInParam - | _ -> - nullability - in - let final_nullability = - if - is_provisional_annotation_mode - && AnnotatedNullability.can_be_considered_for_provisional_annotation nullability - then AnnotatedNullability.ProvisionallyNullable (ProvisionalAnnotation.Method proc_name) - else nullability - in - final_nullability - - -(* Given annotations for method signature, extract nullability information - for return type and params *) -let extract_for_ret ~proc_name ~is_callee_in_trust_list ~nullsafe_mode ~is_third_party - ~is_provisional_annotation_mode ret_type ret_annotations param_info = - let has_propagates_nullable_in_param = - List.exists param_info ~f:(fun {param_annotated_type= {nullability}} -> - match nullability with - | AnnotatedNullability.Nullable AnnotatedNullability.AnnotatedPropagatesNullable -> - true - | _ -> - false ) - in - let return_nullability = - nullability_for_return ~proc_name ~is_callee_in_trust_list ~nullsafe_mode ~is_third_party - ret_type ~is_provisional_annotation_mode ret_annotations ~has_propagates_nullable_in_param - in - { ret_annotation_deprecated= ret_annotations - ; ret_annotated_type= AnnotatedType.{nullability= return_nullability; typ= ret_type} } - - -let get_param_nullability ~is_callee_in_trust_list ~nullsafe_mode ~is_third_party param_name - param_type param_annotations = - if Mangled.is_this param_name then AnnotatedNullability.StrictNonnull ImplicitThis - else - AnnotatedNullability.of_type_and_annotation ~is_callee_in_trust_list ~nullsafe_mode - ~is_third_party param_type param_annotations - - -(* When getting param indices, we might need to offset to account for synthetic virtual params *) -let get_param_index_offset param_nullabilities = - match param_nullabilities with - | AnnotatedNullability.StrictNonnull ImplicitThis :: _ -> - 1 - | _ -> - 0 - - -let correct_by_provisional_annotations ~proc_name param_nullabilities = - let index_offset = get_param_index_offset param_nullabilities in - List.mapi param_nullabilities ~f:(fun param_index nullability -> - if AnnotatedNullability.can_be_considered_for_provisional_annotation nullability then - AnnotatedNullability.ProvisionallyNullable - (ProvisionalAnnotation.Param {method_info= proc_name; num= param_index - index_offset}) - else nullability ) - - -let extract_for_params ~proc_name ~is_callee_in_trust_list ~nullsafe_mode ~is_third_party - ~is_provisional_annotation_mode param_info = - let param_nullability = - List.map param_info ~f:(fun (param_name, typ, annotations) -> - get_param_nullability ~is_callee_in_trust_list ~nullsafe_mode ~is_third_party param_name typ - annotations ) - in - let corrected_nullability = - if is_provisional_annotation_mode then - correct_by_provisional_annotations ~proc_name param_nullability - else param_nullability - in - List.map2_exn param_info corrected_nullability - ~f:(fun (mangled, typ, param_annotation_deprecated) nullability -> - {param_annotation_deprecated; mangled; param_annotated_type= AnnotatedType.{nullability; typ}} ) - - -let get_impl ~is_callee_in_trust_list ~nullsafe_mode ~is_provisional_annotation_mode - ({ProcAttributes.proc_name; ret_type; ret_annots} as proc_attributes) : t = - let proc_name = - Procname.as_java_exn ~explanation:"AnnotatedSignature.get:: should call only for Java methods" - proc_name - in - let is_third_party = - ThirdPartyAnnotationInfo.is_third_party_proc - (ThirdPartyAnnotationGlobalRepo.get_repo ()) - proc_name - in - let param_info = ProcAttributes.get_formals proc_attributes in - let params = - extract_for_params ~proc_name ~is_callee_in_trust_list ~nullsafe_mode ~is_third_party - ~is_provisional_annotation_mode param_info - in - let ret = - extract_for_ret ~proc_name ~is_callee_in_trust_list ~nullsafe_mode ~is_third_party - ~is_provisional_annotation_mode ret_type ret_annots params - in - let kind = if is_third_party then ThirdParty Unregistered else FirstParty in - {nullsafe_mode; kind; ret; params} - - -let get = get_impl ~is_provisional_annotation_mode:false - -let get_for_class_under_analysis tenv proc_attributes = - (* Signature makes special meaning when the method is inside the class we are currently analysing. - Various non-nullable levels (as dictated by nullsafe mode of the class) - make sense only for external (for the class under analysis) methods. - But in context of currently analyzed class we effectively have two levels of nullability for signatures: - nullable and (strict) non-null. - We achieve it via passing Strict mode to the signature extractor. - *) - let result = - get_impl ~is_callee_in_trust_list:false ~nullsafe_mode:NullsafeMode.Strict proc_attributes - ~is_provisional_annotation_mode:Config.nullsafe_annotation_graph - in - (* Don't forget about the original mode *) - let nullsafe_mode = NullsafeMode.of_procname tenv proc_attributes.ProcAttributes.proc_name in - {result with nullsafe_mode} - - -let pp_ia fmt ia = if not (List.is_empty ia) then F.fprintf fmt "%a " Annot.Item.pp ia - -let pp_annotated_param fmt {mangled; param_annotation_deprecated; param_annotated_type} = - F.fprintf fmt " %a%a %a" pp_ia param_annotation_deprecated AnnotatedType.pp param_annotated_type - Mangled.pp mangled - - -let pp proc_name fmt annotated_signature = - let {ret_annotation_deprecated; ret_annotated_type} = annotated_signature.ret in - F.fprintf fmt "[%a] %a%a %a (%a )" NullsafeMode.pp annotated_signature.nullsafe_mode pp_ia - ret_annotation_deprecated AnnotatedType.pp ret_annotated_type - (Procname.pp_simplified_string ~withclass:false) - proc_name (Pp.comma_seq pp_annotated_param) annotated_signature.params - - -let mk_ann_str s = {Annot.class_name= s; parameters= []} - -let mk_ia_nullable ia = - if Annotations.ia_is_nullable ia then ia else mk_ann_str Annotations.nullable :: ia - - -let mark_ia_nullability ia x = if x then mk_ia_nullable ia else ia - -(** Override existing information about nullability for a given type and set it to either nullable - or nonnull *) -let set_modelled_nullability_for_annotated_type annotated_type should_set_nullable = - let nullability = - if should_set_nullable then AnnotatedNullability.Nullable ModelledNullable - else AnnotatedNullability.StrictNonnull ModelledNonnull - in - AnnotatedType.{annotated_type with nullability} - - -let set_modelled_nullability proc_name asig model_source (nullability_for_ret, params_nullability) = - let set_modelled_nullability_for_param param should_set_nullable = - { param with - param_annotation_deprecated= - mark_ia_nullability param.param_annotation_deprecated should_set_nullable - ; param_annotated_type= - set_modelled_nullability_for_annotated_type param.param_annotated_type should_set_nullable - } - in - let set_modelled_nullability_for_ret ret should_set_nullable = - { ret_annotation_deprecated= - mark_ia_nullability ret.ret_annotation_deprecated should_set_nullable - ; ret_annotated_type= - set_modelled_nullability_for_annotated_type ret.ret_annotated_type should_set_nullable } - in - let final_params = - let fail () = - L.die InternalError - "Annotation for procedure %a has wrong number of arguments.@\n Annotated signature: %a" - Procname.pp_unique_id proc_name (pp proc_name) asig - in - let rec model_param_nullability original_params params_nullability = - match (original_params, params_nullability) with - | param :: params_tail, nullability_tail when Mangled.is_this param.mangled -> - (* Skip "this" param - there is no notion of "nullable this" *) - param :: model_param_nullability params_tail nullability_tail - | param :: params_tail, should_set_nullable :: nullability_tail -> - set_modelled_nullability_for_param param should_set_nullable - :: model_param_nullability params_tail nullability_tail - | [], _ :: _ | _ :: _, [] -> - (* One list extausted before the other one *) - fail () - | [], [] -> - [] - in - model_param_nullability asig.params params_nullability - in - match model_source with - | Unregistered -> - Logging.die InternalError "the method should be either internally or externally modelled" - | ModelledInternally | InThirdPartyRepo _ -> - { asig with - ret= set_modelled_nullability_for_ret asig.ret nullability_for_ret - ; kind= ThirdParty model_source - ; params= final_params } diff --git a/infer/src/nullsafe/AnnotatedSignature.mli b/infer/src/nullsafe/AnnotatedSignature.mli deleted file mode 100644 index 980c657031b..00000000000 --- a/infer/src/nullsafe/AnnotatedSignature.mli +++ /dev/null @@ -1,52 +0,0 @@ -(* - * 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. - *) - -(** Method signature with annotations. *) - -open! IStd - -type t = - {nullsafe_mode: NullsafeMode.t; kind: kind; ret: ret_signature; params: param_signature list} -[@@deriving compare, equal] - -and ret_signature = {ret_annotation_deprecated: Annot.Item.t; ret_annotated_type: AnnotatedType.t} -[@@deriving compare] - -and param_signature = - { param_annotation_deprecated: Annot.Item.t - ; mangled: Mangled.t - ; param_annotated_type: AnnotatedType.t } -[@@deriving compare] - -and kind = - | FirstParty (** Code under control. Its nullability should be expressed via annotations. *) - | ThirdParty of third_party_model_source [@deriving compare] - -and third_party_model_source = - | Unregistered - (** This is an unregistered third party method. It's nullability is best effort based on its - annotations. Lack of annotation is treated depending on the mode. *) - | ModelledInternally - | InThirdPartyRepo of {filename: string; line_number: int} -[@@deriving compare] - -val get_non_virtual_params : t -> param_signature list -(** List of params, not counting the optional first ("this") param used to represent non-static - methods. *) - -val set_modelled_nullability : Procname.t -> t -> third_party_model_source -> bool * bool list -> t -(** Override nullability for a function signature given its modelled nullability (for ret value and - params) *) - -val get : is_callee_in_trust_list:bool -> nullsafe_mode:NullsafeMode.t -> ProcAttributes.t -> t -(** Get a method signature with annotations from a proc_attributes. *) - -val get_for_class_under_analysis : Tenv.t -> ProcAttributes.t -> t -(** Signature of the method belonging to the currently analyzed class. *) - -val pp : Procname.t -> Format.formatter -> t -> unit -(** Pretty print a method signature with annotations. *) diff --git a/infer/src/nullsafe/AnnotatedType.ml b/infer/src/nullsafe/AnnotatedType.ml deleted file mode 100644 index 6aeb13e6e4f..00000000000 --- a/infer/src/nullsafe/AnnotatedType.ml +++ /dev/null @@ -1,12 +0,0 @@ -(* - * 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. - *) -open! IStd - -type t = {nullability: AnnotatedNullability.t; typ: Typ.t} [@@deriving compare, equal] - -let pp fmt {nullability; typ} = - Format.fprintf fmt "%a %a" AnnotatedNullability.pp nullability (Typ.pp_full Pp.text) typ diff --git a/infer/src/nullsafe/AnnotatedType.mli b/infer/src/nullsafe/AnnotatedType.mli deleted file mode 100644 index caeb0bc6a58..00000000000 --- a/infer/src/nullsafe/AnnotatedType.mli +++ /dev/null @@ -1,14 +0,0 @@ -(* - * 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. - *) - -open! IStd - -(** Formal type in program together with its nullability information. *) - -type t = {nullability: AnnotatedNullability.t; typ: Typ.t} [@@deriving compare, equal] - -val pp : Format.formatter -> t -> unit diff --git a/infer/src/nullsafe/AnnotationGraph.ml b/infer/src/nullsafe/AnnotationGraph.ml deleted file mode 100644 index 06334436761..00000000000 --- a/infer/src/nullsafe/AnnotationGraph.ml +++ /dev/null @@ -1,224 +0,0 @@ -(* - * 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. - *) -open! IStd - -type point_info = - { id: string - ; annotation: ProvisionalAnnotation.t - ; mutable num_violations: int - ; mutable dependent_points: ProvisionalAnnotation.t list } - -module AnnotationMap = Caml.Map.Make (struct - type t = ProvisionalAnnotation.t [@@deriving compare] -end) - -let get_provisional_annotation = function - | AnnotatedNullability.ProvisionallyNullable provisional_annotation -> - Some provisional_annotation - | _ -> - None - - -(* Corresponding provisional annotations for the return value and params, if any *) -let annotations_of_signature AnnotatedSignature.{ret; params} = - let annotated_nullability = - ret.ret_annotated_type.nullability - :: List.map params ~f:(fun AnnotatedSignature.{param_annotated_type= {nullability}} -> - nullability ) - in - List.filter_map annotated_nullability ~f:get_provisional_annotation - - -(* Given a list of provisional annotations, construct corresponding nodes for the graph *) -let graph_nodes_of_provisional_annotations annotations = - let get_id index annotation = - (* Symbolic prefix corresponding to annotation point type for convenience*) - let prefix = - match annotation with - | ProvisionalAnnotation.Field _ -> - "f" - | ProvisionalAnnotation.Method _ -> - "m" - | ProvisionalAnnotation.Param _ -> - "p" - in - Format.sprintf "%s%d" prefix index - in - let annotation_points = - List.mapi annotations ~f:(fun index annotation -> - ( annotation - , {id= get_id index annotation; annotation; num_violations= 0; dependent_points= []} ) ) - in - AnnotationMap.of_seq (Stdlib.List.to_seq annotation_points) - - -let build_graph_nodes tenv class_struct class_name = - let class_typ = Typ.mk_struct class_name in - let field_annotations = - class_struct.Struct.fields - (* Sorting to get the stable list *) - |> List.sort ~compare:Struct.compare_field - |> List.filter_map ~f:(fun (field_name, _, _) -> - let AnnotatedField.{annotated_type= {nullability}} = - Option.value_exn - (AnnotatedField.get tenv field_name ~class_typ ~class_under_analysis:class_name) - in - get_provisional_annotation nullability ) - in - let method_signatures = - (* Sorting to get the stable list *) - class_struct.Struct.methods - |> List.sort ~compare:Procname.compare - |> List.map ~f:(fun proc_name -> - let proc_attributes = Option.value_exn (PatternMatch.lookup_attributes tenv proc_name) in - AnnotatedSignature.get_for_class_under_analysis tenv proc_attributes ) - in - let method_and_param_annotations = - List.map method_signatures ~f:annotations_of_signature |> List.concat - in - graph_nodes_of_provisional_annotations (field_annotations @ method_and_param_annotations) - - -let get_offending_annotations = function - | ProvisionalViolation.Assignment violation -> - AssignmentRule.ProvisionalViolation.offending_annotations violation - | ProvisionalViolation.Dereference violation -> - DereferenceRule.ProvisionalViolation.offending_annotations violation - - -let get_fix_annotation = function - | ProvisionalViolation.Assignment violation -> - AssignmentRule.ProvisionalViolation.fix_annotation violation - | ProvisionalViolation.Dereference _ -> - None - - -let update_by_violation nodes provisional_violation = - let fix_annotation = get_fix_annotation provisional_violation in - let offending_annotations = get_offending_annotations provisional_violation in - List.iter offending_annotations ~f:(fun annotation -> - match AnnotationMap.find_opt annotation nodes with - | Some annotation_point -> ( - match fix_annotation with - | Some fix_annotation -> - (* If that provisional annotation becomes real [@Nullable], that would raise an issue fixable by the other annotation. - Add the new edge in the graph. - *) - if AnnotationMap.mem fix_annotation nodes then - annotation_point.dependent_points <- - List.dedup_and_sort - (fix_annotation :: annotation_point.dependent_points) - ~compare:ProvisionalAnnotation.compare - else ( - (* This is an edge case sitation. There is a corresponding ProvisionalAnnotation for this Java class, but - it is not in the list of the known provisional annotations for the class. - This can happen for e.g. syntethic fields / autogenerated code or other not yet supporte cases. - Conservatively record this situation as an unfixable violation. - *) - Logging.debug Analysis Medium - "Did not find a node for %a in the graph: recording a violation instead" - ProvisionalAnnotation.pp fix_annotation ; - annotation_point.num_violations <- annotation_point.num_violations + 1 ) - | None -> - (* If that provisional annotation becomes real [@Nullable], that would lead to a violation - * (not related to other provisional annotations). - *) - annotation_point.num_violations <- annotation_point.num_violations + 1 ) - | None -> - (* This is not a real annotation point. This can happen for synthetic fields etc. - *) - Logging.debug Analysis Medium - "Did not find a node for %a in the graph: not processing this annotation" - ProvisionalAnnotation.pp annotation ) - - -(* Given a list of provisional violations, connect corresponding nodes in the annotation graph *) -let update_nodes_and_set_edges nodes provisional_violations = - List.iter provisional_violations ~f:(update_by_violation nodes) - - -let get_kind_json = function - | ProvisionalAnnotation.Field _ -> - `Field - | ProvisionalAnnotation.Method _ -> - `Method - | ProvisionalAnnotation.Param _ -> - `Param - - -let get_field_name_json = function - | ProvisionalAnnotation.Field {field_name} -> - Some (Fieldname.get_field_name field_name) - | ProvisionalAnnotation.Method _ | ProvisionalAnnotation.Param _ -> - None - - -let get_param_num_json = function - | ProvisionalAnnotation.Param {num} -> - Some num - | ProvisionalAnnotation.Method _ | ProvisionalAnnotation.Field _ -> - None - - -let java_type_to_string java_type = Pp.string_of_pp (Typ.pp_java ~verbose:true) java_type - -let get_access_level tenv proc_name = - let proc_attributes = PatternMatch.lookup_attributes_exn tenv (Procname.Java proc_name) in - match ProcAttributes.get_access proc_attributes with - | Default -> - `Default - | Public -> - `Public - | Private -> - `Private - | Protected -> - `Protected - - -let get_method_info_json tenv annotation = - let open IOption.Let_syntax in - let+ proc_name = - match annotation with - | ProvisionalAnnotation.Param {method_info} -> - Some method_info - | ProvisionalAnnotation.Method proc_name -> - Some proc_name - | ProvisionalAnnotation.Field _ -> - None - in - let method_name = Procname.Java.get_method proc_name in - let params = Procname.Java.get_parameters proc_name |> List.map ~f:java_type_to_string in - let access_level = get_access_level tenv proc_name in - Jsonbug_t.{method_name; params; access_level} - - -let to_json_annotation_point tenv graph {id; annotation; num_violations; dependent_points} : - Jsonbug_t.annotation_point = - { id - ; kind= get_kind_json annotation - ; method_info= get_method_info_json tenv annotation - ; field_name= get_field_name_json annotation - ; param_num= get_param_num_json annotation - ; num_violations - ; dependent_point_ids= - List.map dependent_points ~f:(fun dependent_point -> - let node = AnnotationMap.find dependent_point graph in - node.id ) } - - -(* Convert the graph to the JSON representation *) -let to_json tenv graph = - AnnotationMap.bindings graph |> List.map ~f:snd - (* Sort by ids to get any stable order *) - |> List.sort ~compare:(fun {id= id1} {id= id2} -> String.compare id1 id2) - |> List.map ~f:(to_json_annotation_point tenv graph) - - -let build_graph tenv class_struct class_name provisional_violations = - let graph = build_graph_nodes tenv class_struct class_name in - update_nodes_and_set_edges graph provisional_violations ; - to_json tenv graph diff --git a/infer/src/nullsafe/AnnotationGraph.mli b/infer/src/nullsafe/AnnotationGraph.mli deleted file mode 100644 index 3bba6fe709d..00000000000 --- a/infer/src/nullsafe/AnnotationGraph.mli +++ /dev/null @@ -1,18 +0,0 @@ -(* - * 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. - *) -open! IStd - -(** Annotation graph for a Java class comprises of: * Nodes (annotation points): potential places in - the class declaration to be annotated as [@Nullable] * For each annotation point the graph - stores what will happen if this annotation point becomes real [@Nullable] (new violations that - will arise) * Edges: if annotating a point A will require annotating another point B as - [@Nullable], A -> B are connected in the graph. *) - -val build_graph : - Tenv.t -> Struct.t -> Typ.name -> ProvisionalViolation.t list -> Jsonbug_t.annotation_point list -(** Given a Java class and the list of all provisional violations found in that class, build the - annotation graph *) diff --git a/infer/src/nullsafe/AssignmentRule.ml b/infer/src/nullsafe/AssignmentRule.ml deleted file mode 100644 index 559fb2d2ac1..00000000000 --- a/infer/src/nullsafe/AssignmentRule.ml +++ /dev/null @@ -1,300 +0,0 @@ -(* - * 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. - *) -open! IStd - -type violation = {lhs: AnnotatedNullability.t; rhs: InferredNullability.t} -[@@deriving compare, equal] - -module ProvisionalViolation = struct - type t = - { fix_annotation: ProvisionalAnnotation.t option - ; offending_annotations: ProvisionalAnnotation.t list } - - let offending_annotations {offending_annotations} = offending_annotations - - let fix_annotation {fix_annotation} = fix_annotation - - let from {lhs; rhs} = - let offending_annotations = InferredNullability.get_provisional_annotations rhs in - if List.is_empty offending_annotations then None - else - let fix_annotation = - match lhs with - | AnnotatedNullability.ProvisionallyNullable annotation -> - Some annotation - | _ -> - None - in - Some {offending_annotations; fix_annotation} -end - -module ReportableViolation = struct - type t = {nullsafe_mode: NullsafeMode.t; violation: violation} - - type assignment_type = - | PassingParamToFunction of function_info - | AssigningToField of Fieldname.t - | ReturningFromFunction of Procname.Java.t - [@@deriving compare, equal] - - and function_info = - { param_signature: AnnotatedSignature.param_signature - ; actual_param_expression: string - ; param_index: int - ; annotated_signature: AnnotatedSignature.t - ; procname: Procname.Java.t } - - let from nullsafe_mode ({lhs; rhs} as violation) = - let falls_under_optimistic_third_party = - Config.nullsafe_optimistic_third_party_in_default_mode - && NullsafeMode.equal nullsafe_mode Default - (* Treat third party params as if they were [@Nullable] *) - && Nullability.equal (AnnotatedNullability.get_nullability lhs) ThirdPartyNonnull - in - let is_non_reportable = - falls_under_optimistic_third_party - || (* In certain modes, we trust rhs to be non-nullable and don't report violation *) - Nullability.is_considered_nonnull ~nullsafe_mode (InferredNullability.get_nullability rhs) - in - if is_non_reportable then None else Some {nullsafe_mode; violation} - - - let get_origin_opt assignment_type origin = - let should_show_origin = - match assignment_type with - | PassingParamToFunction {actual_param_expression} -> - not - (ErrorRenderingUtils.is_object_nullability_self_explanatory - ~object_expression:actual_param_expression origin ) - | AssigningToField _ | ReturningFromFunction _ -> - true - in - if should_show_origin then Some origin else None - - - let pp_param_name fmt mangled = - let name = Mangled.to_string mangled in - if String.is_substring name ~substring:"arg" then - (* The real name was not fetched for whatever reason, this is an autogenerated name *) - Format.fprintf fmt "" - else Format.fprintf fmt "(%a)" MarkupFormatter.pp_monospaced name - - - (* A slight adapter over [NullsafeIssue.make]: the same signature but additionally accepts an alternative method *) - let make_issue_with_recommendation ~description ~rhs_origin ~issue_type ~loc ~severity ~field_name - = - (* If there is an alternative method to propose, tell about it at the end of the description *) - let alternative_method = - ErrorRenderingUtils.find_alternative_nonnull_method_description rhs_origin - in - let alternative_recommendation = - Option.value_map alternative_method - ~f: - (Format.asprintf " If you don't expect null, use %a instead." - MarkupFormatter.pp_monospaced ) - ~default:"" - in - let full_description = Format.sprintf "%s%s" description alternative_recommendation in - let nullable_methods = - match rhs_origin with TypeOrigin.MethodCall origin -> [origin] | _ -> [] - in - NullsafeIssue.make ~description:full_description ~issue_type ~loc ~severity ~field_name - |> NullsafeIssue.with_nullable_methods nullable_methods - - - let mk_issue_for_bad_param_passed - {annotated_signature; param_signature; actual_param_expression; param_index; procname} - ~param_nullability_kind ~nullability_evidence - ~(make_issue_factory : description:string -> issue_type:IssueType.t -> NullsafeIssue.t) = - let nullability_evidence_as_suffix = - Option.value_map nullability_evidence ~f:(fun evidence -> ": " ^ evidence) ~default:"" - in - let annotated_param_nullability = param_signature.param_annotated_type.nullability in - let module MF = MarkupFormatter in - let argument_description = - if String.equal actual_param_expression "null" then "is `null`" - else - let nullability_descr = - match param_nullability_kind with - | ErrorRenderingUtils.UserFriendlyNullable.Null -> - "`null`" - | ErrorRenderingUtils.UserFriendlyNullable.Nullable -> - "nullable" - in - Format.asprintf "%a is %s" MF.pp_monospaced actual_param_expression nullability_descr - in - let issue_type = IssueType.eradicate_parameter_not_nullable in - let issue = - match AnnotatedNullability.get_nullability annotated_param_nullability with - | Nullability.Null -> - Logging.die Logging.InternalError "Unexpected param nullability: Null" - | Nullability.Nullable -> - Logging.die Logging.InternalError "Passing anything to a nullable param should be allowed" - | Nullability.ThirdPartyNonnull -> - (* This is a special case. While for FB codebase we can assume "not annotated hence not nullable" rule for all_allow_listed signatures, - This is not the case for third party functions, which can have different conventions, - So we can not just say "param is declared as non-nullable" like we say for FB-internal or modelled case: - param can be nullable according to API but it was just not annotated. - So we phrase it differently to remain truthful, but as specific as possible. - *) - let suggested_third_party_sig_file = - ThirdPartyAnnotationInfo.lookup_related_sig_file_for_proc - (ThirdPartyAnnotationGlobalRepo.get_repo ()) - procname - in - let where_to_add_signature = - Option.value_map suggested_third_party_sig_file - ~f:(fun sig_file_name -> - ThirdPartyAnnotationGlobalRepo.get_user_friendly_third_party_sig_file_name - ~filename:sig_file_name ) - (* this can happen when third party is registered in a deprecated way (not in third party repository) *) - ~default:"the third party signature storage" - in - let procname_str = Procname.Java.to_simplified_string ~withclass:true procname in - let description = - Format.asprintf - "Third-party %a is missing a signature that would allow passing a nullable to param \ - #%d%a. Actual argument %s%s. Consider adding the correct signature of %a to %s." - MF.pp_monospaced procname_str - (param_index + 1) (* human-readable param number is indexed from 1 *) - pp_param_name param_signature.mangled argument_description - nullability_evidence_as_suffix MF.pp_monospaced procname_str where_to_add_signature - in - make_issue_factory ~description ~issue_type - |> NullsafeIssue.with_third_party_dependent_methods [(procname, annotated_signature)] - (* Equivalent to non-null from user point of view *) - | Nullability.ProvisionallyNullable - | Nullability.LocallyCheckedNonnull - | Nullability.LocallyTrustedNonnull - | Nullability.UncheckedNonnull - | Nullability.StrictNonnull -> - let nonnull_evidence = - match annotated_signature.kind with - | FirstParty | ThirdParty Unregistered -> - "" - | ThirdParty ModelledInternally -> - " (according to nullsafe internal models)" - | ThirdParty (InThirdPartyRepo {filename; line_number}) -> - Format.sprintf " (see %s at line %d)" - (ThirdPartyAnnotationGlobalRepo.get_user_friendly_third_party_sig_file_name - ~filename ) - line_number - in - let description = - Format.asprintf "%a: parameter #%d%a is declared non-nullable%s but the argument %s%s." - MF.pp_monospaced - (Procname.Java.to_simplified_string ~withclass:true procname) - (param_index + 1) (* human-readable param number is indexed from 1 *) - pp_param_name param_signature.mangled nonnull_evidence argument_description - nullability_evidence_as_suffix - in - make_issue_factory ~description ~issue_type - in - issue |> NullsafeIssue.with_parameter_not_nullable_info ~param_index ~proc_name:procname - - - let field_name_of_assignment_type = function - | AssigningToField field_name -> - Some field_name - | PassingParamToFunction _ | ReturningFromFunction _ -> - None - - - let mk_nullsafe_issue_for_explicitly_nullable_values ~assignment_type ~rhs_origin ~nullsafe_mode - ~explicit_rhs_nullable_kind ~assignment_location = - let nullability_evidence = - get_origin_opt assignment_type rhs_origin - |> Option.bind ~f:(fun origin -> TypeOrigin.get_description origin) - in - let nullability_evidence_as_suffix = - Option.value_map nullability_evidence ~f:(fun evidence -> ": " ^ evidence) ~default:"" - in - (* A "factory" - a high-order function for creating the nullsafe issue: fill in what is already known at this point. - The rest to be filled by the client *) - let make_issue_factory = - make_issue_with_recommendation ~rhs_origin - ~severity:(NullsafeMode.severity nullsafe_mode) - ~loc:assignment_location - ~field_name:(field_name_of_assignment_type assignment_type) - in - match assignment_type with - | PassingParamToFunction function_info -> - mk_issue_for_bad_param_passed function_info ~nullability_evidence - ~param_nullability_kind:explicit_rhs_nullable_kind ~make_issue_factory - | AssigningToField field_name -> - let rhs_description = - match explicit_rhs_nullable_kind with - | ErrorRenderingUtils.UserFriendlyNullable.Null -> - "`null`" - | ErrorRenderingUtils.UserFriendlyNullable.Nullable -> - "a nullable" - in - let description = - Format.asprintf "%a is declared non-nullable but is assigned %s%s." - MarkupFormatter.pp_monospaced - (Fieldname.get_field_name field_name) - rhs_description nullability_evidence_as_suffix - in - make_issue_factory ~description ~issue_type:IssueType.eradicate_field_not_nullable - | ReturningFromFunction function_proc_name -> - let return_description = - match explicit_rhs_nullable_kind with - | ErrorRenderingUtils.UserFriendlyNullable.Null -> - (* Return `null` in all_allow_listed branches *) - "`null`" - | ErrorRenderingUtils.UserFriendlyNullable.Nullable -> - "a nullable value" - in - let description = - Format.asprintf "%a: return type is declared non-nullable but the method returns %s%s." - MarkupFormatter.pp_monospaced - (Procname.Java.to_simplified_string ~withclass:false function_proc_name) - return_description nullability_evidence_as_suffix - in - make_issue_factory ~description ~issue_type:IssueType.eradicate_return_not_nullable - - - let make_nullsafe_issue ~assignment_location assignment_type {nullsafe_mode; violation= {rhs}} = - let rhs_origin = InferredNullability.get_simple_origin rhs in - let user_friendly_nullable = - ErrorRenderingUtils.UserFriendlyNullable.from_nullability - (InferredNullability.get_nullability rhs) - |> IOption.if_none_eval ~f:(fun () -> - Logging.die InternalError - "get_description:: Assignment violation should not be possible for non-nullable \ - values on right hand side" ) - in - match user_friendly_nullable with - | ErrorRenderingUtils.UserFriendlyNullable.UntrustedNonnull untrusted_kind -> - (* Attempt to assigning a value which is not explictly declared as nullable, - but still can not be trusted in this particular mode. - *) - ErrorRenderingUtils.mk_nullsafe_issue_for_untrusted_values ~nullsafe_mode ~untrusted_kind - ~bad_usage_location:assignment_location rhs_origin - | ErrorRenderingUtils.UserFriendlyNullable.ExplainablyNullable explicit_kind -> - (* Attempt to assigning a value that can be explained to the user as nullable. *) - mk_nullsafe_issue_for_explicitly_nullable_values ~assignment_type ~rhs_origin ~nullsafe_mode - ~explicit_rhs_nullable_kind:explicit_kind ~assignment_location -end - -let check ~lhs ~rhs = - match (lhs, InferredNullability.get_nullability rhs) with - | AnnotatedNullability.ProvisionallyNullable _, Nullability.ProvisionallyNullable -> - (* This is a special case. Assignment of something that comes from provisionally nullable annotation to something that - is annotated as provisionally nullable is a (provisional) violation. - (With an exception when it is an assignment to the same annotation e.g. in recursion calls; - but such exceptions are non-essential for the purposes of calculation of the annotation graph. - ) - *) - Error {lhs; rhs} - | _ -> - let is_subtype = - Nullability.is_subtype - ~supertype:(AnnotatedNullability.get_nullability lhs) - ~subtype:(InferredNullability.get_nullability rhs) - in - Result.ok_if_true is_subtype ~error:{lhs; rhs} diff --git a/infer/src/nullsafe/AssignmentRule.mli b/infer/src/nullsafe/AssignmentRule.mli deleted file mode 100644 index b0a0a90c3ce..00000000000 --- a/infer/src/nullsafe/AssignmentRule.mli +++ /dev/null @@ -1,60 +0,0 @@ -(* - * 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. - *) -open! IStd - -(** Assignment rule should be checked when a value is assigned to a location. Assignment can be - explicit (lhs = rhs) or implicit (e.g. returning from a function). This rule checks if null can - be passed to a place that does not expect null. *) - -type violation [@@deriving compare, equal] - -val check : lhs:AnnotatedNullability.t -> rhs:InferredNullability.t -> (unit, violation) result -(** If `null` can leak from a "less strict" type to "more strict" type, this is an Assignment Rule - violation. *) - -(** Violation that will occur if the provisional annotation becomes real [@Nullable] *) -module ProvisionalViolation : sig - type t - - val offending_annotations : t -> ProvisionalAnnotation.t list - (** Non-empty list of corresponding provisional annotations (adding any of those will lead to an - issue) *) - - val fix_annotation : t -> ProvisionalAnnotation.t option - (** If there is a place such as adding [@Nullable] will fix the issue, this is the one. *) - - val from : violation -> t option - (** If the violation is provisional (so is not real but will become real when the annotation is - added), create it. *) -end - -(** Violation that needs to be reported to the user. *) -module ReportableViolation : sig - type t - - val from : NullsafeMode.t -> violation -> t option - (** Depending on the mode, violation might or might not be important enough to be reported to the - user. If it should NOT be reported for that mode, this function will return None. *) - - type assignment_type = - | PassingParamToFunction of function_info - | AssigningToField of Fieldname.t - | ReturningFromFunction of Procname.Java.t - [@@deriving compare, equal] - - and function_info = - { param_signature: AnnotatedSignature.param_signature - ; actual_param_expression: string - ; param_index: int - ; annotated_signature: AnnotatedSignature.t - ; procname: Procname.Java.t } - - val make_nullsafe_issue : - assignment_location:Location.t -> assignment_type -> t -> NullsafeIssue.t - (** Given context around violation, return error message together with the info where to put this - message *) -end diff --git a/infer/src/nullsafe/ClassLevelAnalysis.ml b/infer/src/nullsafe/ClassLevelAnalysis.ml deleted file mode 100644 index ea1f59fd7b2..00000000000 --- a/infer/src/nullsafe/ClassLevelAnalysis.ml +++ /dev/null @@ -1,338 +0,0 @@ -(* - * 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. - *) - -open! IStd -module L = Logging - -let log_issue ?proc_name ~issue_log ~loc ~severity ~nullsafe_extra issue_type error_message = - let extras = - Jsonbug_t. - { nullsafe_extra= Some nullsafe_extra - ; cost_polynomial= None - ; cost_degree= None - ; copy_type= None - ; config_usage_extra= None - ; taint_extra= None } - in - let proc_name = Option.value proc_name ~default:Procname.Linters_dummy_method in - let trace = [Errlog.make_trace_element 0 loc error_message []] in - Reporting.log_issue_external proc_name ~severity_override:severity ~issue_log ~loc ~extras - ~ltr:trace Eradicate issue_type error_message - - -(* If the issue is related to violation of nullability type system rules *) -let is_typing_rules_violation = function - | TypeErr.Condition_redundant _ | TypeErr.Over_annotation _ -> - (* Those are not nullability type system violations per se *) - false - | TypeErr.Inconsistent_subclass _ - | TypeErr.Nullable_dereference _ - | TypeErr.Field_not_initialized _ - | TypeErr.Bad_assignment _ -> - true - - -(* Yes, if the issue is a) "type violation" issue b) reportable to the user in a given mode *) -let is_reportable_typing_rules_violation ~nullsafe_mode issue = - is_typing_rules_violation issue && TypeErr.is_reportable ~nullsafe_mode issue - - -let get_reportable_typing_rules_violations modes_with_issues = - List.filter modes_with_issues ~f:(fun (nullsafe_mode, issue) -> - is_reportable_typing_rules_violation ~nullsafe_mode issue ) - |> List.map ~f:(fun (_, a) -> a) - - -let get_reportable_typing_rules_violations_for_mode ~nullsafe_mode issues = - List.map issues ~f:(fun issue -> (nullsafe_mode, issue)) |> get_reportable_typing_rules_violations - - -type meta_issue = - { issue_type: IssueType.t - ; description: string - ; severity: IssueType.severity - ; meta_issue_info: Jsonbug_t.nullsafe_meta_issue_info } - -let mode_to_json mode = - let open NullsafeMode in - match mode with - | Default -> - `Default - | Local Trust.All -> - `LocalTrustAll - | Local (Trust.Only trust_list) when Trust.is_trust_none trust_list -> - `LocalTrustNone - | Local (Trust.Only _) -> - `LocalTrustSome - | Strict -> - `Strict - - -let is_clean_in_mode nullsafe_mode all_issues = - get_reportable_typing_rules_violations_for_mode ~nullsafe_mode all_issues |> List.is_empty - - -(* Return the maximum mode where we still have zero issues, or None if no such mode exists. -*) -let calc_strictest_mode_with_zero_issues all_issues = - let modes_to_try = NullsafeMode.[Strict; Local Trust.none; Local Trust.All; Default] in - List.find modes_to_try ~f:(fun mode -> is_clean_in_mode mode all_issues) - - -(* The maximum strict mode this mode can be promoted to with still having zero issues, if exists *) -let calc_mode_to_promote_to curr_mode all_issues = - let open IOption.Let_syntax in - let* strictest_mode = calc_strictest_mode_with_zero_issues all_issues in - if NullsafeMode.is_stricter_than ~stricter:strictest_mode ~weaker:curr_mode then - Some strictest_mode - else None - - -(* analyze all issues for the current class (including all nested and anonymous classes recursively) - and classify them into one meta-issue. -*) -let make_meta_issue modes_and_issues top_level_class_mode top_level_class_name = - let currently_reportable_issues = get_reportable_typing_rules_violations modes_and_issues in - List.iter currently_reportable_issues ~f:(fun issue -> - L.debug Analysis Medium "Issue: %a@\n" TypeErr.pp_err_instance issue ) ; - let currently_reportable_issue_count = List.length currently_reportable_issues in - let all_issues = List.map modes_and_issues ~f:(fun (_, a) -> a) in - let mode_to_promote_to = - if currently_reportable_issue_count > 0 then - (* This is not an optimization - consider a class with a nested class that is in the stricter mode than top level mode, - and has issues in this mode, but not in the top level mode. - This class is already broken now, so mode to promote to should be None. - But [calc_mode_to_promote_to] can return some mode for this case, which would be wrong. *) - None - else calc_mode_to_promote_to top_level_class_mode all_issues - in - let meta_issue_info = - Jsonbug_t. - { num_issues= currently_reportable_issue_count - ; num_fixmes= 0 - ; curr_nullsafe_mode= mode_to_json top_level_class_mode - ; can_be_promoted_to= Option.map mode_to_promote_to ~f:mode_to_json } - in - let issue_type, description, severity = - if NullsafeMode.equal top_level_class_mode Default then - match mode_to_promote_to with - | Some _ -> - let message = - Format.sprintf - "Congrats! `%s` is free of nullability issues. Mark it \ - `@Nullsafe(Nullsafe.Mode.LOCAL)` to prevent regressions." - (JavaClassName.classname top_level_class_name) - in - (IssueType.eradicate_meta_class_can_be_nullsafe, message, IssueType.Advice) - | None -> - (* This class can not be made @Nullsafe without extra work *) - let issue_count_to_make_nullsafe = - get_reportable_typing_rules_violations_for_mode - ~nullsafe_mode:(NullsafeMode.Local NullsafeMode.Trust.All) all_issues - |> List.length - in - ( IssueType.eradicate_meta_class_needs_improvement - , Format.asprintf "`%s` needs %d issues to be fixed in order to be marked @Nullsafe." - (JavaClassName.classname top_level_class_name) - issue_count_to_make_nullsafe - , IssueType.Info ) - else if currently_reportable_issue_count > 0 then - (* This class is @Nullsafe, but broken. This should not happen often if there is enforcement for - @Nullsafe mode error in the target codebase. *) - ( IssueType.eradicate_meta_class_needs_improvement - , Format.asprintf - "@Nullsafe classes should have exactly zero nullability issues. `%s` has %d." - (JavaClassName.classname top_level_class_name) - currently_reportable_issue_count - , IssueType.Info ) - else - ( IssueType.eradicate_meta_class_is_nullsafe - , Format.asprintf "Class %a is free of nullability issues." JavaClassName.pp - top_level_class_name - , IssueType.Info ) - in - {issue_type; description; severity; meta_issue_info} - - -let get_class_loc source_file Struct.{class_info} = - let default = - {Location.file= source_file; line= 1; col= 0; macro_file_opt= None; macro_line= -1} - in - match class_info with - | JavaClassInfo {loc} -> - (* In rare cases location is not present, fall back to the first line of the file *) - Option.value loc ~default - | _ -> - L.internal_error "class_info should be present for Java classes and be of JavaClassInfo type" ; - default - - -(* Meta issues are those related to null-safety of the class in general, not concrete nullability violations *) -let report_meta_issue_for_top_level_class tenv source_file class_name class_struct class_info - issue_log = - if Option.is_some (JavaClassName.get_outer_class_name class_name) then - (* We record meta-issues only for top-level classes *) issue_log - else - let current_mode = NullsafeMode.of_class tenv class_name in - (* For purposes of aggregation, we consider all nested summaries as belonging to this class *) - let class_names_and_summaries = - AggregatedSummaries.ClassInfo.get_recursive_summaries class_info - in - let class_loc = get_class_loc source_file class_struct in - let all_issues = - List.map class_names_and_summaries ~f:(fun (class_name, NullsafeSummary.{issues}) -> - let mode_for_class_name = NullsafeMode.of_class tenv class_name in - List.map issues ~f:(fun issue -> (mode_for_class_name, issue)) ) - |> List.fold ~init:[] ~f:( @ ) - in - let {issue_type; description; severity; meta_issue_info} = - make_meta_issue all_issues current_mode class_name - in - let package = JavaClassName.package class_name in - let class_name = JavaClassName.classname class_name in - let nullsafe_extra = - Jsonbug_t. - { class_name - ; package - ; method_info= None - ; inconsistent_param_index= None - ; parameter_not_nullable_info= None - ; meta_issue_info= Some meta_issue_info - ; unvetted_3rd_party= None - ; nullable_methods= None - ; field= None - ; annotation_graph= None - ; redundant_fixme_info= None } - in - log_issue ~issue_log ~loc:class_loc ~severity ~nullsafe_extra issue_type description - - -(* Optimization - if issues are disabled, don't bother analyzing them *) -let should_analyze_meta_issues () = - (not Config.filtering) || IssueType.eradicate_meta_class_can_be_nullsafe.enabled - || IssueType.eradicate_meta_class_needs_improvement.enabled - || IssueType.eradicate_meta_class_is_nullsafe.enabled - - -let analyze_meta_issue_for_top_level_class tenv source_file class_name class_struct class_info - issue_log = - if should_analyze_meta_issues () then - report_meta_issue_for_top_level_class tenv source_file class_name class_struct class_info - issue_log - else issue_log - - -let analyze_nullsafe_annotations tenv source_file class_name class_struct issue_log = - let loc = get_class_loc source_file class_struct in - let nullsafe_extra = - let package = JavaClassName.package class_name in - let class_name = JavaClassName.classname class_name in - Jsonbug_t. - { class_name - ; package - ; method_info= None - ; inconsistent_param_index= None - ; parameter_not_nullable_info= None - ; meta_issue_info= None - ; unvetted_3rd_party= None - ; nullable_methods= None - ; field= None - ; annotation_graph= None - ; redundant_fixme_info= None } - in - match NullsafeMode.check_problematic_class_annotation tenv class_name with - | Ok () -> - issue_log - | Error NullsafeMode.RedundantNestedClassAnnotation -> - let description = - Format.sprintf - "`%s`: the same @Nullsafe mode is already specified in the outer class, so this \ - annotation can be removed." - (JavaClassName.classname class_name) - in - log_issue ~issue_log ~loc ~nullsafe_extra ~severity:Advice - IssueType.eradicate_redundant_nested_class_annotation description - | Error (NullsafeMode.NestedModeIsWeaker (ExtraTrustClass wrongly_trusted_classes)) -> - (* The list can not be empty *) - let example_of_wrongly_trusted_class = List.nth_exn wrongly_trusted_classes 0 in - let description = - Format.sprintf - "Nested classes cannot add classes to trust list if they are not in the outer class \ - trust list. Remove `%s` from trust list." - (JavaClassName.classname example_of_wrongly_trusted_class) - in - log_issue ~issue_log ~loc ~nullsafe_extra ~severity:Warning - IssueType.eradicate_bad_nested_class_annotation description - | Error (NullsafeMode.NestedModeIsWeaker Other) -> - let description = - Format.sprintf - "`%s`: nested classes are disallowed to weaken @Nullsafe mode specified in the outer \ - class. This annotation will be ignored." - (JavaClassName.classname class_name) - in - log_issue ~issue_log ~loc ~nullsafe_extra ~severity:Warning - IssueType.eradicate_bad_nested_class_annotation description - - -let report_annotation_graph source_file class_name class_struct annotation_graph issue_log = - let class_loc = get_class_loc source_file class_struct in - let package = JavaClassName.package class_name in - let class_name = JavaClassName.classname class_name in - let nullsafe_extra = - Jsonbug_t. - { class_name - ; package - ; method_info= None - ; inconsistent_param_index= None - ; parameter_not_nullable_info= None - ; meta_issue_info= None - ; unvetted_3rd_party= None - ; nullable_methods= None - ; field= None - ; annotation_graph= Some annotation_graph - ; redundant_fixme_info= None } - in - log_issue ~issue_log ~loc:class_loc ~severity:IssueType.Info ~nullsafe_extra - IssueType.eradicate_annotation_graph "" - - -let build_and_report_annotation_graph tenv source_file class_name class_struct class_info issue_log - = - if not Config.nullsafe_annotation_graph then issue_log - else - let class_typ_name = Typ.JavaClass class_name in - let provisional_violations = - AggregatedSummaries.ClassInfo.get_summaries class_info - |> List.map ~f:(fun NullsafeSummary.{issues} -> issues) - |> List.concat - |> List.filter_map ~f:ProvisionalViolation.of_issue - in - let annotation_graph = - AnnotationGraph.build_graph tenv class_struct class_typ_name provisional_violations - in - report_annotation_graph source_file class_name class_struct annotation_graph issue_log - - -let analyze_class_impl tenv source_file class_name class_struct class_info issue_log = - issue_log - |> analyze_meta_issue_for_top_level_class tenv source_file class_name class_struct class_info - |> analyze_nullsafe_annotations tenv source_file class_name class_struct - |> build_and_report_annotation_graph tenv source_file class_name class_struct class_info - - -let analyze_class tenv source_file class_info issue_log = - if SourceFile.has_extension ~ext:Config.kotlin_source_extension source_file then issue_log - else - let class_name = AggregatedSummaries.ClassInfo.get_class_name class_info in - match Tenv.lookup tenv (Typ.JavaClass class_name) with - | Some class_struct -> - analyze_class_impl tenv source_file class_name class_struct class_info issue_log - | None -> - L.debug Analysis Medium - "%a: could not load class info in environment: skipping class analysis@\n" - JavaClassName.pp class_name ; - issue_log diff --git a/infer/src/nullsafe/ClassLevelAnalysis.mli b/infer/src/nullsafe/ClassLevelAnalysis.mli deleted file mode 100644 index f6d9543ae62..00000000000 --- a/infer/src/nullsafe/ClassLevelAnalysis.mli +++ /dev/null @@ -1,22 +0,0 @@ -(* - * 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. - *) - -open! IStd - -(** This stage is called for each Java class after all corresponding proc summaries are calculated. *) - -val analyze_class : - Tenv.t -> SourceFile.t -> AggregatedSummaries.ClassInfo.t -> IssueLog.t -> IssueLog.t -(** Given aggregated summary for a class, analyze it, and return updated issue log, if necessary. - This function will be called for each non-trivial{^ 1} anonymous class in the file, including - nested classes. Order of calls is not specified. - - {^ 1}The class is non-trivial if it has at least one procedure, or contains at least one nested - non-trivial class. - - (Note that [IssueLog.t] is a mutable type so it can be actually mutated by this function: - returning [IssueLog.t] is done for convenient chaining.) *) diff --git a/infer/src/nullsafe/DereferenceRule.ml b/infer/src/nullsafe/DereferenceRule.ml deleted file mode 100644 index 17536430760..00000000000 --- a/infer/src/nullsafe/DereferenceRule.ml +++ /dev/null @@ -1,150 +0,0 @@ -(* - * 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. - *) -open! IStd - -type violation = {nullability: InferredNullability.t} [@@deriving compare, equal] - -module ProvisionalViolation = struct - type t = {offending_annotations: ProvisionalAnnotation.t list} - - let offending_annotations {offending_annotations} = offending_annotations - - let from {nullability} = - let offending_annotations = InferredNullability.get_provisional_annotations nullability in - if List.is_empty offending_annotations then None else Some {offending_annotations} -end - -module ReportableViolation = struct - type t = {nullsafe_mode: NullsafeMode.t; violation: violation} - - type dereference_type = - | MethodCall of Procname.Java.t - | AccessToField of Fieldname.t - | AccessByIndex of {index_desc: string} - | ArrayLengthAccess - [@@deriving compare, equal] - - let from nullsafe_mode ({nullability} as violation) = - if - Nullability.is_considered_nonnull ~nullsafe_mode - (InferredNullability.get_nullability nullability) - then None - else Some {nullsafe_mode; violation} - - - let get_origin_opt ~nullable_object_descr origin = - let should_show_origin = - match nullable_object_descr with - | Some object_expression -> - not (ErrorRenderingUtils.is_object_nullability_self_explanatory ~object_expression origin) - | None -> - true - in - if should_show_origin then Some origin else None - - - let mk_nullsafe_issue_for_explicitly_nullable_values ~explicit_kind ~dereference_type - dereference_location ~nullsafe_mode ~nullable_object_descr ~nullable_object_origin = - let module MF = MarkupFormatter in - let what_is_dereferred_str = - match dereference_type with - | MethodCall _ | AccessToField _ -> ( - match nullable_object_descr with - | None -> - "Object" - (* Just describe an object itself *) - | Some descr -> - MF.monospaced_to_string descr ) - | ArrayLengthAccess | AccessByIndex _ -> ( - (* In Java, those operations can be applied only to arrays *) - match nullable_object_descr with - | None -> - "Array" - | Some descr -> - Format.sprintf "Array %s" (MF.monospaced_to_string descr) ) - in - let action_descr = - match dereference_type with - | MethodCall method_name -> - Format.sprintf "calling %s" - (MF.monospaced_to_string (Procname.Java.to_simplified_string method_name)) - | AccessToField field_name -> - Format.sprintf "accessing field %s" - (MF.monospaced_to_string (Fieldname.to_simplified_string field_name)) - | AccessByIndex {index_desc} -> - Format.sprintf "accessing at index %s" (MF.monospaced_to_string index_desc) - | ArrayLengthAccess -> - "accessing its length" - in - let origin_descr = - get_origin_opt ~nullable_object_descr nullable_object_origin - |> Option.bind ~f:(fun origin -> TypeOrigin.get_description origin) - |> Option.value_map ~f:(fun origin -> ": " ^ origin) ~default:"" - in - let alternative_method_description = - ErrorRenderingUtils.find_alternative_nonnull_method_description nullable_object_origin - in - let alternative_recommendation = - Option.value_map alternative_method_description - ~f:(fun descr -> - Format.asprintf " If this is intentional, use %a instead." MF.pp_monospaced descr ) - ~default:"" - in - let description = - match explicit_kind with - | ErrorRenderingUtils.UserFriendlyNullable.Null -> - Format.sprintf - "NullPointerException will be thrown at this line! %s is `null` and is dereferenced \ - via %s%s." - what_is_dereferred_str action_descr origin_descr - | ErrorRenderingUtils.UserFriendlyNullable.Nullable -> - Format.sprintf "%s is nullable and is not locally checked for null when %s%s.%s" - what_is_dereferred_str action_descr origin_descr alternative_recommendation - in - let nullable_methods = - match nullable_object_origin with TypeOrigin.MethodCall origin -> [origin] | _ -> [] - in - NullsafeIssue.make ~description ~issue_type:IssueType.eradicate_nullable_dereference - ~loc:dereference_location - ~severity:(NullsafeMode.severity nullsafe_mode) - ~field_name:None - |> NullsafeIssue.with_nullable_methods nullable_methods - - - let make_nullsafe_issue {nullsafe_mode; violation= {nullability}} ~dereference_location - dereference_type ~nullable_object_descr = - let user_friendly_nullable = - ErrorRenderingUtils.UserFriendlyNullable.from_nullability - (InferredNullability.get_nullability nullability) - |> IOption.if_none_eval ~f:(fun () -> - Logging.die InternalError - "get_description:: Dereference violation should not be possible for non-nullable \ - values" ) - in - let nullable_object_origin = InferredNullability.get_simple_origin nullability in - match user_friendly_nullable with - | ErrorRenderingUtils.UserFriendlyNullable.UntrustedNonnull untrusted_kind -> - (* Attempt to dereference a value which is not explictly declared as nullable, - but still can not be trusted in this particular mode. - *) - ErrorRenderingUtils.mk_nullsafe_issue_for_untrusted_values ~nullsafe_mode ~untrusted_kind - ~bad_usage_location:dereference_location nullable_object_origin - | ErrorRenderingUtils.UserFriendlyNullable.ExplainablyNullable explicit_kind -> - (* Attempt to dereference value that can be explained to the user as nullable. *) - mk_nullsafe_issue_for_explicitly_nullable_values ~explicit_kind ~dereference_type - ~nullsafe_mode dereference_location ~nullable_object_descr ~nullable_object_origin -end - -let check nullability = - match InferredNullability.get_nullability nullability with - (* StrictNonnull is the only "real" value that is not null according to type system rules. - Other values can not be fully trusted. - *) - | Nullability.StrictNonnull -> - Ok () - | _ -> - Error {nullability} diff --git a/infer/src/nullsafe/DereferenceRule.mli b/infer/src/nullsafe/DereferenceRule.mli deleted file mode 100644 index 60f03520df4..00000000000 --- a/infer/src/nullsafe/DereferenceRule.mli +++ /dev/null @@ -1,55 +0,0 @@ -(* - * 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. - *) -open! IStd - -(** Dereference rule should be checked every type an object is dereferenced. The rule checks if the - reference is nullable. *) - -type violation [@@deriving compare, equal] - -val check : InferredNullability.t -> (unit, violation) result -(** violation of Dereference rule reflects possibility of dereferencing of `null`. Note that this - might or might not be severe enough to be reported to the user, depending on the mode - agreements. *) - -(** Violation that will occur if the provisional annotation becomes real [@Nullable] *) -module ProvisionalViolation : sig - type t - - val offending_annotations : t -> ProvisionalAnnotation.t list - (** Non-empty list of corresponding provisional annotations (adding any of those will lead to an - issue) *) - - val from : violation -> t option - (** If the violation is provisional (so is not real but will become real when the annotation is - added), create it. *) -end - -(** Violation that needs to be reported to the user. *) -module ReportableViolation : sig - type t - - type dereference_type = - | MethodCall of Procname.Java.t - | AccessToField of Fieldname.t - | AccessByIndex of {index_desc: string} - | ArrayLengthAccess - [@@deriving compare, equal] - - val from : NullsafeMode.t -> violation -> t option - (** Depending on the mode, violation might or might not be important enough to be reported to the - user. If it should NOT be reported for that mode, this function will return None. *) - - val make_nullsafe_issue : - t - -> dereference_location:Location.t - -> dereference_type - -> nullable_object_descr:string option - -> NullsafeIssue.t - (** Given context around violation, return error message together with the info where to put this - message *) -end diff --git a/infer/src/nullsafe/EradicateReporting.ml b/infer/src/nullsafe/EradicateReporting.ml deleted file mode 100644 index bd526260bb9..00000000000 --- a/infer/src/nullsafe/EradicateReporting.ml +++ /dev/null @@ -1,48 +0,0 @@ -(* - * 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. - *) - -open! IStd - -let get_proc_name proc_attrs = - match ProcAttributes.get_proc_name proc_attrs with - | Procname.Java java_pname -> - java_pname - | _ -> - Logging.die InternalError "Unexpected attempt to report a nullsafe error on a non-java method" - - -let report_error {IntraproceduralAnalysis.proc_desc; tenv; err_log} checker ?(field_name = None) - nullsafe_issue = - let proc_attrs = Procdesc.get_attributes proc_desc in - let issue_type = NullsafeIssue.get_issue_type nullsafe_issue in - let description = NullsafeIssue.get_description nullsafe_issue in - let severity = NullsafeIssue.get_severity nullsafe_issue in - let loc = NullsafeIssue.get_loc nullsafe_issue in - let proc_name = get_proc_name proc_attrs in - let nullsafe_extra = Some (NullsafeIssue.get_nullsafe_extra nullsafe_issue proc_name) in - let extras = - Jsonbug_t. - { nullsafe_extra - ; cost_degree= None - ; cost_polynomial= None - ; copy_type= None - ; config_usage_extra= None - ; taint_extra= None } - in - let suppressed = Reporting.is_suppressed tenv proc_attrs issue_type ~field_name in - if suppressed then Logging.debug Analysis Medium "Reporting is suppressed!@\n" - else - let localized_description = Localise.verbatim_desc description in - let issue_to_report = - {IssueToReport.issue_type; description= localized_description; ocaml_pos= None} - in - let trace = [Errlog.make_trace_element 0 loc description []] in - let node = AnalysisState.get_node_exn () in - let session = AnalysisState.get_session () in - Reporting.log_issue_from_summary ~severity_override:severity proc_desc err_log - ~node:(BackendNode {node}) - ~session ~loc ~ltr:trace checker issue_to_report ~extras diff --git a/infer/src/nullsafe/EradicateReporting.mli b/infer/src/nullsafe/EradicateReporting.mli deleted file mode 100644 index b5eaf9423a5..00000000000 --- a/infer/src/nullsafe/EradicateReporting.mli +++ /dev/null @@ -1,15 +0,0 @@ -(* - * 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. - *) - -open! IStd - -val report_error : - IntraproceduralAnalysis.t - -> Checker.t - -> ?field_name:Fieldname.t option - -> NullsafeIssue.t - -> unit diff --git a/infer/src/nullsafe/ErrorRenderingUtils.ml b/infer/src/nullsafe/ErrorRenderingUtils.ml deleted file mode 100644 index c70d074e82e..00000000000 --- a/infer/src/nullsafe/ErrorRenderingUtils.ml +++ /dev/null @@ -1,284 +0,0 @@ -(* - * 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. - *) - -open! IStd -module F = Format - -module UserFriendlyNullable = struct - type t = ExplainablyNullable of explainably_nullable_kind | UntrustedNonnull of untrusted_kind - - and explainably_nullable_kind = Nullable | Null - - and untrusted_kind = ThirdPartyNonnull | UncheckedNonnull | LocallyCheckedNonnull - - let from_nullability = function - | Nullability.Nullable -> - Some (ExplainablyNullable Nullable) - | Nullability.Null -> - Some (ExplainablyNullable Null) - | Nullability.UncheckedNonnull -> - Some (UntrustedNonnull UncheckedNonnull) - | Nullability.LocallyCheckedNonnull -> - Some (UntrustedNonnull LocallyCheckedNonnull) - | Nullability.ThirdPartyNonnull -> - Some (UntrustedNonnull ThirdPartyNonnull) - | Nullability.LocallyTrustedNonnull -> - (* The value is trusted in the current mode by definition, hence is not treated as nullable. *) - None - | Nullability.ProvisionallyNullable -> - (* from the user-facing point of view, this is a non-null *) None - | Nullability.StrictNonnull -> - None -end - -let is_object_nullability_self_explanatory ~object_expression (object_origin : TypeOrigin.t) = - (* Fundamentally, object can be of two kinds: - 1. Indirect: local variable that was instantiated before. - In this case, normally origin is NOT trivial - (because for complex flows it might be tricky to figure out where the offending - initialization came from, and it is a good idea to at least point to the line). - 2. Direct: some sort of expression. In this case, this expression itself is often - self-explanatory and it is OK to be offended.Infer - NOTE: code below is heuristic, it indends to cover most practical cases and does not try - to be 100% precise. - *) - match object_origin with - | NullConst _ -> - (* Expect either a local variable or null literal (latter case is trivial) *) - String.equal object_expression "null" - | CurrMethodParameter (Normal {mangled}) -> - (* Either local variable or literally parameter. In latter case, its nullability is - self-explanatory because the user can quickly see the current method signature. - *) - let param_name = Mangled.to_string mangled in - String.equal object_expression param_name - | CurrMethodParameter ObjectEqualsOverride -> - (* This needs a dedicated explanation for the user *) - false - | Field {field_name} -> - (* Either local variable or expression like `.field_name`. Latter case is trivial: - the user can quickly go to field_name definition and see if its annotation. *) - let field_name_str = Fieldname.get_field_name field_name in - String.is_suffix object_expression ~suffix:field_name_str - | MethodCall {pname; annotated_signature= {kind}} -> ( - match kind with - | FirstParty | ThirdParty Unregistered -> - (* Either local variable or expression like .method_name(...). - Latter case is self-explanatory: it is easy to the user to jump to definition - and check out the method annotation. - *) - let method_name = Procname.Java.to_simplified_string pname in - String.is_suffix object_expression ~suffix:method_name - | ThirdParty ModelledInternally | ThirdParty (InThirdPartyRepo _) -> - (* This is non-trivial and should always be explained to the user *) - false ) - (* These cases are not yet supported because they normally mean non-nullable case, for which - we don't render important messages yet. - *) - | NonnullConst _ - | This - | New - | CallToGetKnownToContainsKey - | ArrayLengthResult - | ArrayAccess - | InferredNonnull _ - | OptimisticFallback -> - false - - -type message_info = - { offending_object: string - ; object_loc: Location.t - ; why_dont_trust_explanation: string - (** A short message describing why don't we trust the value to be not null *) - ; what_is_used: string - ; recommendation: string - ; third_party_dependent_methods: (Procname.Java.t * AnnotatedSignature.t) list - ; issue_type: IssueType.t } - -let get_field_class_name field_name = - let class_with_field = Fieldname.to_simplified_string field_name in - String.rsplit2 class_with_field ~on:'.' - |> Option.value_map ~f:(fun (classname, _) -> classname) ~default:"the field class" - - -let why_dont_trust_explanation_first_party nullsafe_mode = - match nullsafe_mode with - | NullsafeMode.Strict -> - "`@Nullsafe(STRICT)` prohibits using values coming from non-strict classes without a check" - | NullsafeMode.Local (NullsafeMode.Trust.Only _) -> - "`@Nullsafe(trust={...})` prohibits using values coming from non-`@Nullsafe` classes without \ - a check, unless the class is in the trust list" - | _ -> - Logging.die InternalError "why_dont_trust_explanation_first_party:: not applicable to %a" - NullsafeMode.pp nullsafe_mode - - -let why_dont_trust_explanation_third_party nullsafe_mode ~is_field = - let mode_str = - match nullsafe_mode with NullsafeMode.Default -> "Nullsafe" | _ -> "`@Nullsafe` mode" - in - F.sprintf "%s prohibits using values coming from not vetted third party %s without a check" - mode_str - (if is_field then "fields" else "methods") - - -let mk_strictification_advice_unchecked_or_locally_checked_case_only nullsafe_mode untrusted_kind - ~what_to_strictify = - match untrusted_kind with - | UserFriendlyNullable.UncheckedNonnull | UserFriendlyNullable.LocallyCheckedNonnull -> ( - match nullsafe_mode with - | NullsafeMode.Strict -> - F.sprintf "make `%s` nullsafe strict" what_to_strictify - | NullsafeMode.Local _ -> - F.sprintf "make `%s` @Nullsafe (or add it to trust list)" what_to_strictify - | NullsafeMode.Default -> - Logging.die InternalError - "mk_recommendation_unchecked_or_locally_checked_case_only:: should not be called for \ - default mode" ) - | UserFriendlyNullable.ThirdPartyNonnull -> - Logging.die InternalError - "mk_recommendation_unchecked_or_locally_checked_case_only:: not applicable to \ - ThirdPartyNonnull case" - - -let mk_recommendation_for_third_party_field nullsafe_mode field = - match nullsafe_mode with - | NullsafeMode.Strict -> - F.sprintf "access %s via a nullsafe strict getter" field - | NullsafeMode.Local _ | NullsafeMode.Default -> - F.sprintf "access %s via a nullsafe getter" field - - -let get_info object_origin nullsafe_mode untrusted_kind = - match object_origin with - | TypeOrigin.MethodCall {pname; call_loc; annotated_signature} -> - let offending_object = - F.asprintf "%a" MarkupFormatter.pp_monospaced - (Procname.Java.to_simplified_string ~withclass:true pname) - in - let object_loc = call_loc in - let what_is_used = "Result of this call" in - let why_dont_trust_explanation, recommendation, issue_type, third_party_dependent_methods = - match untrusted_kind with - | UserFriendlyNullable.ThirdPartyNonnull -> - let suggested_third_party_sig_file = - ThirdPartyAnnotationInfo.lookup_related_sig_file_for_proc - (ThirdPartyAnnotationGlobalRepo.get_repo ()) - pname - in - let where_to_add_signature = - Option.value_map suggested_third_party_sig_file - ~f:(fun sig_file_name -> - ThirdPartyAnnotationGlobalRepo.get_user_friendly_third_party_sig_file_name - ~filename:sig_file_name ) - (* this can happen when third party is registered in a deprecated way (not in third party repository) *) - ~default:"the third party signature storage" - in - let why_dont_trust_explanation = - why_dont_trust_explanation_third_party nullsafe_mode ~is_field:false - in - ( why_dont_trust_explanation - , F.sprintf "add the correct signature to %s" where_to_add_signature - , IssueType.eradicate_unvetted_third_party_in_nullsafe - , [(pname, annotated_signature)] ) - | UserFriendlyNullable.UncheckedNonnull | UserFriendlyNullable.LocallyCheckedNonnull -> - let why_dont_trust_explanation = why_dont_trust_explanation_first_party nullsafe_mode in - let recommendation = - let what_to_strictify = Procname.Java.get_simple_class_name pname in - mk_strictification_advice_unchecked_or_locally_checked_case_only nullsafe_mode - untrusted_kind ~what_to_strictify - in - let issue_type = IssueType.eradicate_unchecked_usage_in_nullsafe in - (why_dont_trust_explanation, recommendation, issue_type, []) - in - { offending_object - ; object_loc - ; why_dont_trust_explanation - ; what_is_used - ; third_party_dependent_methods - ; recommendation - ; issue_type } - | TypeOrigin.Field {field_name; access_loc} -> - let qualified_name = - F.asprintf "%a" MarkupFormatter.pp_monospaced (Fieldname.to_simplified_string field_name) - in - let unqualified_name = - F.asprintf "%a" MarkupFormatter.pp_monospaced (Fieldname.get_field_name field_name) - in - let object_loc = access_loc in - (* TODO: currently we do not support third-party annotations for fields. Because of this, - render error like it is a non-stict class. *) - let what_is_used = "This field" in - let why_dont_trust_explanation, recommendation, issue_type = - match untrusted_kind with - | UserFriendlyNullable.ThirdPartyNonnull -> - ( why_dont_trust_explanation_third_party nullsafe_mode ~is_field:true - , mk_recommendation_for_third_party_field nullsafe_mode unqualified_name - , IssueType.eradicate_unvetted_third_party_in_nullsafe ) - | UserFriendlyNullable.UncheckedNonnull | UserFriendlyNullable.LocallyCheckedNonnull -> - let why_dont_trust_explanation = why_dont_trust_explanation_first_party nullsafe_mode in - let recommendation = - mk_strictification_advice_unchecked_or_locally_checked_case_only nullsafe_mode - untrusted_kind ~what_to_strictify:(get_field_class_name field_name) - in - ( why_dont_trust_explanation - , recommendation - , IssueType.eradicate_unchecked_usage_in_nullsafe ) - in - { offending_object= qualified_name - ; object_loc - ; why_dont_trust_explanation - ; what_is_used - ; recommendation - ; third_party_dependent_methods= [] - ; issue_type } - | other -> - Logging.die InternalError - "get_info:: untrusted_kind is possible only for MethodCall and Field origins, got %s \ - instead" - (TypeOrigin.to_string other) - - -let mk_nullsafe_issue_for_untrusted_values ~nullsafe_mode ~untrusted_kind ~bad_usage_location - object_origin = - let { offending_object - ; object_loc - ; why_dont_trust_explanation - ; what_is_used - ; recommendation - ; third_party_dependent_methods - ; issue_type } = - get_info object_origin nullsafe_mode untrusted_kind - in - let description = - F.asprintf - "%s: %s. %s is used at line %d. Either add a local check for null or assertion, or %s." - offending_object why_dont_trust_explanation what_is_used bad_usage_location.Location.line - recommendation - in - NullsafeIssue.make ~description ~issue_type ~loc:object_loc - ~severity:(NullsafeMode.severity nullsafe_mode) - ~field_name:None - |> NullsafeIssue.with_third_party_dependent_methods third_party_dependent_methods - - -let find_alternative_nonnull_method_description nullable_origin = - let open IOption.Let_syntax in - match nullable_origin with - | TypeOrigin.MethodCall {pname} -> - let* ModelTables.{package_name; class_name; method_name} = - Models.find_nonnullable_alternative pname - in - let+ original_package_name = Procname.Java.get_package pname in - if String.equal original_package_name package_name then - (* The same package that is from origin - omit name for simplicity *) - class_name ^ "." ^ method_name ^ "()" - else (* Fully qualified name *) - package_name ^ "." ^ class_name ^ "." ^ method_name ^ "()" - | _ -> - None diff --git a/infer/src/nullsafe/ErrorRenderingUtils.mli b/infer/src/nullsafe/ErrorRenderingUtils.mli deleted file mode 100644 index 911eb5bc604..00000000000 --- a/infer/src/nullsafe/ErrorRenderingUtils.mli +++ /dev/null @@ -1,54 +0,0 @@ -(* - * 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. - *) - -(** Helper tools for nicer rendering nullsafe error. *) - -open! IStd - -(** "Effectively nullable values" from the user perspective. Depending on context, convention, and - mode, Nullsafe treats such and such things as nullable or non-null. At some point this needs to - be explain to the user. *) -module UserFriendlyNullable : sig - type t = - | ExplainablyNullable of explainably_nullable_kind - (** Value that is nullable according to nullsafe semantics and conventions. It can be - nullable because of an explicit annotation, models, default nullability conventions, - etc. *) - | UntrustedNonnull of untrusted_kind - (** Value is not nullable per se, but we still can not treat it as non-null in current mode. - From the user perspective, it is a very different case: violations of this type need to - be explained in a way so that it is clear why exactly can not nullsafe trust it in this - context. *) - - and explainably_nullable_kind = Nullable | Null - - and untrusted_kind = ThirdPartyNonnull | UncheckedNonnull | LocallyCheckedNonnull - - val from_nullability : Nullability.t -> t option -end - -val is_object_nullability_self_explanatory : object_expression:string -> TypeOrigin.t -> bool -(** In order to understand why such and such object is nullable (or not nullable), we render its - origin. In some cases this is redundant and adds extra noise for the user. *) - -val mk_nullsafe_issue_for_untrusted_values : - nullsafe_mode:NullsafeMode.t - -> untrusted_kind:UserFriendlyNullable.untrusted_kind - -> bad_usage_location:Location.t - -> TypeOrigin.t - -> NullsafeIssue.t -(** Situation when we tried to use nonnull values in a nullsafe mode that does not trust them to be - non-nullable: [untrusted_kind]. From the user perspective, this case is different from normal - nullable assignment or dereference violation: what needs to be described is why does not this - mode trust this value (and what are possible actions). NOTE: Location of the error will be NOT - in the place when the value is used (that is [bad_usage_location]), but where the value is first - obtained from. *) - -val find_alternative_nonnull_method_description : TypeOrigin.t -> string option -(** If type origin is the result of a nullable method call that have a known nonnullable alternative - (the one that does the check inside), return the string representation of that alternative - suitable for error messaging. *) diff --git a/infer/src/nullsafe/FileLevelAnalysis.ml b/infer/src/nullsafe/FileLevelAnalysis.ml deleted file mode 100644 index 6d9a615fa89..00000000000 --- a/infer/src/nullsafe/FileLevelAnalysis.ml +++ /dev/null @@ -1,61 +0,0 @@ -(* - * 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. - *) - -open! IStd - -let get_java_class_name = function - | Typ.JavaClass java_class_name -> - Some java_class_name - | _ -> - None - - -(* Fetch the class and summaries for each procedure *) -let get_summaries - ({procedures; analyze_file_dependency} : NullsafeSummary.t InterproceduralAnalysis.file_t) = - let open IOption.Let_syntax in - List.filter_map procedures ~f:(fun procname -> - let* class_name = Procname.get_class_type_name procname in - let* java_class_name = get_java_class_name class_name in - let* summary = analyze_file_dependency procname in - return (java_class_name, summary) ) - - -(* Analyze the class and all its nested children recursively *) -let rec analyze_class_and_nested tenv source_file issue_log class_info = - (* Analyze the class itself *) - let updated_log = ClassLevelAnalysis.analyze_class tenv source_file class_info issue_log in - (* Analyze its nested children *) - AggregatedSummaries.ClassInfo.get_nested_classes_info class_info - |> List.fold ~init:updated_log ~f:(analyze_class_and_nested tenv source_file) - - -(* Given aggregated information about the top-level class, analyze it and its nested children *) -let analyze_top_level_class tenv source_file issue_log top_level_class_info = - let is_from_third_party = - ThirdPartyAnnotationInfo.is_third_party_class_name - (ThirdPartyAnnotationGlobalRepo.get_repo ()) - (AggregatedSummaries.ClassInfo.get_class_name top_level_class_info) - in - if is_from_third_party then (* Don't analyze third party classes *) - issue_log - else analyze_class_and_nested tenv source_file issue_log top_level_class_info - - -let analyze_file ({InterproceduralAnalysis.file_exe_env; source_file} as analysis_data) = - Logging.debug Analysis Medium "Starting file level analysis of %a@\n" SourceFile.pp source_file ; - let all_summaries = get_summaries analysis_data in - let tenv = Exe_env.load_java_global_tenv file_exe_env in - let top_level_classes = AggregatedSummaries.aggregate all_summaries in - List.iter top_level_classes ~f:(fun top_level_class -> - Logging.debug Analysis Medium "Hierarchy for a top level class:@\n%a@\n" - AggregatedSummaries.ClassInfo.pp top_level_class ) ; - let issue_log = - List.fold top_level_classes ~init:IssueLog.empty ~f:(analyze_top_level_class tenv source_file) - in - Logging.debug Analysis Medium "Finished file level analysis of %a@\n" SourceFile.pp source_file ; - issue_log diff --git a/infer/src/nullsafe/FileLevelAnalysis.mli b/infer/src/nullsafe/FileLevelAnalysis.mli deleted file mode 100644 index 840f1e10d90..00000000000 --- a/infer/src/nullsafe/FileLevelAnalysis.mli +++ /dev/null @@ -1,12 +0,0 @@ -(* - * 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. - *) - -open! IStd - -val analyze_file : NullsafeSummary.t InterproceduralAnalysis.file_t -> IssueLog.t -(** File-level callback for nullsafe. Is called after all proc-level callbacks are called and - calculated their summaries. At this stage, additional issues can be emitted. *) diff --git a/infer/src/nullsafe/IDEnv.ml b/infer/src/nullsafe/IDEnv.ml deleted file mode 100644 index bc2a2c5e61a..00000000000 --- a/infer/src/nullsafe/IDEnv.ml +++ /dev/null @@ -1,62 +0,0 @@ -(* - * 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. - *) - -open! IStd - -(** Environment for temporary identifiers used in instructions. Lazy implementation: only created - when actually used. *) - -type t = Exp.t Ident.Hash.t Lazy.t - -let create_ proc_desc = - let map = Ident.Hash.create 1 in - let do_instr _ = function - | Sil.Load {id; e} -> - Ident.Hash.add map id e - | Sil.Call ((res_ident, _), Exp.Const (Const.Cfun fname), [(Exp.Var src_ident, _); _], _, _) - (* [id] = __cast([ex]) when [ex] is a frontend temporary, try to unfold it further *) - when Procname.equal fname BuiltinDecl.__cast -> - Ident.Hash.find_opt map src_ident - |> Option.iter ~f:(fun ex -> Ident.Hash.add map res_ident ex) - | _ -> - () - in - Procdesc.iter_instrs do_instr proc_desc ; - map - - -(* lazy implementation, only create when used *) -let create proc_desc = - let map = lazy (create_ proc_desc) in - map - - -let lookup map_ id = - let map = Lazy.force map_ in - Ident.Hash.find_opt map id - - -let expand_expr idenv e = - match e with Exp.Var id -> ( match lookup idenv id with Some e' -> e' | None -> e ) | _ -> e - - -let expand_expr_temps idenv node exp_ = - let exp = expand_expr idenv exp_ in - match exp with - | Exp.Lvar pvar when Pvar.is_frontend_tmp pvar -> ( - match Decompile.find_program_variable_assignment node pvar with - | None -> - exp - | Some (_, id) -> - expand_expr idenv (Exp.Var id) ) - | _ -> - exp - - -(** Return true if the expression is a temporary variable introduced by the front-end. *) -let exp_is_temp idenv e = - match expand_expr idenv e with Exp.Lvar pvar -> Pvar.is_frontend_tmp pvar | _ -> false diff --git a/infer/src/nullsafe/IDEnv.mli b/infer/src/nullsafe/IDEnv.mli deleted file mode 100644 index a97607df97b..00000000000 --- a/infer/src/nullsafe/IDEnv.mli +++ /dev/null @@ -1,22 +0,0 @@ -(* - * 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. - *) - -open! IStd - -(** Environment for temporary identifiers used in instructions. Lazy implementation: only created - when actually used. *) - -type t - -val create : Procdesc.t -> t - -val expand_expr : t -> Exp.t -> Exp.t - -val exp_is_temp : t -> Exp.t -> bool - -val expand_expr_temps : t -> Procdesc.Node.t -> Exp.t -> Exp.t -(** Stronger version of expand_expr which also expands a temporary variable. *) diff --git a/infer/src/nullsafe/InferredNullability.ml b/infer/src/nullsafe/InferredNullability.ml deleted file mode 100644 index ef93af84cd9..00000000000 --- a/infer/src/nullsafe/InferredNullability.ml +++ /dev/null @@ -1,76 +0,0 @@ -(* - * 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. - *) - -open! IStd - -type t = - { nullability: Nullability.t - ; origins: TypeOrigin.t list (** Origins responsible for this nullability type *) } -[@@deriving compare, equal] - -let rec sanitize_origin = function - (* Collapse consecutive chains of InferredNonnull to get rid of infinite chains in loops and - hence allowing to reach the fixpoint *) - | TypeOrigin.InferredNonnull - {previous_origin= TypeOrigin.InferredNonnull {previous_origin= underlying}} -> - TypeOrigin.InferredNonnull {previous_origin= underlying} |> sanitize_origin - | other -> - other - - -let create origin = - {nullability= TypeOrigin.get_nullability origin; origins= [sanitize_origin origin]} - - -let get_nullability {nullability} = nullability - -let is_nonnullish {nullability} = Nullability.is_nonnullish nullability - -let pp fmt {nullability} = Nullability.pp fmt nullability - -(* Join two lists with removing duplicates and preserving the order of join *) -let join_origins origins1 origins2 = - (IList.append_no_duplicates ~cmp:TypeOrigin.compare |> Staged.unstage) origins1 origins2 - - -let join t1 t2 = - let joined_nullability = Nullability.join t1.nullability t2.nullability in - let is_equal_to_t1 = Nullability.equal t1.nullability joined_nullability in - let is_equal_to_t2 = Nullability.equal t2.nullability joined_nullability in - (* Origin complements nullability information. It is the best effort to explain how was the nullability inferred. - If nullability is fully determined by one of the arguments, origin should be get from this argument. - Otherwise we apply heuristics to choose origin either from t1 or t2. - *) - let joined_origins = - match (is_equal_to_t1, is_equal_to_t2) with - | _ when Nullability.equal t1.nullability Nullability.Null -> - t1.origins - | _ when Nullability.equal t2.nullability Nullability.Null -> - t2.origins - | true, false -> - (* Nullability was fully determined by t1. *) - t1.origins - | false, true -> - (* Nullability was fully determined by t2 *) - t2.origins - | false, false | true, true -> - (* Nullability is not fully determined by neither t1 nor t2 - join both lists - *) - join_origins t1.origins t2.origins - in - {nullability= joined_nullability; origins= joined_origins} - - -let get_simple_origin t = List.nth_exn t.origins 0 - -let get_provisional_annotations t = - List.filter_map t.origins ~f:TypeOrigin.get_provisional_annotation - |> List.dedup_and_sort ~compare:ProvisionalAnnotation.compare - - -let origin_is_fun_defined t = - match get_simple_origin t with TypeOrigin.MethodCall {is_defined; _} -> is_defined | _ -> false diff --git a/infer/src/nullsafe/InferredNullability.mli b/infer/src/nullsafe/InferredNullability.mli deleted file mode 100644 index 362630114f5..00000000000 --- a/infer/src/nullsafe/InferredNullability.mli +++ /dev/null @@ -1,45 +0,0 @@ -(* - * 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. - *) - -open! IStd - -(** Module to represent nullability of expressions inferred during flow-sensitive symbolic - execution. NOTE: This is complementaty to {!InferredNullability.t}. {!InferredNullability} - contains info about _formal_ nullability (what does the code say about nullability of a given - type, according to explicit annotations and implicit agreements (e.g. models)). In contrast, - InferredNullability represents what Nullsafe thinks about such and such expression according to - its type inference rules. *) - -type t [@@deriving compare, equal] - -val get_nullability : t -> Nullability.t - -val create : TypeOrigin.t -> t - -val is_nonnullish : t -> bool -(** Check whether corresponding [Nullability] is [Nullability.is_nonnullish] *) - -val get_simple_origin : t -> TypeOrigin.t -(** The simple explanation of how was nullability inferred. *) - -val get_provisional_annotations : t -> ProvisionalAnnotation.t list - -val join : t -> t -> t -(** This is what happens with nullability when we join two flows in CFG, e.g. - - {[ - if(something) { - a = e1; - } else { - a = e2; - } - // what is nullability of `a` at this point? - ]} *) - -val origin_is_fun_defined : t -> bool - -val pp : Format.formatter -> t -> unit diff --git a/infer/src/nullsafe/InheritanceRule.ml b/infer/src/nullsafe/InheritanceRule.ml deleted file mode 100644 index df2c5f7a150..00000000000 --- a/infer/src/nullsafe/InheritanceRule.ml +++ /dev/null @@ -1,114 +0,0 @@ -(* - * 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. - *) -open! IStd - -type violation = {base: Nullability.t; overridden: Nullability.t} [@@deriving compare, equal] - -type type_role = Param | Ret - -module ReportableViolation = struct - type t = {nullsafe_mode: NullsafeMode.t; violation: violation} - - type violation_type = - | InconsistentParam of {param_description: string; param_index: int} - | InconsistentReturn - [@@deriving compare, equal] - - let from nullsafe_mode ({base; overridden} as violation) = - if - Nullability.is_nonnullish base && Nullability.is_nonnullish overridden - (* When both nullabilities are kind-of non-nullable we don't want to raise the - issue. Without this suppression there will be a lot of non-actionable issues - raised for classes in one [NullsafeMode] inheriting from classes in the other - [NullsafeMode]. *) - (* TODO(T62521386): consider using caller context when determining nullability to get - rid of white-lists. *) - then None - else Some {nullsafe_mode; violation} - - - let is_java_lang_object_equals procname = - match (Procname.Java.get_class_name procname, Procname.Java.get_method procname) with - | "java.lang.Object", "equals" -> - true - | _ -> - false - - - let make_nullsafe_issue _ violation_type ~nullsafe_mode ~loc ~base_proc_name ~overridden_proc_name - = - let module MF = MarkupFormatter in - let base_method_descr = Procname.Java.to_simplified_string ~withclass:true base_proc_name in - let overridden_method_descr = - Procname.Java.to_simplified_string ~withclass:true overridden_proc_name - in - let description = - match violation_type with - | InconsistentReturn -> - Format.asprintf - "Child method %a is not substitution-compatible with its parent: the return type is \ - declared as nullable, but parent method %a is missing `@Nullable` declaration. Either \ - mark the parent as `@Nullable` or ensure the child does not return `null`." - MF.pp_monospaced overridden_method_descr MF.pp_monospaced base_method_descr - | InconsistentParam {param_description; param_index} -> - if is_java_lang_object_equals base_proc_name then - (* This is a popular enough case to make error message specific *) - Format.asprintf - "Parameter %a is missing `@Nullable` declaration: according to the Java \ - Specification, for any object `x` call `x.equals(null)` should properly return \ - false." - MF.pp_monospaced param_description - else - let position_to_human_readable_string = function - | 1 -> - "First" - | 2 -> - "Second" - | 3 -> - "Third" - | n -> - string_of_int n ^ "th" - in - Format.asprintf - "%s parameter %a of method %a is missing `@Nullable` declaration when overriding %a. \ - The parent method declared it can handle `null` for this param, so the child should \ - also declare that." - (position_to_human_readable_string - ((* For the user reporting, index param positions from 1 *) param_index + 1) ) - MF.pp_monospaced param_description MF.pp_monospaced overridden_method_descr - MF.pp_monospaced base_method_descr - in - let severity = NullsafeMode.severity nullsafe_mode in - let issue_type, param_index = - match violation_type with - | InconsistentReturn -> - (IssueType.eradicate_inconsistent_subclass_return_annotation, None) - | InconsistentParam {param_index} -> - (IssueType.eradicate_inconsistent_subclass_parameter_annotation, Some param_index) - in - NullsafeIssue.make ~description ~loc ~issue_type ~severity ~field_name:None - |> NullsafeIssue.with_inconsistent_param_index param_index -end - -let check type_role ~base ~overridden = - if Nullability.equal Nullability.ThirdPartyNonnull base then - (* In context of inheritance check, third party declarations in base are treated optimistically. - Meaning return values are assumed [@Nullable] and params are assumed [@NonNull]. - This is done for compatibility reasons so existing [@Nullsafe] classes are preserved Nullsafe. - *) - Ok () - else - let subtype, supertype = - match type_role with - | Ret -> - (* covariance for ret *) - (overridden, base) - | Param -> - (* contravariance for param *) - (base, overridden) - in - Result.ok_if_true (Nullability.is_subtype ~subtype ~supertype) ~error:{base; overridden} diff --git a/infer/src/nullsafe/InheritanceRule.mli b/infer/src/nullsafe/InheritanceRule.mli deleted file mode 100644 index cf5b5138cd3..00000000000 --- a/infer/src/nullsafe/InheritanceRule.mli +++ /dev/null @@ -1,51 +0,0 @@ -(* - * 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. - *) -open! IStd - -(** Inheritance rule: - - + Return type for an overridden method is covariant: overridden method is allowed to narrow down - the return value to a subtype of the one from the base method; this means it is OK to make the - return value non-null when it was nullable in the base) - + Parameter type for an overridden method is contravariant. It is OK for a derived method to - accept nullable in the params even if the base does not accept nullable. - - NOTE: Rule 1) is based on Java covariance rule for the return type. In contrast, rule 2) is - nullsafe specific as Java does not support type contravariance for method params. *) - -type violation [@@deriving compare, equal] - -type type_role = Param | Ret - -val check : type_role -> base:Nullability.t -> overridden:Nullability.t -> (unit, violation) result -(** See description of the rule in the header of the file. Note that formal fact of violation might - or might not be reported to the user, depending on the mode. See [to_reportable_violation] *) - -(** Violation that needs to be reported to the user. *) -module ReportableViolation : sig - type t - - type violation_type = - | InconsistentParam of {param_description: string; param_index: int} - | InconsistentReturn - [@@deriving compare, equal] - - val from : NullsafeMode.t -> violation -> t option - (** Depending on the mode, violation might or might not be important enough to be reported to the - user. If it should NOT be reported for that mode, this function will return None. *) - - val make_nullsafe_issue : - t - -> violation_type - -> nullsafe_mode:NullsafeMode.t - -> loc:Location.t - -> base_proc_name:Procname.Java.t - -> overridden_proc_name:Procname.Java.t - -> NullsafeIssue.t - (** Given context around violation, return error message together with the info where to put this - message *) -end diff --git a/infer/src/nullsafe/Initializers.ml b/infer/src/nullsafe/Initializers.ml deleted file mode 100644 index aff1e446ddc..00000000000 --- a/infer/src/nullsafe/Initializers.ml +++ /dev/null @@ -1,142 +0,0 @@ -(* - * 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. - *) - -open! IStd - -type init = Procname.t * Procdesc.t - -let equal_class_opt = [%equal: string option] - -let final_typestates initializers_current_class tenv typecheck_proc = - (* Get the private methods, from the same class, directly called by the initializers. *) - let get_private_called (initializers : init list) : init list = - let res = ref [] in - let do_proc (init_pn, init_pd) = - let filter callee_pn callee_attributes = - let is_private = - ProcAttributes.equal_access callee_attributes.ProcAttributes.access Private - in - let same_class = - let get_class_opt pn = - match pn with - | Procname.Java pn_java -> - Some (Procname.Java.get_class_name pn_java) - | _ -> - None - in - equal_class_opt (get_class_opt init_pn) (get_class_opt callee_pn) - in - is_private && same_class - in - let private_called = - PatternMatch.proc_calls (PatternMatch.lookup_attributes tenv) init_pd filter - in - let do_called (callee_pn, _) = - match Procdesc.load callee_pn with - | Some callee_pd -> - res := (callee_pn, callee_pd) :: !res - | None -> - () - in - List.iter ~f:do_called private_called - in - List.iter ~f:do_proc initializers ; - !res - in - (* Get the initializers recursively called by computing a fixpoint. - Start from the initializers of the current class and the current procedure. *) - let initializers_recursive : init list = - let initializers_base_case = initializers_current_class in - let res = ref [] in - let seen = ref Procname.Set.empty in - let mark_seen (initializers : init list) : unit = - List.iter ~f:(fun (pn, _) -> seen := Procname.Set.add pn !seen) initializers ; - res := !res @ initializers - in - let rec fixpoint initializers_old = - let initializers_new = get_private_called initializers_old in - let initializers_new' = - List.filter ~f:(fun (pn, _) -> not (Procname.Set.mem pn !seen)) initializers_new - in - mark_seen initializers_new' ; - if not (List.is_empty initializers_new') then fixpoint initializers_new' - in - mark_seen initializers_base_case ; - fixpoint initializers_base_case ; - !res - in - (* Get the final typestates of all the initializers. *) - let final_typestates = ref [] in - let get_final_typestate (pname, pdesc) = - match typecheck_proc false pname pdesc None with - | _, Some final_typestate -> - final_typestates := (pname, final_typestate) :: !final_typestates - | _, None -> - () - in - List.iter ~f:get_final_typestate initializers_recursive ; - List.rev !final_typestates - - -let pname_and_pdescs_with tenv curr_pname f = - let res = ref [] in - let filter pname = - match PatternMatch.lookup_attributes tenv pname with - | Some proc_attributes -> - f (pname, proc_attributes) - | None -> - false - in - let do_proc pname = - if filter pname then - match Procdesc.load pname with Some pdesc -> res := (pname, pdesc) :: !res | None -> () - in - List.iter ~f:do_proc (SourceFiles.get_procs_in_file curr_pname) ; - List.rev !res - - -let get_class pn = - match pn with Procname.Java pn_java -> Some (Procname.Java.get_class_name pn_java) | _ -> None - - -let is_annotated_initializer tenv proc_name = - PatternMatch.lookup_attributes tenv proc_name - |> Option.exists ~f:(fun ProcAttributes.{ret_annots} -> Annotations.ia_is_initializer ret_annots) - - -let is_annotated_initializer_in_chain tenv proc_name = - PatternMatch.override_exists (is_annotated_initializer tenv) tenv proc_name - - -(* Should the (non-constructor) function be considered an initializer method *) -let is_initializer tenv proc_attributes = - (* Either modelled as initializer or the descendent of such a method *) - PatternMatch.Java.method_is_initializer tenv proc_attributes - || (* Or explicitly marked @Initializer or the descendent of such a method *) - is_annotated_initializer_in_chain tenv proc_attributes.ProcAttributes.proc_name - - -(** Typestates after the current procedure and all initializer procedures. *) -let final_initializer_typestates_lazy tenv curr_pname curr_pdesc typecheck_proc = - lazy - (let initializers_current_class = - pname_and_pdescs_with tenv curr_pname (function pname, proc_attributes -> - is_initializer tenv proc_attributes - && equal_class_opt (get_class pname) (get_class curr_pname) ) - in - final_typestates ((curr_pname, curr_pdesc) :: initializers_current_class) tenv typecheck_proc - ) - - -(** Typestates after all constructors. *) -let final_constructor_typestates_lazy tenv curr_pname typecheck_proc = - lazy - (let constructors_current_class = - pname_and_pdescs_with tenv curr_pname (fun (pname, _) -> - Procname.is_constructor pname && equal_class_opt (get_class pname) (get_class curr_pname) ) - in - final_typestates constructors_current_class tenv typecheck_proc ) diff --git a/infer/src/nullsafe/Initializers.mli b/infer/src/nullsafe/Initializers.mli deleted file mode 100644 index f7300f00c5d..00000000000 --- a/infer/src/nullsafe/Initializers.mli +++ /dev/null @@ -1,34 +0,0 @@ -(* - * 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. - *) - -open! IStd - -(** Functionality for evaluating typestates after class initialization. Nullsafe's notion of - initialization augments Java's one in the following way: - - + A method is considered "initializer" if it is either marked as [@Initializer] or is in the - list of known initializer methods (e.g. onCreate()). - + For each constructor, Nullsafe assumes all initializer methods will be called after the - constructor is called. (Nullsafe does not check if this assumption is indeed true). - + Nullsafe uses assumption 2, in order to check if class fields were initialized or not. (a - non-nullable field is considered not initialized if it is not initialized in at least one - constructor). *) - -val final_initializer_typestates_lazy : - Tenv.t - -> Procname.t - -> Procdesc.t - -> (bool -> Procname.t -> Procdesc.t -> 'a option -> 'b * 'c option) - -> (Procname.t * 'c) list lazy_t -(** Typestates after the current constructor and all initializer procedures. *) - -val final_constructor_typestates_lazy : - Tenv.t - -> Procname.t - -> (bool -> Procname.t -> Procdesc.t -> 'a option -> 'b * 'c option) - -> (Procname.t * 'c) list lazy_t -(** Typestates after all constructors. *) diff --git a/infer/src/nullsafe/Nullability.ml b/infer/src/nullsafe/Nullability.ml deleted file mode 100644 index 3e9bce55ef8..00000000000 --- a/infer/src/nullsafe/Nullability.ml +++ /dev/null @@ -1,100 +0,0 @@ -(* - * 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. - *) - -open! IStd -module F = Format - -type t = - | Null - | Nullable - | ThirdPartyNonnull - | UncheckedNonnull - | LocallyTrustedNonnull - | LocallyCheckedNonnull - | ProvisionallyNullable - | StrictNonnull -[@@deriving compare, equal] - -type pair = t * t [@@deriving compare, equal] - -let top = Nullable - -let join x y = - match (x, y) with - | Null, Null -> - Null - | Null, _ | _, Null -> - Nullable - | Nullable, _ | _, Nullable -> - Nullable - | ThirdPartyNonnull, _ | _, ThirdPartyNonnull -> - ThirdPartyNonnull - | ProvisionallyNullable, _ | _, ProvisionallyNullable -> - ProvisionallyNullable - | UncheckedNonnull, _ | _, UncheckedNonnull -> - UncheckedNonnull - | LocallyTrustedNonnull, _ | _, LocallyTrustedNonnull -> - LocallyTrustedNonnull - | LocallyCheckedNonnull, _ | _, LocallyCheckedNonnull -> - LocallyCheckedNonnull - | StrictNonnull, StrictNonnull -> - StrictNonnull - - -let is_subtype ~subtype ~supertype = equal (join subtype supertype) supertype - -let is_considered_nonnull ~nullsafe_mode nullability = - if equal ProvisionallyNullable nullability then - (* Deliberately ignoring modes for this type - (ProvisionallyNullable is currently not indented to work well with all features for non-default modes) *) - true - else - let least_required = - match nullsafe_mode with - | NullsafeMode.Strict -> - StrictNonnull - | NullsafeMode.Local (NullsafeMode.Trust.Only trust_list) - when NullsafeMode.Trust.is_trust_none trust_list -> - (* Though "trust none" is technically a subcase of trust some, - we need this pattern to be different from the one below so we can detect possible - promotions from "trust some" to "trust none" *) - LocallyCheckedNonnull - | NullsafeMode.Local (NullsafeMode.Trust.Only _) -> - LocallyTrustedNonnull - | NullsafeMode.Local NullsafeMode.Trust.All -> - UncheckedNonnull - | NullsafeMode.Default -> - (* We trust all internal method declarations, and, if specified separately, - even not annotated third party. *) - if Config.nullsafe_optimistic_third_party_in_default_mode then ThirdPartyNonnull - else UncheckedNonnull - in - is_subtype ~subtype:nullability ~supertype:least_required - - -let is_nonnullish t = is_considered_nonnull ~nullsafe_mode:NullsafeMode.Default t - -let to_string = function - | Null -> - "Null" - | Nullable -> - "Nullable" - | ProvisionallyNullable -> - "ProvisionallyNullable" - | ThirdPartyNonnull -> - "ThirdPartyNonnull" - | UncheckedNonnull -> - "UncheckedNonnull" - | LocallyTrustedNonnull -> - "LocallyTrustedNonnull" - | LocallyCheckedNonnull -> - "LocallyCheckedNonnull" - | StrictNonnull -> - "StrictNonnull" - - -let pp fmt t = F.fprintf fmt "%s" (to_string t) diff --git a/infer/src/nullsafe/Nullability.mli b/infer/src/nullsafe/Nullability.mli deleted file mode 100644 index 57920d746b7..00000000000 --- a/infer/src/nullsafe/Nullability.mli +++ /dev/null @@ -1,76 +0,0 @@ -(* - * 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. - *) - -open! IStd -module F = Format - -(** Nullability is a central concept for Nullsafe type-checker. Informally, nullability is a "type" - \- set of values together with some additional context. All nullsafe is interested about if - whether a value can be null, and if it can, what are potential causes of leaking the null inside - it. Formally, nullability values form a lattice with partial order and upper bound operations. - Practically, most of nullsafe core should remain agnostic over exact values of nullability (e.g. - pattern-mathching over them should be a rare case. Core of typechecker should deal with - subtyping and joins instead.) *) - -type t = - | Null (** The only possible value for that type is null *) - | Nullable (** No guarantees on the nullability *) - | ThirdPartyNonnull - (** Values coming from third-party methods and fields not explictly annotated neither as - [@Nullable], nor as [@Nonnull]. We still consider those as non-nullable but with the least - level of confidence. *) - | UncheckedNonnull - (** The type comes from a signature that is annotated (explicitly or implicitly according to - conventions) as non-nullable. Hovewer, the class is not checked under [@Nullsafe], so its - actual nullability is not truthworhy, as the class might contain arbitrary number of - nullability issues *) - | LocallyTrustedNonnull - (** The same as [UncheckedNonnull], but the value comes from a class explicitly listed as - trusted in the class under analysis. It is less truthworthy than [LocallyCheckedNonnull] - as no actual verification were performed. *) - | LocallyCheckedNonnull - (** Non-nullable value that comes from a class checked under [@NullsafeLocal] mode. Local mode - type-checks files against its dependencies but does not require the dependencies to be - transitively checked. Therefore this type of non-nullable value is differentiated from - StrictNonnull. *) - | ProvisionallyNullable - (** Only for "annotation graph" mode. Indicates the value coming from a not annotated object - in the class under analysis. Since the object is not annotated, it should be treated as - non-null for all cases related to user-facing issue generation. *) - | StrictNonnull - (** Non-nullable value with the highest degree of certainty, because it is either: - - - a non-null literal, - - an expression that semantically cannot be null, - - comes from code checked under [@NullsafeStrict] mode, - - comes from a third-party method with an appropriate built-in model, user-defined - nullability signature, or explicit [@NonNull] annotation. - - The latter two are potential sources of unsoundness issues for nullsafe, but we need to - strike the balance between the strictness of analysis, convenience, and real-world risk. *) -[@@deriving compare, equal] - -type pair = t * t [@@deriving compare, equal] - -val top : t -(** The most generic type. *) - -val is_subtype : subtype:t -> supertype:t -> bool -(** A is a subtype of B, if all values of A can be represented in B. Subtype relation is reflexive: - everything is a subtype of itself. *) - -val join : t -> t -> t -(** Unique upper bound over two types: the most precise type that is a supertype of both. - Practically, joins occur e.g. when two branches of execution flow are getting merged. *) - -val is_considered_nonnull : nullsafe_mode:NullsafeMode.t -> t -> bool -(** Check whether a given nullability is considered non-nullable within a given [nullsafe_mode]. *) - -val is_nonnullish : t -> bool -(** Check whether a given nullability is one of the non-nullable types with no regards to the mode. *) - -val pp : F.formatter -> t -> unit diff --git a/infer/src/nullsafe/Nullsafe.mld b/infer/src/nullsafe/Nullsafe.mld deleted file mode 100644 index 8024776f281..00000000000 --- a/infer/src/nullsafe/Nullsafe.mld +++ /dev/null @@ -1,8 +0,0 @@ -{0 Nullsafe [@Nullable] Checker} - -Nullsafe (also called Eradicate) is a type checker for [@Nullable] -annotations in Java. The goal is to eradicate null pointer -exceptions. See the online docs at -{:http://fbinfer.com/docs/eradicate}. - -All modules: {!module-Nullsafe}. diff --git a/infer/src/nullsafe/NullsafeInit.ml b/infer/src/nullsafe/NullsafeInit.ml deleted file mode 100644 index 78c27ab0fe2..00000000000 --- a/infer/src/nullsafe/NullsafeInit.ml +++ /dev/null @@ -1,51 +0,0 @@ -(* - * 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. - *) -open! IStd - -let load_third_party_repo ~absolute_path_to_repo_dir = - match Sys.is_directory absolute_path_to_repo_dir with - | `Yes -> ( - match ThirdPartyAnnotationInfoLoader.load ~path_to_repo_dir:absolute_path_to_repo_dir with - | Ok storage -> - storage - | Error error -> - Logging.die Logging.InternalError "Error while reading 3rd party annotation repo: %a" - ThirdPartyAnnotationInfoLoader.pp_load_error error ) - | _ -> - Logging.die Logging.InternalError - "Could not locate 3rd party annotation repository: expected location %s" - absolute_path_to_repo_dir - - -let get_absolute_path_to_repo_dir path_to_repo_dir = - if Filename.is_absolute path_to_repo_dir then - (* By agreement, this means absolute path *) - path_to_repo_dir - else - (* By agreement, this means path relative to inferconfig dir *) - match Config.inferconfig_dir with - | None -> - Logging.die Logging.InternalError - "Could not locate .inferconfig directory, which is required for resolving the path to \ - third party annotation repository" - | Some inferconfig_dir -> - inferconfig_dir ^/ path_to_repo_dir - - -let create_global_storage () = - match Config.nullsafe_third_party_signatures with - | Some path_to_repo_dir -> - let absolute_path_to_repo_dir = get_absolute_path_to_repo_dir path_to_repo_dir in - (load_third_party_repo ~absolute_path_to_repo_dir, Some absolute_path_to_repo_dir) - (* Create empty *) - | None -> - (ThirdPartyAnnotationInfo.create_storage (), None) - - -let init () = - let storage, absolute_path_to_repo = create_global_storage () in - ThirdPartyAnnotationGlobalRepo.initialize ~absolute_path_to_repo storage diff --git a/infer/src/nullsafe/NullsafeInit.mli b/infer/src/nullsafe/NullsafeInit.mli deleted file mode 100644 index 91a1c46247b..00000000000 --- a/infer/src/nullsafe/NullsafeInit.mli +++ /dev/null @@ -1,10 +0,0 @@ -(* - * 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. - *) -open! IStd - -val init : unit -> unit -(** This function should be called once before nullsafe checker is called *) diff --git a/infer/src/nullsafe/NullsafeIssue.ml b/infer/src/nullsafe/NullsafeIssue.ml deleted file mode 100644 index 342bad860f8..00000000000 --- a/infer/src/nullsafe/NullsafeIssue.ml +++ /dev/null @@ -1,165 +0,0 @@ -(* - * 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. - *) - -open! IStd - -type parameter_not_nullable_info = - {param_index: int; proc_name: Procname.Java.t (** Offending method called *)} - -type t = - { issue_type: IssueType.t - ; description: string (** Human-readable description *) - ; loc: Location.t (** Where to report the error *) - ; field_name: Fieldname.t option (** If the issue is about a field, here's this field *) - ; inconsistent_param_index: int option - (** Only for "inconsistent subclass param annotation" issue *) - ; parameter_not_nullable_info: parameter_not_nullable_info option - (** Only for "Parameter Not Nullable" issue *) - ; severity: IssueType.severity - ; nullable_methods: TypeOrigin.method_call_origin list - (** If the issue is associated with misusing nullable values coming from method calls, - here's the list *) - ; third_party_dependent_methods: (Procname.Java.t * AnnotatedSignature.t) list } - -let make ~issue_type ~description ~loc ~severity ~field_name = - { issue_type - ; description - ; loc - ; inconsistent_param_index= None - ; parameter_not_nullable_info= None - ; severity - ; third_party_dependent_methods= [] - ; nullable_methods= [] - ; field_name } - - -let with_third_party_dependent_methods methods t = {t with third_party_dependent_methods= methods} - -let with_nullable_methods methods t = {t with nullable_methods= methods} - -let with_inconsistent_param_index index t = {t with inconsistent_param_index= index} - -let with_parameter_not_nullable_info ~param_index ~proc_name t = - {t with parameter_not_nullable_info= Some {param_index; proc_name}} - - -let get_issue_type {issue_type} = issue_type - -let get_description {description} = description - -let get_loc {loc} = loc - -let get_severity {severity} = severity - -let to_third_party_nullability = function - | AnnotatedNullability.Nullable _ -> - ThirdPartyMethod.Nullable - | _ -> - ThirdPartyMethod.Nonnull - - -(* Given a third party method, convert it to `.sig` format according to the current - source file annotations *) -let to_third_party_method_according_to_source_code_annotations (proc_name, annotated_signature) = - let ThirdPartyAnnotationInfo.{class_name; method_name; param_types} = - ThirdPartyAnnotationInfo.unique_repr_of_java_proc_name proc_name - in - (* We need to provide annotations for return value and param. - Do it according to the current annotations from the source code - (assuming everything not @Nullable is non-null). - *) - let ret_nullability = - annotated_signature.AnnotatedSignature.ret.ret_annotated_type.nullability - |> to_third_party_nullability - in - let params_nullability = - AnnotatedSignature.get_non_virtual_params annotated_signature - |> List.map ~f:(fun AnnotatedSignature.{param_annotated_type= {nullability}} -> - to_third_party_nullability nullability ) - in - let params = - match List.zip param_types params_nullability with - | Ok params -> - params - | Unequal_lengths -> - (* This can happen for some synthetic methods. In this case just fallback to non-nullable annotations. *) - List.map param_types ~f:(fun typ -> (typ, ThirdPartyMethod.Nonnull)) - in - ThirdPartyMethod.{class_name; method_name; ret_nullability; params} - - -let to_nullable_method_json nullable_methods = - List.map nullable_methods ~f:(fun TypeOrigin.{pname; call_loc} -> - Jsonbug_t. - { class_name= Procname.Java.get_simple_class_name pname - ; method_name= Procname.Java.get_method pname - ; package= Procname.Java.get_package pname |> Option.value ~default:"" - ; call_line= call_loc.Location.line } ) - - -let java_type_to_string java_type = Pp.string_of_pp (Typ.pp_java ~verbose:true) java_type - -let get_params_string_list procname = - Procname.Java.get_parameters procname |> List.map ~f:java_type_to_string - - -let parameter_not_nullable_info_to_json {param_index; proc_name} : - Jsonbug_t.parameter_not_nullable_info = - let package_name = Procname.Java.get_package proc_name in - let class_name = Procname.Java.get_simple_class_name proc_name in - let method_info = - Jsonbug_t.{name= Procname.Java.get_method proc_name; params= get_params_string_list proc_name} - in - Jsonbug_t.{class_name; package_name; method_info; param_index} - - -let get_nullsafe_extra - { third_party_dependent_methods - ; nullable_methods - ; inconsistent_param_index - ; parameter_not_nullable_info - ; field_name } proc_name = - let class_name = Procname.Java.get_simple_class_name proc_name in - let package = Procname.Java.get_package proc_name in - let unvetted_3rd_party_list = - List.map third_party_dependent_methods - ~f:to_third_party_method_according_to_source_code_annotations - |> List.map ~f:ThirdPartyMethod.to_canonical_string - in - let unvetted_3rd_party = - if List.is_empty unvetted_3rd_party_list then None else Some unvetted_3rd_party_list - in - let nullable_methods = - if List.is_empty nullable_methods then None else Some (to_nullable_method_json nullable_methods) - in - let field = - Option.map field_name ~f:(fun field_name -> - let field = Fieldname.get_field_name field_name in - let class_typ_name = Fieldname.get_class_name field_name in - let java_class_name = Typ.Name.Java.get_java_class_name_exn class_typ_name in - Jsonbug_t. - { class_name= JavaClassName.classname java_class_name - ; package_name= JavaClassName.package java_class_name - ; field } ) - in - let method_params = get_params_string_list proc_name in - let method_info = Jsonbug_t.{name= Procname.Java.get_method proc_name; params= method_params} in - let parameter_not_nullable_info = - Option.map ~f:parameter_not_nullable_info_to_json parameter_not_nullable_info - in - Jsonbug_t. - { class_name - ; package - ; method_info= Some method_info - ; inconsistent_param_index - ; parameter_not_nullable_info - ; meta_issue_info= None - ; unvetted_3rd_party - ; nullable_methods - ; field - ; annotation_graph= None - ; redundant_fixme_info= None } diff --git a/infer/src/nullsafe/NullsafeIssue.mli b/infer/src/nullsafe/NullsafeIssue.mli deleted file mode 100644 index 4af5016b412..00000000000 --- a/infer/src/nullsafe/NullsafeIssue.mli +++ /dev/null @@ -1,40 +0,0 @@ -(* - * 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. - *) -open! IStd - -(** Information about the nullsafe issue to be reported to the user / put into the result json *) - -type t - -val make : - issue_type:IssueType.t - -> description:string - -> loc:Location.t - -> severity:IssueType.severity - -> (* If the issue is about a field (e.g. field not nullable etc.), here's this field *) - field_name:Fieldname.t option - -> t - -val with_third_party_dependent_methods : (Procname.Java.t * AnnotatedSignature.t) list -> t -> t - -val with_nullable_methods : TypeOrigin.method_call_origin list -> t -> t - -val with_inconsistent_param_index : int option -> t -> t -(** Only for the "Inconsistent subclass param annotation" issue *) - -val with_parameter_not_nullable_info : param_index:int -> proc_name:Procname.Java.t -> t -> t -(** Only for the "Paremeter not nullable" issue *) - -val get_issue_type : t -> IssueType.t - -val get_description : t -> string - -val get_loc : t -> Location.t - -val get_severity : t -> IssueType.severity - -val get_nullsafe_extra : t -> Procname.Java.t -> Jsonbug_t.nullsafe_extra diff --git a/infer/src/nullsafe/NullsafeMode.ml b/infer/src/nullsafe/NullsafeMode.ml deleted file mode 100644 index cd5a67cc5cb..00000000000 --- a/infer/src/nullsafe/NullsafeMode.ml +++ /dev/null @@ -1,255 +0,0 @@ -(* - * 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. - *) - -open! IStd -module F = Format - -module Trust = struct - type trust_list = JavaClassName.Set.t [@@deriving compare, equal] - - type t = All | Only of trust_list [@@deriving compare, equal] - - let is_trust_none trust_list = JavaClassName.Set.is_empty trust_list - - let none = Only JavaClassName.Set.empty - - let extract_trust_list = function - | Annot.Array class_values -> - (* The only elements of this array can be class names; therefore short-circuit and return None if it's not the case. *) - let trust_list = - IList.traverse_opt class_values ~f:(fun el -> - match el with - | Annot.Class class_typ -> - Typ.name class_typ - |> Option.map ~f:(fun name -> Typ.Name.Java.get_java_class_name_exn name) - | _ -> - None ) - in - Option.map trust_list ~f:JavaClassName.Set.of_list - | _ -> - None - - - let of_annot annot = - let open IOption.Let_syntax in - let trust_all = Annot.find_parameter annot ~name:"trustAll" in - let* trust_list = Annot.find_parameter annot ~name:"value" in - let* trust_classes = extract_trust_list trust_list in - match trust_all with - | None -> - return (Only trust_classes) - | Some (Annot.Bool trust_all') -> - if trust_all' then return All else return (Only trust_classes) - | _ -> - None - - - let is_in_trust_list t name = - match t with - | All -> - (* We are interested only in explicit lists *) - false - | Only classes -> - JavaClassName.Set.exists (JavaClassName.equal name) classes - - - let is_stricter ~stricter ~weaker = - let is_stricter_trust_list stricter_set weaker_set = - (* stricter trust list should be a strict subset of the weaker one *) - JavaClassName.Set.cardinal stricter_set < JavaClassName.Set.cardinal weaker_set - && JavaClassName.Set.subset stricter_set weaker_set - in - match (stricter, weaker) with - | All, All | All, Only _ -> - false - | Only _, All -> - true - | Only stricter_trust_list, Only weaker_trust_list -> - is_stricter_trust_list stricter_trust_list weaker_trust_list - - - let intersect trust1 trust2 = - match (trust1, trust2) with - | Only classes, All -> - Only classes - | All, Only classes -> - Only classes - | Only list1, Only list2 -> - Only (JavaClassName.Set.inter list1 list2) - | All, All -> - All - - - let pp fmt t = - match t with - | All -> - F.fprintf fmt "all" - | Only names when JavaClassName.Set.is_empty names -> - F.fprintf fmt "none" - | Only _names -> - F.fprintf fmt "selected" -end - -type t = Default | Local of Trust.t | Strict [@@deriving compare, equal] - -let pp fmt t = - match t with - | Default -> - F.fprintf fmt "Def" - | Strict -> - F.fprintf fmt "Strict" - | Local trust -> - F.fprintf fmt "Local(trust=%a)" Trust.pp trust - - -let of_annot annot = - let open IOption.Let_syntax in - let* mode = Annot.find_parameter annot ~name:"value" in - match mode with - | Annot.Enum {value= "STRICT"} -> - return Strict - | Annot.Enum {value= "LOCAL"} -> ( - match Annot.find_parameter annot ~name:"trustOnly" with - | None -> - (* When trustOnly values is missing, the default is in effect, which is Trust.All *) - return (Local Trust.All) - | Some (Annot.Annot trustOnly') -> - let* trust = Trust.of_annot trustOnly' in - return (Local trust) - | Some _ -> - None ) - | _ -> - None - - -let extract_user_defined_class_name java_class_name = - (* Anonymous inner classes are not proper classes and can not be annotated. Refer to underlying user class *) - JavaClassName.get_user_defined_class_if_anonymous_inner java_class_name - |> Option.value ~default:java_class_name - - -let extract_mode_from_explicit_class_annotation tenv classname = - match PatternMatch.type_name_get_annotation tenv (Typ.JavaClass classname) with - | Some annots -> ( - if Annotations.ia_is_nullsafe_strict annots then Strict - else - match Annotations.ia_find_nullsafe annots with - | Some nullsafe_annot -> - Option.value (of_annot nullsafe_annot) ~default:Default - | _ -> - Default ) - | None -> - Default - - -(** Get the minimal mode that is stricter or equal than both of given modes *) -let intersect mode1 mode2 = - match (mode1, mode2) with - | Strict, _ | _, Strict -> - Strict - | Local trust1, Local trust2 -> - Local (Trust.intersect trust1 trust2) - | Local trust, Default | Default, Local trust -> - Local trust - | Default, Default -> - Default - - -let of_class tenv class_name = - (* The mode of the class is the strictest over this class's mode annotation and its outer classes *) - let rec of_class_and_outer_classes class_name = - let curr_class_mode = extract_mode_from_explicit_class_annotation tenv class_name in - match JavaClassName.get_outer_class_name class_name with - | Some outer_name -> - intersect curr_class_mode (of_class_and_outer_classes outer_name) - | None -> - curr_class_mode - in - let user_defined_class = extract_user_defined_class_name class_name in - of_class_and_outer_classes user_defined_class - - -let of_java_procname tenv pname = - let class_name = Procname.Java.get_class_type_name pname in - of_class tenv (Typ.Name.Java.get_java_class_name_exn class_name) - - -let of_procname tenv pname = - match pname with - | Procname.Java jn -> - of_java_procname tenv jn - | _ -> - Logging.die InternalError "Unexpected non-Java procname %a" Procname.pp pname - - -let is_stricter_than ~stricter ~weaker = - let strict_level mode = match mode with Default -> 0 | Local _ -> 1 | Strict -> 2 in - match (stricter, weaker) with - | Local stricter_trust, Local weaker_trust -> - Trust.is_stricter ~stricter:stricter_trust ~weaker:weaker_trust - | _ -> - strict_level stricter > strict_level weaker - - -type nested_class_annotation_problem = - | RedundantNestedClassAnnotation - | NestedModeIsWeaker of weak_type - -and weak_type = ExtraTrustClass of JavaClassName.t list | Other - -let check_problematic_class_annotation tenv user_defined_class = - if JavaClassName.is_anonymous_inner_class_name user_defined_class then - Logging.die InternalError - "check_problematic_class_annotation: should not be called for anonymous classes, but was \ - called for %a" - JavaClassName.pp user_defined_class ; - Option.value_map - (JavaClassName.get_outer_class_name user_defined_class) - ~f:(fun outer_class_name -> - (* Check if the mode of the nested class contradicts the outer's one or is redundant *) - let nested_mode = extract_mode_from_explicit_class_annotation tenv user_defined_class in - if equal Default nested_mode then (* Nested class is not explicitly annotated. *) - Ok () - else - let outer_mode = of_class tenv outer_class_name in - if equal nested_mode outer_mode then Error RedundantNestedClassAnnotation - else - match (nested_mode, outer_mode) with - | Local (Trust.Only nested_trust_list), Local (Trust.Only outer_trust_list) -> - (* Special processing for pair of two Trust(some). - The problem if when there is an attempt to introduce additional trust in nested that did not exist - in outer. - *) - let extra_trusted_classes_in_nested = - JavaClassName.Set.diff nested_trust_list outer_trust_list - in - if not (JavaClassName.Set.is_empty extra_trusted_classes_in_nested) then - Error - (NestedModeIsWeaker - (ExtraTrustClass (JavaClassName.Set.elements extra_trusted_classes_in_nested)) - ) - else Ok () - | _ -> - if is_stricter_than ~stricter:outer_mode ~weaker:nested_mode then - Error (NestedModeIsWeaker Other) - else Ok () ) - ~default:(Ok ()) - - -let is_in_trust_list t name = - match t with Strict | Default -> false | Local trust -> Trust.is_in_trust_list trust name - - -let severity = function - | Strict | Local _ -> - (* Explicit @Nullsafe modes suppose that enforcement is made on CI side to not allow violations in the codebase. - Hence it should be an error. - *) - IssueType.Error - | Default -> - (* Enforcement is not supposed to be setup in default modes. *) - IssueType.Warning diff --git a/infer/src/nullsafe/NullsafeMode.mli b/infer/src/nullsafe/NullsafeMode.mli deleted file mode 100644 index 4fb90fea275..00000000000 --- a/infer/src/nullsafe/NullsafeMode.mli +++ /dev/null @@ -1,71 +0,0 @@ -(* - * 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. - *) - -open! IStd - -(** Represents a type-checking mode of nullsafe. *) - -module Trust : sig - [@@@warning "-unused-value-declaration"] - - type trust_list - - val is_trust_none : trust_list -> bool - - type t = All | Only of trust_list [@@deriving compare, equal] - - val none : t - - val of_annot : Annot.t -> t option - (** Returns [Trust.t] when provided annotation matches the format of [@TrustList], otherwise - [None]. *) - - val pp : Format.formatter -> t -> unit -end - -type t = Default | Local of Trust.t | Strict [@@deriving compare, equal] - -val of_annot : Annot.t -> t option - [@@warning "-unused-value-declaration"] -(** Returns [t] when provided annotation matches the format of [@Nullsafe], otherwise [None]. *) - -val of_class : Tenv.t -> JavaClassName.t -> t -(** Extracts mode information from class annotations *) - -val of_procname : Tenv.t -> Procname.t -> t -(** Extracts mode information from a class where procname is defined. Should be called for Java - procnames only; throws otherwise *) - -val of_java_procname : Tenv.t -> Procname.Java.t -> t -(** Extracts mode information from a class where procname is defined. Should be called for Java - procnames only; throws otherwise *) - -val is_in_trust_list : t -> JavaClassName.t -> bool -(** Check whether [JavaClassName.t] is in explicit trust list specified in the mode *) - -val is_stricter_than : stricter:t -> weaker:t -> bool -(** Check whether [stricter] is (strongly) stricter than [weaker] *) - -val severity : t -> IssueType.severity -(** Provides a default choice of issue severity for a particular mode. Rule is: severity should be - ERROR if and only if it is enforced. *) - -val pp : Format.formatter -> t -> unit - -type nested_class_annotation_problem = - | RedundantNestedClassAnnotation - (** Nested mode is explicitly annotated exactly like the outer one. *) - | NestedModeIsWeaker of weak_type (** Attempt to relax the mode imposed in the outer class. *) - -and weak_type = - | ExtraTrustClass of JavaClassName.t list (** Nested class has this extra list of classes *) - | Other - -val check_problematic_class_annotation : - Tenv.t -> JavaClassName.t -> (unit, nested_class_annotation_problem) result -(** Given a (not anonymous) class name, check if there are semantic problems with [@Nullsafe] mode - annotation for this class *) diff --git a/infer/src/nullsafe/NullsafeSummary.ml b/infer/src/nullsafe/NullsafeSummary.ml deleted file mode 100644 index 502ab136cfe..00000000000 --- a/infer/src/nullsafe/NullsafeSummary.ml +++ /dev/null @@ -1,14 +0,0 @@ -(* - * 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. - *) - -open! IStd - -(** Summary of per-procedure nullsafe result analysis *) - -type t = {issues: TypeErr.err_instance list} - -let pp fmt {issues} = Format.fprintf fmt "Issues: %d" (List.length issues) diff --git a/infer/src/nullsafe/NullsafeSummary.mli b/infer/src/nullsafe/NullsafeSummary.mli deleted file mode 100644 index a7086cbbbad..00000000000 --- a/infer/src/nullsafe/NullsafeSummary.mli +++ /dev/null @@ -1,17 +0,0 @@ -(* - * 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. - *) - -open! IStd - -(** Summary of per-procedure nullsafe result analysis *) - -type t = - { issues: TypeErr.err_instance list - (** List of issues found during analysis. Note that not all of them were necessarily - reported to the user (some of them might be deemed non actionable.) *) } - -val pp : Format.formatter -> t -> unit diff --git a/infer/src/nullsafe/OverAnnotatedRule.ml b/infer/src/nullsafe/OverAnnotatedRule.ml deleted file mode 100644 index e852dc8bc85..00000000000 --- a/infer/src/nullsafe/OverAnnotatedRule.ml +++ /dev/null @@ -1,45 +0,0 @@ -(* - * 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. - *) -open! IStd - -type violation = {declared_nullability: Nullability.t; can_be_narrowed_to: Nullability.t} -[@@deriving compare, equal] - -let check ~what ~by_rhs_upper_bound = - match (what, by_rhs_upper_bound) with - | Nullability.Nullable, Nullability.StrictNonnull - | Nullability.Nullable, Nullability.UncheckedNonnull -> - Error {declared_nullability= what; can_be_narrowed_to= by_rhs_upper_bound} - | Nullability.Null, Nullability.StrictNonnull | Nullability.Null, Nullability.UncheckedNonnull -> - (* Null, Nonnull pais is an edge case that we don't expect to arise in practice for two reasons: - 1. The only way to declare something as Null is to declare it is java.lang.Void, but nullsafe - does not know about it just yet. - 2. Even if it knew, such could should not compile: the only way it could compile if it returns `null` directly. - *) - Error {declared_nullability= what; can_be_narrowed_to= by_rhs_upper_bound} - | _ -> - Ok () - - -type violation_type = - | FieldOverAnnoted of Fieldname.t - | ReturnOverAnnotated of Procname.Java.t (** Return value of a method can be made non-nullable *) -[@@deriving compare, equal] - -let violation_description _ violation_type = - let module MF = MarkupFormatter in - let nullable_annotation = "@Nullable" in - match violation_type with - | FieldOverAnnoted field_name -> - Format.asprintf "Field %a is always initialized in the constructor but is declared %a" - MF.pp_monospaced - (Fieldname.to_simplified_string field_name) - MF.pp_monospaced nullable_annotation - | ReturnOverAnnotated proc_name -> - Format.asprintf "Method %a is annotated with %a but never returns null." MF.pp_monospaced - (Procname.Java.to_simplified_string proc_name) - MF.pp_monospaced nullable_annotation diff --git a/infer/src/nullsafe/OverAnnotatedRule.mli b/infer/src/nullsafe/OverAnnotatedRule.mli deleted file mode 100644 index 13310fdf95a..00000000000 --- a/infer/src/nullsafe/OverAnnotatedRule.mli +++ /dev/null @@ -1,32 +0,0 @@ -(* - * 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. - *) -open! IStd - -(** Checks if a type in signature (e.g. return value) can be made more specific. NOTE: This rule is - complementatary to assignment rule. While assignment rule checks a single assignment `lhs = - rhs`, this rule checks checks ALL assignments to `lhs` in the program. NOTE: Violation of this - rule is not a type violation, hence it should never be surfaced as error: `lhs`'s type can be - intentionally made broad by code author (e.g. to anticipate future changes in the - implementation). Heuristits are required to correctly surface overannotated rule to the user. - This rule is useful for some scenarios, especially for nullability code conversions when it is - expected that some signatures were annotated with [@Nullable] defensively, so surfacing such - cases can improve API and make migration smooth. *) - -type violation [@@deriving compare, equal] - -val check : what:Nullability.t -> by_rhs_upper_bound:Nullability.t -> (unit, violation) result -(** Checks if the declared type for `what` can be narrowed, based on the information about all - assignments `what = rhs_i`. If an upper bound of `rhs_i` over ALL assignents `what = rhs_i` that - exist in the program is a _strict_ subtype of lhs, `lhs`'s type can be narrowed to be that upper - bound. *) - -type violation_type = - | FieldOverAnnoted of Fieldname.t - | ReturnOverAnnotated of Procname.Java.t (** Return value of a method can be made non-nullable *) -[@@deriving compare, equal] - -val violation_description : violation -> violation_type -> string diff --git a/infer/src/nullsafe/ProvisionalAnnotation.ml b/infer/src/nullsafe/ProvisionalAnnotation.ml deleted file mode 100644 index 3fd92183e7f..00000000000 --- a/infer/src/nullsafe/ProvisionalAnnotation.ml +++ /dev/null @@ -1,21 +0,0 @@ -(* - * 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. - *) -open! IStd - -type t = - | Field of {field_name: Fieldname.t} - | Method of Procname.Java.t - | Param of {method_info: Procname.Java.t; num: int} -[@@deriving compare, equal] - -let pp fmt = function - | Field {field_name} -> - Format.fprintf fmt "Field(%a)" Fieldname.pp field_name - | Method proc_name -> - Format.fprintf fmt "Method(%a)" Procname.pp (Procname.Java proc_name) - | Param {method_info; num} -> - Format.fprintf fmt "Param(%d, %a)" num Procname.pp (Procname.Java method_info) diff --git a/infer/src/nullsafe/ProvisionalAnnotation.mli b/infer/src/nullsafe/ProvisionalAnnotation.mli deleted file mode 100644 index 98de14d6174..00000000000 --- a/infer/src/nullsafe/ProvisionalAnnotation.mli +++ /dev/null @@ -1,20 +0,0 @@ -(* - * 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. - *) -open! IStd - -(** Provisional annotation is an imaginary [@Nullable] annotation that is added to a class element. - * (It implies the corresponding element is NOT annotated as [@Nullable] in the source code.) * - The purpose of provisional annotation is to compute Nullability graph: what would happen if such - and such * element was annotated as [@Nullable]. *) - -type t = - | Field of {field_name: Fieldname.t} - | Method of Procname.Java.t - | Param of {method_info: Procname.Java.t; num: int} -[@@deriving compare, equal] - -val pp : Format.formatter -> t -> unit diff --git a/infer/src/nullsafe/ProvisionalViolation.ml b/infer/src/nullsafe/ProvisionalViolation.ml deleted file mode 100644 index b1c851a75b6..00000000000 --- a/infer/src/nullsafe/ProvisionalViolation.ml +++ /dev/null @@ -1,25 +0,0 @@ -(* - * 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. - *) - -open! IStd - -type t = - | Assignment of AssignmentRule.ProvisionalViolation.t - | Dereference of DereferenceRule.ProvisionalViolation.t - -let of_issue = function - | TypeErr.Condition_redundant _ - | TypeErr.Field_not_initialized _ - | TypeErr.Inconsistent_subclass _ - | TypeErr.Over_annotation _ -> - None - | TypeErr.Nullable_dereference {dereference_violation} -> - DereferenceRule.ProvisionalViolation.from dereference_violation - |> Option.map ~f:(fun violation -> Dereference violation) - | TypeErr.Bad_assignment {assignment_violation} -> - AssignmentRule.ProvisionalViolation.from assignment_violation - |> Option.map ~f:(fun violation -> Assignment violation) diff --git a/infer/src/nullsafe/ProvisionalViolation.mli b/infer/src/nullsafe/ProvisionalViolation.mli deleted file mode 100644 index 498eb4b55e4..00000000000 --- a/infer/src/nullsafe/ProvisionalViolation.mli +++ /dev/null @@ -1,17 +0,0 @@ -(* - * 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. - *) - -open! IStd - -(** A simple wrapper over provisional violations for Rules *) - -type t = - | Assignment of AssignmentRule.ProvisionalViolation.t - | Dereference of DereferenceRule.ProvisionalViolation.t - -val of_issue : TypeErr.err_instance -> t option -(** If the nullsafe issue is associated with a provisional violation, return it *) diff --git a/infer/src/nullsafe/ThirdPartyAnnotationGlobalRepo.ml b/infer/src/nullsafe/ThirdPartyAnnotationGlobalRepo.ml deleted file mode 100644 index 1974580351a..00000000000 --- a/infer/src/nullsafe/ThirdPartyAnnotationGlobalRepo.ml +++ /dev/null @@ -1,53 +0,0 @@ -(* - * 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. - *) - -open! IStd - -type repo_with_metadata = - {repo: ThirdPartyAnnotationInfo.storage; absolute_path_to_repo: string option} - -let repo_with_metadata = ref None - -let initialize ~absolute_path_to_repo repo = - match !repo_with_metadata with - | None -> - repo_with_metadata := Some {absolute_path_to_repo; repo} - | Some _ -> - Logging.die Logging.InternalError "Attempt to initialize global 3rd party repository twice" - - -let get_repo_with_metadata () = - match !repo_with_metadata with - | None -> - Logging.die Logging.InternalError "Attempt to access not initialized 3rd party repository" - | Some repo_with_metadata -> - repo_with_metadata - - -let get_repo () = (get_repo_with_metadata ()).repo - -let get_absolute_path_to_repo () = (get_repo_with_metadata ()).absolute_path_to_repo - -let get_last_2_dirs path = - let dirs = String.split path ~on:'/' in - let last_2_dirs = List.drop dirs (List.length dirs - 2) in - String.concat last_2_dirs ~sep:"/" - - -let get_from_absolute_path () = - (* If we got file_name from somewhere, it definitely means the repo exists. *) - Option.value_exn (get_absolute_path_to_repo ()) - (* Enough details to have an idea where to look *) - |> get_last_2_dirs - - -let get_user_friendly_third_party_sig_file_name ~filename = - let path = - Config.nullsafe_third_party_location_for_messaging_only - |> Option.value ~default:(get_from_absolute_path ()) - in - path ^ "/" ^ filename diff --git a/infer/src/nullsafe/ThirdPartyAnnotationGlobalRepo.mli b/infer/src/nullsafe/ThirdPartyAnnotationGlobalRepo.mli deleted file mode 100644 index 243fdeb6ffa..00000000000 --- a/infer/src/nullsafe/ThirdPartyAnnotationGlobalRepo.mli +++ /dev/null @@ -1,20 +0,0 @@ -(* - * 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. - *) - -open! IStd - -(** A singleton. Should be initialized once prior to start of nullsafe. *) - -val initialize : absolute_path_to_repo:string option -> ThirdPartyAnnotationInfo.storage -> unit -(** Should be initialized exactly once before access. Path to repo is None if there is no repo on - the disk. *) - -val get_repo : unit -> ThirdPartyAnnotationInfo.storage -(** Can be accessed only when initialization was done *) - -val get_user_friendly_third_party_sig_file_name : filename:string -> string -(** Given filename containing the repo signatures, render it in a way convenient for the user *) diff --git a/infer/src/nullsafe/ThirdPartyAnnotationInfo.ml b/infer/src/nullsafe/ThirdPartyAnnotationInfo.ml deleted file mode 100644 index a72275127b8..00000000000 --- a/infer/src/nullsafe/ThirdPartyAnnotationInfo.ml +++ /dev/null @@ -1,135 +0,0 @@ -(* - * 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. - *) -open! IStd -module Hashtbl = Caml.Hashtbl - -type signature_info = {filename: string; line_number: int; signature: ThirdPartyMethod.t} - -type unique_repr = - { class_name: ThirdPartyMethod.fully_qualified_type - ; method_name: ThirdPartyMethod.method_name - ; param_types: ThirdPartyMethod.fully_qualified_type list } -[@@deriving sexp] - -let pp_unique_repr fmt signature = Sexp.pp fmt (sexp_of_unique_repr signature) - -let unique_repr_of_signature ThirdPartyMethod.{class_name; method_name; params} = - {class_name; method_name; param_types= List.map params ~f:(fun (param_type, _) -> param_type)} - - -let java_type_to_string java_type = Pp.string_of_pp (Typ.pp_java ~verbose:true) java_type - -let unique_repr_of_java_proc_name java_proc_name = - let class_name = Procname.Java.get_class_name java_proc_name in - let method_name = - if Procname.Java.is_constructor java_proc_name then ThirdPartyMethod.Constructor - else ThirdPartyMethod.Method (Procname.Java.get_method java_proc_name) - in - let param_types = - Procname.Java.get_parameters java_proc_name |> List.map ~f:java_type_to_string - in - {class_name; method_name; param_types} - - -type storage = {signature_map: signature_map; filenames: string list} - -and signature_map = (unique_repr, signature_info) Hashtbl.t - -let create_storage () = {signature_map= Hashtbl.create 1; filenames= []} - -type file_parsing_error = - {line_number: int; unparsable_method: string; parsing_error: ThirdPartyMethod.parsing_error} - -let pp_parsing_error fmt {line_number; unparsable_method; parsing_error} = - Format.fprintf fmt "Line %d: Could not parse method '%s': %s" line_number unparsable_method - (ThirdPartyMethod.string_of_parsing_error parsing_error) - - -(* Consequtively evaluates results for all elements in a list, - returns Ok (folded results) if all succeeded, or the first error. - The evaluator function [f] has access to element's index. -*) -let bind_list_with_index ~init list ~f = - List.foldi list ~init:(Ok init) ~f:(fun index acc elem -> - Result.bind acc ~f:(fun acc -> f acc index elem) ) - - -let is_whitespace_or_comment line = - let stripped_line = String.strip line in - String.is_empty stripped_line || String.is_prefix stripped_line ~prefix:"//" - - -let parse_line_and_add_to_storage signature_map ~filename ~line_index line = - let open Result in - if is_whitespace_or_comment line then Ok signature_map - else - ThirdPartyMethod.parse line - >>= fun signature -> - let key = unique_repr_of_signature signature in - Ok - ( Hashtbl.add signature_map key {filename; line_number= line_index + 1; signature} ; - signature_map ) - - -let add_from_signature_file storage ~filename ~lines = - (* each line in a file should represent a method signature *) - let open Result in - let new_filenames = storage.filenames @ [filename] in - bind_list_with_index lines ~init:storage.signature_map - ~f:(fun signature_map line_index method_as_str -> - parse_line_and_add_to_storage signature_map ~filename ~line_index method_as_str - |> Result.map_error ~f:(fun parsing_error -> - {line_number= line_index + 1; unparsable_method= method_as_str; parsing_error} ) ) - >>= fun new_map -> Ok {signature_map= new_map; filenames= new_filenames} - - -let find_nullability_info {signature_map} unique_repr = Hashtbl.find_opt signature_map unique_repr - -let does_package_match_file ~package sig_filename = - (* Filename should be of form , where is prefix - for third party packages that should be declared in this file *) - let allowed_package_prefix = String.chop_suffix_exn sig_filename ~suffix:".sig" in - String.is_prefix package ~prefix:allowed_package_prefix - - -let lookup_related_sig_file {filenames} ~package = - List.filter filenames ~f:(does_package_match_file ~package) - (* In case two different files match the package, we choose the most specific; - it will have the longest length *) - |> List.max_elt ~compare:(fun name1 name2 -> String.length name1 - String.length name2) - - -let lookup_related_sig_file_for_proc storage procname = - let package = Procname.Java.get_package procname in - Option.bind package ~f:(fun package -> lookup_related_sig_file storage ~package) - - -let is_third_party_proc storage procname = - let is_from_config = Procname.Java.is_external procname in - let lookup_sig_file _ = lookup_related_sig_file_for_proc storage procname in - is_from_config || Option.is_some (lookup_sig_file ()) - - -let is_third_party_class_name storage java_class_name = - IOption.Let_syntax.( - let is_from_config = JavaClassName.is_external_via_config java_class_name in - let lookup_sig_file _ = - let* package = JavaClassName.package java_class_name in - lookup_related_sig_file storage ~package - in - is_from_config || Option.is_some (lookup_sig_file ()) ) - - -(* There is a bit of duplication relative to [is_third_party_proc] due to mismatch between - [Typ.Name.Java] and [JavaClassName]. When those types are consolidated would be a good - idea to refactor this function. *) -let is_third_party_typ storage typ = - match Typ.name typ with - | Some (Typ.JavaClass java_class_name) -> - is_third_party_class_name storage java_class_name - | _ -> - false diff --git a/infer/src/nullsafe/ThirdPartyAnnotationInfo.mli b/infer/src/nullsafe/ThirdPartyAnnotationInfo.mli deleted file mode 100644 index bd9481bfb5a..00000000000 --- a/infer/src/nullsafe/ThirdPartyAnnotationInfo.mli +++ /dev/null @@ -1,62 +0,0 @@ -(* - * 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. - *) -open! IStd - -(** In-memory storage the information about nullability annotation of third-party methods. *) - -type signature_info = - { filename: string (** File where the particular signature is stored *) - ; line_number: int (** Line number with this signature *) - ; signature: ThirdPartyMethod.t } - -(** The minimum information that is needed to _uniquely_ identify the method. That why we don't - - - include e.g. return type, access quilifiers, or whether the method is static (because Java - - overload resolution rules ignore these things). In contrast, parameter types are essential, - - because Java allows several methods with different types. *) -type unique_repr = - { class_name: ThirdPartyMethod.fully_qualified_type - ; method_name: ThirdPartyMethod.method_name - ; param_types: ThirdPartyMethod.fully_qualified_type list } - -val pp_unique_repr : Format.formatter -> unique_repr -> unit - -val unique_repr_of_java_proc_name : Procname.Java.t -> unique_repr - -type storage - -val create_storage : unit -> storage - -type file_parsing_error = - {line_number: int; unparsable_method: string; parsing_error: ThirdPartyMethod.parsing_error} - -val pp_parsing_error : Format.formatter -> file_parsing_error -> unit - -val add_from_signature_file : - storage -> filename:string -> lines:string list -> (storage, file_parsing_error) result -(** Parse the information from the signature file, and add it to the storage *) - -val find_nullability_info : storage -> unique_repr -> signature_info option -(** The main method. Do we have an information about the third-party method? If we do not, or it is - not a third-party method, returns None. Otherwise returns the nullability information. *) - -val lookup_related_sig_file : storage -> package:string -> string option -(** If the package is third-party, return the relevant .sig file to add signatures for this package. *) - -val lookup_related_sig_file_for_proc : storage -> Procname.Java.t -> string option -(** If the function is third-party (based on its package), return relevant .sig file *) - -val is_third_party_proc : storage -> Procname.Java.t -> bool -(** Checks whether a required procname comes from third-party code based on available .sig files and - config flags. NOTE: considering config flags is done for compatibility with the legacy behaviour - and will be removed in the future *) - -val is_third_party_typ : storage -> Typ.t -> bool -(** See [is_third_party_proc]. *) - -val is_third_party_class_name : storage -> JavaClassName.t -> bool -(** See [is_third_party_proc]. *) diff --git a/infer/src/nullsafe/ThirdPartyAnnotationInfoLoader.ml b/infer/src/nullsafe/ThirdPartyAnnotationInfoLoader.ml deleted file mode 100644 index 644457bcf82..00000000000 --- a/infer/src/nullsafe/ThirdPartyAnnotationInfoLoader.ml +++ /dev/null @@ -1,28 +0,0 @@ -(* - * 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. - *) -open! IStd - -type load_error = {filename: string; parsing_error: ThirdPartyAnnotationInfo.file_parsing_error} - -let pp_load_error fmt {filename; parsing_error} = - Format.fprintf fmt "Could not parse %s: %a" filename ThirdPartyAnnotationInfo.pp_parsing_error - parsing_error - - -let add_from_file storage ~path_to_repo_dir ~sig_file = - let lines = In_channel.read_lines (path_to_repo_dir ^ "/" ^ sig_file) in - ThirdPartyAnnotationInfo.add_from_signature_file storage ~filename:sig_file ~lines - |> Result.map_error ~f:(fun parsing_error -> {filename= sig_file; parsing_error}) - - -let load ~path_to_repo_dir = - (* Sequentally load information from all .sig files *) - Sys.ls_dir path_to_repo_dir - |> List.filter ~f:(String.is_suffix ~suffix:".sig") - |> List.fold_result ~init:(ThirdPartyAnnotationInfo.create_storage ()) - ~f:(fun accumulated_storage sig_file -> - add_from_file accumulated_storage ~path_to_repo_dir ~sig_file ) diff --git a/infer/src/nullsafe/ThirdPartyAnnotationInfoLoader.mli b/infer/src/nullsafe/ThirdPartyAnnotationInfoLoader.mli deleted file mode 100644 index 051bc96b304..00000000000 --- a/infer/src/nullsafe/ThirdPartyAnnotationInfoLoader.mli +++ /dev/null @@ -1,15 +0,0 @@ -(* - * 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. - *) -open! IStd - -type load_error - -val pp_load_error : Format.formatter -> load_error -> unit - -val load : path_to_repo_dir:string -> (ThirdPartyAnnotationInfo.storage, load_error) result -(** Given a path to a repo with 3rd annotation info, loads it from a disk to in-memory - representation. After this is done, information can be requested via [ThirdPartyAnnotationInfo]. *) diff --git a/infer/src/nullsafe/ThirdPartyMethod.ml b/infer/src/nullsafe/ThirdPartyMethod.ml deleted file mode 100644 index ef8320dbd12..00000000000 --- a/infer/src/nullsafe/ThirdPartyMethod.ml +++ /dev/null @@ -1,186 +0,0 @@ -(* - * 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. - *) -open! IStd -open Result.Monad_infix - -type t = - { class_name: fully_qualified_type - ; method_name: method_name - ; ret_nullability: type_nullability - ; params: (fully_qualified_type * type_nullability) list } - -and fully_qualified_type = string [@@deriving sexp] - -and method_name = Constructor | Method of string - -and type_nullability = Nullable | Nonnull [@@deriving sexp] - -type parsing_error = - | BadStructure - | BadClassName - | BadMethodName - | BadReturnNullability - | BadParamList - | BadParam - -let string_of_parsing_error = function - | BadStructure -> - "Accepted format is #()[]" - | BadMethodName -> - "Method name should be a valid identifier" - | BadClassName -> - "Class name should be fully qualified, including package name" - | BadReturnNullability -> - "Unexpected string after the closing parenthesis, expected @Nullable" - | BadParamList -> - "Params should be separated by a comma, followed by a single space" - | BadParam -> - "Each param should have form of [@Nullable] " - - -let nullable_annotation = "@Nullable" - -let identifier_regexp = lazy (Str.regexp "[_a-zA-Z][_a-zA-Z0-9]*$") - -let class_regexp = - (* package should be a list of valid identifiers, separared by '.' *) - let package_name = "[_a-zA-Z][_a-zA-Z0-9\\.]*" in - (* class name should be a list of identifiers, separated by $ (a symbol for a nested class) *) - let class_name = "[_a-zA-Z][_a-zA-Z0-9\\$]*" in - lazy (Str.regexp (package_name ^ "\\." ^ class_name ^ "$")) - - -let type_regexp = - (* identifiers, possiblibly separated by `.` (package delimiter) or `$` (a symbol for a nested class) *) - lazy (Str.regexp "[_a-zA-Z][_a-zA-Z0-9\\$\\.]*") - - -let parse_class str = - if Str.string_match (Lazy.force class_regexp) str 0 then Ok str else Error BadClassName - - -let parse_param_type str = - if Str.string_match (Lazy.force type_regexp) str 0 then Ok str else Error BadParam - - -let parse_param str = - match String.split str ~on:' ' with - | [nullability_str; typ] -> - Result.ok_if_true (String.equal nullable_annotation nullability_str) ~error:BadParam - >>= fun _ -> parse_param_type typ >>= fun parsed_typ -> Ok (parsed_typ, Nullable) - | [typ] -> - parse_param_type typ >>= fun parsed_typ -> Ok (parsed_typ, Nonnull) - | [] -> - Error BadParamList - | _ -> - Error BadParam - - -(* Given a list of results and a binding function, returns Ok (results) if all results - are Ok or the first error if any of results has Error *) -let bind_list list_of_results ~f = - List.fold list_of_results ~init:(Ok []) ~f:(fun acc element -> - acc - >>= fun accumulated_success_results -> - f element >>= fun success_result -> Ok (accumulated_success_results @ [success_result]) ) - - -let strip_first_space str = - String.chop_prefix str ~prefix:" " |> Result.of_option ~error:BadParamList - - -let split_params str = - (* Empty case is the special one: lack of params mean an empty list, - not a list of a single empty string *) - if String.is_empty str then Ok [] - else - String.split str ~on:',' - |> List.mapi ~f:(fun param_index param_as_str -> (param_index, param_as_str)) - |> bind_list ~f:(fun (param_index, str) -> - match param_index with - | 0 -> - Ok str - | _ -> - (* Params should be separated by ", ", so we expect a space after each comma *) - strip_first_space str ) - - -let parse_params str = split_params str >>= fun params -> bind_list params ~f:parse_param - -let parse_method_name str = - match str with - | "" -> - Ok Constructor - | _ as method_name -> - if Str.string_match (Lazy.force identifier_regexp) method_name 0 then Ok (Method method_name) - else Error BadMethodName - - -let match_after_close_brace (split_result : Str.split_result list) = - (* After close brace there can be either nothing or return type nullability information *) - match split_result with - | [] -> - Ok Nonnull - | [Text nullability] -> - Result.ok_if_true - (String.equal (" " ^ nullable_annotation) nullability) - ~error:BadReturnNullability - >>= fun _ -> Ok Nullable - | _ -> - Error BadReturnNullability - - -let match_after_open_brace (split_result : Str.split_result list) = - (* We expect to see ), so let's look for `)` *) - ( match split_result with - | Text params :: Delim ")" :: rest -> - Ok (params, rest) - | Delim ")" :: rest -> - Ok ("", rest) - | _ -> - Error BadStructure ) - >>= fun (params, rest) -> - parse_params params - >>= fun parsed_params -> - match_after_close_brace rest >>= fun ret_nullability -> Ok (parsed_params, ret_nullability) - - -let hashsign_and_parentheses = lazy (Str.regexp "[#()]") - -let parse str = - (* Expected string is #(), - let's look what is between #, (, and ) *) - match Str.full_split (Lazy.force hashsign_and_parentheses) (String.rstrip str) with - | Text class_name_str :: Delim "#" :: Text method_name_str :: Delim "(" :: rest -> - parse_class class_name_str - >>= fun class_name -> - parse_method_name method_name_str - >>= fun method_name -> - match_after_open_brace rest - >>= fun (params, ret_nullability) -> Ok {class_name; method_name; ret_nullability; params} - | _ -> - Error BadStructure - - -let to_canonical_string - { class_name: fully_qualified_type - ; method_name: method_name - ; ret_nullability: type_nullability - ; params } = - let method_name_to_string = function Constructor -> "" | Method name -> name in - let param_to_string (typ, nullability) = - match nullability with Nullable -> Format.sprintf "@Nullable %s" typ | Nonnull -> typ - in - let param_list_to_string params = List.map params ~f:param_to_string |> String.concat ~sep:", " in - let ret_to_string = function Nullable -> " @Nullable" | Nonnull -> "" in - Format.sprintf "%s#%s(%s)%s" class_name - (method_name_to_string method_name) - (param_list_to_string params) (ret_to_string ret_nullability) - - -(* Pretty print exactly as the canonical representation, for convenience *) -let pp = Pp.of_string ~f:to_canonical_string diff --git a/infer/src/nullsafe/ThirdPartyMethod.mli b/infer/src/nullsafe/ThirdPartyMethod.mli deleted file mode 100644 index c3d5e85530f..00000000000 --- a/infer/src/nullsafe/ThirdPartyMethod.mli +++ /dev/null @@ -1,41 +0,0 @@ -(* - * 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. - *) - -(** A helper module responsible for representing nullability information for a single 3rd party - method, as well with functionality to read this information from the 3rd party nullability - repository. *) - -open! IStd - -type t = - { class_name: fully_qualified_type - ; method_name: method_name - ; ret_nullability: type_nullability - ; params: (fully_qualified_type * type_nullability) list } - -and fully_qualified_type = string [@@deriving sexp] - -and method_name = Constructor | Method of string [@@deriving sexp] - -and type_nullability = Nullable | Nonnull [@@deriving sexp] - -type parsing_error - -val string_of_parsing_error : parsing_error -> string - -val parse : string -> (t, parsing_error) result -(** Given a string representing nullability information for a given third-party method, return the - method signature and nullability of its params and return values. The string should come from a - repository storing 3rd party annotations. E.g. - ["package.name.Class$NestedClass#foo(package.name.SomeClass, @Nullable package.name.OtherClass) - @Nullable"] *) - -val to_canonical_string : t -> string - -val pp : Format.formatter -> t -> unit -(** String representation as it can be parsed via [parse] - #() *) diff --git a/infer/src/nullsafe/dune b/infer/src/nullsafe/dune deleted file mode 100644 index 7f55665f82f..00000000000 --- a/infer/src/nullsafe/dune +++ /dev/null @@ -1,33 +0,0 @@ -; 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. - -(library - (name Nullsafe) - (public_name infer.Nullsafe) - (flags - (:standard - -open - Core - -open - IR - -open - IStdlib - -open - IStd - -open - ATDGenerated - -open - IBase - -open - Absint - -open - Biabduction)) - (libraries core IStdlib ATDGenerated IBase IR Absint Biabduction) - (preprocess - (pps ppx_compare ppx_sexp_conv))) - -(documentation - (package infer) - (mld_files Nullsafe)) diff --git a/infer/src/nullsafe/eradicate.ml b/infer/src/nullsafe/eradicate.ml deleted file mode 100644 index 63aad247391..00000000000 --- a/infer/src/nullsafe/eradicate.ml +++ /dev/null @@ -1,257 +0,0 @@ -(* - * 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. - *) - -open! IStd -module L = Logging -module F = Format - -(* make sure that this is initialized *) -let () = NullsafeInit.init () - -let callback1 ({IntraproceduralAnalysis.proc_desc= curr_pdesc; _} as analysis_data) ~curr_java_pname - find_canonical_duplicate calls_this checks idenv annotated_signature linereader proc_loc : - bool * TypeState.t option = - let curr_pname = Procdesc.get_proc_name curr_pdesc in - let add_formal typestate (param_signature : AnnotatedSignature.param_signature) = - let pvar = Pvar.mk param_signature.mangled curr_pname in - let formal_name = param_signature.mangled in - let origin = - if Mangled.is_this formal_name then - (* `this` is technically an implicit method param, but from syntactic and semantic points of view it - has very special meaning and nullability limitations (it can never be null). - *) - TypeOrigin.This - (* An extra feature of Nullsafe. If the method overrides `Object.equals()`, but does not specify @Nullable annotation for the param, - a normal "inconsistent subclass parameter annotation" will be issued. - But apart from this we want to issue additional violations every time the value is used as a non-nullable. - This is to distinct "benign" cases where the value is simply not annotated from real bugs where e.g. the value is actually dereferenced. - We achieve this via having a special TypeOrigin for this param, which will be considered as nullable in relevant places. - *) - else if - PatternMatch.Java.is_override_of_lang_object_equals curr_pname - && (* Turn this feature off for annotation graph mode. In this mode not annotated, but potentially nullable params - are treated in special way, and this conflicts with the trick that is being made here. - *) - not Config.nullsafe_annotation_graph - then TypeOrigin.CurrMethodParameter ObjectEqualsOverride - else TypeOrigin.CurrMethodParameter (Normal param_signature) - in - let inferred_nullability = InferredNullability.create origin in - TypeState.add pvar - (param_signature.param_annotated_type.typ, inferred_nullability) - typestate ~descr:"registering formal param" - in - let get_initial_typestate () = - let typestate_empty = TypeState.empty in - List.fold ~f:add_formal ~init:typestate_empty annotated_signature.AnnotatedSignature.params - in - (* Check the nullable flag computed for the return value and report inconsistencies. *) - let check_return ~java_pname find_canonical_duplicate exit_node final_typestate - annotated_signature loc : unit = - let {AnnotatedSignature.ret_annotated_type} = annotated_signature.AnnotatedSignature.ret in - let ret_pvar = Procdesc.get_ret_var curr_pdesc in - let ret_range = TypeState.lookup_pvar ret_pvar final_typestate in - let typ_found_opt = - match ret_range with Some (typ_found, _) -> Some typ_found | None -> None - in - (* TODO(T54088319): model this in AnnotatedNullability *) - let ret_implicitly_nullable = - String.equal (PatternMatch.get_type_name ret_annotated_type.typ) "java.lang.Void" - in - AnalysisState.set_node exit_node ; - if not (List.is_empty checks.TypeCheck.check_ret_type) then - List.iter - ~f:(fun f -> f curr_pdesc ret_annotated_type.typ typ_found_opt loc) - checks.TypeCheck.check_ret_type ; - if checks.TypeCheck.eradicate then - EradicateChecks.check_return_annotation analysis_data ~java_pname find_canonical_duplicate - ret_range annotated_signature ret_implicitly_nullable loc - in - let do_before_dataflow initial_typestate = - if Config.eradicate_verbose then - L.result "Initial Typestate@\n%a@." TypeState.pp initial_typestate - in - let do_after_dataflow ~java_pname find_canonical_duplicate final_typestate = - let exit_node = Procdesc.get_exit_node curr_pdesc in - check_return ~java_pname find_canonical_duplicate exit_node final_typestate annotated_signature - proc_loc - in - let module DFTypeCheck = DataFlow.MakeDF (struct - type t = TypeState.t - - let equal = TypeState.equal - - let join = TypeState.join - - let pp_name fmt = F.pp_print_string fmt "eradicate" - - let do_node node typestate = - AnalysisCallbacks.html_debug_new_node_session ~pp_name node ~f:(fun () -> - AnalysisState.set_node node ; - if Config.write_html then L.d_printfln "before:@\n%a@\n" TypeState.pp typestate ; - let {TypeCheck.normal_flow_typestate; exception_flow_typestates} = - TypeCheck.typecheck_node analysis_data calls_this checks idenv find_canonical_duplicate - annotated_signature typestate node linereader - in - let normal_flow_typestates = - Option.value_map normal_flow_typestate ~f:(fun a -> [a]) ~default:[] - in - (normal_flow_typestates, exception_flow_typestates) ) - - - let proc_throws _ = DataFlow.DontKnow - end) in - let initial_typestate = get_initial_typestate () in - do_before_dataflow initial_typestate ; - let transitions = DFTypeCheck.run curr_pdesc initial_typestate in - match transitions (Procdesc.get_exit_node curr_pdesc) with - | DFTypeCheck.Transition (final_typestate, _, _) -> - do_after_dataflow ~java_pname:curr_java_pname find_canonical_duplicate final_typestate ; - (!calls_this, Some final_typestate) - | DFTypeCheck.Dead_state -> - (!calls_this, None) - - -let analyze_one_procedure ~java_pname - ({IntraproceduralAnalysis.tenv; proc_desc; _} as analysis_data) calls_this checks - annotated_signature linereader proc_loc : unit = - let idenv = IDEnv.create proc_desc in - let find_duplicate_nodes = State.mk_find_duplicate_nodes proc_desc in - let find_canonical_duplicate node = - let duplicate_nodes = find_duplicate_nodes node in - try Procdesc.NodeSet.min_elt duplicate_nodes with Caml.Not_found -> node - in - let proc_name = Procdesc.get_proc_name proc_desc in - let caller_nullsafe_mode = NullsafeMode.of_procname tenv proc_name in - let typecheck_proc do_checks pname pdesc proc_details_opt = - let ann_sig, loc, idenv_pn = - match proc_details_opt with - | Some (ann_sig, loc, idenv_pn) -> - (ann_sig, loc, idenv_pn) - | None -> - let is_callee_in_trust_list = - let callee_class = Procname.get_class_type_name pname in - Option.exists callee_class ~f:(fun class_name -> - Typ.Name.Java.get_java_class_name_exn class_name - |> NullsafeMode.is_in_trust_list caller_nullsafe_mode ) - in - let ann_sig = - Models.get_modelled_annotated_signature ~is_callee_in_trust_list tenv - (Procdesc.get_attributes pdesc) - in - let loc = Procdesc.get_loc pdesc in - (ann_sig, loc, IDEnv.create pdesc) - in - let checks', calls_this' = - if do_checks then (checks, calls_this) - else ({TypeCheck.eradicate= false; check_ret_type= []}, ref false) - in - (* NOTE: [analysis_data] has the new [pdesc] *) - callback1 - {analysis_data with proc_desc= pdesc} - find_canonical_duplicate calls_this' checks' idenv_pn ann_sig linereader loc - in - let do_final_typestate typestate_opt calls_this = - let do_typestate typestate = - let start_node = Procdesc.get_start_node proc_desc in - if - (not calls_this) - (* if 'this(...)' is called, no need to check initialization *) - && checks.TypeCheck.eradicate - then ( - let typestates_for_curr_constructor_and_all_initializer_methods = - Initializers.final_initializer_typestates_lazy tenv proc_name proc_desc - (typecheck_proc ~curr_java_pname:java_pname) - in - let typestates_for_all_constructors_incl_current = - Initializers.final_constructor_typestates_lazy tenv proc_name - (typecheck_proc ~curr_java_pname:java_pname) - in - EradicateChecks.check_constructor_initialization analysis_data find_canonical_duplicate - start_node ~nullsafe_mode:annotated_signature.AnnotatedSignature.nullsafe_mode - ~typestates_for_curr_constructor_and_all_initializer_methods - ~typestates_for_all_constructors_incl_current proc_loc ; - if Config.eradicate_verbose then L.result "Final Typestate@\n%a@." TypeState.pp typestate ) - in - match typestate_opt with None -> () | Some typestate -> do_typestate typestate - in - let calls_this, final_typestate_opt = - typecheck_proc ~curr_java_pname:java_pname true proc_name proc_desc - (Some (annotated_signature, proc_loc, idenv)) - in - do_final_typestate final_typestate_opt calls_this ; - if checks.TypeCheck.eradicate then - EradicateChecks.check_overridden_annotations analysis_data find_canonical_duplicate - ~proc_name:java_pname annotated_signature ; - () - - -(* If we need to skip analysis, returns a string reason for debug, otherwise None *) -let find_reason_to_skip_analysis proc_name proc_desc = - if Procdesc.is_kotlin proc_desc then Some "Kotlin source file" - else - match proc_name with - | Procname.Java java_pname -> - if Procname.Java.is_access_method java_pname then Some "access method" - else if - ThirdPartyAnnotationInfo.is_third_party_proc - (ThirdPartyAnnotationGlobalRepo.get_repo ()) - java_pname - then Some "third party method" - else if (Procdesc.get_attributes proc_desc).ProcAttributes.is_bridge_method then - Some "bridge method" - else if Procname.Java.is_autogen_method java_pname then Some "autogenerated method" - else None - | _ -> - Some "not a Java method" - - -(** Entry point for the nullsafe procedure-level analysis. *) -let analyze checks ({IntraproceduralAnalysis.proc_desc; tenv; _} as analysis_data) : - NullsafeSummary.t option = - let proc_name = Procdesc.get_proc_name proc_desc in - L.debug Analysis Medium "Analysis of %a@\n" Procname.pp proc_name ; - match find_reason_to_skip_analysis proc_name proc_desc with - | Some reason -> - L.debug Analysis Medium "Skipping analysis: %s@\n" reason ; - None - | None -> - (* start the analysis! *) - let calls_this = ref false in - let java_pname = - Procname.as_java_exn ~explanation:"Would have skipped the analysis otherwise" proc_name - in - let annotated_signature = - AnnotatedSignature.get_for_class_under_analysis tenv (Procdesc.get_attributes proc_desc) - in - L.debug Analysis Medium "Signature: %a@\n" (AnnotatedSignature.pp proc_name) - annotated_signature ; - let loc = Procdesc.get_loc proc_desc in - let linereader = LineReader.create () in - (* Initializing TypeErr signleton. *) - TypeErr.reset () ; - (* The main method - during this the actual analysis will happen and TypeErr will be populated with - issues (and some of them - reported). - *) - analyze_one_procedure ~java_pname analysis_data calls_this checks annotated_signature - linereader loc ; - (* Collect issues that were detected during analysis and put them in summary for further processing *) - let issues = TypeErr.get_errors () |> List.map ~f:(fun (issues, _) -> issues) in - (* Report errors of "farall" class - those could not be reported during analysis phase. *) - TypeErr.report_forall_issues_and_reset analysis_data - ~nullsafe_mode:annotated_signature.nullsafe_mode ; - Some {NullsafeSummary.issues} - - -let analyze_procedure analysis_data = - let checks = {TypeCheck.eradicate= true; check_ret_type= []} in - analyze checks analysis_data - - -let analyze_for_immutable_cast_checker check_return_type analysis_data = - let checks = {TypeCheck.eradicate= false; check_ret_type= [check_return_type]} in - analyze checks analysis_data diff --git a/infer/src/nullsafe/eradicate.mli b/infer/src/nullsafe/eradicate.mli deleted file mode 100644 index c14765d0de8..00000000000 --- a/infer/src/nullsafe/eradicate.mli +++ /dev/null @@ -1,18 +0,0 @@ -(* - * 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. - *) - -open! IStd - -(** The main entry point for Nullsafe typechecker. *) - -val analyze_procedure : IntraproceduralAnalysis.t -> NullsafeSummary.t option -(** Proc-level callback for nullsafe. *) - -val analyze_for_immutable_cast_checker : - TypeCheck.check_return_type -> IntraproceduralAnalysis.t -> NullsafeSummary.t option -(** For checkers that explore eradicate/nullsafe infra, but not part of nullsafe.Annot Call the - given check_return_type at the end of every procedure. *) diff --git a/infer/src/nullsafe/eradicateChecks.ml b/infer/src/nullsafe/eradicateChecks.ml deleted file mode 100644 index 7147fa34e9b..00000000000 --- a/infer/src/nullsafe/eradicateChecks.ml +++ /dev/null @@ -1,559 +0,0 @@ -(* - * 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. - *) - -open! IStd - -(** Module for the checks called by Eradicate. *) - -module L = Logging - -let explain_expr tenv node e = - match Errdesc.exp_rv_dexp tenv node e with - | Some de -> - Some (DecompiledExp.to_string de) - | None -> - None - - -let is_virtual = function - | AnnotatedSignature.{mangled} :: _ when Mangled.is_this mangled -> - true - | _ -> - false - - -let check_object_dereference ({IntraproceduralAnalysis.tenv; _} as analysis_data) ~nullsafe_mode - find_canonical_duplicate node instr_ref object_exp dereference_type inferred_nullability loc = - Result.iter_error (DereferenceRule.check inferred_nullability) ~f:(fun dereference_violation -> - let nullable_object_descr = explain_expr tenv node object_exp in - let type_error = - TypeErr.Nullable_dereference - {dereference_violation; dereference_location= loc; nullable_object_descr; dereference_type} - in - TypeErr.register_error analysis_data find_canonical_duplicate type_error (Some instr_ref) - ~nullsafe_mode ) - - -(** [expr] is an expression that was explicitly compared with `null`. At the same time, [expr] had - [inferred_nullability] before the comparision. Check if the comparision is redundant and emit an - issue, if this is the case. *) -let check_condition_for_redundancy - ({IntraproceduralAnalysis.tenv; proc_desc= curr_pdesc; _} as analysis_data) ~is_always_true - find_canonical_duplicate node ~nullsafe_mode expr typ inferred_nullability idenv linereader loc - instr_ref : unit = - let contains_instanceof_throwable pdesc node = - (* Check if the current procedure has a catch Throwable. *) - (* That always happens in the bytecode generated by try-with-resources. *) - let loc = Procdesc.Node.get_loc node in - let throwable_found = ref false in - let typ_is_throwable {Typ.desc} = - match desc with - | Typ.Tstruct (Typ.JavaClass _ as name) -> - String.equal (Typ.Name.name name) "java.lang.Throwable" - | _ -> - false - in - let do_instr = function - | Sil.Call (_, Exp.Const (Const.Cfun pn), [_; (Exp.Sizeof {typ}, _)], _, _) - when Procname.equal pn BuiltinDecl.__instanceof && typ_is_throwable typ -> - throwable_found := true - | _ -> - () - in - let do_node n = - if Location.equal loc (Procdesc.Node.get_loc n) then - Instrs.iter ~f:do_instr (Procdesc.Node.get_instrs n) - in - Procdesc.iter_nodes do_node pdesc ; - !throwable_found - in - let from_try_with_resources () : bool = - (* heuristic to check if the condition is the translation of try-with-resources *) - match LineReader.from_loc linereader loc with - | Some line -> - (not (String.is_substring ~substring:"==" line || String.is_substring ~substring:"!=" line)) - && String.is_substring ~substring:"}" line - && contains_instanceof_throwable curr_pdesc node - | None -> - false - in - let is_temp = IDEnv.exp_is_temp idenv expr in - let should_report = - (* NOTE: We have different levels of non-nullability. In practice there is a big difference - between certainty for different cases: whether expression is the result of something that can not be null - due to language semantics, or comes from something that is merely not declared as nullable. - We report both types of issues now, which is OK since this warning is turned off by default. - In the future we might as well start reporting only for cases where we are more confident. - Hovewer keep in mind that for code analysis purposes all condition redudant warnings can be useful, - particularly they help to find popular functions that should have been annotated as nullable, but were not made so. - *) - (* NOTE: We don't report the opposite case, namely when the expression is known to be always `null`. Consider changing this. - *) - InferredNullability.is_nonnullish inferred_nullability - && Config.eradicate_condition_redundant && (not is_temp) && PatternMatch.type_is_class typ - && (not (from_try_with_resources ())) - && not (InferredNullability.origin_is_fun_defined inferred_nullability) - in - if should_report then - (* TODO(T61051649) this is not really condition. This is an expression. - This leads to inconsistent messaging like "condition `some_variable` is always true". - So Condition_redundant should either accept expression, or this should pass a condition. - *) - let condition_descr = explain_expr tenv node expr in - let nonnull_origin = InferredNullability.get_simple_origin inferred_nullability in - TypeErr.register_error analysis_data find_canonical_duplicate - (TypeErr.Condition_redundant {is_always_true; loc; condition_descr; nonnull_origin}) - (Some instr_ref) ~nullsafe_mode - - -(** Check an assignment to a field. *) -let check_field_assignment - ({IntraproceduralAnalysis.tenv; proc_desc= curr_pdesc; _} as analysis_data) ~nullsafe_mode - find_canonical_duplicate node instr_ref typestate ~expr_rhs ~field_type loc fname - (annotated_field : AnnotatedField.t) typecheck_expr : unit = - L.d_with_indent "check_field_assignment" ~f:(fun () -> - let curr_pname = Procdesc.get_proc_name curr_pdesc in - let curr_pattrs = Procdesc.get_attributes curr_pdesc in - let _, inferred_nullability_rhs = - L.d_strln "Typechecking rhs" ; - typecheck_expr node instr_ref typestate expr_rhs - (* TODO(T54687014) optimistic default might be an unsoundness issue - investigate *) - (field_type, InferredNullability.create TypeOrigin.OptimisticFallback) - loc - in - let field_is_injector_readwrite () = - Annotations.ia_is_field_injector_readwrite annotated_field.annotation_deprecated - in - let field_is_in_cleanup_context () = - let AnnotatedSignature.{ret_annotation_deprecated} = - (* TODO(T62825735): support trusted callees for fields *) - (Models.get_modelled_annotated_signature ~is_callee_in_trust_list:false tenv curr_pattrs) - .ret - in - Annotations.ia_is_cleanup ret_annotation_deprecated - in - let declared_nullability = annotated_field.annotated_type.nullability in - let assignment_check_result = - AssignmentRule.check ~lhs:declared_nullability ~rhs:inferred_nullability_rhs - in - Result.iter_error assignment_check_result ~f:(fun assignment_violation -> - let should_report = - (not (AndroidFramework.is_destroy_method curr_pname)) - && PatternMatch.type_is_class field_type - && (not (Fieldname.is_java_outer_instance fname)) - && (not (field_is_injector_readwrite ())) - && not (field_is_in_cleanup_context ()) - in - if should_report then - TypeErr.register_error analysis_data find_canonical_duplicate - (TypeErr.Bad_assignment - { assignment_violation - ; assignment_location= loc - ; assignment_type= AssignmentRule.ReportableViolation.AssigningToField fname } ) - (Some instr_ref) ~nullsafe_mode ) ) - - -(* Check if the field declared as not nullable (implicitly or explicitly). If the field is - absent, we optimistically assume it is not nullable. - - TODO(T54687014) investigate if this leads to unsoundness issues in practice *) -let is_field_declared_as_nonnull annotated_field_opt = - let is_nonnullish AnnotatedField.{annotated_type= {nullability}} = - Nullability.is_nonnullish (AnnotatedNullability.get_nullability nullability) - in - Option.exists annotated_field_opt ~f:is_nonnullish - - -let lookup_field_in_typestate pname field_name typestate = - let pvar = Pvar.mk (Mangled.from_string (Fieldname.to_string field_name)) pname in - TypeState.lookup_pvar pvar typestate - - -(* Given a predicate over field name, look ups the field and returns a predicate - over this field value in a typestate, or true if there is no such a field in typestate *) -let convert_predicate predicate_over_field_name field_name (pname, typestate) = - let range_for_field = lookup_field_in_typestate pname field_name typestate in - Option.exists range_for_field ~f:predicate_over_field_name - - -(* Given a list of typestates, does a given predicate about the field hold for at least one of them? *) -let predicate_holds_for_some_typestate typestate_list field_name ~predicate = - List.exists typestate_list ~f:(convert_predicate predicate field_name) - - -(* Given the typestate and the field, what is the upper bound of field nullability in this typestate? - *) -let get_nullability_upper_bound_for_typestate proc_name field_name typestate = - let range_for_field = lookup_field_in_typestate proc_name field_name typestate in - match range_for_field with - | None -> - (* There is no information about the field type in typestate (field was not assigned in all paths). - It gives the most generic upper bound. - *) - Nullability.top - (* We were able to lookup the field. Its nullability gives precise upper bound. *) - | Some (_, inferred_nullability) -> - InferredNullability.get_nullability inferred_nullability - - -(* Given the list of typestates (each corresponding to the final result of executing of some function), - and the field, what is the upper bound of field nullability joined over all typestates? -*) -let get_nullability_upper_bound field_name typestate_list = - (* Join upper bounds for all typestates in the list *) - List.fold typestate_list ~init:Nullability.StrictNonnull ~f:(fun acc (proc_name, typestate) -> - Nullability.join acc - (get_nullability_upper_bound_for_typestate proc_name field_name typestate) ) - - -(** Check field initialization for a given constructor *) -let check_constructor_initialization - ({IntraproceduralAnalysis.tenv; proc_desc= curr_constructor_pdesc; _} as analysis_data) - find_canonical_duplicate start_node ~nullsafe_mode - ~typestates_for_curr_constructor_and_all_initializer_methods - ~typestates_for_all_constructors_incl_current loc : unit = - AnalysisState.set_node start_node ; - if Procname.is_constructor (Procdesc.get_proc_name curr_constructor_pdesc) then - match - PatternMatch.get_this_type_nonstatic_methods_only - (Procdesc.get_attributes curr_constructor_pdesc) - with - | Some {desc= Tptr (({desc= Tstruct name} as ts), _)} -> ( - match Tenv.lookup tenv name with - | Some {fields} -> - let do_field (field_name, field_type, _) = - let annotated_field = - AnnotatedField.get tenv field_name ~class_typ:ts ~class_under_analysis:name - in - let is_initialized_by_framework = - match annotated_field with - | None -> - false - | Some {annotation_deprecated} -> - (* Initialized by Dependency Injection framework *) - Annotations.ia_is_field_injector_readonly annotation_deprecated - (* Initialized by Json framework *) - || Annotations.ia_is_json_field annotation_deprecated - in - let is_initialized_in_either_constructor_or_initializer = - let is_initialized = function - | TypeOrigin.Field {object_origin= TypeOrigin.This} -> - (* Circular initialization - does not count *) - false - | _ -> - (* Found in typestate, hence the field was initialized *) - true - in - predicate_holds_for_some_typestate - (Lazy.force typestates_for_curr_constructor_and_all_initializer_methods) field_name - ~predicate:(fun (_, nullability) -> - is_initialized (InferredNullability.get_simple_origin nullability) ) - in - (* TODO(T54584721) This check is completely independent of the current constuctor we check. - This check should be moved out of this function. Until it is done, - we issue one over-annotated warning per constructor, which does not make a lot of sense. *) - let field_nullability_upper_bound_over_all_typestates () = - get_nullability_upper_bound field_name - (Lazy.force typestates_for_all_constructors_incl_current) - in - let should_check_field_initialization = - let in_current_class = - let fld_cname = Fieldname.get_class_name field_name in - Typ.Name.equal name fld_cname - in - (* various frameworks initialize spefic fields behind the scenes, - we assume they are doing it well - *) - (not is_initialized_by_framework) - (* primitive types can not be null so initialization check is not needed *) - && PatternMatch.type_is_class field_type - && in_current_class - && (not (Fieldname.is_java_outer_instance field_name)) - (* not user code, unactionable errors *) - && not (Fieldname.is_java_synthetic field_name) - in - if should_check_field_initialization then ( - (* Check if non-null field is not initialized. *) - if - is_field_declared_as_nonnull annotated_field - && not is_initialized_in_either_constructor_or_initializer - then - if - Config.nullsafe_disable_field_not_initialized_in_nonstrict_classes - && NullsafeMode.equal nullsafe_mode NullsafeMode.Default - then - (* Behavior needed for backward compatibility, where we are not ready to surface this type of errors by default. - Hovewer, this error should be always turned on for @NullsafeStrict classes. - *) - () - else - TypeErr.register_error analysis_data find_canonical_duplicate - (TypeErr.Field_not_initialized {field_name; loc}) - None ~nullsafe_mode ; - (* Check if field is over-annotated. *) - match annotated_field with - | None -> - () - | Some annotated_field -> - if Config.eradicate_field_over_annotated then - let what = - AnnotatedNullability.get_nullability - annotated_field.annotated_type.nullability - in - let by_rhs_upper_bound = field_nullability_upper_bound_over_all_typestates () in - Result.iter_error (OverAnnotatedRule.check ~what ~by_rhs_upper_bound) - ~f:(fun over_annotated_violation -> - TypeErr.register_error analysis_data find_canonical_duplicate - (TypeErr.Over_annotation - { over_annotated_violation - ; loc - ; violation_type= OverAnnotatedRule.FieldOverAnnoted field_name } ) - ~nullsafe_mode None ) ) - in - List.iter ~f:do_field fields - | None -> - () ) - | _ -> - () - - -let check_return_not_nullable analysis_data ~nullsafe_mode ~java_pname find_canonical_duplicate loc - (ret_signature : AnnotatedSignature.ret_signature) ret_inferred_nullability = - (* Returning from a function is essentially an assignment the actual return value to the formal `return` *) - let lhs = ret_signature.ret_annotated_type.nullability in - let rhs = ret_inferred_nullability in - Result.iter_error (AssignmentRule.check ~lhs ~rhs) ~f:(fun assignment_violation -> - TypeErr.register_error analysis_data find_canonical_duplicate - (Bad_assignment - { assignment_violation - ; assignment_location= loc - ; assignment_type= ReturningFromFunction java_pname } ) - None ~nullsafe_mode ) - - -let check_return_overrannotated ~java_pname analysis_data find_canonical_duplicate loc - ~nullsafe_mode (ret_signature : AnnotatedSignature.ret_signature) ret_inferred_nullability = - (* Returning from a function is essentially an assignment the actual return value to the formal `return` *) - let what = AnnotatedNullability.get_nullability ret_signature.ret_annotated_type.nullability in - (* In our CFG implementation, there is only one place where we return from a function - (all execution flow joins are already made), hence inferreed nullability of returns gives us - correct upper bound. - *) - let by_rhs_upper_bound = InferredNullability.get_nullability ret_inferred_nullability in - Result.iter_error (OverAnnotatedRule.check ~what ~by_rhs_upper_bound) - ~f:(fun over_annotated_violation -> - TypeErr.register_error analysis_data find_canonical_duplicate - (Over_annotation - {over_annotated_violation; loc; violation_type= ReturnOverAnnotated java_pname} ) - None ~nullsafe_mode ) - - -(** Check the annotations when returning from a method. *) -let check_return_annotation analysis_data ~java_pname find_canonical_duplicate ret_range - (annotated_signature : AnnotatedSignature.t) ret_implicitly_nullable loc : unit = - match ret_range with - (* Disables the warnings since it is not clear how to annotate the return value of lambdas *) - | Some _ when Procname.Java.is_lambda java_pname -> - () - | Some (_, ret_inferred_nullability) -> - (* TODO(T54308240) Model ret_implicitly_nullable in AnnotatedNullability *) - if not ret_implicitly_nullable then - check_return_not_nullable ~java_pname analysis_data - ~nullsafe_mode:annotated_signature.nullsafe_mode find_canonical_duplicate loc - annotated_signature.ret ret_inferred_nullability ; - if Config.eradicate_return_over_annotated then - check_return_overrannotated ~java_pname analysis_data find_canonical_duplicate loc - annotated_signature.ret ~nullsafe_mode:annotated_signature.nullsafe_mode - ret_inferred_nullability - | None -> - () - - -(** Check the receiver of a virtual call. *) -let check_call_receiver analysis_data ~nullsafe_mode find_canonical_duplicate node typestate - call_params callee_pname (instr_ref : TypeErr.InstrRef.t) loc typecheck_expr : unit = - match call_params with - | ((original_this_e, this_e), typ) :: _ -> - let _, this_inferred_nullability = - typecheck_expr node instr_ref typestate this_e - (* TODO(T54687014) optimistic default might be an unsoundness issue - investigate *) - (typ, InferredNullability.create TypeOrigin.OptimisticFallback) - loc - in - check_object_dereference analysis_data ~nullsafe_mode find_canonical_duplicate node instr_ref - original_this_e (DereferenceRule.ReportableViolation.MethodCall callee_pname) - this_inferred_nullability loc - | [] -> - () - - -type resolved_param = - { param_index: int - ; formal: AnnotatedSignature.param_signature - ; actual: Exp.t * InferredNullability.t - ; is_formal_propagates_nullable: bool } - -(** Check the parameters of a call. *) -let check_call_parameters ({IntraproceduralAnalysis.tenv; _} as analysis_data) ~nullsafe_mode - ~callee_pname ~callee_annotated_signature find_canonical_duplicate node resolved_params loc - instr_ref : unit = - let check {param_index; formal; actual= orig_e2, nullability_actual} = - let report ~nullsafe_mode assignment_violation = - let actual_param_expression = - match explain_expr tenv node orig_e2 with - | Some descr -> - descr - | None -> - "formal parameter " ^ Mangled.to_string formal.mangled - in - TypeErr.register_error analysis_data find_canonical_duplicate - (Bad_assignment - { assignment_violation - ; assignment_location= loc - ; assignment_type= - PassingParamToFunction - { param_signature= formal - ; actual_param_expression - ; param_index - ; annotated_signature= callee_annotated_signature - ; procname= callee_pname } } ) - (Some instr_ref) ~nullsafe_mode - in - if PatternMatch.type_is_class formal.param_annotated_type.typ then - (* Passing a param to a function is essentially an assignment the actual param value - to the formal param *) - let lhs = formal.param_annotated_type.nullability in - let rhs = nullability_actual in - Result.iter_error (AssignmentRule.check ~lhs ~rhs) ~f:(report ~nullsafe_mode) - in - List.iter ~f:check resolved_params - - -let check_inheritance_rule_for_return analysis_data find_canonical_duplicate loc ~nullsafe_mode - ~overridden_proc_name ~base_proc_name ~base_nullability ~overridden_nullability = - Result.iter_error - (InheritanceRule.check InheritanceRule.Ret ~base:base_nullability - ~overridden:overridden_nullability ) ~f:(fun inheritance_violation -> - TypeErr.register_error analysis_data find_canonical_duplicate - (Inconsistent_subclass - { inheritance_violation - ; loc - ; violation_type= InconsistentReturn - ; overridden_proc_name - ; base_proc_name } ) - None ~nullsafe_mode ) - - -let check_inheritance_rule_for_param analysis_data find_canonical_duplicate loc ~nullsafe_mode - ~overridden_param_name ~base_proc_name ~param_index ~base_nullability ~overridden_nullability - ~overridden_proc_name = - Result.iter_error - (InheritanceRule.check InheritanceRule.Param ~base:base_nullability - ~overridden:overridden_nullability ) ~f:(fun inheritance_violation -> - TypeErr.register_error analysis_data find_canonical_duplicate - (Inconsistent_subclass - { inheritance_violation - ; violation_type= - InconsistentParam - {param_index; param_description= Mangled.to_string overridden_param_name} - ; base_proc_name - ; loc - ; overridden_proc_name } ) - None ~nullsafe_mode ) - - -let check_inheritance_rule_for_params analysis_data find_canonical_duplicate loc ~nullsafe_mode - ~base_proc_name ~base_signature ~overridden_signature ~overridden_proc_name = - let base_params = base_signature.AnnotatedSignature.params in - let overridden_params = overridden_signature.AnnotatedSignature.params in - let zipped_params = List.zip base_params overridden_params in - match zipped_params with - | Ok base_and_overridden_params -> - let has_implicit_this_param = is_virtual base_params in - (* Check the rule for each pair of base and overridden param *) - List.iteri base_and_overridden_params - ~f:(fun - index - ( AnnotatedSignature.{param_annotated_type= {nullability= annotated_nullability_base}} - , AnnotatedSignature. - { mangled= overridden_param_name - ; param_annotated_type= {nullability= annotated_nullability_overridden} } ) - -> - check_inheritance_rule_for_param analysis_data find_canonical_duplicate loc ~nullsafe_mode - ~overridden_param_name ~base_proc_name - ~param_index: - ( if has_implicit_this_param then - (* The first param in the list is implicit (not real part of the signature) and should not be counted *) - index - 1 - else index ) - ~base_nullability:(AnnotatedNullability.get_nullability annotated_nullability_base) - ~overridden_proc_name - ~overridden_nullability: - (AnnotatedNullability.get_nullability annotated_nullability_overridden) ) - | Unequal_lengths -> - (* Skip checking. - TODO (T5280249): investigate why argument lists can be of different length. *) - () - - -(** Check both params and return values for complying for co- and contravariance *) -let check_inheritance_rule_for_signature analysis_data find_canonical_duplicate loc ~nullsafe_mode - ~base_proc_name ~base_signature ~overridden_signature ~overridden_proc_name = - (* Check params *) - check_inheritance_rule_for_params analysis_data find_canonical_duplicate loc ~nullsafe_mode - ~base_proc_name ~base_signature ~overridden_signature ~overridden_proc_name ; - (* Check return value *) - if Procname.Java.is_external base_proc_name then - (* the analysis should not report return type inconsistencies with external code *) () - else - (* Check if return value is consistent with the base *) - let base_nullability = - AnnotatedNullability.get_nullability - base_signature.AnnotatedSignature.ret.ret_annotated_type.nullability - in - let overridden_nullability = - AnnotatedNullability.get_nullability - overridden_signature.AnnotatedSignature.ret.ret_annotated_type.nullability - in - check_inheritance_rule_for_return analysis_data find_canonical_duplicate loc ~nullsafe_mode - ~base_proc_name ~base_nullability ~overridden_nullability ~overridden_proc_name - - -(** Checks if the annotations are consistent with the derived classes and with the implemented - interfaces *) -let check_overridden_annotations ({IntraproceduralAnalysis.tenv; proc_desc; _} as analysis_data) - find_canonical_duplicate annotated_signature ~proc_name = - let start_node = Procdesc.get_start_node proc_desc in - let loc = Procdesc.Node.get_loc start_node in - let check_if_base_signature_matches_current base_proc_name = - match base_proc_name with - | Procname.Java base_java_proc_name -> ( - match PatternMatch.lookup_attributes tenv base_proc_name with - | Some base_attributes -> - let base_signature = - (* TODO(T62825735): fully support trusted callees. Note that for inheritance - rule it doesn't make much difference, but would be nice to refactor anyway. *) - Models.get_modelled_annotated_signature ~is_callee_in_trust_list:false tenv - base_attributes - in - check_inheritance_rule_for_signature analysis_data - ~nullsafe_mode:annotated_signature.AnnotatedSignature.nullsafe_mode - find_canonical_duplicate loc ~base_proc_name:base_java_proc_name ~base_signature - ~overridden_signature:annotated_signature ~overridden_proc_name:proc_name - | None -> - (* Could not find the attributes - optimistically skipping the check *) - (* TODO(T54687014) ensure this is not an issue in practice *) - () ) - | _ -> - (* The base method is not a Java method. This is a weird situation and should not happen in practice. - TODO(T54687014) ensure this is not an issue in practice - *) - () - in - (* Iterate over all methods the current method overrides and see the current - method is compatible with all of them *) - PatternMatch.override_iter check_if_base_signature_matches_current tenv (Procname.Java proc_name) diff --git a/infer/src/nullsafe/immutableChecker.ml b/infer/src/nullsafe/immutableChecker.ml deleted file mode 100644 index 325b2c2769c..00000000000 --- a/infer/src/nullsafe/immutableChecker.ml +++ /dev/null @@ -1,48 +0,0 @@ -(* - * 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. - *) - -open! IStd - -(** Check an implicit cast when returning an immutable collection from a method whose type is - mutable. *) -let check_immutable_cast analysis_data proc_desc typ_expected typ_found_opt loc : unit = - match typ_found_opt with - | Some typ_found -> ( - let casts = - [ ("java.util.List", "com.google.common.collect.ImmutableList") - ; ("java.util.Map", "com.google.common.collect.ImmutableMap") - ; ("java.util.Set", "com.google.common.collect.ImmutableSet") ] - in - let in_casts expected given = - List.exists - ~f:(fun (x, y) -> - String.equal (Typ.Name.name expected) x && String.equal (Typ.Name.name given) y ) - casts - in - match - (PatternMatch.type_get_class_name typ_expected, PatternMatch.type_get_class_name typ_found) - with - | Some name_expected, Some name_given -> - if in_casts name_expected name_given then - let description = - Format.asprintf - "Method %s returns %a but the return type is %a. Make sure that users of this \ - method do not try to modify the collection." - (Procname.to_simplified_string (Procdesc.get_proc_name proc_desc)) - Typ.Name.pp name_given Typ.Name.pp name_expected - in - let issue_type = IssueType.checkers_immutable_cast in - EradicateReporting.report_error analysis_data ImmutableCast - (NullsafeIssue.make ~loc ~description ~severity:Warning ~issue_type ~field_name:None) - | _ -> - () ) - | None -> - () - - -let analyze analysis_data = - Eradicate.analyze_for_immutable_cast_checker (check_immutable_cast analysis_data) analysis_data diff --git a/infer/src/nullsafe/modelTables.ml b/infer/src/nullsafe/modelTables.ml deleted file mode 100644 index dbfdc75bb60..00000000000 --- a/infer/src/nullsafe/modelTables.ml +++ /dev/null @@ -1,901 +0,0 @@ -(* - * 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. - *) - -open! IStd -module Hashtbl = Caml.Hashtbl - -(* - * This file is a big bunch of tables; they read better with really long lines. - * @nolint - *) - -(* the type should be treated as Nonnull *) -let o = false - -(* the type should be treated as Nullable *) -and n = true - -(* create signature where both return type and params are nonnull *) -let all_nonnull num_params = (o, List.init num_params ~f:(fun _ -> o)) - -(* o means signature with N nonnull params and nonnull return type *) - -let o1 = all_nonnull 1 - -let o2 = all_nonnull 2 - -let o3 = all_nonnull 3 - -let o4 = all_nonnull 4 - -let o5 = all_nonnull 5 - -let o6 = all_nonnull 6 - -let o7 = all_nonnull 7 - -let o8 = all_nonnull 8 - -let o9 = all_nonnull 9 - -let o10 = all_nonnull 10 - -let o11 = all_nonnull 11 - -let o12 = all_nonnull 12 - -(* n stands for signature with nonnull return type and N nullable params *) - -let n1 = (o, [n]) - -let n2 = (o, [n; n]) - -let n3 = (o, [n; n; n]) - -(* the second argument is nullable, everything else is nonnull *) -let on = (o, [o; n]) - -(* container add *) -let ca = if Config.nullsafe_strict_containers then (o, [o]) else (o, [n]) - -(* container get *) -let cg = if Config.nullsafe_strict_containers then (n, [o]) else (n, [n]) - -(* container put *) -let cp = if Config.nullsafe_strict_containers then (n, [o; o]) else (n, [n; n]) - -(* container remove *) -let cr = if Config.nullsafe_strict_containers then (n, [o]) else (n, [n]) - -(* nullable getter *) -let ng = (n, []) - -let check_not_null_parameter_list, check_not_null_list = - (* The first integer in the tuple is the index (counting from 1) of the argument to be asserted. - Commonly it is 1. - *) - let list = - [ ( 1 - , (o, [n; n]) - , "com.facebook.common.internal.Preconditions.checkNotNull(java.lang.Object,java.lang.Object):java.lang.Object" - ) - ; ( 1 - , (o, [n; n; n]) - , "com.facebook.common.internal.Preconditions.checkNotNull(java.lang.Object,java.lang.String,java.lang.Object[]):java.lang.Object" - ) - ; ( 1 - , (o, [n]) - , "com.facebook.common.internal.Preconditions.checkNotNull(java.lang.Object):java.lang.Object" - ) - ; ( 1 - , (o, [n; n]) - , "com.facebook.common.preconditions.Preconditions.checkNotNull(java.lang.Object,java.lang.String):java.lang.Object" - ) - ; ( 1 - , (o, [n; n; n]) - , "com.facebook.common.preconditions.Preconditions.checkNotNull(java.lang.Object,java.lang.String,java.lang.Object[]):java.lang.Object" - ) - ; ( 1 - , (o, [n]) - , "com.facebook.common.preconditions.Preconditions.checkNotNull(java.lang.Object):java.lang.Object" - ) - ; ( 1 - , (o, [n; n]) - , "com.google.common.base.Preconditions.checkNotNull(java.lang.Object,java.lang.Object):java.lang.Object" - ) - ; ( 1 - , (o, [n; n; n]) - , "com.google.common.base.Preconditions.checkNotNull(java.lang.Object,java.lang.String,java.lang.Object[]):java.lang.Object" - ) - ; ( 1 - , (o, [n]) - , "com.google.common.base.Preconditions.checkNotNull(java.lang.Object):java.lang.Object" ) - ; (1, (o, [n]), "com.google.common.base.Verify.verifyNotNull(java.lang.Object):java.lang.Object") - ; ( 1 - , (o, [n; n; n]) - , "com.google.common.base.Verify.verifyNotNull(java.lang.Object,java.lang.String,java.lang.Object[]):java.lang.Object" - ) - ; (1, (o, [n]), "org.junit.Assert.assertNotNull(java.lang.Object):void") - ; ( 2 - , (* a non-traditional method - the second parameter is the object to be asserted, the first is the description *) - (o, [n; n]) - , "org.junit.Assert.assertNotNull(java.lang.String,java.lang.Object):void" ) - ; ( 1 - , (o, [n]) - , "com.facebook.infer.annotation.Assertions.assertNotNull(java.lang.Object):java.lang.Object" - ) - ; ( 1 - , (o, [n; o]) - , "com.facebook.infer.annotation.Assertions.assertNotNull(java.lang.Object,java.lang.String):java.lang.Object" - ) - ; ( 1 - , (o, [n]) - , "com.facebook.infer.annotation.Assertions.assumeNotNull(java.lang.Object):java.lang.Object" - ) - ; ( 1 - , (o, [n; o]) - , "com.facebook.infer.annotation.Assertions.assumeNotNull(java.lang.Object,java.lang.String):java.lang.Object" - ) - ; ( 1 - , (o, [n; o]) - , "com.facebook.infer.annotation.Assertions.nullsafeFIXME(java.lang.Object,java.lang.String):java.lang.Object" - ) - ; ( 1 - , (o, [n]) - , "androidx.core.util.Preconditions.checkNotNull(java.lang.Object):java.lang.Object" ) - ; ( 1 - , (o, [n; n]) - , "androidx.core.util.Preconditions.checkNotNull(java.lang.Object,java.lang.Object):java.lang.Object" - ) ] - in - (List.map ~f:(fun (x, _, z) -> (x, z)) list, List.map ~f:(fun (_, y, z) -> (y, z)) list) - - -let check_state_list = - [ ((o, [o]), "Preconditions.checkState(boolean):void") - ; ((o, [o]), "com.facebook.common.internal.Preconditions.checkState(boolean):void") - ; ( (o, [o; n]) - , "com.facebook.common.internal.Preconditions.checkState(boolean,java.lang.Object):void" ) - ; ( (o, [o; n; n]) - , "com.facebook.common.internal.Preconditions.checkState(boolean,java.lang.String,java.lang.Object[]):void" - ) - ; ((o, [o]), "com.facebook.common.preconditions.Preconditions.checkState(boolean):void") - ; ( (o, [o; n]) - , "com.facebook.common.preconditions.Preconditions.checkState(boolean,java.lang.Object):void" ) - ; ( (o, [o; n; n]) - , "com.facebook.common.preconditions.Preconditions.checkState(boolean,java.lang.String,java.lang.Object[]):void" - ) - ; ((o, [o]), "com.google.common.base.Preconditions.checkState(boolean):void") - ; ((o, [o; n]), "com.google.common.base.Preconditions.checkState(boolean,java.lang.Object):void") - ; ( (o, [o; n; n]) - , "com.google.common.base.Preconditions.checkState(boolean,java.lang.String,java.lang.Object[]):void" - ) - ; ((o, [o]), "com.facebook.infer.annotation.Assertions.assertCondition(boolean):void") - ; ( (o, [o; o]) - , "com.facebook.infer.annotation.Assertions.assertCondition(boolean,java.lang.String):void" ) - ; ((o, [o]), "com.facebook.infer.annotation.Assertions.assumeCondition(boolean):void") - ; ( (o, [o; o]) - , "com.facebook.infer.annotation.Assertions.assumeCondition(boolean,java.lang.String):void" ) ] - - -let check_argument_list = - [ ((o, [o]), "com.facebook.common.internal.Preconditions.checkArgument(boolean):void") - ; ( (o, [o; n]) - , "com.facebook.common.internal.Preconditions.checkArgument(boolean,java.lang.Object):void" ) - ; ( (o, [o; n; n]) - , "com.facebook.common.internal.Preconditions.checkArgument(boolean,java.lang.String,java.lang.Object[]):void" - ) - ; ((o, [o]), "com.facebook.common.preconditions.Preconditions.checkArgument(boolean):void") - ; ( (o, [o; n]) - , "com.facebook.common.preconditions.Preconditions.checkArgument(boolean,java.lang.Object):void" - ) - ; ( (o, [o; n; n]) - , "com.facebook.common.preconditions.Preconditions.checkArgument(boolean,java.lang.String,java.lang.Object[]):void" - ) - ; ((o, [o]), "com.google.common.base.Preconditions.checkArgument(boolean):void") - ; ( (o, [o; n]) - , "com.google.common.base.Preconditions.checkArgument(boolean,java.lang.Object):void" ) - ; ( (o, [o; n; n]) - , "com.google.common.base.Preconditions.checkArgument(boolean,java.lang.String,java.lang.Object[]):void" - ) ] - - -(** Models for boolean functions that return true on null. *) -let true_on_null_list = - [ (n1, "android.text.TextUtils.isEmpty(java.lang.CharSequence):boolean") - ; (n1, "com.google.common.base.Strings.isNullOrEmpty(java.lang.String):boolean") ] - - -(** Models for Map.containsKey *) -let containsKey_list = - [ (n1, "com.google.common.collect.ImmutableMap.containsKey(java.lang.Object):boolean") - ; (n1, "java.util.Map.containsKey(java.lang.Object):boolean") ] - - -(** Models for Map.put *) -let mapPut_list = - [ ( cp - , "com.google.common.collect.ImmutableMap.put(java.lang.Object,java.lang.Object):java.lang.Object" - ) - ; (cp, "java.util.Map.put(java.lang.Object,java.lang.Object):java.lang.Object") ] - - -type nonnull_alternative_method = {package_name: string; class_name: string; method_name: string} - -(* Nullable methods that have non-nullable alternatives. - Format is a triple: (, , ), *) -let nullable_methods_with_nonnull_alternatives_list = - [ ( (n, [o]) - , "android.view.View.findViewById(int):android.view.View" - (* View.requireViewById() is inaccessible from older APIs and will lead to a runtime crash *) - , {package_name= "androidx.core.view"; class_name= "ViewCompat"; method_name= "requireViewById"} - ) - ; ( (n, [o]) - , "android.app.Activity.findViewById(int):android.view.View" - , { package_name= "androidx.core.app" - ; class_name= "ActivityCompat" - ; method_name= "requireViewById" } ) - ; ( (n, []) - , "androidx.fragment.app.Fragment.getArguments():android.os.Bundle" - , { package_name= "androidx.fragment.app" - ; class_name= "Fragment" - ; method_name= "requireArguments" } ) - ; ( (n, []) - , "androidx.fragment.app.Fragment.getContext():android.content.Context" - , {package_name= "androidx.fragment.app"; class_name= "Fragment"; method_name= "requireContext"} - ) - ; ( (n, []) - , "androidx.fragment.app.Fragment.getActivity():androidx.fragment.app.FragmentActivity" - , {package_name= "androidx.fragment.app"; class_name= "Fragment"; method_name= "requireActivity"} - ) - ; ( (n, []) - , "androidx.fragment.app.Fragment.getHost():java.lang.Object" - , {package_name= "androidx.fragment.app"; class_name= "Fragment"; method_name= "requireHost"} ) - ; ( (n, []) - , "androidx.fragment.app.Fragment.getParentFragment():androidx.fragment.app.Fragment" - , { package_name= "androidx.fragment.app" - ; class_name= "Fragment" - ; method_name= "requireParentFragment" } ) - ; ( (n, []) - , "androidx.fragment.app.Fragment.getView():android.view.View" - , {package_name= "androidx.fragment.app"; class_name= "Fragment"; method_name= "requireView"} ) - ] - - -let nullable_method_with_nonnull_alternatives_nullability_list = - let result = - List.map nullable_methods_with_nonnull_alternatives_list - ~f:(fun (nullability, method_descr, _) -> (nullability, method_descr)) - in - List.iter result ~f:(fun ((ret_nullability, _param_nullability), _) -> - if not (Bool.equal ret_nullability n) then - Logging.die Logging.InternalError "Function is expected to be nullable" ) ; - result - - -(* Methods with signatures that are not special enough to be described in one of lists above *) -let annotated_list_nullability_other = - [ (o1, "android.text.SpannableString.valueOf(java.lang.CharSequence):android.text.SpannableString") - ; (o1, "android.app.AlarmManager.cancel(android.app.PendingIntent):void") - ; (o1, "android.net.Uri.parse(java.lang.String):android.net.Uri") - ; (n1, "android.os.Parcel.writeList(java.util.List):void") - ; (n2, "android.os.Parcel.writeParcelable(android.os.Parcelable,int):void") - ; (n1, "android.os.Parcel.writeString(java.lang.String):void") - ; ( (o, [o; o]) - , "androidx.core.view.ViewCompat.requireViewById(android.view.View,int):android.view.View" ) - ; ( (o, [o; o]) - , "androidx.core.app.ActivityCompat.requireViewById(android.app.Activity,int):android.view.View" - ) - ; ((o, []), "androidx.fragment.app.Fragment.requireArguments():android.os.Bundle") - ; ((o, []), "androidx.fragment.app.Fragment.requireContext():android.content.Context") - ; ( (o, []) - , "androidx.fragment.app.Fragment.requireActivity():androidx.fragment.app.FragmentActivity" ) - ; ((o, []), "androidx.fragment.app.Fragment.requireHost():java.lang.Object") - ; ( (o, []) - , "androidx.fragment.app.Fragment.requireParentFragment():androidx.fragment.app.Fragment" ) - ; ((o, []), "androidx.fragment.app.Fragment.requireView():android.view.View") - ; ( (o, [o; o; n; n; n]) - , "com.android.sdklib.build.ApkBuilder.(java.io.File,java.io.File,java.io.File,java.lang.String,java.io.PrintStream)" - ) - ; ( (o, [n]) - , "com.android.manifmerger.ManifestMerger.xmlFileAndLine(org.w3c.dom.Node):com.android.manifmerger.IMergerLog$FileAndLine" - ) - ; ( on - , "com.android.util.CommandLineParser$Mode.process(com.android.util.CommandLineParser$Arg,java.lang.String):java.lang.Object" - ) - (* The following FbInjector functions are not being called explicitly, they are artifacts of DI framework. - They are technically @Nullable, but the case when they are returning null is rather exceptional, - so the right tradeoff here is to treat them as non-nullables. - *) - ; ( (o, [o; o; n]) - , "com.facebook.inject.FbInjector.lazyInstance(int,int,com.facebook.inject.InjectionContext):java.lang.Object" - ) - ; ( (o, [o; o; n]) - , "com.facebook.inject.FbInjector.lazyApplicationInstance(int,int,com.facebook.inject.InjectionContext):java.lang.Object" - ) - ; ( (o, [o; n]) - , "com.facebook.inject.FbInjector.localInstance(int,com.facebook.inject.InjectionContext):java.lang.Object" - ) - ; ( on - , "com.google.common.base.Objects$ToStringHelper.add(java.lang.String,java.lang.Object):com.google.common.base.Objects$ToStringHelper" - ) - ; (n2, "com.google.common.base.Objects.equal(java.lang.Object,java.lang.Object):boolean") - ; ( n1 - , "com.google.common.base.Optional.fromNullable(java.lang.Object):com.google.common.base.Optional" - ) - ; ((n, []), "com.google.common.base.Optional.orNull():java.lang.Object") - ; (n1, "com.google.common.base.Strings.nullToEmpty(java.lang.String):java.lang.String") - ; (cg, "com.google.common.collect.ImmutableMap.get(java.lang.Object):java.lang.Object") - ; (* container get *) - ( o1 - , "com.google.common.collect.ImmutableList$Builder.add(java.lang.Object):com.google.common.collect.ImmutableList$Builder" - ) - ; ( o1 - , "com.google.common.collect.ImmutableList$Builder.addAll(java.lang.Iterable):com.google.common.collect.ImmutableList$Builder" - ) - ; ( o1 - , "com.google.common.collect.ImmutableList.of(java.lang.Object):com.google.common.collect.ImmutableList" - ) - ; ( o2 - , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" - ) - ; ( o3 - , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" - ) - ; ( o4 - , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" - ) - ; ( o5 - , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" - ) - ; ( o6 - , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" - ) - ; ( o7 - , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" - ) - ; ( o8 - , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" - ) - ; ( o9 - , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" - ) - ; ( o10 - , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" - ) - ; ( o11 - , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" - ) - ; ( o12 - , "com.google.common.collect.ImmutableList.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableList" - ) - ; ( o1 - , "com.google.common.collect.ImmutableList.copyOf(java.lang.Iterable):com.google.common.collect.ImmutableList" - ) - ; ( o1 - , "com.google.common.collect.ImmutableList.copyOf(java.util.Collection):com.google.common.collect.ImmutableList" - ) - ; ( o1 - , "com.google.common.collect.ImmutableList.copyOf(java.util.Iterator):com.google.common.collect.ImmutableList" - ) - ; ( o1 - , "com.google.common.collect.ImmutableList.copyOf(java.lang.Object):com.google.common.collect.ImmutableList" - ) - ; ( o2 - , "com.google.common.collect.ImmutableList.sortedCopyOf(java.util.Comparator,java.lang.Iterable):com.google.common.collect.ImmutableList" - ) - ; ( o1 - , "com.google.common.collect.ImmutableSet.of(java.lang.Object):com.google.common.collect.ImmutableSet" - ) - ; ( o2 - , "com.google.common.collect.ImmutableSet.of(java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableSet" - ) - ; ( o3 - , "com.google.common.collect.ImmutableSet.of(java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableSet" - ) - ; ( o4 - , "com.google.common.collect.ImmutableSet.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableSet" - ) - ; ( o5 - , "com.google.common.collect.ImmutableSet.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableSet" - ) - ; ( o1 - , "com.google.common.collect.ImmutableSet.copyOf(java.lang.Iterable):com.google.common.collect.ImmutableSet" - ) - ; ( o1 - , "com.google.common.collect.ImmutableSet.copyOf(java.util.Collection):com.google.common.collect.ImmutableSet" - ) - ; ( o1 - , "com.google.common.collect.ImmutableSet.copyOf(java.util.Iterator):com.google.common.collect.ImmutableSet" - ) - ; ( o1 - , "com.google.common.collect.ImmutableSet.copyOf(java.lang.Object):com.google.common.collect.ImmutableSet" - ) - ; ( o1 - , "com.google.common.collect.ImmutableSortedSet$Builder.add(java.lang.Object):com.google.common.collect.ImmutableSortedSet$Builder" - ) - ; ( o2 - , "com.google.common.collect.ImmutableMap.of(java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableMap" - ) - ; ( o4 - , "com.google.common.collect.ImmutableMap.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableMap" - ) - ; ( o6 - , "com.google.common.collect.ImmutableMap.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableMap" - ) - ; ( o8 - , "com.google.common.collect.ImmutableMap.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableMap" - ) - ; ( o10 - , "com.google.common.collect.ImmutableMap.of(java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object,java.lang.Object):com.google.common.collect.ImmutableMap" - ) - ; ( o1 - , "com.google.common.collect.ImmutableMap.copyOf(java.util.Map):com.google.common.collect.ImmutableMap" - ) - ; ( o1 - , "com.google.common.collect.ImmutableMap.copyOf(java.lang.Iterable):com.google.common.collect.ImmutableMap" - ) - ; ( o1 - , "com.google.common.util.concurrent.SettableFuture.setException(java.lang.Throwable):boolean" - ) - ; (o1, "java.io.File.(java.lang.String)") - ; (n1, "java.io.PrintStream.print(java.lang.String):void") - ; ((n, [o]), "java.lang.Class.getResource(java.lang.String):java.net.URL") - ; (o1, "java.lang.Class.isAssignableFrom(java.lang.Class):boolean") - ; (n1, "java.lang.Integer.equals(java.lang.Object):boolean") - ; (o1, "java.lang.Integer.parseInt(java.lang.String):int") - ; (o1, "java.lang.Long.parseLong(java.lang.String):long") - ; (n1, "java.lang.Object.equals(java.lang.Object):boolean") - ; (n2, "java.lang.RuntimeException.(java.lang.String,java.lang.Throwable)") - ; (n1, "java.lang.String.equals(java.lang.Object):boolean") - ; (n1, "java.lang.StringBuilder.append(java.lang.String):java.lang.StringBuilder") - ; ((n, [o]), "java.lang.System.getProperty(java.lang.String):java.lang.String") - ; ((n, [o]), "java.lang.System.getenv(java.lang.String):java.lang.String") - ; ( on - , "java.net.URLClassLoader.newInstance(java.net.URL[],java.lang.ClassLoader):java.net.URLClassLoader" - ) - ; (ng, "java.nio.file.Path.getParent():java.nio.file.Path") - ; (n1, "java.util.AbstractList.equals(java.lang.Object):boolean") - ; (ca, "java.util.ArrayList.add(java.lang.Object):boolean") - ; (ca, "java.util.List.add(java.lang.Object):boolean") - ; (cg, "java.util.Map.get(java.lang.Object):java.lang.Object") - ; (cr, "java.util.Map.remove(java.lang.Object):java.lang.Object") - ; (cp, "java.util.Map.put(java.lang.Object,java.lang.Object):java.lang.Object") - ; (cg, "java.util.HashMap.get(java.lang.Object):java.lang.Object") - ; (cr, "java.util.HashMap.remove(java.lang.Object):java.lang.Object") - ; (cp, "java.util.HashMap.put(java.lang.Object,java.lang.Object):java.lang.Object") - ; (cg, "java.util.concurrent.ConcurrentHashMap.get(java.lang.Object):java.lang.Object") - ; (cr, "java.util.concurrent.ConcurrentHashMap.remove(java.lang.Object):java.lang.Object") - ; (cg, "java.util.AbstractMap.get(java.lang.Object):java.lang.Object") - ; (cr, "java.util.AbstractMap.remove(java.lang.Object):java.lang.Object") - ; (cg, "java.util.concurrent.ConcurrentSkipListMap.get(java.lang.Object):java.lang.Object") - ; (cr, "java.util.concurrent.ConcurrentSkipListMap.remove(java.lang.Object):java.lang.Object") - ; (cg, "java.util.EnumMap.get(java.lang.Object):java.lang.Object") - ; (cr, "java.util.EnumMap.remove(java.lang.Object):java.lang.Object") - ; (cg, "java.util.Hashtable.get(java.lang.Object):java.lang.Object") - ; (cr, "java.util.Hashtable.remove(java.lang.Object):java.lang.Object") - ; (cg, "java.util.IdentityHashMap.get(java.lang.Object):java.lang.Object") - ; (cr, "java.util.IdentityHashMap.remove(java.lang.Object):java.lang.Object") - ; (cg, "java.util.LinkedHashMap.get(java.lang.Object):java.lang.Object") - ; (cr, "java.util.LinkedHashMap.remove(java.lang.Object):java.lang.Object") - ; (cg, "java.util.TreeMap.get(java.lang.Object):java.lang.Object") - ; (cr, "java.util.TreeMap.remove(java.lang.Object):java.lang.Object") - ; (cg, "java.util.WeakHashMap.get(java.lang.Object):java.lang.Object") - ; (cr, "java.util.WeakHashMap.remove(java.lang.Object):java.lang.Object") - ; ( (n, [o]) - , "javax.lang.model.element.Element.getAnnotation(java.lang.Class):java.lang.annotation.Annotation" - ) - ; (ng, "javax.lang.model.element.Element.getEnclosingElement():javax.lang.model.element.Element") - ; ( ng - , "javax.lang.model.element.ExecutableElement.getDefaultValue():javax.lang.model.element.AnnotationValue" - ) - ; ( ng - , "javax.lang.model.element.PackageElement.getEnclosingElement():javax.lang.model.element.Element" - ) - ; (ng, "javax.lang.model.element.VariableElement.getConstantValue():java.lang.Object") - ; (ng, "javax.lang.model.type.WildcardType.getSuperBound():javax.lang.model.type.TypeMirror") - ; ( (n, [o]) - , "javax.lang.model.util.Elements.getPackageElement(java.lang.CharSequence):javax.lang.model.element.PackageElement" - ) - ; ( (n, [o]) - , "javax.lang.model.util.Elements.getTypeElement(java.lang.CharSequence):javax.lang.model.element.TypeElement" - ) - ; ( (n, [o]) - , "javax.lang.model.util.Elements.getDocComment(javax.lang.model.element.Element):java.lang.String" - ) - ; ( o1 - , "javax.lang.model.util.Elements.getElementValuesWithDefaults(javax.lang.model.element.AnnotationMirror):java.util.Map" - ) - ; (o1, "javax.lang.model.util.Elements.isDeprecated(javax.lang.model.element.Element):boolean") - ; ( o1 - , "javax.lang.model.util.Elements.getBinaryName(javax.lang.model.element.TypeElement):javax.lang.model.element.Name" - ) - ; ( o1 - , "javax.lang.model.util.Elements.getPackageOf(javax.lang.model.element.Element):javax.lang.model.element.PackageElement" - ) - ; ( o1 - , "javax.lang.model.util.Elements.getAllMembers(javax.lang.model.element.TypeElement):java.util.List" - ) - ; ( o1 - , "javax.lang.model.util.Elements.getAllAnnotationMirrors(javax.lang.model.element.Element):java.util.List" - ) - ; ( o2 - , "javax.lang.model.util.Elements.hides(javax.lang.model.element.Element, \ - javax.lang.model.element.Element):boolean" ) - ; ( o3 - , "javax.lang.model.util.Elements.overrides(javax.lang.model.element.ExecutableElement, \ - javax.lang.model.element.ExecutableElement, javax.lang.model.element.TypeElement):boolean" ) - ; ( o1 - , "javax.lang.model.util.Types.asElement(javax.lang.model.type.TypeMirror):javax.lang.model.element.Element" - ) - ; ( o2 - , "javax.lang.model.util.Types.isSameType(javax.lang.model.type.TypeMirror, \ - javax.lang.model.type.TypeMirror):boolean" ) - ; ( o2 - , "javax.lang.model.util.Types.isSubtype(javax.lang.model.type.TypeMirror, \ - javax.lang.model.type.TypeMirror):boolean" ) - ; ( o2 - , "javax.lang.model.util.Types.isAssignable(javax.lang.model.type.TypeMirror, \ - javax.lang.model.type.TypeMirror):boolean" ) - ; ( o2 - , "javax.lang.model.util.Types.contains(javax.lang.model.type.TypeMirror, \ - javax.lang.model.type.TypeMirror):boolean" ) - ; ( o2 - , "javax.lang.model.util.Types.isSubsignature(javax.lang.model.type.ExecutableType, \ - javax.lang.model.type.ExecutableType):boolean" ) - ; ( o1 - , "javax.lang.model.util.Types.directSupertypes(javax.lang.model.type.TypeMirror):java.util.List" - ) - ; ( o1 - , "javax.lang.model.util.Types.erasure(javax.lang.model.type.TypeMirror):javax.lang.model.type.TypeMirror" - ) - ; ( o1 - , "javax.lang.model.util.Types.boxedClass(javax.lang.model.type.PrimitiveType):javax.lang.model.element.TypeElement" - ) - ; ( o1 - , "javax.lang.model.util.Types.unboxedType(javax.lang.model.type.TypeMirror):javax.lang.model.type.PrimitiveType" - ) - ; ( o1 - , "javax.lang.model.util.Types.capture(javax.lang.model.type.TypeMirror):javax.lang.model.type.TypeMirror" - ) - ; ( o1 - , "javax.lang.model.util.Types.getArrayType(javax.lang.model.type.TypeMirror):javax.lang.model.type.ArrayType" - ) - ; ( o2 - , "javax.lang.model.util.Types.getWildcardType(javax.lang.model.type.TypeMirror, \ - javax.lang.model.type.TypeMirror):javax.lang.model.type.WildcardType" ) - ; ( o2 - , "javax.lang.model.util.Types.getDeclaredType(javax.lang.model.element.TypeElement, \ - javax.lang.model.type.TypeMirror[]):javax.lang.model.type.DeclaredType" ) - ; ( o3 - , "javax.lang.model.util.Types.getDeclaredType(javax.lang.model.type.DeclaredType, \ - javax.lang.model.element.TypeElement, \ - javax.lang.model.type.TypeMirror[]):javax.lang.model.type.DeclaredType" ) - ; ( o2 - , "javax.lang.model.util.Types.asMemberOf(javax.lang.model.type.DeclaredType, \ - javax.lang.model.element.Element):javax.lang.model.type.TypeMirror" ) - ; ( n3 - , "javax.tools.JavaCompiler.getStandardFileManager(javax.tools.DiagnosticListener,java.util.Locale,java.nio.charset.Charset):javax.tools.StandardJavaFileManager" - ) - ; (ng, "javax.tools.JavaFileObject.getAccessLevel():javax.lang.model.element.Modifier") - ; (ng, "javax.tools.JavaFileObject.getNestingKind():javax.lang.model.element.NestingKind") - ; ( o2 - , "com.sun.source.util.SourcePositions.getStartPosition(com.sun.source.tree.CompilationUnitTree, \ - com.sun.source.tree.Tree):long" ) - ; ( o2 - , "com.sun.source.util.SourcePositions.getEndPosition(com.sun.source.tree.CompilationUnitTree, \ - com.sun.source.tree.Tree):long" ) - ; ( (n, [o; o]) - , "com.sun.source.util.TreePath.getPath(com.sun.source.tree.CompilationUnitTree, \ - com.sun.source.tree.Tree):com.sun.source.util.TreePath" ) - ; ( (n, [o; o]) - , "com.sun.source.util.TreePath.getPath(com.sun.source.util.TreePath, \ - com.sun.source.tree.Tree):com.sun.source.util.TreePath" ) - ; ( (n, [o]) - , "com.sun.source.util.Trees.getTree(javax.lang.model.element.Element):com.sun.source.tree.Tree" - ) - ; ( (n, [o]) - , "com.sun.source.util.Trees.getTree(javax.lang.model.element.TypeElement):com.sun.source.tree.ClassTree" - ) - ; ( (n, [o]) - , "com.sun.source.util.Trees.getTree(javax.lang.model.element.ExecutableElement):com.sun.source.tree.MethodTree" - ) - ; ( (n, [o; o]) - , "com.sun.source.util.Trees.getTree(javax.lang.model.element.Element, \ - javax.lang.model.element.AnnotationMirror):com.sun.source.tree.Tree" ) - ; ( (n, [o; o; o]) - , "com.sun.source.util.Trees.getTree(javax.lang.model.element.Element, \ - javax.lang.model.element.AnnotationMirror, \ - javax.lang.model.element.AnnotationValue):com.sun.source.tree.Tree" ) - ; ( o2 - , "com.sun.source.util.Trees.getPath(com.sun.source.tree.CompilationUnitTree, \ - com.sun.source.tree.Tree):com.sun.source.util.TreePath" ) - ; ( (n, [o]) - , "com.sun.source.util.Trees.getPath(javax.lang.model.element.Element):com.sun.source.util.TreePath" - ) - ; ( (n, [o; o]) - , "com.sun.source.util.Trees.getPath(javax.lang.model.element.Element, \ - javax.lang.model.element.AnnotationMirror):com.sun.source.util.TreePath" ) - ; ( (n, [o; o; o]) - , "com.sun.source.util.Trees.getPath(javax.lang.model.element.Element, \ - javax.lang.model.element.AnnotationMirror, \ - javax.lang.model.element.AnnotationValue):com.sun.source.util.TreePath" ) - ; ( (n, [o]) - , "com.sun.source.util.Trees.getElement(com.sun.source.util.TreePath):javax.lang.model.element.Element" - ) - ; ( (n, [o]) - , "com.sun.source.util.Trees.getTypeMirror(com.sun.source.util.TreePath):javax.lang.model.type.TypeMirror" - ) - ; ( (n, [o]) - , "com.sun.source.util.Trees.getScope(com.sun.source.util.TreePath):com.sun.source.tree.Scope" - ) - ; ( (n, [o]) - , "com.sun.source.util.Trees.getDocComment(com.sun.source.util.TreePath):java.lang.String" ) - ; ( o2 - , "com.sun.source.util.Trees.isAccessible(com.sun.source.tree.Scope, \ - javax.lang.model.element.TypeElement):boolean" ) - ; ( o3 - , "com.sun.source.util.Trees.isAccessible(com.sun.source.tree.Scope, \ - javax.lang.model.element.Element, javax.lang.model.type.DeclaredType):boolean" ) - ; ( o1 - , "com.sun.source.util.Trees.getOriginalType(javax.lang.model.type.ErrorType):javax.lang.model.type.TypeMirror" - ) - ; ( (o, [o; o; o; o]) - , "com.sun.source.util.Trees.printMessage(javax.tools.Diagnostic.Kind, java.lang.CharSequence, \ - com.sun.source.tree.Tree, com.sun.source.tree.CompilationUnitTree):void" ) - ; ( o1 - , "com.sun.source.util.Trees.getLub(com.sun.source.tree.CatchTree):javax.lang.model.type.TypeMirror" - ) - ; ( (n, [o; n; n]) - , "org.w3c.dom.Document.setUserData(java.lang.String,java.lang.Object,org.w3c.dom.UserDataHandler):java.lang.Object" - ) - ; ( (n, [o; n; n]) - , "org.w3c.dom.Node.setUserData(java.lang.String,java.lang.Object,org.w3c.dom.UserDataHandler):java.lang.Object" - ) - ; (* References *) - (ng, "java.lang.ref.Reference.get():java.lang.Object") - ; (ng, "java.lang.ref.PhantomReference.get():java.lang.Object") - ; (ng, "java.lang.ref.SoftReference.get():java.lang.Object") - ; (ng, "java.lang.ref.WeakReference.get():java.lang.Object") - ; (ng, "java.util.concurrent.atomic.AtomicReference.get():java.lang.Object") ] - - -(** Models for nullability *) -let annotated_list_nullability = - let result = - check_not_null_list @ check_state_list @ check_argument_list @ true_on_null_list - @ nullable_method_with_nonnull_alternatives_nullability_list @ annotated_list_nullability_other - in - List.find_a_dup result ~compare:(fun (_, descr1) (_, descr2) -> String.compare descr1 descr2) - |> Option.iter ~f:(fun (_, duplicate_method_descr) -> - Logging.die Logging.InternalError "Nullability table contains a duplicate %s" - duplicate_method_descr ) ; - result - - -(** Models for methods that do not return *) -let noreturn_list = [((o, [o]), "java.lang.System.exit(int):void")] - -type model_table_t = (string, bool * bool list) Hashtbl.t - -let mk_table list = - let map = Hashtbl.create 1 in - List.iter ~f:(function v, pn_id -> Hashtbl.replace map pn_id v) list ; - map - - -let annotated_table_nullability = mk_table annotated_list_nullability - -let check_not_null_table, check_not_null_parameter_table = - (mk_table check_not_null_list, mk_table check_not_null_parameter_list) - - -let check_state_table = mk_table check_state_list - -let check_argument_table = mk_table check_argument_list - -let containsKey_table = mk_table containsKey_list - -let mapPut_table = mk_table mapPut_list - -let noreturn_table = mk_table noreturn_list - -let true_on_null_table = mk_table true_on_null_list - -let nonnull_alternatives_table = - let method_descr_to_alternative = - List.map nullable_methods_with_nonnull_alternatives_list - ~f:(fun (_, method_descr, alternative) -> (alternative, method_descr)) - in - mk_table method_descr_to_alternative - - -let field_nullability = - [ ("android.content.pm.ApplicationInfo.dataDir", o) - ; ("android.content.pm.ApplicationInfo.deviceProtectedDataDir", o) - ; ("android.content.pm.ApplicationInfo.nativeLibraryDir", o) - ; ("android.content.pm.ApplicationInfo.publicSourceDir", o) - ; ("android.content.pm.ApplicationInfo.sourceDir", o) - ; ("android.content.pm.ApplicationInfo.storageUuid", o) - ; ( "android.content.pm.ApplicationInfo.processName" - , o (* From the "process" attribute or, if not set, the same as packageName *) ) - ; ( "android.content.pm.PackageInfo.activities" - , n (* Array of all tags included under , or null if there were none *) - ) - ; ( "android.content.pm.PackageInfo.applicationInfo" - , n (* Information collected from the tag, or null if there was none. *) ) - ; ("android.content.pm.PackageInfo.packageName", o) - ; ("android.content.pm.PackageInfo.signatures", o) - ; ( "android.content.pm.PackageInfo.services" - , n (* Array of all tags included under , or null if there were none. *) - ) - ; ("android.content.pm.PackageInfo.versionName", o) - ; ( "android.content.pm.PackageItemInfo.metaData" - , n - (* This field will only be filled in if you set the PackageManager#GET_META_DATA flag when requesting the info *) - ) - ; ("android.content.pm.PackageItemInfo.name", o) - ; ("android.content.pm.PackageItemInfo.packageName", o) - ; ( "android.content.pm.ResolveInfo.activityInfo" - , n (* Exactly one of activityInfo, serviceInfo, or providerInfo will be non-null. *) ) - ; ( "android.content.pm.ResolveInfo.serviceInfo" - , n (* Exactly one of activityInfo, serviceInfo, or providerInfo will be non-null. *) ) - ; ( "android.content.pm.ResolveInfo.providerInfo" - , n (* Exactly one of activityInfo, serviceInfo, or providerInfo will be non-null. *) ) - ; ("android.content.res.Configuration.locale", o) - ; ("android.graphics.BitmapFactory$Options.inBitmap", n) - ; ("android.graphics.Paint.Align.CENTER", o) - ; ("android.graphics.Paint.Align.LEFT", o) - ; ("android.graphics.Paint.Align.RIGHT", o) - ; ("android.graphics.Paint.Cap.BUTT", o) - ; ("android.graphics.Paint.Cap.ROUND", o) - ; ("android.graphics.Paint.Cap.SQUARE", o) - ; ("android.graphics.Paint.Join.BEVEL", o) - ; ("android.graphics.Paint.Join.MITER", o) - ; ("android.graphics.Paint.Join.ROUND", o) - ; ("android.graphics.Paint.Style.FILL", o) - ; ("android.graphics.Paint.Style.FILL_AND_STROKE", o) - ; ("android.graphics.Paint.Style.STROKE", o) - ; ("android.graphics.Typeface.BOLD", o) - ; ("android.graphics.Typeface.DEFAULT", o) - ; ("android.graphics.Typeface.DEFAULT_BOLD", o) - ; ("android.graphics.Typeface.MONOSPACE", o) - ; ("android.graphics.Typeface.SANS_SERIF", o) - ; ("android.graphics.Typeface.SERIF", o) - ; ("android.hardware.SensorEvent.values", o) - ; ("android.net.Uri.EMPTY", o) - ; ("android.provider.ContactsContract$Contacts.CONTENT_URI", o) - ; ("android.provider.MediaStore$Images$Media.EXTERNAL_CONTENT_URI", o) - ; ("android.provider.MediaStore$Images$Media.INTERNAL_CONTENT_URI", o) - ; ("android.provider.Settings$Global.CONTENT_URI", o) - ; ("android.provider.Settings$Secure.CONTENT_URI", o) - ; ("android.provider.Settings$System.CONTENT_URI", o) - ; ("android.provider.Settings$System.DEFAULT_ALARM_ALERT_URI", o) - ; ("android.provider.Settings$System.DEFAULT_NOTIFICATION_URI", o) - ; ("android.provider.Settings$System.DEFAULT_RINGTONE_URI", o) - ; ("android.opengl.EGL10.EGL_NO_CONTEXT", o) - ; ("android.opengl.EGL10.EGL_NO_DISPLAY", o) - ; ("android.opengl.EGL10.EGL_NO_SURFACE", o) - ; ("android.opengl.EGL14.EGL_NO_CONTEXT", o) - ; ("android.opengl.EGL14.EGL_NO_DISPLAY", o) - ; ("android.opengl.EGL14.EGL_NO_SURFACE", o) - ; ("android.os.Bundle.EMPTY", o) - ; ("android.os.Build.BOARD", o) - ; ("android.os.Build.BOOTLOADER", o) - ; ("android.os.Build.BRAND", o) - ; ("android.os.Build.DEVICE", o) - ; ("android.os.Build.DISPLAY", o) - ; ("android.os.Build.FINGERPRINT", o) - ; ("android.os.Build.HARDWARE", o) - ; ("android.os.Build.HOST", o) - ; ("android.os.Build.ID", o) - ; ("android.os.Build.MANUFACTURER", o) - ; ("android.os.Build.MODEL", o) - ; ("android.os.Build.PRODUCT", o) - ; ("android.os.Build.TAGS", o) - ; ("android.os.Build.TYPE", o) - ; ("android.os.Build.USER", o) - ; ("android.os.Build.SERIAL", o) - ; ("android.os.Build.SUPPORTED_ABIS", o) - ; ( "android.os.Message.obj" - , n (* An extra object going with the message - null if was not attached *) ) - ; ( "android.util.Pair.first" - , o - (* Deliberate UNSOUNDNESS. Since we don't support annotations in generic params, - for the sake of usability we consider Pair to be non-nullable, - which is true in majority of usages. - This is a concious decision: Pair is a useful utility class, and without this - it would be barely usable in practice. - This is also on par with our policy for arrays and other containers, which are considered - non-nullable. - *) ) - ; ("android.net.wifi.WifiConfiguration.SSID", o) - ; ("android.util.Pair.second", o) - ; ("android.util.Patterns.DOMAIN_NAME", o) - ; ("android.util.Patterns.EMAIL_ADDRESS", o) - ; ("android.util.Patterns.IP_ADDRESS", o) - ; ("android.util.Patterns.PHONE", o) - ; ("android.util.Patterns.WEB_URL", o) - ; ("android.view.View.ALPHA", o) - ; ("android.view.View.ROTATION", o) - ; ("android.view.View.ROTATION_X", o) - ; ("android.view.View.ROTATION_Y", o) - ; ("android.view.View.SCALE_X", o) - ; ("android.view.View.SCALE_Y", o) - ; ("android.view.View.TRANSLATION_X", o) - ; ("android.view.View.TRANSLATION_Y", o) - ; ("android.view.View.TRANSLATION_Z", o) - ; ("android.view.View.X", o) - ; ("android.view.View.Y", o) - ; ("android.view.View.Z", o) - ; ("androidx.core.text.TextDirectionHeuristicsCompat.ANYRTL_LTR", o) - ; ("androidx.core.text.TextDirectionHeuristicsCompat.FIRSTSTRONG_LTR", o) - ; ("androidx.core.text.TextDirectionHeuristicsCompat.FIRSTSTRONG_RTL", o) - ; ("androidx.core.text.TextDirectionHeuristicsCompat.LOCALE", o) - ; ("androidx.core.text.TextDirectionHeuristicsCompat.LTR", o) - ; ("androidx.core.text.TextDirectionHeuristicsCompat.RTL", o) - ; ("java.lang.Boolean.FALSE", o) - ; ("java.lang.Boolean.TRUE", o) - ; ("java.lang.Boolean.TYPE", o) - ; ("java.lang.Byte.TYPE", o) - ; ("java.lang.Double.TYPE", o) - ; ("java.lang.Float.TYPE", o) - ; ("java.lang.Integer.TYPE", o) - ; ("java.lang.Long.TYPE", o) - ; ("java.lang.Short.TYPE", o) - ; ("java.lang.System.err", o) - ; ("java.lang.System.in", o) - ; ("java.lang.System.out", o) - ; ("java.lang.Void.TYPE", o) - ; ("java.nio.ByteOrder.BIG_ENDIAN", o) - ; ("java.nio.ByteOrder.LITTLE_ENDIAN", o) - ; ("java.nio.channels.FileChannel$MapMode.READ_ONLY", o) - ; ("java.nio.channels.FileChannel$MapMode.READ_WRITE", o) - ; ("java.nio.channels.FileChannel$MapMode.PRIVATE", o) - ; ("java.nio.charset.StandardCharsets.ISO_8859_1", o) - ; ("java.nio.charset.StandardCharsets.US_ASCII", o) - ; ("java.nio.charset.StandardCharsets.UTF_16", o) - ; ("java.nio.charset.StandardCharsets.UTF_16BE", o) - ; ("java.nio.charset.StandardCharsets.UTF_16LE", o) - ; ("java.nio.charset.StandardCharsets.UTF_8", o) - ; ("java.util.Locale.CANADA", o) - ; ("java.util.Locale.CANADA_FRENCH", o) - ; ("java.util.Locale.CHINA", o) - ; ("java.util.Locale.CHINESE", o) - ; ("java.util.Locale.ENGLISH", o) - ; ("java.util.Locale.FRANCE", o) - ; ("java.util.Locale.FRENCH", o) - ; ("java.util.Locale.GERMAN", o) - ; ("java.util.Locale.GERMANY", o) - ; ("java.util.Locale.ITALIAN", o) - ; ("java.util.Locale.ITALY", o) - ; ("java.util.Locale.JAPAN", o) - ; ("java.util.Locale.JAPANESE", o) - ; ("java.util.Locale.KOREA", o) - ; ("java.util.Locale.KOREAN", o) - ; ("java.util.Locale.PRC", o) - ; ("java.util.Locale.PRIVATE_USE_EXTENSION", o) - ; ("java.util.Locale.ROOT", o) - ; ("java.util.Locale.SIMPLIFIED_CHINESE", o) - ; ("java.util.Locale.TAIWAN", o) - ; ("java.util.Locale.TRADITIONAL_CHINESE", o) - ; ("java.util.Locale.UK", o) - ; ("java.util.Locale.UNICODE_LOCALE_EXTENSION", o) - ; ("java.util.Locale.US", o) - ; ( "com.google.android.exoplayer2.source.dash.manifest.AdaptationSet.representations" - , o (* Set in the constructor, where this is non-nullable *) ) - ; ("com.google.android.exoplayer2.source.dash.manifest.Representation.format", o) - ; ("com.google.android.exoplayer2.upstream.DataSpec.fbDataSpecExtension", o) - ; ( "com.google.android.exoplayer2.upstream.DataSpec.uri" - , o (* Set in the constructor, where this is non-nullable *) ) - ; ("com.fasterxml.jackson.databind.node.JsonNodeFactory.instance", o) ] - - -let field_nullability_table = - let table = Hashtbl.create 1 in - List.iter field_nullability ~f:(fun (name, nullability) -> Hashtbl.add table name nullability) ; - table diff --git a/infer/src/nullsafe/modelTables.mli b/infer/src/nullsafe/modelTables.mli deleted file mode 100644 index ae5edc92ed1..00000000000 --- a/infer/src/nullsafe/modelTables.mli +++ /dev/null @@ -1,49 +0,0 @@ -(* - * 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. - *) - -open! IStd - -type model_table_t = (string, bool * bool list) Caml.Hashtbl.t - -(* The key is a unique string representation of a method. - The value is nullability of its return value and params correspondingly. - true corresponds to Nullable. -*) - -val annotated_table_nullability : model_table_t - -val check_not_null_table : model_table_t -(** List of methods known to perform a non-nullable assertion *) - -val check_not_null_parameter_table : (string, int) Caml.Hashtbl.t -(** The key is a string representation of a method known to perform a non-nullable assertion. The - value is an index (starting from 1) of an argument which nullability is being asserted. *) - -val check_state_table : model_table_t - -val check_argument_table : model_table_t - -val containsKey_table : model_table_t - -val mapPut_table : model_table_t - -val noreturn_table : model_table_t - -val true_on_null_table : model_table_t - -(** Used to describe a method complementary to a given one. Contains information needed for - reporting (hence does not describe the whole signature). *) -type nonnull_alternative_method = {package_name: string; class_name: string; method_name: string} - -val nonnull_alternatives_table : (string, nonnull_alternative_method) Caml.Hashtbl.t -(** The key is a string representation of a [@Nullable] method. The value is the description of - non-nullable alternative: a method does the same, but never returns null (does a null check - inside). *) - -val field_nullability_table : (string, bool) Caml.Hashtbl.t -(** Table of known fields whos nullability is explicitly modelled. Keys are field full names; value - [true] means nullable *) diff --git a/infer/src/nullsafe/models.ml b/infer/src/nullsafe/models.ml deleted file mode 100644 index edb46ef2db9..00000000000 --- a/infer/src/nullsafe/models.ml +++ /dev/null @@ -1,145 +0,0 @@ -(* - * 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. - *) - -open! IStd -module Hashtbl = Caml.Hashtbl -open ModelTables - -(** Unique representation of a method signature in form of a string. *) -let to_unique_id proc_name = Procname.to_unique_id (Procname.Java proc_name) - -let match_method_name proc_name name = String.equal (Procname.Java.get_method proc_name) name - -let table_has_procedure table proc_name = - let proc_id = to_unique_id proc_name in - try - ignore (Hashtbl.find table proc_id) ; - true - with Caml.Not_found -> false - - -let to_modelled_nullability ThirdPartyMethod.{ret_nullability; params} = - let is_nullable = function - | ThirdPartyMethod.Nullable -> - true - | ThirdPartyMethod.Nonnull -> - false - in - (is_nullable ret_nullability, List.map params ~f:(fun (_, nullability) -> is_nullable nullability)) - - -(* Some methods *) -let get_special_method_modelled_nullability tenv java_proc_name = - let open IOption.Let_syntax in - (* TODO: convert the implementation that does not use PatternMatch *) - let proc_name = Procname.Java java_proc_name in - let* class_name = Procname.get_class_type_name proc_name in - if PatternMatch.Java.is_enum tenv class_name then - match (Procname.get_method proc_name, Procname.get_parameters proc_name) with - (* values() is a synthetic enum method that is never null *) - | "values", [] -> - Some (false, []) - (* valueOf() is a synthetic enum method that is never null *) - | "valueOf", [Procname.Parameter.JavaParameter param_type_name] - when Typ.equal param_type_name StdTyp.Java.pointer_to_java_lang_string -> - Some (false, [false]) - | _ -> - None - else None - - -let get_modelled_annotated_signature ~is_callee_in_trust_list tenv proc_attributes = - let proc_name = - Procname.as_java_exn proc_attributes.ProcAttributes.proc_name - ~explanation:"get_modelled_annotated_signature should be called for Java methods only" - in - let nullsafe_mode = NullsafeMode.of_java_procname tenv proc_name in - let annotated_signature = - AnnotatedSignature.get ~is_callee_in_trust_list ~nullsafe_mode proc_attributes - in - let proc_id = to_unique_id proc_name in - (* Look in the infer internal models *) - let correct_by_internal_models ann_sig = - let modelled_nullability = - (* Look at internal model tables *) - Hashtbl.find_opt annotated_table_nullability proc_id - (* Maybe it is a special method whose nullability is predefined *) - |> IOption.if_none_evalopt ~f:(fun () -> - get_special_method_modelled_nullability tenv proc_name ) - in - Option.value_map modelled_nullability - ~f: - (AnnotatedSignature.set_modelled_nullability (Procname.Java proc_name) ann_sig - ModelledInternally ) - ~default:ann_sig - in - (* Look at external models *) - let correct_by_external_models ann_sig = - ThirdPartyAnnotationInfo.unique_repr_of_java_proc_name proc_name - |> ThirdPartyAnnotationInfo.find_nullability_info (ThirdPartyAnnotationGlobalRepo.get_repo ()) - |> Option.map ~f:(fun ThirdPartyAnnotationInfo.{signature; filename; line_number} -> - (to_modelled_nullability signature, filename, line_number) ) - |> Option.value_map - (* If we found information in third-party repo, overwrite annotated signature *) - ~f:(fun (modelled_nullability, filename, line_number) -> - AnnotatedSignature.set_modelled_nullability (Procname.Java proc_name) ann_sig - (InThirdPartyRepo {filename; line_number}) - modelled_nullability ) - ~default:ann_sig - in - (* External models overwrite internal ones *) - annotated_signature |> correct_by_internal_models |> correct_by_external_models - - -let is_check_not_null proc_name = - table_has_procedure check_not_null_table proc_name || match_method_name proc_name "checkNotNull" - - -let get_check_not_null_parameter proc_name = - let proc_id = to_unique_id proc_name in - Hashtbl.find_opt check_not_null_parameter_table proc_id - - -let is_check_state proc_name = - table_has_procedure check_state_table proc_name || match_method_name proc_name "checkState" - - -let is_check_argument proc_name = - table_has_procedure check_argument_table proc_name || match_method_name proc_name "checkArgument" - - -let is_noreturn proc_name = table_has_procedure noreturn_table proc_name - -let is_true_on_null proc_name = table_has_procedure true_on_null_table proc_name - -let is_false_on_null proc_name = - (* The only usecase for now - consider all overrides of `Object.equals()` correctly - implementing the Java specification contract (returning false on null). *) - PatternMatch.Java.is_override_of_lang_object_equals (Procname.Java proc_name) - - -let is_containsKey proc_name = table_has_procedure containsKey_table proc_name - -let is_mapPut proc_name = table_has_procedure mapPut_table proc_name - -let find_nonnullable_alternative proc_name = - (* NOTE: For now we fetch this info from internal models. - It is a good idea to support this feature in a user-facing third party repository. *) - let proc_id = to_unique_id proc_name in - Hashtbl.find_opt nonnull_alternatives_table proc_id - - -let is_field_nonnullable field_name = - Logging.d_with_indent "is_field_nonnullable" ~f:(fun () -> - let full_name = Fieldname.to_full_string field_name in - let from_model = - Hashtbl.find_opt field_nullability_table full_name - (* Models return `is_nullable`, we need `is_notnull` *) - |> Option.map ~f:not - in - Logging.d_printfln "Model for %s: %a" full_name (Pp.option Format.pp_print_bool) from_model ; - from_model ) diff --git a/infer/src/nullsafe/models.mli b/infer/src/nullsafe/models.mli deleted file mode 100644 index 818aa1927f6..00000000000 --- a/infer/src/nullsafe/models.mli +++ /dev/null @@ -1,52 +0,0 @@ -(* - * 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. - *) - -open! IStd - -(** Methods dealing with specific knowledge about code in important third libraries, standard - libraries, etc *) - -val get_modelled_annotated_signature : - is_callee_in_trust_list:bool -> Tenv.t -> ProcAttributes.t -> AnnotatedSignature.t -(** Return the annotated signature of the procedure, taking into account models. External models - take precedence over internal ones. *) - -val is_check_not_null : Procname.Java.t -> bool -(** Check if the procedure is one of the known methods asserting nullability of the object. Nullsafe - should understand that both the argument and return value are non-nullable after the call. *) - -val get_check_not_null_parameter : Procname.Java.t -> int option -(** Parameter number (starting from 1) for a procedure known to produce a non-nullable assertion. - [None] if the function is not known to be an aseertion OR the parameter number is not known *) - -val is_check_state : Procname.Java.t -> bool -(** Check if the procedure is one of the known Preconditions.checkState. *) - -val is_check_argument : Procname.Java.t -> bool -(** Check if the procedure is one of the known Preconditions.checkArgument. *) - -val is_noreturn : Procname.Java.t -> bool -(** Check if the procedure does not return. *) - -val is_true_on_null : Procname.Java.t -> bool -(** Check if the procedure returns true on null. *) - -val is_false_on_null : Procname.Java.t -> bool -(** Check if the procedure returns false on null. *) - -val is_containsKey : Procname.Java.t -> bool -(** Check if the procedure is Map.containsKey(). *) - -val is_mapPut : Procname.Java.t -> bool -(** Check if the procedure is Map.put(). *) - -val find_nonnullable_alternative : Procname.Java.t -> ModelTables.nonnull_alternative_method option -(** Check if a (nullable) method has a non-nullable alternative: A method that does the same as - [proc_name] but asserts the result is not null before returning to the caller. *) - -val is_field_nonnullable : Fieldname.t -> bool option -(** Check if a given field has known nullability: true = nonnull, false = null *) diff --git a/infer/src/nullsafe/typeCheck.ml b/infer/src/nullsafe/typeCheck.ml deleted file mode 100644 index 0fe46c8174b..00000000000 --- a/infer/src/nullsafe/typeCheck.ml +++ /dev/null @@ -1,1438 +0,0 @@ -(* - * 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. - *) - -open! IStd -module F = Format -module L = Logging -module DExp = DecompiledExp - -type typecheck_result = - {normal_flow_typestate: TypeState.t option; exception_flow_typestates: TypeState.t list} - -(** Module to treat selected complex expressions as constants. *) -module ComplexExpressions = struct - let procname_instanceof pname = Procname.equal BuiltinDecl.__instanceof pname - - let is_annotated_with predicate tenv procname = - match PatternMatch.lookup_attributes tenv procname with - | Some proc_attributes -> - let annotated_signature = - (* TODO(T62825735): fully support trusted callees *) - Models.get_modelled_annotated_signature ~is_callee_in_trust_list:false tenv - proc_attributes - in - let AnnotatedSignature.{ret_annotation_deprecated} = annotated_signature.ret in - predicate ret_annotation_deprecated - | None -> - false - - - (* given a predicate that requries Java procname, return a filter that requires generic prodname - *) - let java_predicate_to_pname_predicate java_predicate pname = - match pname with Procname.Java java_pname -> java_predicate java_pname | _ -> false - - - let procname_is_false_on_null tenv procname = - is_annotated_with Annotations.ia_is_false_on_null tenv procname - || java_predicate_to_pname_predicate Models.is_false_on_null procname - - - let procname_is_true_on_null tenv procname = - is_annotated_with Annotations.ia_is_true_on_null tenv procname - || java_predicate_to_pname_predicate Models.is_true_on_null procname - - - let procname_containsKey = java_predicate_to_pname_predicate Models.is_containsKey - - (** Recognize *all* the procedures treated specially in conditionals *) - let procname_used_in_condition pn = - procname_instanceof pn || procname_containsKey pn || BuiltinDecl.is_declared pn - - - exception Not_handled - - (* Convert an expression to a unique string. *) - (* This is used to turn complex expressions into pvar's.*) - (* Arbitrary function parameters and field access are allowed *) - (* when the relevant options are active. *) - let exp_to_string_map_dexp tenv map_dexp node' exp = - let rec dexp_to_string dexp = - let case_not_handled () = raise Not_handled in - match dexp with - | DExp.Darray (de1, de2) -> - dexp_to_string de1 ^ "[" ^ dexp_to_string de2 ^ "]" - | DExp.Darrow (de, f) | DExp.Ddot (de, f) -> - dexp_to_string de ^ "." ^ Fieldname.to_string f - | DExp.Dbinop (op, de1, de2) -> - "(" ^ dexp_to_string de1 ^ Binop.str Pp.text op ^ dexp_to_string de2 ^ ")" - | DExp.Dconst (Const.Cfun pn) -> - Procname.to_unique_id pn - | DExp.Dconst c -> - F.asprintf "%a" (Const.pp Pp.text) c - | DExp.Dderef de -> - dexp_to_string de - | DExp.Dfcall (fun_dexp, args, _, {CallFlags.cf_virtual= isvirtual}) - | DExp.Dretcall (fun_dexp, args, _, {CallFlags.cf_virtual= isvirtual}) -> - let pp_arg fmt de = F.pp_print_string fmt (dexp_to_string de) in - let pp_args fmt des = Pp.comma_seq pp_arg fmt des in - let pp fmt = - let virt = if isvirtual then "V" else "" in - F.fprintf fmt "%a(%a)%s" pp_arg fun_dexp pp_args args virt - in - F.asprintf "%t" pp - | (DExp.Dpvar pv | DExp.Dpvaraddr pv) when not (Pvar.is_frontend_tmp pv) -> - Pvar.to_string pv - | DExp.Dpvar _ | DExp.Dpvaraddr _ (* front-end variable -- this should not happen) *) -> - case_not_handled () - | DExp.Dunop (op, de) -> - Unop.to_string op ^ dexp_to_string de - | DExp.Dsizeof _ -> - case_not_handled () - | DExp.Dunknown -> - case_not_handled () - in - match map_dexp (Errdesc.exp_rv_dexp tenv node' exp) with - | Some de -> ( - try Some (dexp_to_string de) with Not_handled -> None ) - | None -> - None - - - let exp_to_string tenv node' exp = - let map_dexp de_opt = de_opt in - exp_to_string_map_dexp tenv map_dexp node' exp -end - -(* ComplexExpressions *) - -type check_return_type = Procdesc.t -> Typ.t -> Typ.t option -> Location.t -> unit - -type find_canonical_duplicate = Procdesc.Node.t -> Procdesc.Node.t - -type checks = {eradicate: bool; check_ret_type: check_return_type list} - -(** Typecheck an expression. *) -let rec typecheck_expr ({IntraproceduralAnalysis.tenv; proc_desc= curr_proc_desc} as analysis_data) - ~nullsafe_mode find_canonical_duplicate visited checks node instr_ref typestate e tr_default loc - : TypeState.range = - L.d_with_indent "typecheck_expr" ~pp_result:TypeState.pp_range ~f:(fun () -> - L.d_printfln "Expr: %a" Exp.pp e ; - match e with - (* null literal or 0 *) - | _ when Exp.is_null_literal e -> - let typ, _ = tr_default in - (* 0 is not the same thing as null. They are encoded as the same thing in SIL. - We distinct them by type. - *) - if PatternMatch.type_is_class typ then - (typ, InferredNullability.create (TypeOrigin.NullConst loc)) - else - (* 0 const (this is not the same as null) *) - (typ, InferredNullability.create (TypeOrigin.NonnullConst loc)) - | Exp.Const _ -> - let typ, _ = tr_default in - (* We already considered case of null literal above, so this is a non-null const. *) - (typ, InferredNullability.create (TypeOrigin.NonnullConst loc)) - | Exp.Lvar pvar -> - TypeState.lookup_pvar pvar typestate - |> IOption.if_none_eval ~f:(fun () -> - L.d_strln "WARNING: could not lookup Pvar in typestate: fallback to default" ; - tr_default ) - | Exp.Var id -> - TypeState.lookup_id id typestate - |> IOption.if_none_eval ~f:(fun () -> - L.d_strln "WARNING: could not lookup Id in typestate: fallback to default" ; - tr_default ) - | Exp.Exn e1 -> - typecheck_expr analysis_data ~nullsafe_mode find_canonical_duplicate visited checks node - instr_ref typestate e1 tr_default loc - | Exp.Lfield (exp, field_name, typ) -> - let _, _ = tr_default in - let _, inferred_nullability = - typecheck_expr analysis_data ~nullsafe_mode find_canonical_duplicate visited checks node - instr_ref typestate exp - (* TODO(T54687014) optimistic default might be an unsoundness issue - investigate *) - (typ, InferredNullability.create TypeOrigin.OptimisticFallback) - loc - in - let object_origin = InferredNullability.get_simple_origin inferred_nullability in - let curr_procname = - Procdesc.get_proc_name curr_proc_desc - |> Procname.as_java_exn - ~explanation:"typecheck_expr: attempt to typecheck non-Java method" - in - let class_under_analysis = Procname.Java.get_class_type_name curr_procname in - let tr_new = - match AnnotatedField.get tenv field_name ~class_typ:typ ~class_under_analysis with - | Some AnnotatedField.{annotated_type= field_type} -> - ( field_type.typ - , InferredNullability.create - (TypeOrigin.Field {object_origin; field_name; field_type; access_loc= loc}) ) - | None -> - L.d_strln "WARNING: could not lookup field annotation: fallback to default" ; - tr_default - in - if checks.eradicate then - EradicateChecks.check_object_dereference analysis_data ~nullsafe_mode - find_canonical_duplicate node instr_ref exp (AccessToField field_name) - inferred_nullability loc ; - tr_new - | Exp.Lindex (array_exp, index_exp) -> - let _, inferred_nullability = - typecheck_expr analysis_data ~nullsafe_mode find_canonical_duplicate visited checks node - instr_ref typestate array_exp tr_default loc - in - let index_desc = - match EradicateChecks.explain_expr tenv node index_exp with Some s -> s | None -> "?" - in - if checks.eradicate then - EradicateChecks.check_object_dereference analysis_data ~nullsafe_mode - find_canonical_duplicate node instr_ref array_exp - (AccessByIndex {index_desc}) - inferred_nullability loc ; - let typ, _ = tr_default in - (typ, InferredNullability.create TypeOrigin.ArrayAccess) - | _ -> - L.d_strln "WARNING: unknown expression: fallback to default" ; - tr_default ) - - -(* Handle the case where a field access X.f happens via a temporary variable $Txxx. - This has been observed in assignments this.f = exp when exp contains an ifthenelse. - Reconstuct the original expression knowing: the origin of $Txxx is 'this'. *) -let handle_field_access_via_temporary idenv curr_pname typestate exp = - let name_is_temporary name = - let prefix = "$T" in - String.is_prefix ~prefix name - in - let pvar_get_origin pvar = - match TypeState.lookup_pvar pvar typestate with - | Some (_, inferred_nullability) -> - Some (InferredNullability.get_simple_origin inferred_nullability) - | None -> - None - in - let handle_temporary e = - match IDEnv.expand_expr idenv e with - | Exp.Lvar pvar when name_is_temporary (Pvar.to_string pvar) -> ( - match pvar_get_origin pvar with - | Some TypeOrigin.This -> - let pvar' = Pvar.mk Mangled.this curr_pname in - Some (Exp.Lvar pvar') - | _ -> - None ) - | _ -> - None - in - match exp with - | Exp.Lfield (e, fn, typ) -> - let exp' = - match handle_temporary e with Some e' -> Exp.Lfield (e', fn, typ) | None -> exp - in - exp' - | _ -> - exp - - -(* Try to convert a function call to a pvar that originated it; fallback to an original expression in case of failure *) -let funcall_exp_to_original_pvar_exp tenv curr_pname typestate exp ~is_assignment ~call_node ~node - id = - match Decompile.find_normal_variable_funcall call_node id with - | Some (Exp.Const (Const.Cfun pn), _, _, _) - when not (ComplexExpressions.procname_used_in_condition pn) -> ( - match ComplexExpressions.exp_to_string tenv node exp with - | None -> - exp - | Some exp_str -> - let pvar = Pvar.mk (Mangled.from_string exp_str) curr_pname in - let already_defined_in_typestate = Option.is_some (TypeState.lookup_pvar pvar typestate) in - if is_assignment && already_defined_in_typestate then exp - (* Don't overwrite pvar representing result of function call. *) - else Exp.Lvar pvar ) - | _ -> - exp - - -let add_field_to_typestate_if_absent tenv access_loc typestate pvar object_origin field_name - ~field_class_typ ~class_under_analysis = - L.d_with_indent "add_field_to_typestate_if_absent" ~f:(fun () -> - match TypeState.lookup_pvar pvar typestate with - | Some _ -> - typestate - | None -> ( - match - AnnotatedField.get tenv field_name ~class_typ:field_class_typ ~class_under_analysis - with - | Some AnnotatedField.{annotated_type= field_type} -> - let range = - ( field_type.typ - , InferredNullability.create - (TypeOrigin.Field {object_origin; field_name; field_type; access_loc}) ) - in - let _, inferred_nullability = range in - L.d_printfln "Fieldname %a: typ=%a, inferred_nullability=%a" Fieldname.pp field_name - AnnotatedType.pp field_type InferredNullability.pp inferred_nullability ; - TypeState.add ~descr:"add_field_to_typestate_if_absent" pvar range typestate - | None -> - typestate ) ) - - -(* This does two things: - 1. The same as [convert_complex_exp_to_pvar] - 2. On top of this, if expr corresponds to a field access, stores this field in the typestate - (if not stored yet). -*) -let convert_complex_exp_to_pvar_and_register_field_in_typestate tenv idenv curr_pname - (curr_annotated_signature : AnnotatedSignature.t) ~node ~(original_node : Procdesc.Node.t) - ~is_assignment exp_ typestate loc = - L.d_with_indent "convert_complex_exp_to_pvar_and_register_field_in_typestate" - ~pp_result:(fun f (exp, typestate) -> - F.fprintf f "Exp: %a;@\nTypestate: @\n%a" Exp.pp exp TypeState.pp typestate ) - ~f:(fun () -> - let exp = - handle_field_access_via_temporary idenv curr_pname typestate (IDEnv.expand_expr idenv exp_) - in - let default = (exp, typestate) in - match exp with - | Exp.Var id when Option.is_some (Decompile.find_normal_variable_funcall node id) -> - ( funcall_exp_to_original_pvar_exp tenv curr_pname typestate exp ~is_assignment - ~call_node:node ~node id - , typestate ) - | Exp.Lvar pvar when Pvar.is_frontend_tmp pvar -> ( - let frontend_variable_assignment = - Decompile.find_program_variable_assignment original_node pvar - in - match frontend_variable_assignment with - | Some (call_node, id) -> - ( funcall_exp_to_original_pvar_exp tenv curr_pname typestate exp ~is_assignment - ~call_node ~node id - , typestate ) - | _ -> - default ) - | Exp.Lvar _ -> - default - | Exp.Lfield (exp_, fn, field_class_typ) -> - let inner_origin = - ( match exp_ with - | Exp.Lvar pvar -> - TypeState.lookup_pvar pvar typestate - | Exp.Var id -> - TypeState.lookup_id id typestate - | _ -> - None ) - |> Option.value_map - ~f:(fun (_, nullability) -> InferredNullability.get_simple_origin nullability) - ~default:TypeOrigin.OptimisticFallback - in - let exp' = IDEnv.expand_expr_temps idenv original_node exp_ in - let is_parameter_field pvar = - (* parameter.field *) - let name = Pvar.get_name pvar in - let filter AnnotatedSignature.{mangled} = Mangled.equal mangled name in - List.exists ~f:filter curr_annotated_signature.params - in - let is_static_field pvar = - (* static field *) - Pvar.is_global pvar - in - let pvar_to_str pvar = - if Exp.is_this (Exp.Lvar pvar) then "" else Pvar.to_string pvar ^ "_" - in - let class_under_analysis = - Procname.Java.get_class_type_name - (Procname.as_java_exn curr_pname ~explanation:"Attempt to typecheck non-Java procname") - in - let res = - match exp' with - | Exp.Lvar pv when is_parameter_field pv || is_static_field pv -> - let fld_name = pvar_to_str pv ^ Fieldname.to_string fn in - let pvar = Pvar.mk (Mangled.from_string fld_name) curr_pname in - let typestate' = - add_field_to_typestate_if_absent tenv loc typestate pvar inner_origin fn - ~field_class_typ ~class_under_analysis - in - (Exp.Lvar pvar, typestate') - | Exp.Lfield (_exp', fn', _) when Fieldname.is_java_outer_instance fn' -> - (* handle double dereference when accessing a field from an outer class *) - let fld_name = Fieldname.to_string fn' ^ "_" ^ Fieldname.to_string fn in - let pvar = Pvar.mk (Mangled.from_string fld_name) curr_pname in - let typestate' = - add_field_to_typestate_if_absent tenv loc typestate pvar inner_origin fn - ~field_class_typ ~class_under_analysis - in - (Exp.Lvar pvar, typestate') - | Exp.Lvar _ | Exp.Lfield _ -> ( - (* treat var.field1. ... .fieldn as a constant *) - match ComplexExpressions.exp_to_string tenv node exp with - | Some exp_str -> - let pvar = Pvar.mk (Mangled.from_string exp_str) curr_pname in - let typestate' = - add_field_to_typestate_if_absent tenv loc typestate pvar inner_origin fn - ~field_class_typ ~class_under_analysis - in - (Exp.Lvar pvar, typestate') - | None -> - default ) - | _ -> - default - in - res - | _ -> - default ) - - -(* Tries to find (or create a synthetic) pvar variable name that originated the given expression. - - This can be a "normal" pvar (e.g. a local variable or field parameter). - - Additionally, this can be a "synthetic" pvar corresponding to lookups: - - pvar representing "result of a function call" - - pvar representing field access. - Such synthetic pvars are needed to store once inferred nullability to make nullsafe remember - it in future accesses (so that the next call of the same method or access to the same field - does not require the programmer to write a check. - What is the difference between ~node and ~original_node? I don't know. This is an artifact of refactoring of - very old code. Sorry, dear future supporter, if names don't make sense. -*) -let convert_complex_exp_to_pvar tenv idenv curr_pname ~is_assignment - (curr_annotated_signature : AnnotatedSignature.t) ~node ~(original_node : Procdesc.Node.t) exp - typestate loc = - L.d_with_indent "convert_complex_exp_to_pvar" ~pp_result:Exp.pp ~f:(fun () -> - (* For now, we implement the function via the generic version that modifies the typestate. - *) - let exp, _ = - convert_complex_exp_to_pvar_and_register_field_in_typestate tenv idenv curr_pname - curr_annotated_signature ~is_assignment ~node ~original_node exp typestate loc - in - L.d_printfln "Disregarding updated typestate" ; - exp ) - - -let constructor_check_calls_this curr_pname calls_this pn = - match (curr_pname, pn) with - | Procname.Java curr_pname_java, Procname.Java pn_java -> - if - String.equal - (Procname.Java.get_class_name curr_pname_java) - (Procname.Java.get_class_name pn_java) - then calls_this := true - | _ -> - () - - -(* Drops hidden and synthetic parameters which we do not check in a call. *) -let drop_unchecked_params calls_this curr_pname proc_attributes params = - let pname = proc_attributes.ProcAttributes.proc_name in - if Procname.is_constructor pname then - match PatternMatch.get_this_type_nonstatic_methods_only proc_attributes with - | Some _ -> - constructor_check_calls_this curr_pname calls_this pname ; - (* Drop reference parameters to this and outer objects. *) - let is_hidden_parameter (n, _, _) = - Mangled.is_this n || Str.string_match (Str.regexp "$bcvar[0-9]+") (Mangled.to_string n) 0 - in - let rec drop_n_args ntl = - match ntl with fp :: tail when is_hidden_parameter fp -> 1 + drop_n_args tail | _ -> 0 - in - let n = drop_n_args proc_attributes.ProcAttributes.formals in - let visible_params = IList.drop params n in - (* Drop the trailing hidden parameter if the constructor is synthetic. *) - if proc_attributes.ProcAttributes.is_synthetic_method then - List.take visible_params (List.length visible_params - 1) - else visible_params - | None -> - params - else params - - -(* Drop parameters from the signature which we do not check in a call. *) -let drop_unchecked_signature_params proc_attributes annotated_signature = - if - Procname.is_constructor proc_attributes.ProcAttributes.proc_name - && proc_attributes.ProcAttributes.is_synthetic_method - then - List.take annotated_signature.AnnotatedSignature.params - (List.length annotated_signature.AnnotatedSignature.params - 1) - else annotated_signature.AnnotatedSignature.params - - -(* Apply a function to a pvar and its associated content if front-end generated. *) -let pvar_apply instr_ref idenv tenv curr_pname curr_annotated_signature loc handle_pvar typestate - pvar node = - L.d_with_indent "pvar_apply" ~pp_result:TypeState.pp ~f:(fun () -> - let typestate' = handle_pvar typestate pvar in - let curr_node = TypeErr.InstrRef.get_node instr_ref in - let frontent_variable_assignment = - if Pvar.is_frontend_tmp pvar then Decompile.find_program_variable_assignment curr_node pvar - else None - in - match frontent_variable_assignment with - | None -> - typestate' - | Some (node', id) -> ( - (* handle the case where pvar is a frontend-generated program variable *) - let exp = IDEnv.expand_expr idenv (Exp.Var id) in - match - convert_complex_exp_to_pvar ~is_assignment:false tenv idenv curr_pname - curr_annotated_signature ~node:node' ~original_node:node exp typestate' loc - with - | Exp.Lvar pvar' -> - handle_pvar typestate' pvar' - | _ -> - typestate' ) ) - - -(* typecheck_expr with fewer parameters, using a common template for typestate range *) -let typecheck_expr_simple analysis_data ~nullsafe_mode find_canonical_duplicate calls_this checks - node instr_ref typestate1 exp1 typ1 origin1 loc1 = - typecheck_expr analysis_data ~nullsafe_mode find_canonical_duplicate calls_this checks node - instr_ref typestate1 exp1 - (typ1, InferredNullability.create origin1) - loc1 - - -(* check if there are errors in exp1 *) -let typecheck_expr_for_errors analysis_data ~nullsafe_mode find_canonical_duplicate calls_this - checks node instr_ref typestate1 exp1 loc1 : unit = - ignore - (typecheck_expr_simple analysis_data ~nullsafe_mode find_canonical_duplicate calls_this checks - node instr_ref typestate1 exp1 StdTyp.void TypeOrigin.OptimisticFallback loc1 ) - - -(** Get the values of a vararg parameter given the pvar used to assign the elements by looking for - array assignments to the pvar. *) -let java_get_vararg_values node pvar idenv = - let values_of_instr acc = function - | Sil.Store {e1= Exp.Lindex (array_exp, _); e2= content_exp} - when Exp.equal (Exp.Lvar pvar) (IDEnv.expand_expr idenv array_exp) -> - (* Each vararg argument is an assignment to a pvar denoting an array of objects. *) - content_exp :: acc - | _ -> - acc - in - let values_of_node acc n = - Procdesc.Node.get_instrs n |> Instrs.fold ~f:values_of_instr ~init:acc - in - match Decompile.find_program_variable_assignment node pvar with - | Some (node', _) -> - Procdesc.fold_slope_range node' node ~f:values_of_node ~init:[] - | None -> - [] - - -(* Handle Preconditions.checkNotNull. *) -let do_preconditions_check_not_null - ({IntraproceduralAnalysis.proc_desc= curr_pdesc; tenv; _} as analysis_data) instr_ref - find_canonical_duplicate node loc curr_annotated_signature checks call_params idenv - parameter_num ~is_vararg typestate' = - (* clear the nullable flag of the first parameter of the procedure *) - let clear_nullable_flag ~nullsafe_mode typestate'' pvar = - (* remove the nullable flag for the given pvar *) - match TypeState.lookup_pvar pvar typestate'' with - | Some (t, nullability) -> - let should_report = - Config.eradicate_condition_redundant - (* TODO: This condition should be extracted into a dedicated rule *) - && InferredNullability.is_nonnullish nullability - && not (InferredNullability.origin_is_fun_defined nullability) - in - ( if checks.eradicate && should_report then - let cond = Exp.BinOp (Binop.Ne, Exp.Lvar pvar, Exp.null) in - TypeErr.register_error analysis_data find_canonical_duplicate - (Condition_redundant - { is_always_true= true - ; loc - ; condition_descr= EradicateChecks.explain_expr tenv node cond - ; nonnull_origin= InferredNullability.get_simple_origin nullability } ) - (Some instr_ref) ~nullsafe_mode ) ; - let previous_origin = InferredNullability.get_simple_origin nullability in - let new_origin = TypeOrigin.InferredNonnull {previous_origin} in - TypeState.add pvar - (t, InferredNullability.create new_origin) - typestate'' ~descr:"check_not_null function argument" - | None -> - typestate' - in - let rec find_parameter n eetl1 = - match (n, eetl1) with - | n, _ :: eetl2 when n > 1 -> - find_parameter (n - 1) eetl2 - | 1, ((_, Exp.Lvar pvar), typ) :: _ -> - Some (pvar, typ) - | _ -> - None - in - match find_parameter parameter_num call_params with - | Some (pvar, _) -> - let curr_pname = Procdesc.get_proc_name curr_pdesc in - if is_vararg then - let do_vararg_value e ts = - match IDEnv.expand_expr idenv e with - | Exp.Lvar pvar1 -> - pvar_apply instr_ref idenv tenv curr_pname curr_annotated_signature loc - (clear_nullable_flag - ~nullsafe_mode:curr_annotated_signature.AnnotatedSignature.nullsafe_mode ) - ts pvar1 node - | _ -> - ts - in - let vararg_values = java_get_vararg_values node pvar idenv in - List.fold_right ~f:do_vararg_value vararg_values ~init:typestate' - else - pvar_apply instr_ref idenv tenv curr_pname curr_annotated_signature loc - (clear_nullable_flag - ~nullsafe_mode:curr_annotated_signature.AnnotatedSignature.nullsafe_mode ) - typestate' pvar node - | None -> - typestate' - - -(* Handle Preconditions.checkState for &&-separated conditions x!=null. *) -let do_preconditions_check_state instr_ref idenv tenv curr_pname curr_annotated_signature - call_params loc node typestate' = - let set_nonnull_to_pvar typestate1 pvar = - (* handle the annotation flag for pvar *) - match TypeState.lookup_pvar pvar typestate1 with - | Some (t, nullability) -> - let previous_origin = InferredNullability.get_simple_origin nullability in - let new_origin = TypeOrigin.InferredNonnull {previous_origin} in - TypeState.add pvar - (t, InferredNullability.create new_origin) - typestate1 ~descr:"check_state argument" - | None -> - typestate1 - in - let res_typestate = ref typestate' in - let set_nonnull pvar = - (* set nullability for pvar *) - res_typestate := - pvar_apply instr_ref idenv tenv curr_pname curr_annotated_signature loc set_nonnull_to_pvar - !res_typestate pvar node - in - let handle_negated_condition cond_node = - let do_instr instr = - let set_flag expression = - let cond_e = IDEnv.expand_expr_temps idenv cond_node expression in - match - convert_complex_exp_to_pvar ~is_assignment:false tenv idenv curr_pname - curr_annotated_signature ~node:cond_node ~original_node:node cond_e typestate' loc - with - | Exp.Lvar pvar' -> - set_nonnull pvar' - | _ -> - () - in - match instr with - | Sil.Prune (Exp.BinOp (Binop.Eq, Exp.Const (Const.Cint i), cond_e_), _, _, _) - when IntLit.iszero i -> - set_flag cond_e_ - | Sil.Prune (Exp.BinOp (Binop.Eq, cond_e_, Exp.Const (Const.Cint i)), _, _, _) - when IntLit.iszero i -> - set_flag cond_e_ - | _ -> - () - in - Instrs.iter ~f:do_instr (Procdesc.Node.get_instrs cond_node) - in - match call_params with - | ((_, Exp.Lvar pvar), _) :: _ -> ( - (* temporary variable for the value of the boolean condition *) - let curr_node = TypeErr.InstrRef.get_node instr_ref in - let branch = false in - match Decompile.find_boolean_assignment curr_node pvar branch with - (* In foo(cond1 && cond2), the node that sets the result to false - has all the negated conditions as parents. *) - | Some boolean_assignment_node -> - List.iter ~f:handle_negated_condition (Procdesc.Node.get_preds boolean_assignment_node) ; - !res_typestate - | None -> - !res_typestate ) - | _ -> - typestate' - - -let object_typ = StdTyp.Java.pointer_to_java_lang_object - -(* Handle m.put(k,v) as assignment pvar = v for the pvar associated to m.get(k) *) -let do_map_put ({IntraproceduralAnalysis.proc_desc= curr_pdesc; tenv; _} as analysis_data) - call_params callee_pname loc node calls_this checks instr_ref ~nullsafe_mode - find_canonical_duplicate typestate' = - (* Get the proc name for map.get() from map.put() *) - let pname_get_from_pname_put pname_put = - let parameters = [object_typ] in - pname_put - |> Procname.Java.replace_method_name "get" - |> Procname.Java.replace_return_type object_typ - |> Procname.Java.replace_parameters parameters - in - match call_params with - | ((_, Exp.Lvar pv_map), _) :: ((_, exp_key), _) :: ((_, exp_value), typ_value) :: _ -> ( - (* Convert the dexp for k to the dexp for m.get(k) *) - let convert_dexp_key_to_dexp_get dopt = - match dopt with - | Some dexp_key -> - let pname_get = Procname.Java (pname_get_from_pname_put callee_pname) in - let dexp_get = DExp.Dconst (Const.Cfun pname_get) in - let dexp_map = DExp.Dpvar pv_map in - let args = [dexp_map; dexp_key] in - let call_flags = {CallFlags.default with CallFlags.cf_virtual= true} in - Some (DExp.Dretcall (dexp_get, args, loc, call_flags)) - | _ -> - None - in - match - ComplexExpressions.exp_to_string_map_dexp tenv convert_dexp_key_to_dexp_get node exp_key - with - | Some map_get_str -> - let curr_pname = Procdesc.get_proc_name curr_pdesc in - let pvar_map_get = Pvar.mk (Mangled.from_string map_get_str) curr_pname in - TypeState.add pvar_map_get - (typecheck_expr_simple analysis_data ~nullsafe_mode find_canonical_duplicate calls_this - checks node instr_ref typestate' exp_value typ_value TypeOrigin.OptimisticFallback - loc ) - typestate' ~descr:"do_map_put" - | None -> - typestate' ) - | _ -> - typestate' - - -(* Handle assignment fron a temp pvar in a condition. - This recognizes the handling of temp variables in ((x = ...) != null) - - The main idea is to take a quick look back in the CFG for any assignments - from [pvar]. *) -let handle_assignment_in_condition_for_sil_prune idenv node pvar = - L.d_with_indent ~pp_result:(Pp.option Exp.pp) "handle_assignment_in_condition_for_sil_prune" - ~f:(fun () -> - L.d_printfln "Pvar being pruned: %a" (Pvar.pp Pp.text) pvar ; - (* We need to find the first *unique* immediate predecessor with non-empty - list of instructions. Since some nodes like Join_node can have empty list of instrs, - we need to traverse CFG a bit. *) - let rec find_pred_node_with_instrs node = - match Procdesc.Node.get_preds node with - | [pred] -> - if Instrs.is_empty (Procdesc.Node.get_instrs pred) then find_pred_node_with_instrs pred - else Some pred - | _ -> - None - in - (* Inspect instructions within the node to find assignments from [pvar] *) - let find_aliased_var node = - let inspect_instr instr = - match instr with - | Sil.Store {e1= e; e2= e'} -> ( - let expanded_rhs = IDEnv.expand_expr idenv e' in - L.d_printfln "Found store instr: %a; RHS expands to: %a" - (Sil.pp_instr ~print_types:false Pp.text) - instr Exp.pp expanded_rhs ; - match expanded_rhs with Exp.Lvar v when Pvar.equal v pvar -> Some e | _ -> None ) - | _ -> - None - in - (* Here we check last instructions first. IDK if it makes a difference, but - it is at least compatible with the previous behaviour *) - Instrs.find_map (Procdesc.Node.get_instrs node |> Instrs.reverse_order) ~f:inspect_instr - in - match find_pred_node_with_instrs node with - | Some prev_node -> - L.d_printfln "Found non-empty unique predecessor node: #%a" Procdesc.Node.pp prev_node ; - find_aliased_var prev_node - | _ -> - None ) - - -let pp_normalized_cond fmt (_, exp) = Exp.pp fmt exp - -let rec normalize_cond_for_sil_prune_rec idenv ~node ~original_node cond = - L.d_with_indent "normalize_cond_for_sil_prune_rec" ~pp_result:pp_normalized_cond ~f:(fun () -> - L.d_printfln "cond=%a" Exp.pp cond ; - match cond with - | Exp.UnOp (Unop.LNot, c, top) -> - L.d_printfln "UnOp" ; - let node', c' = normalize_cond_for_sil_prune_rec idenv ~node ~original_node c in - (node', Exp.UnOp (Unop.LNot, c', top)) - | Exp.BinOp (bop, c1, c2) -> - L.d_printfln "BinOp" ; - let node', c1' = normalize_cond_for_sil_prune_rec idenv ~node ~original_node c1 in - let node'', c2' = normalize_cond_for_sil_prune_rec idenv ~node:node' ~original_node c2 in - L.d_printfln "c1=%a@\nc2=%a" Exp.pp c1 Exp.pp c2 ; - (node'', Exp.BinOp (bop, c1', c2')) - | Exp.Var _ -> - L.d_printfln "Var" ; - let c' = IDEnv.expand_expr idenv cond in - L.d_printfln "c'=%a" Exp.pp c' ; - if not (Exp.equal c' cond) then - normalize_cond_for_sil_prune_rec idenv ~node ~original_node c' - else (node, c') - | Exp.Lvar pvar when Pvar.is_frontend_tmp pvar -> ( - L.d_printfln "Lvar" ; - match handle_assignment_in_condition_for_sil_prune idenv original_node pvar with - | None -> ( - match Decompile.find_program_variable_assignment node pvar with - | Some (node', id) -> - (node', Exp.Var id) - | None -> - (node, cond) ) - | Some e2 -> - (node, e2) ) - | c -> - L.d_printfln "other" ; - (node, c) ) - - -(* Normalize the condition by resolving temp variables. *) -let normalize_cond_for_sil_prune idenv ~node cond = - normalize_cond_for_sil_prune_rec idenv ~node ~original_node:node cond - - -let rec check_condition_for_sil_prune - ({IntraproceduralAnalysis.proc_desc= curr_pdesc; tenv; _} as analysis_data) idenv calls_this - find_canonical_duplicate loc curr_annotated_signature linereader typestate checks true_branch - instr_ref ~nullsafe_mode ~original_node ~node c : TypeState.t = - let curr_pname = Procdesc.get_proc_name curr_pdesc in - (* check if the expression is coming from a call, and return the arguments *) - let extract_arguments_from_call filter_callee expr = - match expr with - | Exp.Var id -> ( - match Decompile.find_normal_variable_funcall node id with - | Some (Exp.Const (Const.Cfun pn), arguments, _, _) when filter_callee pn -> - Some arguments - | _ -> - None ) - | _ -> - None - in - (* check if the expression is coming from (`a` instanceof `b`), and returns `a`, if it is the case *) - let extract_first_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 *) - let extract_arguments_from_call_to_false_on_null_func e = - extract_arguments_from_call (ComplexExpressions.procname_is_false_on_null tenv) e - in - (* check if the expression is coming from a procedure returning true on null *) - let extract_arguments_from_call_to_true_on_null_func e = - extract_arguments_from_call (ComplexExpressions.procname_is_true_on_null tenv) e - in - (* check if the expression is coming from Map.containsKey *) - let is_from_containsKey expr = - extract_arguments_from_call ComplexExpressions.procname_containsKey expr |> Option.is_some - in - (* Call to x.containsKey(e) returned `true`. - 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) - to a typestate, with correspnding (non-null) type origin. - Returns the updated typestate. - *) - let handle_containsKey_returned_true call_to_containsKey_exr typestate = - let replace_contains_key_with_get_in_a_function_call_expression = function - (* This will replace x.containsKey(e) to x.get(e) *) - | Some - (DExp.Dretcall - (DExp.Dconst (Const.Cfun (Procname.Java pname_java)), args, loc, call_flags) ) -> - let pname_java' = - pname_java - |> Procname.Java.replace_method_name "get" - |> Procname.Java.replace_return_type object_typ - in - let fun_dexp = DExp.Dconst (Const.Cfun (Procname.Java pname_java')) in - Some (DExp.Dretcall (fun_dexp, args, loc, call_flags)) - | _ -> - None - in - let string_representing_call_to_get = - ComplexExpressions.exp_to_string_map_dexp tenv - replace_contains_key_with_get_in_a_function_call_expression node call_to_containsKey_exr - in - match string_representing_call_to_get with - | Some expr_str -> - (* Add pvar representing call to `get` to a typestate, indicating that it is a non-nullable *) - let pvar = Pvar.mk (Mangled.from_string expr_str) curr_pname in - let range = - (StdTyp.void, InferredNullability.create TypeOrigin.CallToGetKnownToContainsKey) - in - let typestate_with_new_pvar = TypeState.add pvar range typestate in - typestate_with_new_pvar - ~descr:"modelling result of Map.get() since containsKey() returned true" - | None -> - typestate - in - let set_nonnull e' typestate2 ~descr = - let handle_pvar typestate' pvar = - match TypeState.lookup_pvar pvar typestate' with - | Some (t, current_nullability) -> - let new_origin = - TypeOrigin.InferredNonnull - {previous_origin= InferredNullability.get_simple_origin current_nullability} - in - let new_nullability = InferredNullability.create new_origin in - TypeState.add pvar (t, new_nullability) typestate' ~descr - | None -> - typestate' - in - match e' with - | Exp.Lvar pvar -> - pvar_apply instr_ref idenv tenv curr_pname curr_annotated_signature loc handle_pvar - typestate2 pvar original_node - | _ -> - typestate2 - in - (* Trace [expr] back to the pvar that originated it, and set its nullability as inferred non-null. - Optionally, if this was already non-nullable, emit a corresponding condition redudant issue. - Returns the updated typestate. - *) - let set_original_pvar_to_nonnull_in_typestate ~with_cond_redundant_check expr typestate ~descr = - (* Trace back to original to pvar *) - let pvar_expr = - convert_complex_exp_to_pvar tenv idenv curr_pname curr_annotated_signature - ~is_assignment:false ~node ~original_node expr typestate loc - in - ( if with_cond_redundant_check then - (* We are about to set [pvar_expr] to nonnull. But what if it already is non-null? - This means the corresponding condition (initiated this PRUNE branch) was redudant. - *) - let typ, inferred_nullability = - typecheck_expr_simple analysis_data ~nullsafe_mode find_canonical_duplicate calls_this - checks original_node instr_ref typestate pvar_expr StdTyp.void - TypeOrigin.OptimisticFallback loc - in - if checks.eradicate then - EradicateChecks.check_condition_for_redundancy analysis_data ~is_always_true:true_branch - find_canonical_duplicate original_node pvar_expr typ inferred_nullability ~nullsafe_mode - idenv linereader loc instr_ref ) ; - set_nonnull pvar_expr typestate ~descr - in - (* Assuming [expr] is a boolean, this is the branch where, according to PRUNE semantics, - We've just ensured that [expr] == false. - Update the typestate accordingly. - *) - let handle_boolean_equal_false expr typestate = - match extract_arguments_from_call_to_true_on_null_func expr with - | Some arguments -> - (* [expr] is false hence, according to true-on-null contract, neither of [arguments] can be 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 - accumulated_typestate ~descr:"@TrueOnNull-proc argument in false branch" ) - arguments - | None -> - typestate - in - (* Assuming [expr] is a boolean, this is the branch where, according to PRUNE semantics, - We've just ensured that [expr] == true. - Update the typestate accordingly. - *) - let handle_boolean_equal_true expr typestate = - match extract_arguments_from_call_to_false_on_null_func expr with - | Some arguments -> - (* [expr] is true hence, according to false-on-null contract, neither of [arguments] can be 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 - accumulated_typestate ~descr:"@FalseOnNull-proc argument in false branch" ) - arguments - | None -> ( - match extract_first_argument_from_instanceof expr with - | Some argument -> - (* ([argument] instanceof == true) implies (argument != null) *) - set_original_pvar_to_nonnull_in_typestate ~with_cond_redundant_check:false argument - typestate ~descr:"instanceof argument in true branch" - | None -> - if is_from_containsKey expr then handle_containsKey_returned_true expr typestate - else typestate ) - in - (* Assuming [expr] is a non-primitive, this is the branch where, according to PRUNE semantics, - We've just ensured that [expr] != null. - Update the typestate accordingly. - *) - let handle_object_not_equal_null expr typestate = - set_original_pvar_to_nonnull_in_typestate ~with_cond_redundant_check:true expr typestate - ~descr:"`!= null` branch" - in - match[@warning "-ambiguous-var-in-pattern-guard"] c with - | Exp.BinOp (Binop.Eq, Exp.Const (Const.Cint i), expr) - | Exp.BinOp (Binop.Eq, expr, Exp.Const (Const.Cint i)) - when IntLit.iszero i -> - (* Zero literal has two different meanings important for Nullsafe: - `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 - *) - handle_boolean_equal_false expr typestate - | Exp.BinOp (Binop.Ne, Exp.Const (Const.Cint i), expr) - | Exp.BinOp (Binop.Ne, expr, Exp.Const (Const.Cint i)) - when IntLit.iszero i -> - (* Zero literal has two different meanings important for Nullsafe: - `null` and `false`, hence the expression means either "some_bool == true" or "some_object != null" - Handle both cases sequentially - *) - typestate |> handle_boolean_equal_true expr |> handle_object_not_equal_null expr - | Exp.UnOp (Unop.LNot, Exp.BinOp (Binop.Eq, e1, e2), _) -> - check_condition_for_sil_prune analysis_data idenv calls_this find_canonical_duplicate loc - curr_annotated_signature linereader typestate checks true_branch instr_ref ~nullsafe_mode - ~original_node ~node - (Exp.BinOp (Binop.Ne, e1, e2)) - | Exp.UnOp (Unop.LNot, Exp.BinOp (Binop.Ne, e1, e2), _) -> - check_condition_for_sil_prune analysis_data idenv calls_this find_canonical_duplicate loc - curr_annotated_signature linereader typestate checks true_branch instr_ref ~nullsafe_mode - ~original_node ~node - (Exp.BinOp (Binop.Eq, e1, e2)) - | _ -> - (* TODO(T54687014): silenced warning may be an unsoundeness issue; investigate *) - typestate - - -(* If the function has @PropagatesNullable params the nullability of result is determined by - nullability of actual values of these params. -*) -let clarify_ret_by_propagates_nullable ret (resolved_params : EradicateChecks.resolved_param list) = - (* Nullability of actual values of params that are marked as propagating nullables *) - let nullability_of_propagates_nullable_params = - List.filter_map resolved_params - ~f:(fun EradicateChecks.{is_formal_propagates_nullable; actual= _, inferred_nullability} -> - Option.some_if is_formal_propagates_nullable inferred_nullability ) - in - match nullability_of_propagates_nullable_params with - | [] -> - ret - | head :: tail -> - (* We got non-empty list of params that propagate null. - It means that nullability of the return value will be determined by actual (inferred) nullability of them. - Joining their nullability will give us the least upper bound of nullability of the result *) - let upper_bound_nullability = - List.fold tail ~init:head ~f:(fun acc nullability -> - InferredNullability.join acc nullability ) - in - let _, ret_typ = ret in - (upper_bound_nullability, ret_typ) - - -let calc_typestate_after_call - ({IntraproceduralAnalysis.proc_desc= curr_pdesc; tenv; _} as analysis_data) - find_canonical_duplicate calls_this checks idenv instr_ref signature_params cflags call_params - ~is_anonymous_inner_class_constructor ~callee_annotated_signature ~callee_attributes - ~callee_pname ~curr_annotated_signature ~nullsafe_mode ~typestate ~typestate1 loc node = - let resolve_param param_index (formal_param, actual_param) = - let (orig_e2, e2), t2 = actual_param in - let _, inferred_nullability_actual = - typecheck_expr analysis_data ~nullsafe_mode find_canonical_duplicate calls_this checks node - instr_ref typestate e2 - (* TODO(T54687014) optimistic default might be an unsoundness issue - investigate *) - (t2, InferredNullability.create TypeOrigin.OptimisticFallback) - loc - in - let actual = (orig_e2, inferred_nullability_actual) in - let is_formal_propagates_nullable = - Annotations.ia_is_propagates_nullable - formal_param.AnnotatedSignature.param_annotation_deprecated - in - EradicateChecks.{param_index; formal= formal_param; actual; is_formal_propagates_nullable} - in - (* Infer nullability of function call result based on its signature *) - let preliminary_resolved_ret = - let ret = callee_annotated_signature.AnnotatedSignature.ret in - let is_defined = not callee_attributes.ProcAttributes.is_defined in - let origin = - TypeOrigin.MethodCall - { pname= callee_pname - ; call_loc= loc - ; annotated_signature= callee_annotated_signature - ; is_defined } - in - (InferredNullability.create origin, ret.ret_annotated_type.typ) - in - let sig_len = List.length signature_params in - let call_len = List.length call_params in - let min_len = min sig_len call_len in - let slice l = - let len = List.length l in - if len > min_len then List.slice l (len - min_len) 0 else l - in - let sig_slice = slice signature_params in - let call_slice = slice call_params in - let sig_call_params = - List.filter - ~f:(fun ((sparam : AnnotatedSignature.param_signature), _) -> - let param_is_this = - Mangled.is_this sparam.mangled - || String.is_prefix ~prefix:"this$" (Mangled.to_string sparam.mangled) - in - not param_is_this ) - (List.zip_exn sig_slice call_slice) - in - let resolved_params = List.mapi ~f:resolve_param sig_call_params in - (* Clarify function call result nullability based on params annotated with @PropagatesNullable - and inferred nullability of those params *) - let ret_respecting_propagates_nullable = - clarify_ret_by_propagates_nullable preliminary_resolved_ret resolved_params - in - let typestate_after_call = - if not is_anonymous_inner_class_constructor then ( - if cflags.CallFlags.cf_virtual && checks.eradicate then - EradicateChecks.check_call_receiver analysis_data ~nullsafe_mode find_canonical_duplicate - node typestate1 call_params callee_pname instr_ref loc - (typecheck_expr analysis_data ~nullsafe_mode find_canonical_duplicate calls_this checks) ; - if checks.eradicate then - EradicateChecks.check_call_parameters ~callee_pname analysis_data ~nullsafe_mode - ~callee_annotated_signature find_canonical_duplicate node resolved_params loc instr_ref ; - if Models.is_check_not_null callee_pname then - match Models.get_check_not_null_parameter callee_pname with - | Some index -> - do_preconditions_check_not_null analysis_data instr_ref find_canonical_duplicate node - loc curr_annotated_signature checks call_params idenv index ~is_vararg:false - typestate1 - | None when Procname.Java.is_vararg callee_pname -> - let last_parameter = List.length call_params in - do_preconditions_check_not_null analysis_data instr_ref find_canonical_duplicate node - loc curr_annotated_signature checks call_params idenv last_parameter ~is_vararg:true - typestate1 - | None -> - (* assume the first parameter is checked for null *) - do_preconditions_check_not_null analysis_data instr_ref find_canonical_duplicate node - loc curr_annotated_signature checks call_params idenv 1 ~is_vararg:false typestate1 - else if Models.is_check_state callee_pname || Models.is_check_argument callee_pname then - let curr_pname = Procdesc.get_proc_name curr_pdesc in - do_preconditions_check_state instr_ref idenv tenv curr_pname curr_annotated_signature - call_params loc node typestate1 - else if Models.is_mapPut callee_pname then - do_map_put analysis_data call_params callee_pname loc node calls_this checks instr_ref - ~nullsafe_mode find_canonical_duplicate typestate1 - else typestate1 ) - else typestate1 - in - (typestate_after_call, ret_respecting_propagates_nullable) - - -(* SIL instruction in form of [ret = fun(args);] where fun is a non-builtin Java function *) -let typecheck_sil_call_function - ({IntraproceduralAnalysis.proc_desc= curr_pdesc; tenv; _} as analysis_data) - find_canonical_duplicate checks instr_ref typestate idenv ~callee_pname curr_annotated_signature - calls_this ~nullsafe_mode ret_id_typ etl_ loc callee_pname_java cflags node = - L.d_with_indent "typecheck_sil_call_function" ~f:(fun () -> - let callee_attributes = - match PatternMatch.lookup_attributes tenv callee_pname with - | Some proc_attributes -> - proc_attributes - | None -> - let formals = - List.mapi - ~f:(fun i (_, typ) -> - let arg = - if Int.equal i 0 && not (Procname.Java.is_static callee_pname_java) then - Mangled.this - else Printf.sprintf "arg%d" i |> Mangled.from_string - in - (arg, typ, Annot.Item.empty) ) - etl_ - in - let ret_type = Procname.Java.get_return_typ callee_pname_java in - let proc_attributes = - { (ProcAttributes.default (SourceFile.invalid __FILE__) callee_pname) with - ProcAttributes.formals - ; ret_type } - in - proc_attributes - in - let curr_pname = Procdesc.get_proc_name curr_pdesc in - let etl = drop_unchecked_params calls_this curr_pname callee_attributes etl_ in - let call_params, typestate1 = - let handle_et (e1, t1) (etl1, typestate1) = - typecheck_expr_for_errors analysis_data ~nullsafe_mode find_canonical_duplicate calls_this - checks node instr_ref typestate e1 loc ; - let e2 = - convert_complex_exp_to_pvar tenv idenv curr_pname curr_annotated_signature - ~is_assignment:false ~node ~original_node:node e1 typestate1 loc - in - (((e1, e2), t1) :: etl1, typestate1) - in - List.fold_right ~f:handle_et etl ~init:([], typestate) - in - let callee_annotated_signature = - match - (Procname.get_class_type_name callee_pname, Procname.get_class_type_name curr_pname) - with - | Some callee_class, Some class_under_analysis - when Typ.Name.equal callee_class class_under_analysis -> - (* The method call is to the method in the same class as we are currently analyzing. *) - AnnotatedSignature.get_for_class_under_analysis tenv callee_attributes - | _ -> - (* The call is to external (relatively to the class under analysis) method. Lookup models, trust lists, etc. *) - let is_callee_in_trust_list = - let caller_nullsafe_mode = NullsafeMode.of_procname tenv curr_pname in - let callee_class = Procname.get_class_type_name callee_pname in - Option.exists callee_class ~f:(fun class_name -> - Typ.Name.Java.get_java_class_name_exn class_name - |> NullsafeMode.is_in_trust_list caller_nullsafe_mode ) - in - Models.get_modelled_annotated_signature ~is_callee_in_trust_list tenv callee_attributes - in - if Config.write_html then - L.d_printfln "Callee signature: %a" - (AnnotatedSignature.pp callee_pname) - callee_annotated_signature ; - let signature_params = - drop_unchecked_signature_params callee_attributes callee_annotated_signature - in - let is_anonymous_inner_class_constructor = - Procname.Java.is_anonymous_inner_class_constructor_exn callee_pname_java - in - let do_return (ret_ta, ret_typ) typestate' = - let mk_return_range () = (ret_typ, ret_ta) in - let id = fst ret_id_typ in - TypeState.add_id id (mk_return_range ()) typestate' ~descr:"typecheck_sil_call_function" - in - let typestate_after_call, finally_resolved_ret = - calc_typestate_after_call analysis_data find_canonical_duplicate calls_this checks idenv - instr_ref signature_params cflags call_params ~is_anonymous_inner_class_constructor - ~callee_annotated_signature ~callee_attributes ~callee_pname:callee_pname_java - ~curr_annotated_signature ~nullsafe_mode ~typestate ~typestate1 loc node - in - do_return finally_resolved_ret typestate_after_call ) - - -(** Typecheck an instruction. *) -let typecheck_instr ({IntraproceduralAnalysis.proc_desc= curr_pdesc; tenv; _} as analysis_data) - calls_this checks (node : Procdesc.Node.t) idenv find_canonical_duplicate - (curr_annotated_signature : AnnotatedSignature.t) instr_ref linereader typestate instr = - let curr_pname = Procdesc.get_proc_name curr_pdesc in - let is_return pvar = - let ret_pvar = Procdesc.get_ret_var curr_pdesc in - Pvar.equal pvar ret_pvar - in - let nullsafe_mode = curr_annotated_signature.nullsafe_mode in - match instr with - | Sil.Metadata (ExitScope (vars, _)) -> - List.fold_right vars ~init:typestate ~f:(fun var astate -> - match var with - | Var.LogicalVar id -> - TypeState.remove_id id astate ~descr:"ExitScope" - | Var.ProgramVar _ -> - astate ) - | Sil.Metadata - ( Abstract _ - | CatchEntry _ - | EndBranches - | Nullify _ - | Skip - | TryEntry _ - | TryExit _ - | VariableLifetimeBegins _ ) -> - typestate - | Sil.Load {id; e; typ; loc} -> - typecheck_expr_for_errors analysis_data ~nullsafe_mode find_canonical_duplicate calls_this - checks node instr_ref typestate e loc ; - let e', typestate' = - convert_complex_exp_to_pvar_and_register_field_in_typestate tenv idenv curr_pname - curr_annotated_signature ~node ~original_node:node ~is_assignment:false e typestate loc - in - TypeState.add_id id - (typecheck_expr_simple analysis_data ~nullsafe_mode find_canonical_duplicate calls_this - checks node instr_ref typestate' e' typ TypeOrigin.OptimisticFallback loc ) - ~descr:"Sil.Load" typestate' - | Sil.Store {e1= Exp.Lvar pvar; e2= Exp.Exn _} when is_return pvar -> - (* skip assignment to return variable where it is an artifact of a throw instruction *) - typestate - | Sil.Store {e1; typ; e2; loc} -> - typecheck_expr_for_errors analysis_data ~nullsafe_mode find_canonical_duplicate calls_this - checks node instr_ref typestate e1 loc ; - let e1' = - convert_complex_exp_to_pvar tenv idenv curr_pname curr_annotated_signature ~node - ~original_node:node ~is_assignment:true e1 typestate loc - in - let check_field_assign () = - match e1 with - | Exp.Lfield (_, field_name, field_class_type) -> ( - let class_under_analysis = - Procname.Java.get_class_type_name - (Procname.as_java_exn curr_pname ~explanation:"Attempt to typecheck non-Java method") - in - match - AnnotatedField.get tenv field_name ~class_typ:field_class_type ~class_under_analysis - with - | Some annotated_field -> - if checks.eradicate then - EradicateChecks.check_field_assignment analysis_data ~nullsafe_mode - find_canonical_duplicate node instr_ref typestate ~expr_rhs:e2 ~field_type:typ - loc field_name annotated_field - (typecheck_expr analysis_data ~nullsafe_mode find_canonical_duplicate calls_this - checks ) - | None -> - L.d_strln "WARNING: could not fetch field declaration; skipping assignment check" ) - | _ -> - () - in - let typestate2 = - match e1' with - | Exp.Lvar pvar -> - TypeState.add pvar - (typecheck_expr_simple analysis_data ~nullsafe_mode find_canonical_duplicate - calls_this checks node instr_ref typestate e2 typ TypeOrigin.OptimisticFallback loc ) - typestate ~descr:"Sil.Store: Exp.Lvar case" - | Exp.Lfield _ -> - typestate - | _ -> - typestate - in - check_field_assign () ; - typestate2 - (* Java `new` operators *) - | Sil.Call ((id, _), Exp.Const (Const.Cfun pn), [(_, typ)], _, _) - when Procname.equal pn BuiltinDecl.__new || Procname.equal pn BuiltinDecl.__new_array -> - (* new never returns null *) - TypeState.add_id id - (typ, InferredNullability.create TypeOrigin.New) - typestate ~descr:"new() operator" - (* Type cast *) - | Sil.Call ((id, _), Exp.Const (Const.Cfun pn), (e, typ) :: _, loc, _) - when Procname.equal pn BuiltinDecl.__cast -> - typecheck_expr_for_errors analysis_data ~nullsafe_mode find_canonical_duplicate calls_this - checks node instr_ref typestate e loc ; - let e' = - convert_complex_exp_to_pvar tenv idenv curr_pname curr_annotated_signature - ~is_assignment:false ~node ~original_node:node e typestate loc - in - (* cast copies the type of the first argument *) - TypeState.add_id id - (typecheck_expr_simple analysis_data ~nullsafe_mode find_canonical_duplicate calls_this - checks node instr_ref typestate e' typ TypeOrigin.OptimisticFallback loc ) - typestate ~descr:"type cast" - (* myarray.length *) - | Sil.Call ((id, _), Exp.Const (Const.Cfun pn), [(array_exp, t)], loc, _) - when Procname.equal pn BuiltinDecl.__get_array_length -> - let _, ta = - typecheck_expr analysis_data ~nullsafe_mode find_canonical_duplicate calls_this checks node - instr_ref typestate array_exp - (* TODO(T54687014) optimistic default might be an unsoundness issue - investigate *) - (t, InferredNullability.create TypeOrigin.OptimisticFallback) - loc - in - if checks.eradicate then - EradicateChecks.check_object_dereference analysis_data ~nullsafe_mode - find_canonical_duplicate node instr_ref array_exp - DereferenceRule.ReportableViolation.ArrayLengthAccess ta loc ; - TypeState.add_id id - (Typ.mk (Tint Typ.IInt), InferredNullability.create TypeOrigin.ArrayLengthResult) - typestate ~descr:"array.length" - (* All other builtins that are not considered above *) - | Sil.Call (_, Exp.Const (Const.Cfun pn), _, _, _) when BuiltinDecl.is_declared pn -> - typestate (* skip other builtins *) - (* Normal call of a function *) - | Sil.Call - ( ret_id_typ - , Exp.Const (Const.Cfun (Procname.Java callee_pname_java as callee_pname)) - , etl_ - , loc - , cflags ) -> - typecheck_sil_call_function analysis_data find_canonical_duplicate checks instr_ref typestate - idenv ~callee_pname curr_annotated_signature calls_this ~nullsafe_mode ret_id_typ etl_ loc - callee_pname_java cflags node - (* Calls instruction that is not a function call *) - | Sil.Call _ -> - (* This is something weird, we don't normally expect this type of instruction - This may be an unsoundness issue. - TODO(T54687014) investigate if this happens in production and add assertion, if not, and handle if gracefully, if yes. - *) - typestate - | Sil.Prune (cond, loc, true_branch, _) -> - let node', normalized_cond = normalize_cond_for_sil_prune idenv ~node cond in - check_condition_for_sil_prune analysis_data idenv calls_this find_canonical_duplicate loc - curr_annotated_signature linereader typestate checks true_branch instr_ref ~nullsafe_mode - ~node:node' ~original_node:node normalized_cond - - -let can_instrunction_throw tenv node instr = - match instr with - | Sil.Call (_, Exp.Const (Const.Cfun callee_pname), _, _, _) -> ( - let callee_attributes_opt = PatternMatch.lookup_attributes tenv callee_pname in - (* We assume if the function is not annotated with throws(), it can not throw an exception. - This is unsound. - TODO(T63305137) nullsafe should assume all methods can throw. - *) - match callee_attributes_opt with - | Some callee_attributes -> - not (List.is_empty callee_attributes.ProcAttributes.exceptions) - | None -> - false ) - | Sil.Store {e1= Exp.Lvar pv} - when Pvar.is_return pv - && Procdesc.Node.equal_nodekind (Procdesc.Node.get_kind node) Procdesc.Node.throw_kind -> - (* Explicit throw instruction *) - true - | _ -> - false - - -(* true if after this instruction the program interrupts *) -let is_noreturn_instruction = function - | Sil.Call (_, Exp.Const (Const.Cfun (Procname.Java callee_pname)), _, _, _) - when Models.is_noreturn callee_pname -> - true - | _ -> - false - - -(** Typecheck the instructions in a cfg node. *) -let typecheck_node ({IntraproceduralAnalysis.tenv; _} as analysis_data) calls_this checks idenv - find_canonical_duplicate annotated_signature typestate node linereader = - if Procdesc.Node.equal_nodekind (Procdesc.Node.get_kind node) Procdesc.Node.exn_sink_kind then - {normal_flow_typestate= None; exception_flow_typestates= []} - else - let instrs = Procdesc.Node.get_instrs node in - let instr_ref_gen = TypeErr.InstrRef.create_generator node in - let canonical_node = find_canonical_duplicate node in - (* typecheck the instruction and accumulate result *) - let fold_instruction - ( { normal_flow_typestate= normal_typestate_prev_opt - ; exception_flow_typestates= exception_flow_typestates_prev } as prev_result ) instr = - if Config.write_html then - L.d_printfln "-----------------------------\nTypecking instr: %a@\n" - (Sil.pp_instr ~print_types:true Pp.text) - instr ; - match normal_typestate_prev_opt with - | None -> - (* no input typestate - abort typechecking and propagate the current result *) - prev_result - | Some normal_flow_typestate_prev -> - let instr_ref = - (* keep unique instruction reference per-node *) - TypeErr.InstrRef.gen instr_ref_gen - in - let normal_flow_typestate = - typecheck_instr analysis_data calls_this checks node idenv find_canonical_duplicate - annotated_signature instr_ref linereader normal_flow_typestate_prev instr - in - if Config.write_html then - L.d_printfln "New state: @\n%a@\n" TypeState.pp normal_flow_typestate ; - if is_noreturn_instruction instr then ( - L.d_strln "Found no return; aborting flow" ; - {prev_result with normal_flow_typestate= None} ) - else - let exception_flow_typestates = - if can_instrunction_throw tenv node instr then ( - (* add the typestate after this instruction to the list of exception typestates *) - L.d_strln "Throwable instruction: adding the typestate to exception list" ; - normal_flow_typestate :: exception_flow_typestates_prev ) - else exception_flow_typestates_prev - in - {normal_flow_typestate= Some normal_flow_typestate; exception_flow_typestates} - in - (* Reset 'always' field for forall errors to false. *) - (* This is used to track if it is set to true for all visit to the node. *) - TypeErr.node_reset_forall canonical_node ; - Instrs.fold instrs ~f:fold_instruction - ~init:{normal_flow_typestate= Some typestate; exception_flow_typestates= []} diff --git a/infer/src/nullsafe/typeCheck.mli b/infer/src/nullsafe/typeCheck.mli deleted file mode 100644 index f1f00a04f10..00000000000 --- a/infer/src/nullsafe/typeCheck.mli +++ /dev/null @@ -1,39 +0,0 @@ -(* - * 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. - *) - -open! IStd - -(** Module type for the type checking functions. *) - -type check_return_type = Procdesc.t -> Typ.t -> Typ.t option -> Location.t -> unit - -type find_canonical_duplicate = Procdesc.Node.t -> Procdesc.Node.t - -type checks = {eradicate: bool; check_ret_type: check_return_type list} - -type typecheck_result = - { normal_flow_typestate: TypeState.t option - (** Typestate at the exit of the node. [None] if node is determined dead end (e.g. noreturn - function). Will be passed to all output nodes of the current node. *) - ; exception_flow_typestates: TypeState.t list - (** If an exception might be thrown after this node, this list should contain all possible - states at which the exception can be thrown. (Can be several states because different - instructions in the single node can potentially throw). These typestates (joined - together) will be passed to all "exception output" nodes of the current node. *) } - -val typecheck_node : - IntraproceduralAnalysis.t - -> bool ref - -> checks - -> IDEnv.t - -> find_canonical_duplicate - -> AnnotatedSignature.t - -> TypeState.t - -> Procdesc.Node.t - -> LineReader.t - -> typecheck_result -(** Main entry point. Typecheck the CFG node given input typestate, and report issues, if found *) diff --git a/infer/src/nullsafe/typeErr.ml b/infer/src/nullsafe/typeErr.ml deleted file mode 100644 index 9f23a1f3e5b..00000000000 --- a/infer/src/nullsafe/typeErr.ml +++ /dev/null @@ -1,371 +0,0 @@ -(* - * 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. - *) - -open! IStd -module Hashtbl = Caml.Hashtbl -module MF = MarkupFormatter -module P = Printf -module F = Format - -(** Module for Type Error messages. *) - -(** Describe the origin of values propagated by the checker. *) -module type InstrRefT = sig - type t [@@deriving compare] - - val equal : t -> t -> bool - - type generator - - val create_generator : Procdesc.Node.t -> generator - - val gen : generator -> t - - val get_node : t -> Procdesc.Node.t - - val hash : t -> int - - val replace_node : t -> Procdesc.Node.t -> t -end - -(* InstrRefT *) - -(** Per-node instruction reference. *) -module InstrRef : InstrRefT = struct - type t = Procdesc.Node.t * int [@@deriving compare, equal] - - type generator = Procdesc.Node.t * int ref - - let hash (n, i) = Hashtbl.hash (Procdesc.Node.hash n, i) - - let get_node (n, _) = n - - let replace_node (_, i) n' = (n', i) - - let create_generator n = (n, ref 0) - - let gen instr_ref_gen = - let node, ir = instr_ref_gen in - incr ir ; - (node, !ir) -end - -(* InstrRef *) - -(** Instance of an error *) -type err_instance = - | Condition_redundant of - { loc: Location.t - ; is_always_true: bool - ; condition_descr: string option - ; nonnull_origin: TypeOrigin.t } - | Inconsistent_subclass of - { loc: Location.t - ; inheritance_violation: InheritanceRule.violation - ; violation_type: InheritanceRule.ReportableViolation.violation_type - ; base_proc_name: Procname.Java.t - ; overridden_proc_name: Procname.Java.t } - | Field_not_initialized of {loc: Location.t; field_name: Fieldname.t} - | Over_annotation of - { loc: Location.t - ; over_annotated_violation: OverAnnotatedRule.violation - ; violation_type: OverAnnotatedRule.violation_type } - | Nullable_dereference of - { dereference_violation: DereferenceRule.violation - ; dereference_location: Location.t - ; dereference_type: DereferenceRule.ReportableViolation.dereference_type - ; nullable_object_descr: string option } - | Bad_assignment of - { assignment_violation: AssignmentRule.violation - ; assignment_location: Location.t - ; assignment_type: AssignmentRule.ReportableViolation.assignment_type } -[@@deriving compare, equal] - -(** Returns whether we can be certain that an [err_instance] is related to synthetic code, - autogen/codegen). - - NOTE: This implementation is very naive and handles only simplest cases (which is actually - enough for our use-case at the moment). The problem here is that in general: - - - any expression that has synthetic code inside can be considered "tainted", - - any expression with a "tainted" sub-expression is also "tainted", - - with "tainted" expressions all bets are off and error reporting should be suppressed. - - We could handle this properly by introducing [Nullability.Synthetic] but this is quite involved - (and hopefully won't be needed). *) -let is_synthetic_err = function - | Bad_assignment {assignment_type; _} -> ( - AssignmentRule.ReportableViolation.( - match assignment_type with - | PassingParamToFunction {procname; _} -> - (* NOTE: this currently doesn't cover the case when the actual - argument involves syntethic code *) - Procname.Java.is_autogen_method procname - | AssigningToField fieldname -> - Fieldname.is_java_synthetic fieldname - | _ -> - false ) ) - | _ -> - false - - -let pp_err_instance fmt err_instance = - match err_instance with - | Condition_redundant _ -> - F.pp_print_string fmt "Condition_redundant" - | Inconsistent_subclass _ -> - F.pp_print_string fmt "Inconsistent_subclass" - | Field_not_initialized _ -> - F.pp_print_string fmt "Field_not_initialized" - | Over_annotation _ -> - F.pp_print_string fmt "Over_annotation" - | Nullable_dereference _ -> - F.pp_print_string fmt "Nullable_dereference" - | Bad_assignment _ -> - F.fprintf fmt "Bad_assignment" - - -module H = Hashtbl.Make (struct - type t = err_instance * InstrRef.t option [@@deriving compare, equal] - - let hash = Hashtbl.hash -end -(* H *)) - -type err_state = {mutable always: bool (** always fires on its associated node *)} - -let err_tbl : err_state H.t = H.create 1 - -(** Reset the error table. *) -let reset () = H.reset err_tbl - -(** Get the forall status of an err_instance. The forall status indicates that the error should be - printed only if it occurs on every path. *) -let get_forall = function - | Condition_redundant _ -> - true - | Field_not_initialized _ -> - false - | Over_annotation _ -> - false - | Bad_assignment _ -> - false - | Inconsistent_subclass _ -> - false - | Nullable_dereference _ -> - false - - -(** Reset the always field of the forall erros in the node, so if they are not set again we know - that they don't fire on every path. *) -let node_reset_forall node = - let iter (err_instance, instr_ref_opt) err_state = - match (instr_ref_opt, get_forall err_instance) with - | Some instr_ref, is_forall -> - let node' = InstrRef.get_node instr_ref in - if is_forall && Procdesc.Node.equal node node' then err_state.always <- false - | None, _ -> - () - in - H.iter iter err_tbl - - -(** Add an error to the error table and return whether it should be printed now. *) -let add_err find_canonical_duplicate err_instance instr_ref_opt = - let is_forall = get_forall err_instance in - if H.mem err_tbl (err_instance, instr_ref_opt) then false (* don't print now *) - else - let instr_ref_opt_deduplicate = - match (is_forall, instr_ref_opt) with - | true, Some instr_ref -> - (* use canonical duplicate for forall checks *) - let node = InstrRef.get_node instr_ref in - let canonical_node = find_canonical_duplicate node in - let instr_ref' = InstrRef.replace_node instr_ref canonical_node in - Some instr_ref' - | _ -> - instr_ref_opt - in - Logging.debug Analysis Medium "Registering an issue: %a@\n" pp_err_instance err_instance ; - H.add err_tbl (err_instance, instr_ref_opt_deduplicate) {always= true} ; - not is_forall - - -(* If an error is related to a particular field, we support suppressing the - error via a supress annotation placed near the field declaration *) -let get_field_name_for_error_suppressing = function - | Over_annotation {violation_type= OverAnnotatedRule.FieldOverAnnoted field_name} -> - Some field_name - | Field_not_initialized {field_name} -> - Some field_name - | Condition_redundant _ - | Over_annotation {violation_type= OverAnnotatedRule.ReturnOverAnnotated _} - (* In case of assignment to a field, it should be fixed case by case for each assignment *) - | Bad_assignment _ - | Nullable_dereference _ - | Inconsistent_subclass _ -> - None - - -(* The condition is redundant because a non-nullable object was (implicitly or explicitly) compared with null. - Describes what exactly made nullsafe believe this is indeed a non-nullable. -*) -let get_nonnull_explanation_for_condition_redudant (nonnull_origin : TypeOrigin.t) = - match nonnull_origin with - | MethodCall {pname} -> - Format.asprintf ": %a is not annotated as `@Nullable`" MF.pp_monospaced - (Procname.Java.to_simplified_string ~withclass:true pname) - | NullConst _ -> - Logging.die Logging.InternalError - "Unexpected origin NullConst: this is for nullable types, should not lead to condition \ - redundant" - | ArrayLengthResult -> - Logging.die Logging.InternalError - "Unexpected origin ArrayLengthAccess: the result is integer, should not be compared with \ - null" - (* TODO: this could be specified more precisely *) - | NonnullConst _ - | Field _ - | CallToGetKnownToContainsKey - | CurrMethodParameter _ - | This - | New - | ArrayAccess - | InferredNonnull _ - | OptimisticFallback -> - " according to the existing annotations" - - -(** If error is reportable to the user, return its information. Otherwise return [None]. *) -let make_nullsafe_issue_if_reportable_lazy ~nullsafe_mode err_instance = - let open IOption.Let_syntax in - if is_synthetic_err err_instance then None - else - match err_instance with - | Condition_redundant {is_always_true; loc; condition_descr; nonnull_origin} -> - Some - ( lazy - (NullsafeIssue.make - ~description: - (P.sprintf "The condition %s might be always %b%s." - (Option.value condition_descr ~default:"") - is_always_true - (get_nonnull_explanation_for_condition_redudant nonnull_origin) ) - ~issue_type:IssueType.eradicate_condition_redundant - ~loc - (* Condition redundant is a very non-precise issue. Depending on the origin of what is compared with null, - this can have a lot of reasons to be actually nullable. - Until it is made non-precise, it is recommended to not turn this warning on. - But even when it is on, this should not be more than advice. - *) - ~severity:IssueType.Advice ~field_name:None ) ) - | Over_annotation {over_annotated_violation; loc; violation_type} -> - Some - ( lazy - (let issue_type, field_name = - match violation_type with - | OverAnnotatedRule.FieldOverAnnoted field_name -> - (IssueType.eradicate_field_over_annotated, Some field_name) - | OverAnnotatedRule.ReturnOverAnnotated _ -> - (IssueType.eradicate_return_over_annotated, None) - in - let description = - OverAnnotatedRule.violation_description over_annotated_violation violation_type - in - NullsafeIssue.make ~description ~issue_type ~loc ~field_name - ~severity: - (* Very non-precise issue. Should be actually turned off unless for experimental purposes. *) - IssueType.Advice ) ) - | Field_not_initialized {field_name; loc} -> - Some - ( lazy - (NullsafeIssue.make - ~description: - (Format.asprintf - "Field %a is declared non-nullable, so it should be initialized in the \ - constructor" - MF.pp_monospaced - (Fieldname.get_field_name field_name) ) - ~issue_type:IssueType.eradicate_field_not_initialized ~loc - ~severity:(NullsafeMode.severity nullsafe_mode) - ~field_name:(Some field_name) ) ) - | Bad_assignment {assignment_location; assignment_type; assignment_violation} -> - (* If violation is reportable, create tuple, otherwise None *) - let+ reportable_violation = - AssignmentRule.ReportableViolation.from nullsafe_mode assignment_violation - in - lazy - (AssignmentRule.ReportableViolation.make_nullsafe_issue ~assignment_location - assignment_type reportable_violation ) - | Nullable_dereference - {dereference_violation; dereference_location; nullable_object_descr; dereference_type} -> - (* If violation is reportable, create tuple, otherwise None *) - let+ reportable_violation = - DereferenceRule.ReportableViolation.from nullsafe_mode dereference_violation - in - lazy - (DereferenceRule.ReportableViolation.make_nullsafe_issue reportable_violation - ~dereference_location dereference_type ~nullable_object_descr ) - | Inconsistent_subclass - {inheritance_violation; violation_type; loc; base_proc_name; overridden_proc_name} -> - (* If violation is reportable, create tuple, otherwise None *) - let+ reportable_violation = - InheritanceRule.ReportableViolation.from nullsafe_mode inheritance_violation - in - lazy - (InheritanceRule.ReportableViolation.make_nullsafe_issue ~nullsafe_mode - reportable_violation violation_type ~loc ~base_proc_name ~overridden_proc_name ) - - -(** If error is reportable to the user, return description, severity etc. Otherwise return None. *) -let make_nullsafe_issue_if_reportable ~nullsafe_mode err_instance = - make_nullsafe_issue_if_reportable_lazy ~nullsafe_mode err_instance |> Option.map ~f:Lazy.force - - -let is_reportable ~nullsafe_mode err_instance = - (* Note: we don't fetch the whole info because the class-level analysis breaks some - assumptions of this function, and also for optimization purposes (saving some string - manipulations). *) - make_nullsafe_issue_if_reportable_lazy ~nullsafe_mode err_instance |> Option.is_some - - -let report_now_if_reportable analysis_data err_instance ~nullsafe_mode = - make_nullsafe_issue_if_reportable ~nullsafe_mode err_instance - |> Option.iter ~f:(fun nullsafe_issue -> - Logging.debug Analysis Medium "About to report: %s" - (NullsafeIssue.get_description nullsafe_issue) ; - let field_name = get_field_name_for_error_suppressing err_instance in - EradicateReporting.report_error analysis_data Eradicate nullsafe_issue ~field_name ) - - -(** Register issue (unless exactly the same issue was already registered). If needed, report this - error immediately. *) -let register_error analysis_data find_canonical_duplicate err_instance ~nullsafe_mode instr_ref_opt - = - let should_report_now = add_err find_canonical_duplicate err_instance instr_ref_opt in - if should_report_now then report_now_if_reportable analysis_data err_instance ~nullsafe_mode - - -let report_forall_issues_and_reset analysis_data ~nullsafe_mode = - let iter (err_instance, instr_ref_opt) err_state = - match (instr_ref_opt, get_forall err_instance) with - | Some instr_ref, is_forall -> - let node = InstrRef.get_node instr_ref in - AnalysisState.set_node node ; - if is_forall && err_state.always then - report_now_if_reportable analysis_data err_instance ~nullsafe_mode - | None, _ -> - () - in - H.iter iter err_tbl ; - reset () - - -let get_errors () = - H.fold - (fun (err_instance, instr_ref_opt) _err_state acc -> (err_instance, instr_ref_opt) :: acc) - err_tbl [] diff --git a/infer/src/nullsafe/typeErr.mli b/infer/src/nullsafe/typeErr.mli deleted file mode 100644 index e793847fd59..00000000000 --- a/infer/src/nullsafe/typeErr.mli +++ /dev/null @@ -1,87 +0,0 @@ -(* - * 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. - *) - -open! IStd - -(** Module for Type Error messages. *) - -module type InstrRefT = sig - type t [@@deriving compare] - - val equal : t -> t -> bool - - type generator - - val create_generator : Procdesc.Node.t -> generator - - val gen : generator -> t - - val get_node : t -> Procdesc.Node.t - - val hash : t -> int - - val replace_node : t -> Procdesc.Node.t -> t -end - -(* InstrRefT *) -module InstrRef : InstrRefT - -(* callee signature *) - -(** Instance of an error *) -type err_instance = - | Condition_redundant of - { loc: Location.t - ; is_always_true: bool - ; condition_descr: string option - ; nonnull_origin: TypeOrigin.t } - | Inconsistent_subclass of - { loc: Location.t - ; inheritance_violation: InheritanceRule.violation - ; violation_type: InheritanceRule.ReportableViolation.violation_type - ; base_proc_name: Procname.Java.t - ; overridden_proc_name: Procname.Java.t } - | Field_not_initialized of {loc: Location.t; field_name: Fieldname.t} - | Over_annotation of - { loc: Location.t - ; over_annotated_violation: OverAnnotatedRule.violation - ; violation_type: OverAnnotatedRule.violation_type } - | Nullable_dereference of - { dereference_violation: DereferenceRule.violation - ; dereference_location: Location.t - ; dereference_type: DereferenceRule.ReportableViolation.dereference_type - ; nullable_object_descr: string option } - | Bad_assignment of - { assignment_violation: AssignmentRule.violation - ; assignment_location: Location.t - ; assignment_type: AssignmentRule.ReportableViolation.assignment_type } -[@@deriving compare] - -val pp_err_instance : Format.formatter -> err_instance -> unit - -val node_reset_forall : Procdesc.Node.t -> unit - -val register_error : - IntraproceduralAnalysis.t - -> (Procdesc.Node.t -> Procdesc.Node.t) - -> err_instance - -> nullsafe_mode:NullsafeMode.t - -> InstrRef.t option - -> unit -(** Register the fact that issue happened. Depending on the error and mode, this error might or - might not be reported to the user. *) - -val report_forall_issues_and_reset : - IntraproceduralAnalysis.t -> nullsafe_mode:NullsafeMode.t -> unit -(** Report registered "forall" issues (if needed), and reset the error table *) - -val is_reportable : nullsafe_mode:NullsafeMode.t -> err_instance -> bool -(** Is a given issue reportable to the user in a given mode? *) - -val reset : unit -> unit - -val get_errors : unit -> (err_instance * InstrRef.t option) list diff --git a/infer/src/nullsafe/typeOrigin.ml b/infer/src/nullsafe/typeOrigin.ml deleted file mode 100644 index 2fa58290efd..00000000000 --- a/infer/src/nullsafe/typeOrigin.ml +++ /dev/null @@ -1,227 +0,0 @@ -(* - * 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. - *) - -open! IStd - -(** Describe the origin of values propagated by the checker. *) - -type method_parameter_origin = Normal of AnnotatedSignature.param_signature | ObjectEqualsOverride -[@@deriving compare, equal] - -type method_call_origin = - { pname: Procname.Java.t - ; call_loc: Location.t - ; annotated_signature: AnnotatedSignature.t - ; is_defined: bool } -[@@deriving compare, equal] - -type t = - | NullConst of Location.t - | NonnullConst of Location.t - | Field of - { object_origin: t (** field's object origin (object is before field access operator `.`) *) - ; field_name: Fieldname.t - ; field_type: AnnotatedType.t - ; access_loc: Location.t } - | CurrMethodParameter of method_parameter_origin - | This - | MethodCall of method_call_origin - | CallToGetKnownToContainsKey - | New - | ArrayLengthResult - | ArrayAccess - | InferredNonnull of {previous_origin: t} - | OptimisticFallback -[@@deriving compare, equal] - -let get_nullability = function - | NullConst _ -> - Nullability.Null - | NonnullConst _ - | This (* `this` can not be null according to Java rules *) - | New (* In Java `new` always create a non-null object *) - | ArrayLengthResult (* integer hence non-nullable *) - | CallToGetKnownToContainsKey (* non-nullable by definition *) - | InferredNonnull _ - (* WARNING: we trade soundness for usability. - In Java, arrays are initialized with null, so accessing array is nullable until it was initialized. - However we assume array access is going to always return non-nullable. - This is because in real life arrays are often initialized straight away. - We currently don't have a nice way to detect initialization, neither automatical nor manual. - Hence we make potentially dangerous choice in favor of pragmatism. - *) - | ArrayAccess - | OptimisticFallback (* non-null is the most optimistic type *) -> - Nullability.StrictNonnull - | Field {field_type= {nullability}} -> - AnnotatedNullability.get_nullability nullability - | CurrMethodParameter (Normal {param_annotated_type= {nullability}}) -> ( - match nullability with - | AnnotatedNullability.Nullable _ -> - (* Annotated as Nullable explicitly or implicitly *) - Nullability.Nullable - | AnnotatedNullability.ProvisionallyNullable _ -> - Nullability.ProvisionallyNullable - | AnnotatedNullability.UncheckedNonnull _ - | AnnotatedNullability.ThirdPartyNonnull - | AnnotatedNullability.LocallyTrustedNonnull - | AnnotatedNullability.LocallyCheckedNonnull - | AnnotatedNullability.StrictNonnull _ -> - (* Nonnull param should be treated as trusted inside this function context: - Things like dereferences or conversions should be allowed without any extra check - independendly of mode. - NOTE. However, in practice a function should be allowed to check any param for null - and be defensive, because no function can gurantee it won't ever be called with `null`, so - in theory we might want to distinct that in the future. - *) - Nullability.StrictNonnull ) - | CurrMethodParameter ObjectEqualsOverride -> - (* `Object.equals(obj)` should expect to be called with null `obj` *) - Nullability.Nullable - | MethodCall {annotated_signature= {ret= {ret_annotated_type= {nullability}}}} -> - AnnotatedNullability.get_nullability nullability - - -let rec to_string = function - | NullConst _ -> - "null" - | NonnullConst _ -> - "Const (nonnull)" - | Field {object_origin; field_name} -> - "Field " ^ Fieldname.to_string field_name ^ " (object: " ^ to_string object_origin ^ ")" - | CurrMethodParameter (Normal {mangled; param_annotated_type= {nullability}}) -> - Format.asprintf "Param %s <%a>" (Mangled.to_string mangled) AnnotatedNullability.pp - nullability - | CurrMethodParameter ObjectEqualsOverride -> - "Param(ObjectEqualsOverride)" - | This -> - "this" - | MethodCall {pname} -> - Printf.sprintf "Fun %s" (Procname.Java.to_simplified_string pname) - | CallToGetKnownToContainsKey -> - "CallToGetKnownToContainsKey" - | New -> - "New" - | ArrayLengthResult -> - "ArrayLength" - | ArrayAccess -> - "ArrayAccess" - | InferredNonnull {previous_origin} -> - Format.sprintf "InferredNonnull(prev:%s)" (to_string previous_origin) - | OptimisticFallback -> - "OptimisticFallback" - - -let atline loc = " at line " ^ string_of_int loc.Location.line - -let get_method_ret_description pname call_loc - AnnotatedSignature.{kind; ret= {ret_annotated_type= {nullability}}} = - let should_show_class_name = - (* Class name is generally redundant: the user knows line number and - can immediatelly go to definition and see the function annotation. - When something is modelled though, let's show the class name as well, so it is - super clear what exactly is modelled. - *) - match kind with - | FirstParty | ThirdParty Unregistered -> - false - | ThirdParty ModelledInternally | ThirdParty (InThirdPartyRepo _) -> - true - in - let ret_nullability = - match nullability with - | AnnotatedNullability.Nullable _ -> - "nullable" - | AnnotatedNullability.ProvisionallyNullable _ -> - (* There should not be scenario where this is explained to the end user. *) - Logging.die InternalError - "get_method_ret_description: Unexpected nullability: ProvisionallyNullable" - | AnnotatedNullability.ThirdPartyNonnull - | AnnotatedNullability.UncheckedNonnull _ - | AnnotatedNullability.LocallyTrustedNonnull - | AnnotatedNullability.LocallyCheckedNonnull - | AnnotatedNullability.StrictNonnull _ -> - "non-nullable" - in - let model_info = - match kind with - | FirstParty | ThirdParty Unregistered -> - "" - | ThirdParty ModelledInternally -> - Format.sprintf " (%s according to nullsafe internal models)" ret_nullability - | ThirdParty (InThirdPartyRepo {filename; line_number}) -> - let filename_to_show = - ThirdPartyAnnotationGlobalRepo.get_user_friendly_third_party_sig_file_name ~filename - in - Format.sprintf " (declared %s in %s at line %d)" ret_nullability filename_to_show - line_number - in - Format.sprintf "call to %s%s%s" - (Procname.Java.to_simplified_string ~withclass:should_show_class_name pname) - (atline call_loc) model_info - - -let get_provisional_annotation = function - | NullConst _ - | NonnullConst _ - | This - | New - | ArrayLengthResult - | CallToGetKnownToContainsKey - | InferredNonnull _ - | ArrayAccess - | OptimisticFallback -> - None - | Field - {field_type= {nullability= AnnotatedNullability.ProvisionallyNullable provisional_annotation}} - -> - Some provisional_annotation - | CurrMethodParameter - (Normal - { param_annotated_type= - {nullability= AnnotatedNullability.ProvisionallyNullable provisional_annotation} } ) -> - Some provisional_annotation - | MethodCall - { annotated_signature= - { ret= - { ret_annotated_type= - {nullability= AnnotatedNullability.ProvisionallyNullable provisional_annotation} - } } } -> - Some provisional_annotation - | Field _ | CurrMethodParameter _ | MethodCall _ -> - None - - -let get_description origin = - match origin with - | NullConst loc -> - Some ("null constant" ^ atline loc) - | Field {field_name; access_loc} -> - Some ("field " ^ Fieldname.get_field_name field_name ^ atline access_loc) - | CurrMethodParameter (Normal {mangled}) -> - Some ("method parameter " ^ Mangled.to_string mangled) - | CurrMethodParameter ObjectEqualsOverride -> - Some "Object.equals() should be able to accept `null`, according to the Java specification" - | MethodCall {pname; call_loc; annotated_signature} -> - Some (get_method_ret_description pname call_loc annotated_signature) - (* These are origins of non-nullable expressions that are result of evaluating of some rvalue. - Because they are non-nullable and they are rvalues, we won't get normal type violations - With them. All we could get is things like condition redundant or overannotated. - But for these issues we currently don't print origins in the error string. - It is a good idea to change this and start printing origins for these origins as well. - *) - | This - | New - | NonnullConst _ - | ArrayLengthResult - | ArrayAccess - | InferredNonnull _ - | CallToGetKnownToContainsKey -> - None - (* A technical origin *) - | OptimisticFallback -> - None diff --git a/infer/src/nullsafe/typeOrigin.mli b/infer/src/nullsafe/typeOrigin.mli deleted file mode 100644 index b1f6a702bd4..00000000000 --- a/infer/src/nullsafe/typeOrigin.mli +++ /dev/null @@ -1,59 +0,0 @@ -(* - * 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. - *) - -open! IStd - -type method_parameter_origin = Normal of AnnotatedSignature.param_signature | ObjectEqualsOverride - -type method_call_origin = - { pname: Procname.Java.t - ; call_loc: Location.t - ; annotated_signature: AnnotatedSignature.t - ; is_defined: bool } -[@@deriving compare] - -type t = - | NullConst of Location.t (** A null literal in the source *) - | NonnullConst of Location.t (** A constant (not equal to null) in the source. *) - | Field of - { object_origin: t (** field's object origin (object is before field access operator `.`) *) - ; field_name: Fieldname.t - ; field_type: AnnotatedType.t - ; access_loc: Location.t } - (** A field access (result of expression `some_object.some_field`) *) - | CurrMethodParameter of method_parameter_origin - (** Parameter of a method we are currently in, *) - | This (* `this` object. Can not be null, according to Java rules. *) - | MethodCall of method_call_origin - | CallToGetKnownToContainsKey - (** This is a result of accessing a map element that is known to contains this particular key, - normally because it was explicitly checked for presense before *) - | New (** A new object creation *) - | ArrayLengthResult (** integer value - result of accessing array.length *) - | ArrayAccess (** Result of accessing an array by index *) - | InferredNonnull of {previous_origin: t} - (** The value is inferred as non-null during flow-sensitive type inference (most commonly from - relevant condition branch or assertion explicitly comparing the value with `null`) *) - | OptimisticFallback - (** A special case: technical type variant. Indicates either cases when something went wrong - during typechecking, and some cases that should be expressed in a better way than using - this type. We fall back to optimistic (not-nullable) type to reduce potential - non-actionable false positives. Ideally we should not see these instances. They should be - either processed gracefully (and a dedicated type constructor should be added), or fixed. - T54687014 tracks unsoundness issues caused by this type. *) -[@@deriving compare, equal] - -val get_nullability : t -> Nullability.t - -val get_provisional_annotation : t -> ProvisionalAnnotation.t option -(** If the origin is associated with provisional annotation, return it *) - -val get_description : t -> string option -(** Get a description to be used for error messages. *) - -val to_string : t -> string -(** Raw string representation. *) diff --git a/infer/src/nullsafe/typeState.ml b/infer/src/nullsafe/typeState.ml deleted file mode 100644 index b16633cd2a8..00000000000 --- a/infer/src/nullsafe/typeState.ml +++ /dev/null @@ -1,87 +0,0 @@ -(* - * 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. - *) - -open! IStd -module L = Logging -module F = Format - -(** Module for typestates: maps from expressions to annotated types, with extensions. *) - -module M = Caml.Map.Make (struct - type t = Exp.t - - let compare = Exp.compare -end) - -type range = Typ.t * InferredNullability.t [@@deriving compare, equal] - -let pp_range fmt (typ, nullability) = - F.fprintf fmt "Typ: %s; Nullability: %a" (Typ.to_string typ) InferredNullability.pp nullability - - -type t = range M.t [@@deriving compare, equal] - -let equal = [%compare.equal: t] - -let empty = M.empty - -let pp fmt typestate = - let pp_one exp (typ, ta) = - F.fprintf fmt " %a -> [%s] %a %a@\n" Exp.pp exp - (TypeOrigin.to_string (InferredNullability.get_simple_origin ta)) - InferredNullability.pp ta (Typ.pp_full Pp.text) typ - in - let pp_map map = M.iter pp_one map in - pp_map typestate - - -let map_join m1 m2 = - let range_join _exp range1_opt range2_opt = - Option.both range1_opt range2_opt - |> Option.map ~f:(fun ((typ1, inferred_nullability1), (_, inferred_nullability2)) -> - (* Unlike nullability that Nullsafe infers, Java does not support local type inference - (for codebases and Java version nullsafe is currently aimed for). - The real type does not depend on types being joined; it is determined by the corresponding type declaration instead. - We don't really use type information for reasons not related to things like diagnostic, and using one of types - serves as a good proxy. - Let's take the left one. - *) - let joined_type = typ1 in - (joined_type, InferredNullability.join inferred_nullability1 inferred_nullability2) ) - in - if phys_equal m1 m2 then m1 else M.merge range_join m1 m2 - - -let join t1 t2 = - let tjoin = map_join t1 t2 in - if Config.write_html then - L.d_printfln "State 1:@.%a@.State 2:@.%a@.After Join:@.%a@." pp t1 pp t2 pp tjoin ; - tjoin - - -let lookup_id id typestate = M.find_opt (Exp.Var id) typestate - -let lookup_pvar pvar typestate = M.find_opt (Exp.Lvar pvar) typestate - -let add_id id range typestate ~descr = - ( if Config.write_html then - let _, nullability = range in - L.d_printfln "Setting %a to Id %a: %s" InferredNullability.pp nullability Ident.pp id descr ) ; - M.add (Exp.Var id) range typestate - - -let add pvar range typestate ~descr = - ( if Config.write_html then - let _, nullability = range in - L.d_printfln "Setting %a to Pvar %a: %s" InferredNullability.pp nullability - Pvar.pp_value_non_verbose pvar descr ) ; - M.add (Exp.Lvar pvar) range typestate - - -let remove_id id typestate ~descr = - if Config.write_html then L.d_printfln "Removing Id %a from typestate: %s" Ident.pp id descr ; - M.remove (Exp.Var id) typestate diff --git a/infer/src/nullsafe/typeState.mli b/infer/src/nullsafe/typeState.mli deleted file mode 100644 index ea4b92f48f4..00000000000 --- a/infer/src/nullsafe/typeState.mli +++ /dev/null @@ -1,38 +0,0 @@ -(* - * 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. - *) - -open! IStd - -(** Module for typestates: maps from expressions to annotated types, with extensions. *) - -(** Typestate *) -type t - -type range = Typ.t * InferredNullability.t - -val pp_range : Format.formatter -> Typ.t * InferredNullability.t -> unit - -val add_id : Ident.t -> range -> t -> descr:string -> t -(** [descr] is for debug logs only *) - -val add : Pvar.t -> range -> t -> descr:string -> t -(** [descr] is for debug logs only *) - -val empty : t - -val equal : t -> t -> bool - -val join : t -> t -> t - -val lookup_id : Ident.t -> t -> range option - -val lookup_pvar : Pvar.t -> t -> range option - -val pp : Format.formatter -> t -> unit - -val remove_id : Ident.t -> t -> descr:string -> t -(** [descr] is for debug logs only *) diff --git a/infer/src/nullsafe/unit/AggregatedSummariesTest.ml b/infer/src/nullsafe/unit/AggregatedSummariesTest.ml deleted file mode 100644 index 44872d9b889..00000000000 --- a/infer/src/nullsafe/unit/AggregatedSummariesTest.ml +++ /dev/null @@ -1,212 +0,0 @@ -(* - * 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. - *) - -open! IStd -open OUnit2 -open AggregatedSummaries -module F = Format - -let test_package = Some "test.package" - -let mock_summary id = - (* For simplicity, mock summaries will be represented as a list with the single issue where id is encoded in description. *) - let mock_issue = - TypeErr.Condition_redundant - { is_always_true= true - ; loc= Location.dummy - ; condition_descr= Some (Int.to_string id) - ; nonnull_origin= TypeOrigin.New } - in - NullsafeSummary.{issues= [mock_issue]} - - -let get_mock_summary_id NullsafeSummary.{issues} = - match issues with - | [TypeErr.Condition_redundant {condition_descr= Some id}] -> - Int.of_string id - | _ -> - assert_failure "Expected mock summary, got something else" - - -type expected_class_info = - { name: string - ; nested: expected_class_info list - ; anm: (string * int list) list - (* List of nested anonymous names together with list of their expected summary ids *) - ; summary_ids: int list (** sorted for convenience *) } - -let assert_summaries_equal actual_summaries expected_ids class_name case_name = - let actual_summaries_ids = - actual_summaries |> List.map ~f:get_mock_summary_id |> List.sort ~compare:Int.compare - in - assert_equal expected_ids actual_summaries_ids - ~msg:(F.sprintf "%s: summary ids for %s did not match" case_name class_name) - - -let assert_anonymous_equal actual expected_name_to_ids case_name = - List.iter expected_name_to_ids ~f:(fun (anonymous_name, expected_summary_ids) -> - let actual_summaries = - JavaClassName.Map.find_opt - (JavaClassName.make ~package:test_package ~classname:anonymous_name) - actual - |> IOption.if_none_eval ~f:(fun () -> - assert_failure - (F.sprintf "%s: Did not find anonymous class info for %s" case_name anonymous_name) ) - in - assert_summaries_equal actual_summaries expected_summary_ids anonymous_name case_name ) - - -let rec assert_expected_list actual expected case_name = - assert_equal (List.length actual) (List.length expected) - ~msg: - (Format.sprintf "%s: Expected list of %d, got %d" case_name (List.length expected) - (List.length actual) ) ; - List.iter expected ~f:(fun {name; summary_ids; nested; anm} -> - (* For each expected info find corresponding actual and compare *) - let actual_info = - List.find actual ~f:(fun actual_info -> - JavaClassName.equal - (ClassInfo.get_class_name actual_info) - (JavaClassName.make ~package:test_package ~classname:name) ) - |> IOption.if_none_eval ~f:(fun () -> - assert_failure (F.sprintf "%s: Did not find class info for %s" case_name name) ) - in - assert_summaries_equal (ClassInfo.get_summaries actual_info) summary_ids name case_name ; - assert_anonymous_equal (ClassInfo.get_nested_anonymous_summaries actual_info) anm case_name ; - (* Now recursively check all children and match them with expected *) - assert_expected_list - (ClassInfo.get_nested_classes_info actual_info) - nested - (F.sprintf "%s:%s" case_name name) ) - - -let aggregate list = - List.map list ~f:(fun (classname, summary_id) -> - (JavaClassName.make ~package:test_package ~classname, mock_summary summary_id) ) - |> AggregatedSummaries.aggregate - - -let single_class = - "single_class" - >:: fun _ -> - assert_expected_list (aggregate []) [] "empty" ; - assert_expected_list - (aggregate [("A", 1)]) - [{name= "A"; summary_ids= [1]; anm= []; nested= []}] - "single method" ; - assert_expected_list - (aggregate [("A", 1); ("A", 5); ("A", 2)]) - [{name= "A"; summary_ids= [1; 2; 5]; anm= []; nested= []}] - "several methods" - - -let several_top_classes = - "several_top_classes" - >:: fun _ -> - assert_expected_list - (aggregate [("A", 1); ("B", 2)]) - [ {name= "A"; summary_ids= [1]; anm= []; nested= []} - ; {name= "B"; summary_ids= [2]; anm= []; nested= []} ] - "two simple classes" ; - assert_expected_list - (aggregate [("A", 1); ("B", 5); ("A", 2); ("C", 7); ("A", 8); ("B", 9)]) - [ {name= "A"; summary_ids= [1; 2; 8]; anm= []; nested= []} - ; {name= "B"; summary_ids= [5; 9]; anm= []; nested= []} - ; {name= "C"; summary_ids= [7]; anm= []; nested= []} ] - "several classes" - - -let one_top_class_with_nested = - "several_top_classes" - >:: fun _ -> - assert_expected_list - (aggregate [("A$B", 1); ("A", 2)]) - [ { name= "A" - ; summary_ids= [2] - ; anm= [] - ; nested= [{name= "A$B"; summary_ids= [1]; anm= []; nested= []}] } ] - "simple nested" ; - assert_expected_list - (aggregate [("A$B", 1); ("A", 2); ("A$B", 3); ("A", 4); ("A$C", 6); ("A$D", 7)]) - [ { name= "A" - ; summary_ids= [2; 4] - ; anm= [] - ; nested= - [ {name= "A$B"; summary_ids= [1; 3]; anm= []; nested= []} - ; {name= "A$C"; summary_ids= [6]; anm= []; nested= []} - ; {name= "A$D"; summary_ids= [7]; anm= []; nested= []} ] } ] - "simple nested, several summaries" ; - assert_expected_list - (aggregate [("A$B", 1)]) - [ { name= "A" - ; summary_ids= [] - ; anm= [] - ; nested= [{name= "A$B"; summary_ids= [1]; anm= []; nested= []}] } ] - "nested should also create ancestors even if they have no summaries" ; - assert_expected_list - (aggregate [("A$B$D$F", 1); ("A$B$C", 2)]) - [ { name= "A" - ; summary_ids= [] - ; anm= [] - ; nested= - [ { name= "A$B" - ; summary_ids= [] - ; anm= [] - ; nested= - [ {name= "A$B$C"; summary_ids= [2]; anm= []; nested= []} - ; { name= "A$B$D" - ; summary_ids= [] - ; anm= [] - ; nested= [{name= "A$B$D$F"; summary_ids= [1]; anm= []; nested= []}] } ] } ] } ] - "nested should also create ancestors even if they have no summaries" - - -let with_anonymous = - "with_anonymous" - >:: fun _ -> - assert_expected_list - (aggregate [("A$B$C$3$4", 1); ("A$5$6", 2); ("A$5", 3); ("A", 4); ("A$5$7", 5); ("A$5$6", 6)]) - [ { name= "A" - ; summary_ids= [4] - ; anm= [("A$5$6", [2; 6]); ("A$5", [3]); ("A$5$7", [5])] - ; nested= - [ { name= "A$B" - ; summary_ids= [] - ; anm= [] - ; nested= [{name= "A$B$C"; summary_ids= []; anm= [("A$B$C$3$4", [1])]; nested= []}] } ] - } ] - "with_anonymous" - - -let several_top_classes_with_nested = - "several_top_classes_with_nested" - >:: fun _ -> - assert_expected_list - (aggregate [("B", 1); ("A", 2); ("C$D", 3); ("A$B$D", 4); ("A$B$D", 5); ("A", 6)]) - [ { name= "A" - ; summary_ids= [2; 6] - ; anm= [] - ; nested= - [ { name= "A$B" - ; summary_ids= [] - ; anm= [] - ; nested= [{name= "A$B$D"; summary_ids= [4; 5]; anm= []; nested= []}] } ] } - ; {name= "B"; summary_ids= [1]; anm= []; nested= []} - ; { name= "C" - ; summary_ids= [] - ; anm= [] - ; nested= [{name= "C$D"; summary_ids= [3]; anm= []; nested= []}] } ] - "several_top_classes_with_nested" - - -let test = - "AggregatedSummariesTest" - >::: [ single_class - ; several_top_classes - ; one_top_class_with_nested - ; with_anonymous - ; several_top_classes_with_nested ] diff --git a/infer/src/nullsafe/unit/AllNullsafeTests.ml b/infer/src/nullsafe/unit/AllNullsafeTests.ml deleted file mode 100644 index 0dc9b8a0223..00000000000 --- a/infer/src/nullsafe/unit/AllNullsafeTests.ml +++ /dev/null @@ -1,10 +0,0 @@ -(* - * 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. - *) - -open! IStd - -let tests = [ThirdPartyAnnotationInfoTests.test; AggregatedSummariesTest.test] diff --git a/infer/src/nullsafe/unit/AllNullsafeTests.mli b/infer/src/nullsafe/unit/AllNullsafeTests.mli deleted file mode 100644 index 38e35a83096..00000000000 --- a/infer/src/nullsafe/unit/AllNullsafeTests.mli +++ /dev/null @@ -1,10 +0,0 @@ -(* - * 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. - *) - -open! IStd - -val tests : OUnit2.test list diff --git a/infer/src/nullsafe/unit/ThirdPartyAnnotationInfoTests.ml b/infer/src/nullsafe/unit/ThirdPartyAnnotationInfoTests.ml deleted file mode 100644 index 580d90d52f0..00000000000 --- a/infer/src/nullsafe/unit/ThirdPartyAnnotationInfoTests.ml +++ /dev/null @@ -1,274 +0,0 @@ -(* - * 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. - *) - -open! IStd -open OUnit2 -module F = Format - -let assert_has_nullability_info ?expected_file ?expected_line storage unique_repr - ~expected_nullability = - match ThirdPartyAnnotationInfo.find_nullability_info storage unique_repr with - | None -> - assert_failure - (F.asprintf "Expected to find info for %a, but it was not found" - ThirdPartyAnnotationInfo.pp_unique_repr unique_repr ) - | Some {filename; line_number; signature} -> - let expected_ret, expected_param_nullability = expected_nullability in - let expected_params = - List.zip_exn unique_repr.ThirdPartyAnnotationInfo.param_types expected_param_nullability - in - let expected_signature = - { ThirdPartyMethod.class_name= unique_repr.ThirdPartyAnnotationInfo.class_name - ; method_name= unique_repr.ThirdPartyAnnotationInfo.method_name - ; ret_nullability= expected_ret - ; params= expected_params } - in - assert_equal expected_signature signature - ~msg: - (F.asprintf "Signature for %a does not match" ThirdPartyAnnotationInfo.pp_unique_repr - unique_repr ) - ~printer:(Pp.string_of_pp ThirdPartyMethod.pp) ; - Option.iter expected_file ~f:(fun expected_file -> - assert_equal expected_file filename ~msg:"Filename does not match" ) ; - Option.iter expected_line ~f:(fun expected_line -> - assert_equal expected_line line_number ~msg:"Line number does not match" ) - - -let assert_no_info storage unique_repr = - match ThirdPartyAnnotationInfo.find_nullability_info storage unique_repr with - | None -> - () - | Some {signature} -> - assert_failure - (F.asprintf "Did not expect to find nullability info for method %a, but found %a" - ThirdPartyAnnotationInfo.pp_unique_repr unique_repr ThirdPartyMethod.pp signature ) - - -let add_from_annot_file_and_check_success storage ~filename ~lines = - match ThirdPartyAnnotationInfo.add_from_signature_file storage ~filename ~lines with - | Ok storage -> - storage - | Error parsing_error -> - assert_failure - (F.asprintf "Expected to parse the file, but it was unparsable: %a" - ThirdPartyAnnotationInfo.pp_parsing_error parsing_error ) - - -let add_from_annot_file_and_check_failure storage ~filename ~lines ~expected_error_line_number = - match ThirdPartyAnnotationInfo.add_from_signature_file storage ~filename ~lines with - | Ok _ -> - assert_failure - "Expected to not be able to parse the file, but it was successfully parsed instead" - | Error {line_number} -> - assert_equal expected_error_line_number line_number ~msg:"Error line number does not match" - ~printer:string_of_int - - -let basic_find = - let open ThirdPartyMethod in - "basic_find" - >:: fun _ -> - let lines = ["a.A#foo(b.B)"; "b.B#bar(c.C, @Nullable d.D) @Nullable"] in - (* Load some functions from the file *) - let storage = - add_from_annot_file_and_check_success ~filename:"test.sig" - (ThirdPartyAnnotationInfo.create_storage ()) - ~lines - in - (* Make sure we can find what we just stored *) - assert_has_nullability_info storage - {class_name= "a.A"; method_name= Method "foo"; param_types= ["b.B"]} - ~expected_nullability:(Nonnull, [Nonnull]) ~expected_file:"test.sig" ~expected_line:1 ; - assert_has_nullability_info storage - {class_name= "b.B"; method_name= Method "bar"; param_types= ["c.C"; "d.D"]} - ~expected_nullability:(Nullable, [Nonnull; Nullable]) - ~expected_file:"test.sig" ~expected_line:2 ; - (* Make sure we can not find stuff we did not store *) - (* Wrong class name *) - assert_no_info storage {class_name= "a.AB"; method_name= Method "foo"; param_types= ["b.B"]} ; - (* Wrong method name *) - assert_no_info storage {class_name= "a.A"; method_name= Method "foo1"; param_types= ["b.B"]} ; - (* Wrong param type *) - assert_no_info storage {class_name= "a.A"; method_name= Method "foo"; param_types= ["c.C"]} ; - (* Not enough params *) - assert_no_info storage {class_name= "a.A"; method_name= Method "foo"; param_types= []} ; - (* Too many params *) - assert_no_info storage {class_name= "a.A"; method_name= Method "foo"; param_types= ["b.B"; "c.C"]} - - -let disregards_whitespace_lines_and_comments = - let open ThirdPartyMethod in - "disregards_comments" - >:: fun _ -> - let lines = [" "; "a.A#foo(b.B)"; ""; "// a.A#bar(b.B)"; "// Hello world"] in - (* Load some functions from the file *) - let storage = - add_from_annot_file_and_check_success ~filename:"test.sig" - (ThirdPartyAnnotationInfo.create_storage ()) - ~lines - in - assert_has_nullability_info storage - {class_name= "a.A"; method_name= Method "foo"; param_types= ["b.B"]} - ~expected_nullability:(Nonnull, [Nonnull]) ~expected_file:"test.sig" ~expected_line:2 ; - (* Commented out signatures should be ignored *) - assert_no_info storage {class_name= "a.A"; method_name= Method "bar"; param_types= ["b.B"]} - - -let overload_resolution = - let open ThirdPartyMethod in - "overload_resolution" - >:: fun _ -> - let lines = - [ "a.b.SomeClass#foo(@Nullable a.b.C1) @Nullable" - ; "a.b.SomeClass#(a.b.C1)" - ; "a.b.SomeClass#foo(@Nullable a.b.C1, @Nullable a.b.C3, c.d.C4) @Nullable" - ; "c.d.OtherClass#foo(@Nullable a.b.C2)" - ; "a.b.SomeClass#()" - ; "a.b.SomeClass#(@Nullable a.b.C2)" - ; "a.b.SomeClass#foo(@Nullable a.b.C2)" ] - in - (* Load some functions from the file *) - let storage = - add_from_annot_file_and_check_success ~filename:"test.sig" - (ThirdPartyAnnotationInfo.create_storage ()) - ~lines - in - (* Make sure we can find what we just stored *) - (* a.b.SomeClass.foo with 1 param *) - assert_has_nullability_info storage - {class_name= "a.b.SomeClass"; method_name= Method "foo"; param_types= ["a.b.C1"]} - ~expected_nullability:(Nullable, [Nullable]) ; - assert_has_nullability_info storage - {class_name= "a.b.SomeClass"; method_name= Method "foo"; param_types= ["a.b.C2"]} - ~expected_nullability:(Nonnull, [Nullable]) ; - (* wrong type *) - assert_no_info storage - {class_name= "a.b.SomeClass"; method_name= Method "foo"; param_types= ["a.b.C3"]} ; - (* wrong class *) - assert_no_info storage - {class_name= "a.b.c.SomeClass"; method_name= Method "foo"; param_types= ["a.b.C1"]} ; - (* wrong method name *) - assert_no_info storage - {class_name= "a.b.SomeClass"; method_name= Method "bar"; param_types= ["a.b.C1"]} ; - (* a.b.SomeClass.foo with many params *) - assert_has_nullability_info storage - { class_name= "a.b.SomeClass" - ; method_name= Method "foo" - ; param_types= ["a.b.C1"; "a.b.C3"; "c.d.C4"] } - ~expected_nullability:(Nullable, [Nullable; Nullable; Nonnull]) ; - (* wrong param order *) - assert_no_info storage - { class_name= "a.b.SomeClass" - ; method_name= Method "foo" - ; param_types= ["a.b.C3"; "a.b.C1"; "c.d.C4"] } ; - (* third param is missing *) - assert_no_info storage - {class_name= "a.b.SomeClass"; method_name= Method "foo"; param_types= ["a.b.C1"; "a.b.C3"]} ; - (* possibility of constructor overload should be respected *) - assert_has_nullability_info storage - {class_name= "a.b.SomeClass"; method_name= Constructor; param_types= []} - ~expected_nullability:(Nonnull, []) ; - assert_has_nullability_info storage - {class_name= "a.b.SomeClass"; method_name= Constructor; param_types= ["a.b.C1"]} - ~expected_nullability:(Nonnull, [Nonnull]) ; - assert_has_nullability_info storage - {class_name= "a.b.SomeClass"; method_name= Constructor; param_types= ["a.b.C2"]} - ~expected_nullability:(Nonnull, [Nullable]) ; - (* wrong param type *) - assert_no_info storage - {class_name= "a.b.SomeClass"; method_name= Constructor; param_types= ["a.b.C3"]} - - -let can_add_several_files = - "can_add_several_files" - >:: fun _ -> - let open ThirdPartyMethod in - (* 1. Add file and check if we added info *) - let file1 = ["a.A#foo(b.B)"; "b.B#bar(c.C, @Nullable d.D) @Nullable"] in - let storage = - add_from_annot_file_and_check_success - (ThirdPartyAnnotationInfo.create_storage ()) - ~filename:"file1.sig" ~lines:file1 - in - assert_has_nullability_info storage - {class_name= "a.A"; method_name= Method "foo"; param_types= ["b.B"]} - ~expected_nullability:(Nonnull, [Nonnull]) ~expected_file:"file1.sig" ~expected_line:1 ; - (* 2. Add another file and check if we added info *) - let file2 = ["e.E#baz(f.F)"; "g.G#(h.H, @Nullable i.I) @Nullable"] in - let storage = add_from_annot_file_and_check_success storage ~filename:"file2.sig" ~lines:file2 in - assert_has_nullability_info storage - {class_name= "e.E"; method_name= Method "baz"; param_types= ["f.F"]} - ~expected_nullability:(Nonnull, [Nonnull]) ~expected_file:"file2.sig" ~expected_line:1 ; - (* 3. Ensure we did not forget the content from the first file *) - assert_has_nullability_info storage - {class_name= "a.A"; method_name= Method "foo"; param_types= ["b.B"]} - ~expected_nullability:(Nonnull, [Nonnull]) ~expected_file:"file1.sig" ~expected_line:1 - - -let should_not_forgive_unparsable_strings = - "should_not_forgive_unparsable_strings" - >:: fun _ -> - let line1 = "a.b.SomeClass#foo(@Nullable a.b.C1) @Nullable" in - let line2_ok = "a.b.SomeClass#(a.b.C1)" in - (* like line2_ok, but one extra open parenthesis *) - let line2_bad = "a.b.SomeClass#((a.b.C1)" in - let line3 = "a.b.SomeClass#foo(@Nullable a.b.C1, @Nullable a.b.C3, c.d.C4) @Nullable" in - let file_ok = [line1; line2_ok; line3] in - let file_bad = [line1; line2_bad; line3] in - (* Ensure we can add the good file, but can not add the bad one *) - add_from_annot_file_and_check_success ~filename:"test.sig" - (ThirdPartyAnnotationInfo.create_storage ()) - ~lines:file_ok - |> ignore ; - add_from_annot_file_and_check_failure ~filename:"test.sig" - (ThirdPartyAnnotationInfo.create_storage ()) - ~lines:file_bad ~expected_error_line_number:2 - - -let assert_third_party storage ~package ~expected_filename = - match ThirdPartyAnnotationInfo.lookup_related_sig_file storage ~package with - | None -> - assert_failure (F.sprintf "Did not find %s" package) - | Some sig_file_name -> - assert_equal expected_filename sig_file_name ~msg:"Unexpected sig file" ~printer:(fun s -> s) - - -let assert_not_third_party storage ~package = - match ThirdPartyAnnotationInfo.lookup_related_sig_file storage ~package with - | None -> - () - | Some sig_file_name -> - assert_failure - (F.sprintf "Did not expect %s to be third-party, but it was wrongly attributed to %s" - package sig_file_name ) - - -let test_is_third_party = - "test_is_third_party" - >:: fun _ -> - let storage = - ThirdPartyAnnotationInfo.create_storage () - |> add_from_annot_file_and_check_success ~filename:"aaa.bbb.sig" ~lines:[] - |> add_from_annot_file_and_check_success ~filename:"aaa.bbb.ccc.sig" ~lines:[] - |> add_from_annot_file_and_check_success ~filename:"bbb.ccc.sig" ~lines:[] - in - assert_not_third_party storage ~package:"aaa" ; - assert_third_party storage ~package:"aaa.bbb" ~expected_filename:"aaa.bbb.sig" ; - assert_third_party storage ~package:"aaa.bbb.xxx.yyy" ~expected_filename:"aaa.bbb.sig" ; - assert_third_party storage ~package:"aaa.bbb.ccc" ~expected_filename:"aaa.bbb.ccc.sig" ; - assert_third_party storage ~package:"aaa.bbb.ccc.ddd.eee" ~expected_filename:"aaa.bbb.ccc.sig" ; - assert_not_third_party storage ~package:"aaa.ccc" - - -let test = - "ThirdPartyAnnotationInfoTests" - >::: [ basic_find - ; disregards_whitespace_lines_and_comments - ; overload_resolution - ; can_add_several_files - ; should_not_forgive_unparsable_strings - ; test_is_third_party ] diff --git a/infer/src/nullsafe/unit/ThirdPartyMethodTests.ml b/infer/src/nullsafe/unit/ThirdPartyMethodTests.ml deleted file mode 100644 index d47faf8af38..00000000000 --- a/infer/src/nullsafe/unit/ThirdPartyMethodTests.ml +++ /dev/null @@ -1,158 +0,0 @@ -(* - * 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. - *) - -open! IStd -module F = Format - -let parse input = - let result = ThirdPartyMethod.parse input in - match result with - | Ok output -> - F.print_string (Sexp.to_string (ThirdPartyMethod.sexp_of_t output)) ; - let output_s = ThirdPartyMethod.to_canonical_string output in - if not (String.equal output_s input) then - F.printf - "@\n\ - FAILED TEST: the canonical string of the parsed object is '%s', which does not match \ - the input string." - output_s - | Error error -> - F.printf "error: %s" (ThirdPartyMethod.string_of_parsing_error error) - - -let%test_module "Third-party Method Tests OK Cases" = - ( module struct - (* No params *) - - let%expect_test "no params" = - parse "a.b.C#foo()" ; - [%expect {|((class_name a.b.C)(method_name(Method foo))(ret_nullability Nonnull)(params()))|}] - - - let%expect_test _ = - parse "a.b.C#foo() @Nullable" ; - [%expect - {|((class_name a.b.C)(method_name(Method foo))(ret_nullability Nullable)(params()))|}] - - - (* One param *) - - let%expect_test _ = - parse "a.b.C#foo(c.d.E)" ; - [%expect - {|((class_name a.b.C)(method_name(Method foo))(ret_nullability Nonnull)(params((c.d.E Nonnull))))|}] - - - let%expect_test _ = - parse "a.b.C#foo(@Nullable c.d.E)" ; - [%expect - {|((class_name a.b.C)(method_name(Method foo))(ret_nullability Nonnull)(params((c.d.E Nullable))))|}] - - - let%expect_test _ = - parse "a.b.C#foo(c.d.E) @Nullable" ; - [%expect - {|((class_name a.b.C)(method_name(Method foo))(ret_nullability Nullable)(params((c.d.E Nonnull))))|}] - - - let%expect_test _ = - parse "a.b.C#foo(@Nullable c.d.E) @Nullable" ; - [%expect - {|((class_name a.b.C)(method_name(Method foo))(ret_nullability Nullable)(params((c.d.E Nullable))))|}] - - - (* Many params *) - - let%expect_test _ = - parse "a.b.C#foo(c.d.E, a.b.C, x.y.Z)" ; - [%expect - {|((class_name a.b.C)(method_name(Method foo))(ret_nullability Nonnull)(params((c.d.E Nonnull)(a.b.C Nonnull)(x.y.Z Nonnull))))|}] - - - let%expect_test _ = - parse "a.b.C#foo(c.d.E, @Nullable a.b.C, x.y.Z)" ; - [%expect - {|((class_name a.b.C)(method_name(Method foo))(ret_nullability Nonnull)(params((c.d.E Nonnull)(a.b.C Nullable)(x.y.Z Nonnull))))|}] - - - let%expect_test _ = - parse "a.b.C#foo(@Nullable c.d.E, a.b.C, @Nullable x.y.Z) @Nullable" ; - [%expect - {|((class_name a.b.C)(method_name(Method foo))(ret_nullability Nullable)(params((c.d.E Nullable)(a.b.C Nonnull)(x.y.Z Nullable))))|}] - - - (* Constructor *) - - let%expect_test _ = - parse "a.b.C#(@Nullable c.d.E, a.b.C, x.y.Z) @Nullable" ; - [%expect - {|((class_name a.b.C)(method_name Constructor)(ret_nullability Nullable)(params((c.d.E Nullable)(a.b.C Nonnull)(x.y.Z Nonnull))))|}] - end ) - - -(* We intentionally don't test all bad cases. - It is generally OK for nullsafe to allow something that is not really valid: - We let other tools to make thorough linting. - Also we don't test exact error type, because this is an implementation detail - needed merely to simplify diagnostics -*) -let%test_module "Third-party Method Tests Bad Cases" = - ( module struct - let%expect_test _ = - parse "" ; - [%expect {| error: Accepted format is #()[] |}] - - - let%expect_test _ = - parse " " ; - [%expect {| error: Accepted format is #()[] |}] - - - let%expect_test _ = - parse "blablabla" ; - [%expect {|error: Accepted format is #()[]|}] - - - let%expect_test "no # delimiter" = - parse "a.b.C.f()" ; - [%expect {|error: Accepted format is #()[]|}] - - - let%expect_test "nested parenthesis" = - parse "a.b.C#f(())" ; - [%expect {|error: Accepted format is #()[]|}] - - - let%expect_test "param names are not accepted" = - parse "a.b.C#f(int param)" ; - [%expect {|error: Each param should have form of [@Nullable] |}] - - - let%expect_test "missed package for class" = - parse "C#f()" ; - [%expect {|error: Class name should be fully qualified, including package name|}] - - - let%expect_test "Missed @ in annotation" = - parse "a.b.C#f(Nullable a.b.C)" ; - [%expect {|error: Each param should have form of [@Nullable] |}] - - - let%expect_test "Extra spaces" = - parse "a.b.C#f( a.b.C )" ; - [%expect {|error: Each param should have form of [@Nullable] |}] - - - let%expect_test "No space after comma" = - parse "a.b.C#f(a.b.C,a.b.C)" ; - [%expect {|error: Params should be separated by a comma, followed by a single space|}] - - - let%expect_test "Param names are not accepted" = - parse "a.b.C#f(@Nullable int param)" ; - [%expect {|error: Each param should have form of [@Nullable] |}] - end ) diff --git a/infer/src/nullsafe/unit/dune b/infer/src/nullsafe/unit/dune deleted file mode 100644 index a7097ebfd79..00000000000 --- a/infer/src/nullsafe/unit/dune +++ /dev/null @@ -1,28 +0,0 @@ -; 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. - -(library - (name NullsafeUnitTests) - (public_name infer.NullsafeUnitTests) - (flags - (:standard - -open - Core - -open - IR - -open - IStdlib - -open - IStd - -open - ATDGenerated - -open - IBase - -open - Nullsafe)) - (libraries oUnit core IStdlib ATDGenerated IBase IR Nullsafe) - (preprocess - (pps ppx_compare ppx_expect ppx_inline_test)) - (inline_tests)) diff --git a/infer/src/pulse/PulseReport.ml b/infer/src/pulse/PulseReport.ml index b32649fae04..5f7e1c17c1f 100644 --- a/infer/src/pulse/PulseReport.ml +++ b/infer/src/pulse/PulseReport.ml @@ -14,7 +14,7 @@ open PulseDomainInterface (* Is nullptr dereference issue in Java class annotated with [@Nullsafe] *) let is_nullptr_dereference_in_nullsafe_class tenv ~is_nullptr_dereference jn = is_nullptr_dereference - && match NullsafeMode.of_java_procname tenv jn with Default -> false | Local _ | Strict -> true + && match NullsafeMode.of_java_procname tenv jn with Default -> false | Local | Strict -> true let report tenv ~is_suppressed ~latent proc_desc err_log diagnostic = diff --git a/infer/src/pulse/dune b/infer/src/pulse/dune index bd3c0909ee6..65489065b7c 100644 --- a/infer/src/pulse/dune +++ b/infer/src/pulse/dune @@ -27,9 +27,7 @@ -open Absint -open - Topllib - -open - Nullsafe)) + Topllib)) (libraries core iter @@ -41,8 +39,7 @@ IR Textuallib Absint - Topllib - Nullsafe) + Topllib) (preprocess (pps ppx_compare diff --git a/infer/tests/codetoanalyze/java/checkers/ImmutableCast.java b/infer/tests/codetoanalyze/java/checkers/ImmutableCast.java deleted file mode 100644 index 07940e18963..00000000000 --- a/infer/tests/codetoanalyze/java/checkers/ImmutableCast.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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.checkers; - -import com.google.common.collect.ImmutableList; -import java.util.ArrayList; -import java.util.List; - -public class ImmutableCast { - - ImmutableList immutableList = ImmutableList.of("a", "b", "c"); - - List badCastFromField() { - return immutableList; - } - - List badCast(ImmutableList list) { - return list; - } - - List goodCast(ImmutableList list) { - return new ArrayList(list); - } -} diff --git a/infer/tests/codetoanalyze/java/checkers/Makefile b/infer/tests/codetoanalyze/java/checkers/Makefile index 43d7754b378..b10ef419458 100644 --- a/infer/tests/codetoanalyze/java/checkers/Makefile +++ b/infer/tests/codetoanalyze/java/checkers/Makefile @@ -7,7 +7,7 @@ TESTS_DIR = ../../.. INFER_OPTIONS = \ --debug-exceptions --no-default-checkers \ - --fragment-retains-view --immutable-cast --printf-args --quandary \ + --fragment-retains-view --printf-args --quandary \ --racerd \ INFERPRINT_OPTIONS = --issues-tests diff --git a/infer/tests/codetoanalyze/java/checkers/issues.exp b/infer/tests/codetoanalyze/java/checkers/issues.exp index 63964823c67..37e1059ea8d 100644 --- a/infer/tests/codetoanalyze/java/checkers/issues.exp +++ b/infer/tests/codetoanalyze/java/checkers/issues.exp @@ -1,8 +1,6 @@ codetoanalyze/java/checkers/FragmentRetainsViewExample.java, codetoanalyze.java.checkers.FragmentRetainsViewExample.onDestroyView():void, 0, CHECKERS_FRAGMENT_RETAINS_VIEW, no_bucket, WARNING, [] codetoanalyze/java/checkers/FragmentRetainsViewExample.java, codetoanalyze.java.checkers.FragmentRetainsViewExample.onDestroyView():void, 0, CHECKERS_FRAGMENT_RETAINS_VIEW, no_bucket, WARNING, [] codetoanalyze/java/checkers/FragmentRetainsViewExample.java, codetoanalyze.java.checkers.FragmentRetainsViewExample.onDestroyView():void, 0, CHECKERS_FRAGMENT_RETAINS_VIEW, no_bucket, WARNING, [] -codetoanalyze/java/checkers/ImmutableCast.java, codetoanalyze.java.checkers.ImmutableCast.badCastFromField():java.util.List, 0, CHECKERS_IMMUTABLE_CAST, no_bucket, WARNING, [Method badCastFromField() returns class com.google.common.collect.ImmutableList but the return type is class java.util.List. Make sure that users of this method do not try to modify the collection.], ImmutableCast, codetoanalyze.java.checkers -codetoanalyze/java/checkers/ImmutableCast.java, codetoanalyze.java.checkers.ImmutableCast.badCast(com.google.common.collect.ImmutableList):java.util.List, 0, CHECKERS_IMMUTABLE_CAST, no_bucket, WARNING, [Method badCast(...) returns class com.google.common.collect.ImmutableList but the return type is class java.util.List. Make sure that users of this method do not try to modify the collection.], ImmutableCast, codetoanalyze.java.checkers codetoanalyze/java/checkers/PrintfArgsChecker.java, codetoanalyze.java.checkers.PrintfArgsChecker.notSuppressed(java.io.PrintStream):void, 1, CHECKERS_PRINTF_ARGS, no_bucket, ERROR, [] codetoanalyze/java/checkers/PrintfArgsChecker.java, codetoanalyze.java.checkers.PrintfArgsChecker.stringInsteadOfInteger(java.io.PrintStream):void, 1, CHECKERS_PRINTF_ARGS, no_bucket, ERROR, [] codetoanalyze/java/checkers/PrintfArgsChecker.java, codetoanalyze.java.checkers.PrintfArgsChecker.wrongNumberOfArguments(java.io.PrintStream):void, 1, CHECKERS_PRINTF_ARGS, no_bucket, ERROR, [] diff --git a/infer/tests/codetoanalyze/java/dependencies/.inferconfig b/infer/tests/codetoanalyze/java/dependencies/.inferconfig index c11a0b096f6..7e21407130e 100644 --- a/infer/tests/codetoanalyze/java/dependencies/.inferconfig +++ b/infer/tests/codetoanalyze/java/dependencies/.inferconfig @@ -4,7 +4,6 @@ "inefficient-keyset-iterator": false, "starvation": false, "racerd": false, - "eradicate": false, "pulse": true, "report-block-list-path-regex": [ ".*\\.class$" diff --git a/infer/tests/codetoanalyze/java/nullsafe-annotation-graph/.inferconfig b/infer/tests/codetoanalyze/java/nullsafe-annotation-graph/.inferconfig deleted file mode 100644 index 0d8fd5da358..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe-annotation-graph/.inferconfig +++ /dev/null @@ -1,3 +0,0 @@ -{ - "nullsafe-annotation-graph": true -} diff --git a/infer/tests/codetoanalyze/java/nullsafe-annotation-graph/AnnotationGraph.java b/infer/tests/codetoanalyze/java/nullsafe-annotation-graph/AnnotationGraph.java deleted file mode 100644 index 956f72631fe..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe-annotation-graph/AnnotationGraph.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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_annotation_graph; - -import javax.annotation.Nullable; - -public class AnnotationGraph { - public String fieldA; - public String fieldB; - public @Nullable String fieldC; - public String fieldD; - - // methodA() depends on `p` and on `fieldD` - private String methodA(String p, boolean flag) { - // fieldA depends on p - fieldA = p; - if (flag) { - return p; - } else { - return fieldD; - } - } - - // methodB() depends on methodA()'s return - private String methodB() { - return methodA("", true); - } - - public String methodC() { - String a = methodB(); - // fieldC depends on methodB() - fieldC = a; - - // return does NOT depend on methodB(): already checked for null - if (a != null) { - return a; - } - - return ""; - } - - private void methodD() { - // fieldB depends on fieldA - fieldB = fieldA; - - // methodF(): param #0 and #2 depend on fieldB, but not param #2 - // (fieldB was checked for null before) - methodF(fieldB, fieldB != null ? fieldB : "", fieldB); - } - - private void methodE() { - // violation for fieldD - SomeExternalClass.acceptsNull(fieldD); - // violation for fieldD - fieldD.toString(); - if (fieldD != null) { - // no violation for fieldD - SomeExternalClass.acceptsNull(fieldD); - } - // no violation for fieldB - SomeExternalClass.doesNotAcceptNull(fieldB); - - if (methodC() != null) { - methodC().toString(); // no violation for methodC - } - } - - private void methodF(String param0, String param1, String param2) { - // violation for fieldA - fieldA.toString(); - - methodC().toString(); // violation for methodC - } - - @Override - public boolean equals(Object obj) { - // violation for obj - return toString() == obj.toString(); - } -} - -class SomeExternalClass { - public static void acceptsNull(@Nullable String a) {} - - public static void doesNotAcceptNull(String a) {} -} diff --git a/infer/tests/codetoanalyze/java/nullsafe-annotation-graph/Makefile b/infer/tests/codetoanalyze/java/nullsafe-annotation-graph/Makefile deleted file mode 100644 index 074683141d6..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe-annotation-graph/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# 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. - -TESTS_DIR = ../../.. - -INFER_OPTIONS = \ - --eradicate-only \ - --debug-exceptions -INFERPRINT_OPTIONS = --issues-tests -SOURCES = $(wildcard *.java) - -include $(TESTS_DIR)/javac.make diff --git a/infer/tests/codetoanalyze/java/nullsafe-annotation-graph/issues.exp b/infer/tests/codetoanalyze/java/nullsafe-annotation-graph/issues.exp deleted file mode 100644 index 80346ec0625..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe-annotation-graph/issues.exp +++ /dev/null @@ -1,129 +0,0 @@ -codetoanalyze/java/nullsafe-annotation-graph/AnnotationGraph.java, codetoanalyze.java.nullsafe_annotation_graph.AnnotationGraph.(), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `fieldA` is declared non-nullable, so it should be initialized in the constructor], AnnotationGraph, codetoanalyze.java.nullsafe_annotation_graph, field:codetoanalyze.java.nullsafe_annotation_graph.AnnotationGraph.fieldA -codetoanalyze/java/nullsafe-annotation-graph/AnnotationGraph.java, codetoanalyze.java.nullsafe_annotation_graph.AnnotationGraph.(), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `fieldB` is declared non-nullable, so it should be initialized in the constructor], AnnotationGraph, codetoanalyze.java.nullsafe_annotation_graph, field:codetoanalyze.java.nullsafe_annotation_graph.AnnotationGraph.fieldB -codetoanalyze/java/nullsafe-annotation-graph/AnnotationGraph.java, codetoanalyze.java.nullsafe_annotation_graph.AnnotationGraph.(), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `fieldD` is declared non-nullable, so it should be initialized in the constructor], AnnotationGraph, codetoanalyze.java.nullsafe_annotation_graph, field:codetoanalyze.java.nullsafe_annotation_graph.AnnotationGraph.fieldD -codetoanalyze/java/nullsafe-annotation-graph/AnnotationGraph.java, Linters_dummy_method, 12, ERADICATE_ANNOTATION_GRAPH, no_bucket, INFO, [], AnnotationGraph, codetoanalyze.java.nullsafe_annotation_graph -AnnotationGraph: - Annotation point: - id: f0 - kind: Field - field_name: fieldA - num_violations: 1 - dependent_point_ids: [f1, p8, p10] - - Annotation point: - id: f1 - kind: Field - field_name: fieldB - num_violations: 1 - dependent_point_ids: [] - - Annotation point: - id: f2 - kind: Field - field_name: fieldD - num_violations: 1 - dependent_point_ids: [m4] - - Annotation point: - id: m4 - kind: Method - method_info: - method_name: methodA - params: java.lang.String, boolean - access_level: Private - num_violations: 0 - dependent_point_ids: [m6] - - Annotation point: - id: m6 - kind: Method - method_info: - method_name: methodB - params: - access_level: Private - num_violations: 0 - dependent_point_ids: [] - - Annotation point: - id: m7 - kind: Method - method_info: - method_name: methodC - params: - access_level: Public - num_violations: 1 - dependent_point_ids: [] - - Annotation point: - id: p10 - kind: Param - method_info: - method_name: methodF - params: java.lang.String, java.lang.String, java.lang.String - access_level: Private - param_num: 2 - num_violations: 0 - dependent_point_ids: [] - - Annotation point: - id: p3 - kind: Param - method_info: - method_name: equals - params: java.lang.Object - access_level: Public - param_num: 0 - num_violations: 1 - dependent_point_ids: [] - - Annotation point: - id: p5 - kind: Param - method_info: - method_name: methodA - params: java.lang.String, boolean - access_level: Private - param_num: 0 - num_violations: 0 - dependent_point_ids: [f0, m4] - - Annotation point: - id: p8 - kind: Param - method_info: - method_name: methodF - params: java.lang.String, java.lang.String, java.lang.String - access_level: Private - param_num: 0 - num_violations: 0 - dependent_point_ids: [] - - Annotation point: - id: p9 - kind: Param - method_info: - method_name: methodF - params: java.lang.String, java.lang.String, java.lang.String - access_level: Private - param_num: 1 - num_violations: 0 - dependent_point_ids: [] - - -codetoanalyze/java/nullsafe-annotation-graph/AnnotationGraph.java, Linters_dummy_method, 12, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`AnnotationGraph` needs 4 issues to be fixed in order to be marked @Nullsafe.], AnnotationGraph, codetoanalyze.java.nullsafe_annotation_graph, issues: 4, curr_mode: "Default" -codetoanalyze/java/nullsafe-annotation-graph/AnnotationGraph.java, codetoanalyze.java.nullsafe_annotation_graph.AnnotationGraph.equals(java.lang.Object):boolean, 0, ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION, no_bucket, WARNING, [Parameter `obj` is missing `@Nullable` declaration: according to the Java Specification, for any object `x` call `x.equals(null)` should properly return false.], AnnotationGraph, codetoanalyze.java.nullsafe_annotation_graph, inconsistent_param_index:0 -codetoanalyze/java/nullsafe-annotation-graph/AnnotationGraph.java, Linters_dummy_method, 87, ERADICATE_ANNOTATION_GRAPH, no_bucket, INFO, [], SomeExternalClass, codetoanalyze.java.nullsafe_annotation_graph -AnnotationGraph: - Annotation point: - id: p0 - kind: Param - method_info: - method_name: doesNotAcceptNull - params: java.lang.String - access_level: Public - param_num: 0 - num_violations: 0 - dependent_point_ids: [] - - -codetoanalyze/java/nullsafe-annotation-graph/AnnotationGraph.java, Linters_dummy_method, 87, ERADICATE_META_CLASS_CAN_BE_NULLSAFE, no_bucket, ADVICE, [Congrats! `SomeExternalClass` is free of nullability issues. Mark it `@Nullsafe(Nullsafe.Mode.LOCAL)` to prevent regressions.], SomeExternalClass, codetoanalyze.java.nullsafe_annotation_graph, issues: 0, curr_mode: "Default", promote_mode: "Strict" diff --git a/infer/tests/codetoanalyze/java/nullsafe/.inferconfig b/infer/tests/codetoanalyze/java/nullsafe/.inferconfig deleted file mode 100644 index 95a49391766..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/.inferconfig +++ /dev/null @@ -1,7 +0,0 @@ -{ - "external-java-packages": [ - "external." - ], - "nullsafe-strict-containers": true, - "nullsafe-third-party-signatures": "third-party-signatures" -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/AlternativeRecommendations.java b/infer/tests/codetoanalyze/java/nullsafe/AlternativeRecommendations.java deleted file mode 100644 index d24974ec7f6..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/AlternativeRecommendations.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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; - -import android.annotation.SuppressLint; -import android.view.View; - -/** - * Test to ensure we have special messaging when misusing known nullable methods that have - * non-nullable alternatives. - */ -public class AlternativeRecommendations { - @SuppressLint("eradicate-field-not-initialized") - View field; - - static void dereference_ShouldSuggestAlternative(View view) { - view.findViewById(2).setId(3); - } - - static void passingParam_ShouldSuggestAlternative(View view) { - acceptsNonnullView(view.findViewById(2)); - } - - static View returnValue_ShouldSuggestAlternative(View view) { - return view.findViewById(2); - } - - void assigningField_ShouldSuggestAlternative(View view) { - field = view.findViewById(2); - } - - static void acceptsNonnullView(View view) {} -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/ButterKnife.java b/infer/tests/codetoanalyze/java/nullsafe/ButterKnife.java deleted file mode 100644 index d0018096df2..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/ButterKnife.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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; - -import javax.annotation.Nullable; - -@interface InjectView {} - -/** Support assignments of null to @InjectView fields, generated by butterknife. */ -public class ButterKnife { - @InjectView String injected; - String normal = ""; // assign to suppress not initialized warning - @Nullable String nullable = ""; // assign to suppress not initialized warning - - void f(String nonNullable) {} - - // When dereferencing, injected should behave as not nullable - - void dereferencingInjectedIsOK() { - int n = injected.length(); - } - - void dereferencingNormalIsOK() { - int n = normal.length(); - } - - void dereferencingNullableIsBAD() { - int n = nullable.length(); - } - - // When returning a value, injected should be treated as non nullable - - String convertingToNotNullableForInjectedIsOK() { - return injected; - } - - String convertingToNotNullableForNormalIsOK() { - return normal; - } - - String convertingToNotNullableForNullableIsBAD() { - return nullable; - } - - // When passing to a non nullable param, injected should be treated as non nullable - - void passingToNullableForInjectedIsOK() { - f(injected); - } - - void passingToNullableForNormalIsOK() { - f(normal); - } - - void passingToNullableForNullableIsBAD() { - f(nullable); - } - - // Assigning null to Injected should be allowed (as if it was nullable) - // (Those assignments are generated by Butterknife framework.) - - void assignNullToInjectedIsOK() { - injected = null; - } - - void assignNullToNullableIsOK() { - nullable = null; - } - - void assignNullToNormalIsBAD() { - normal = null; - } - - class TestNotInitialized { - @InjectView String notInitializedInjectedIsOK; - @Nullable String notInitializedNullableIsOK; - String notInitializedNormalIsBAD; - } -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/CapturedParam.java b/infer/tests/codetoanalyze/java/nullsafe/CapturedParam.java deleted file mode 100644 index 352cd76ef77..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/CapturedParam.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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; - -import javax.annotation.Nullable; - -/** Nullability checks for captured params */ -public class CapturedParam { - - void dereferencingNullableIsBAD(@Nullable Object parameter) { - parameter.toString(); - } - - void dereferencingCapturedNullableShouldBeBAD_FIXME(@Nullable Object parameter) { - Object object = - new Object() { - void foo() { - // Should be disallowed, but it is not the case - // TODO(T53473076) fix the FN. - parameter.toString(); - } - }; - } -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/ConditionRedundant.java b/infer/tests/codetoanalyze/java/nullsafe/ConditionRedundant.java deleted file mode 100644 index 26f2c36a306..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/ConditionRedundant.java +++ /dev/null @@ -1,248 +0,0 @@ -/* - * 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; - -import com.facebook.infer.annotation.Assertions; -import com.google.common.base.Preconditions; -import javax.annotation.Nullable; - -public class ConditionRedundant { - - String fieldNonnull = ""; - @Nullable String fieldNullable = ""; - - @Nullable - String getNullable() { - return null; - } - - String getNonnull() { - return ""; - } - - void compareNEQ_NonnullIsBAD(String s) { - if (s != null) { // BAD: condition redundant - // Do something - } - } - - void compareNEQ_NullableIsOK(@Nullable String s) { - if (s != null) { // OK: comparing with nullable - // Do something - } - } - - void compareEQ_NonnullIsBAD(String s) { - if (s == null) { // BAD: condition redundant - // Do something - } - } - - void compareEQ_NullableIsOK(@Nullable String s) { - if (s == null) { // OK: comparing with nullable - // Do something - } - } - - // `if` is not essential, we test all comparisons expressions - - void outsideOfIfCompareNonnullIsBAD(String s) { - boolean b = s != null; // BAD: condition redundant - } - - void outsideOfIfCompareNullableIsOK(@Nullable String s) { - boolean b = s != null; // OK: comparing with nullable - } - - // comparing with nonnull is redundant even if it is a part of expression - - void conjunctionBothNonnullIsBAD(String s1, String s2) { - if (s1 != null && s2 != null) { // BAD: both clauses are redudant - // Do something - } - } - - void conjunctionOneNonnullIsBAD(@Nullable String s1, String s2) { - if (s1 != null && s2 != null) { // BAD: one clause is redundant - // Do something - } - } - - void conjunctionBothNullableIsOK(@Nullable String s1, @Nullable String s2) { - if (s1 != null && s2 != null) { // OK: both clauses make sense - // Do something - } - } - - void disjunctionBothNonnullIsBAD(String s1, String s2) { - if (s1 != null || s2 != null) { // BAD: both clauses are redudant - // Do something - } - } - - void disjunctionOneNonnullIsBAD(@Nullable String s1, String s2) { - if (s1 != null || s2 != null) { // BAD: one clause is redundant - // Do something - } - } - - void disjunctionBothNullableIsOK(@Nullable String s1, @Nullable String s2) { - if (s1 != null || s2 != null) { // OK: both clauses make sense - // Do something - } - } - - // Adding some irrelevant conditions does not make the issue go away - - void irrelevantConditionWithNonnullIsBAD(String s1, @Nullable String s2, int someInt) { - if (someInt == 1 || s1 == null || s2 != null) { // BAD: check for s1 is redundant - // Do something - } - } - - void irrelevantConditionAllNullablesIsOK(@Nullable String s1, @Nullable String s2, int someInt) { - if (someInt == 1 || s1 == null || s2 != null) { // OK: all clauses maeke sense - // Do something - } - } - - // Comparing expressions (not local variables) with null - - void ternary_NonnullInBothBranchesIsBAD(String s1, String s2, int someInt) { - // BAD: comparing nonnull with null is redundant - if ((someInt == 1 ? s1 : s2) == null) { - // Do something - } - } - - void ternary_NullableInBothBranchesIsOK(@Nullable String s1, @Nullable String s2, int someInt) { - // OK: the result is nullable - if ((someInt == 1 ? s1 : s2) == null) { - // Do something - } - } - - void ternary_NonnullInOneBranch_FirstBranch_IsOK(String s1, @Nullable String s2, int someInt) { - // OK: the result is nullable - if ((someInt == 1 ? s1 : s2) == null) { - // Do something - } - } - - // But if we just swap the order, we have a FP. - // (CFG extracts this expression to a form when one of flows contain only nonnull, hence the - // report). - // TODO(T54065455) Don't report in this case - void FP_ternary_NonnullInOneBranch_SecondBranch_ShouldBeOK( - @Nullable String s1, String s2, int someInt) { - if ((someInt == 1 ? s1 : s2) == null) { // FP: expression can be null - // Do something - } - } - - void testFlowSensitivity(@Nullable String nullable1, @Nullable String nullable2) { - if (nullable1 != null) { // OK: comparing nullable with null - if (nullable1 != null) { // BAD: now nullable1 is nonnull - if (nullable2 != null) { // OK: nullable2 can still be null - if (nullable1 != null) { // BAD: nullable1 is still nonnull - // Do something - } - } - } - } - } - - // Test comparison with functions - - void comparingNonnullFunctionIsBAD() { - if (getNonnull() != null) { // BAD: comparing with nonnull - // do something - } - } - - void comparingNullableFunctionIsOK() { - if (getNullable() != null) { // OK: comparing with nullable - // do something - } - } - - // Test comparison with fields - - void comparingNonnullFieldIsBAD() { - if (fieldNonnull != null) { // BAD: condition redundant - // Do something - } - } - - void comparingNullableFieldIsOK() { - if (fieldNullable != null) { // OK: comparing with nullable - // Do something - } - } - - void comparingNullableFieldThatIsAlreadyCheckedIsBAD() { - if (fieldNullable != null) { // OK: comparing with nullable - if (fieldNullable != null) { - // BAD: at this point we already know field is not nullable - } - } - } - - // Test assertions that are modelled in Nullsafe - - void checkNotNull_NonnullIsBAD(String s) { - Preconditions.checkNotNull(s, "BAD: we already know it is not nullable"); - } - - void checkNotNull_NullableIsOK(@Nullable String s) { - Preconditions.checkNotNull(s, "totally legit check"); - } - - void checkArgument_NonnullIsBAd(String s) { - Preconditions.checkArgument(s != null, "BAD: we know it is not null"); - } - - void checkArgument_NullableIsOK(@Nullable String s) { - Preconditions.checkArgument(s != null, "totally legit check"); - } - - void assertNotNull_NonnullIsBAD(String s) { - Assertions.assertNotNull(s, "BAD: we know it is not null"); - } - - void assertNotNull_NullableIsOK(@Nullable String s) { - Assertions.assertNotNull(s, "totally legit check"); - } - - // Test nullability inference in try-catch - - static void maythrow() throws java.io.IOException {} - - void comparingWithNullIfAssignedBeforeThrowableIsBAD() throws java.io.IOException { - String s = null; - try { - s = "123"; - maythrow(); - } finally { - if (s != null) { // BAD: this is redundant - // Do something - } - } - } - - void comparingWithNullIfAssignedAfterThrowableIsOK() throws java.io.IOException { - String s = null; - try { - maythrow(); - s = "123"; - } finally { - if (s != null) { // OK: if `maythrow` throws, it will indeed be null - // Do something - } - } - } -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/FieldNotInitialized.java b/infer/tests/codetoanalyze/java/nullsafe/FieldNotInitialized.java deleted file mode 100644 index d7081ca3db8..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/FieldNotInitialized.java +++ /dev/null @@ -1,397 +0,0 @@ -/* - * 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; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.os.Bundle; -import android.support.annotation.NonNull; -import com.facebook.infer.annotation.Assertions; -import com.facebook.infer.annotation.Initializer; -import com.facebook.infer.annotation.SuppressFieldNotInitialized; -import com.facebook.infer.annotation.SuppressViewNullability; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.inject.Inject; - -// for butterknife -@interface Bind {} - -public class FieldNotInitialized { - - // different ways to suppress the error - class Suppression { - String notNullIsBAD; // BAD: need to initialize it - - @Nonnull String nonnullIsBAD; // BAD: explicit annotation does not make it better - - @NonNull String nonNullIsBAD; // BAD: explicit annotation does not make it better - - @Nullable String nullableIsOK; // OK: will be init with null - - @SuppressFieldNotInitialized String suppressAnnotationIsOK; // OK: explicitly suppressed - - @SuppressLint("eradicate-field-not-initialized") - String suppressLintIsOK; // OK: explicitly suppressed on lint level - - @SuppressLint("Field Not Initialized") - String suppressLintNoPrefixIsOK; // OK: explicitly suppressed on lint level - - @SuppressLint("some-irrelevant-linter") - String suppressWrongLintIsBAD; // BAD: this suppression is irrelevant - - @Inject String injectIsOK; // Means: assume it will be initialized via dependency injection - - @Bind String bindIsOK; // Means: assume it will be initialized, and ignore null assignment - - // Means: assume it will be initialized, and ignore null assignment - @SuppressViewNullability String suppressViewNullabilityIsOK; - - Suppression() {} - - // Ensure that some suppressions suppress only field not initialized - // and nothing else, but some suppress setting to null as well. - void testNullifyFields() { - bindIsOK = null; // OK: the framework could write null into the field - suppressViewNullabilityIsOK = null; // OK: the framework could write null into the field - nonnullIsBAD = null; // BAD: explicit nonnull annotation does not allow nullifying - nonNullIsBAD = null; // BAD: explicit nonnull annotation does not allow nullifying - injectIsOK = null; // BAD: inject suppressed only initialization issues - suppressAnnotationIsOK = null; // BAD: only initialization issue was suppressed - suppressLintIsOK = null; // BAD: only initialization issue was suppressed - } - } - - class OnlyRead { - Object o; - - OnlyRead() { - Object x = o; // BAD: we merely read this variable, but forgot to initialize - } - } - - class WriteItselfIsBAD { - Object ok; - Object bad; - - WriteItselfIsBAD() { - ok = ""; - bad = bad; // BAD: Can not initialize with itself - } - } - - class InitializationOrder { - Object o1; - Object o2; - - InitializationOrder(int a) { - o1 = o2; // BAD: not initialized - o2 = new Object(); - } - - InitializationOrder(double a) { - o1 = new Object(); // OK - o2 = o1; - } - } - - class OnlyReadIndirect { - Object o1; - Object o2; - - private void indirect() { - Object x = o1; // not initialized - o2 = new Object(); - } - - OnlyReadIndirect() { - indirect(); - } - } - - class ShouldInitializeInAllBranches { - Object f1; - Object f2; - Object f3; - Object f4; - Object f5; - - public ShouldInitializeInAllBranches(int a) { - f4 = new Object(); - Object f5 = new Object(); // BAD: shadowing; not an initialization - if (a == 42) { - f1 = new Object(); - f2 = new Object(); - } else { - f3 = new Object(); - if (a == 43) { - f1 = new Object(); - // BAD: f2 is not initialized in this branch - } else { - f1 = new Object(); - f2 = new Object(); - } - } - } - } - - class InitIfNull { - Object good; - Object shouldBeGood_FIXME; - - public InitIfNull() { - if (good == null) { - good = new Object(); - // bad is considered to be initialized not in all branches. - // (which is bit weird: we know that good is always null by default) - // TODO(T53537343) teach nullsafe that all fields are initially null in constructor - shouldBeGood_FIXME = new Object(); - } - } - } - - class InitCircular { - String bad; - String stillBad; - String good; - - String knownNotNull = ""; - - InitCircular() { - String tmp = bad; - bad = tmp; // BAD: circular initialization - stillBad = bad; // BAD: try to initialize from circularly initalized var - - String tmp2 = knownNotNull; - good = tmp2; // OK - } - } - - class InitWithOtherClass { - class OtherClass { - String nonNull = ""; - @Nullable String nullable = ""; - } - - String bad; - String good; - - InitWithOtherClass(OtherClass x) { - bad = x.nullable; // BAD: might be null - good = x.nonNull; // OK: we know can not be null - } - } - - // Check that Infer does not confuse things - // in copy constuctors. - class InitWithTheSameClass { - String good; - String bad; - - InitWithTheSameClass(InitWithTheSameClass x) { - good = x.good; // OK: this is not a circular initialization - bad = bad; // BAD: this is a circular initialization - } - } - - // A test to ensure suppressions work on constructor level - class Suppressions { - String f1; - String f2; - - // BAD: forgot to initialize f2 - Suppressions(int a) { - f1 = ""; - f(null); // Expect to see "parameter not nullable" issue - } - - // Should suppress both field not initialized warning, - // but not the PARAMETER_NOT_NULLABLE - @SuppressLint("eradicate-field-not-initialized") - Suppressions(int a, int b) { - f(null); - } - - // This annotation correctly suppresses only needed issues - @SuppressFieldNotInitialized - Suppressions(int a, int b, int c) { - f(null); // Expect to see "parameter not nullable" issue - it should NOT be suppressed - } - - void f(String a) {} - } -} - -/** - * There is a predefined list of classes which have known methods that act like initializers. If a - * class extends such special class and initializes a field in such allow listed method, we don't - * require initializing this field in constructor. (NOTE: To do the same in non allow listed class - * one can use @Initializer annotation instead). - */ -class TestKnownInitializers { - - // nullsafe knows that Activity.onCreate is a special initilizer. - class KnownInitializers extends Activity { - - String initInConstructorIsOK; - String initInSpecialMethodIsOK; - String initInUnknownMethodIsBAD; - - KnownInitializers() { - initInConstructorIsOK = ""; - } - - // onCreate is a special method - protected void onCreate(Bundle bundle) { - initInSpecialMethodIsOK = ""; - } - - protected void someOtherMethod(Bundle bundle) { - // BAD: This method is unknown (and does not have @Initializer annotation either). - initInUnknownMethodIsBAD = ""; - } - } - - abstract class FakeActivity { - abstract void onCreate(Bundle bundle); - } - - class SimplyOnCreateWontDoATrick extends FakeActivity { - String initInUnknownMethodIsBAD; - - // Though we use onCreate method, the class does not extend one of the known - // classes that are known to have such a property, so it does not count. - protected void onCreate(Bundle bundle) { - // BAD: This method is unknown (and does not have @Initializer annotation either). - initInUnknownMethodIsBAD = ""; - } - } -} - -/** - * If a method is marked with @Initializer annotation, we essentially treat is as a constuctror: if - * a field is initialized in one of such methods, we assume this method will be called before using - * the field, so we don't consider it "not initialized" error. A popular usecase for that is a - * Builder pattern, when required fields are set not in the constuctor, but in corresponding - * setters, and then build() method checks in runtime that all fields are initialized. - */ -class TestInitializerAnnotation { - String initInConstructorIsOK; - String initInInitilizerMethod1IsOK; - String initInInitilizerMethod2IsOK; - String initInAnyOtherMethodIsBAD; - String initByNullableInInitializedMethodIsBAD; - String dontInitAtAllIsBAD; - @Nullable String dontInitOptionalIsOK; - - TestInitializerAnnotation() { - initInConstructorIsOK = ""; - } - - @Initializer - void set1(String value) { - // OK: we assume set1() will be called before the class is actually used - this.initInInitilizerMethod1IsOK = value; - } - - @Initializer - void set2(String value) { - // OK: we assume set2() will be called before the class is actually used - this.initInInitilizerMethod2IsOK = value; - } - - void set3(String value) { - // BAD: though the field is initialized here, set3 is not marked as @Initialized - this.initInAnyOtherMethodIsBAD = value; - } - - @Initializer - void set4(@Nullable String value) { - // BAD: method is marked as @Initializer, but the value can be null - this.initByNullableInInitializedMethodIsBAD = value; - } - - // Example of a typical usecase: - // a build() method that is supposed to be called before the class is used. - Object build() { - // Fail hard if the required fields are not initialzed. - // Unfortunately, this will lead to "condition redundant" warnings, despite the fact - // that checking for this makes total sense. - // TODO(T53531699) don't issue "condition redundant" warning in this case. - Assertions.assertCondition( - initInInitilizerMethod1IsOK != null && initInInitilizerMethod2IsOK != null); - - // ... do some stuff - - // return some meaninful object - return ""; - } - - abstract class TestFieldNotInitializedBase { - @Initializer - protected abstract void markedInitializerInBase(); - - protected abstract void notMarkedInitializerInBase1(); - - protected abstract void notMarkedInitializerInBase2(); - } - - // Test to ensure we respect parent @Initializer annotations - class TestFieldNotInitializedDerived extends TestFieldNotInitializedBase { - private String field1_OK; - private String field2_BAD; - private String field3_OK; - - @Override - public void markedInitializerInBase() { - // OK: implicitly @Initializer (inherited from the base) - field1_OK = ""; - } - - @Override - public void notMarkedInitializerInBase1() { - // BAD: field not initialized - field2_BAD = ""; - } - - @Override - @Initializer - public void notMarkedInitializerInBase2() { - // OK: explicitly marked as an initializer - field3_OK = ""; - } - } - - // Ensure that chains with non-trivial length work as well - class TestFieldNotInitializedDerivedDerived extends TestFieldNotInitializedDerived { - private String field1_OK; - private String field2_BAD; - private String field3_OK; - - // FPs in constructor - - @Override - public void markedInitializerInBase() { - // OK: implicitly @Initializer (inherited from the base through the chain) - field1_OK = ""; - } - - @Override - public void notMarkedInitializerInBase1() { - // BAD: field not initialized, and the method is not marked as @Initializer in any of the - // bases - field2_BAD = ""; - } - - @Override - public void notMarkedInitializerInBase2() { - // OK: explicitly marked as an initializer in the direct base - field3_OK = ""; - } - } -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/FieldNotNullable.java b/infer/tests/codetoanalyze/java/nullsafe/FieldNotNullable.java deleted file mode 100644 index ce555548077..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/FieldNotNullable.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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; - -import android.support.v4.app.Fragment; -import com.facebook.infer.annotation.Cleanup; -import javax.annotation.Nullable; - -/** - * It is common in Android code to recycle objects (e.g. views) by nullifying them in the "cleanup" - * methods that are called after object lifecycle is over. This allows the GC to recycle without - * waiting for the outer object to be freed. This is safe because these fields are not going to be - * accessed after cleanup. So it is not necessary to annotate those fields with @Nullable. - */ -class CanAssignNullInCleanupMethods extends Fragment { - - String someObject = ""; - - @Override - public void onDestroyView() { - // onDestroyView is a special method: OK to nullify here - someObject = null; - } - - @Override - public void onDestroy() { - // onDestroy is a special method: OK to nullify here - someObject = null; - } - - @Cleanup - public void assignNullInCleanupMethodIsOK() { - // The method is marked as cleanup. - // OK to nullify here. - someObject = null; - } - - public void assignNullInAnyOtherMethodIsBAD() { - someObject = null; // BAD: field is not nullable - } -} - -public class FieldNotNullable { - @Nullable String nullable = ""; - String notNullable = ""; - - String initializeNonNullableWithNullIsBAD = null; - @Nullable String initializeNullableWithNullIsOK = null; - - @Nullable - String getNullable() { - return ""; - } - - String getNotNullable() { - return ""; - } - - void setNullableToNotNullableIsBAD(@Nullable String s) { - notNullable = null; // BAD - notNullable = s; // BAD - notNullable = getNullable(); // BAD (even though getNullable() does not really return null) - } - - void setNullableToNullableIsOK(@Nullable String s) { - nullable = null; // OK - nullable = s; // OK - nullable = getNullable(); // OK - } - - void setNotNullableToNotNullableIsOK(String s) { - notNullable = "abc"; // OK - notNullable = s; // OK - notNullable = getNotNullable(); // OK - } - - void setNullableToExternalIsBAD(@Nullable String s) { - SomeExternalClass obj = new SomeExternalClass(); - obj.externalNotNull = s; - } - - void setNonNullToExternalIsOK(String s) { - SomeExternalClass obj = new SomeExternalClass(); - obj.externalNotNull = s; - } - - void setNullableToExternalNullableIsOK(@Nullable String s) { - SomeExternalClass obj = new SomeExternalClass(); - obj.externalNullable = s; - } -} - -class SomeExternalClass { - public String externalNotNull; - public @Nullable String externalNullable; -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/FieldNullabilityMemoization.java b/infer/tests/codetoanalyze/java/nullsafe/FieldNullabilityMemoization.java deleted file mode 100644 index 76f16ce3032..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/FieldNullabilityMemoization.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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; - -import javax.annotation.Nullable; - -/** - * Nullsafe has a feature: field nullability is memoized within a method. In other words, nullsafe - * assumes: - * - *
    - *
  1. a) There will be no tricky multithreading. - *
  2. b) If a field is set to non-null, the method won't call another method that will nullify it - * back. - *
- * - *

NOTE: This feature is unsound, but assumptions a) and b) mostly hold for real codebases, so - * here nullsafe tradeoffs theoretical unsoundness for practical usability. - * - *

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; - } -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/FieldOverAnnotated.java b/infer/tests/codetoanalyze/java/nullsafe/FieldOverAnnotated.java deleted file mode 100644 index 945cacf6ef0..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/FieldOverAnnotated.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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; - -import javax.annotation.Nullable; - -/** - * The rule: if a nullable field is non-conditionally initialized in all constructors, it is - * overannotated. - */ -public class FieldOverAnnotated { - @Nullable String initializedInAllConstructorsIsBAD; - // if there are branches, but the field is initilized in all branches, - // it is still overannotated. - @Nullable String initilizedInAllConstructorsAndAllBranchesIsBAD; - @Nullable String initiliazedInSomeConstructorsIsOK; - @Nullable String conditionallyInitializedIsOK; - @Nullable String notInitializedIsOK; - // False positive: we set it is @Nullable so this is NOT overannotated but the issue is recorded. - @Nullable String FP_initializedInAllConstructorsButSetToNullInAPublicMethodShouldBeOK; - - FieldOverAnnotated(int a) { - initializedInAllConstructorsIsBAD = ""; - FP_initializedInAllConstructorsButSetToNullInAPublicMethodShouldBeOK = ""; - if (a == 0) { - // We initialize it in the other constructor, but here - // we initialize it only conditionally, hence @Nullable annotation is justified. - conditionallyInitializedIsOK = ""; - } - // check that initialization from both branches still counts as overannotated - if (a == 1) { - initilizedInAllConstructorsAndAllBranchesIsBAD = ""; - } else { - initilizedInAllConstructorsAndAllBranchesIsBAD = ""; - } - initiliazedInSomeConstructorsIsOK = ""; - } - - FieldOverAnnotated(int a, int b) { - initializedInAllConstructorsIsBAD = ""; - FP_initializedInAllConstructorsButSetToNullInAPublicMethodShouldBeOK = ""; - initilizedInAllConstructorsAndAllBranchesIsBAD = ""; - conditionallyInitializedIsOK = ""; - } - - public void setToNull() { - // False positive: we set it is @Nullable so this is NOT overannotated but - // the issue is recorded. - FP_initializedInAllConstructorsButSetToNullInAPublicMethodShouldBeOK = null; - } -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/GeneratedGraphQL.java b/infer/tests/codetoanalyze/java/nullsafe/GeneratedGraphQL.java deleted file mode 100644 index 7b8569fffdf..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/GeneratedGraphQL.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * 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; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.CLASS) -@Target(ElementType.TYPE) -public @interface GeneratedGraphQL {} diff --git a/infer/tests/codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java b/infer/tests/codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java deleted file mode 100644 index e4cb0fd0203..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * 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; - -import external.library.SomeExternalClass; -import javax.annotation.Nullable; - -interface VariousMethods { - String valBoth(String arg); - - @Nullable - String nullableReturn(String arg); - - String nullableArg(@Nullable String arg); - - @Nullable - String nullableBoth(@Nullable String arg); -} - -interface Overloads { - String overload(int arg); - - String overload(@Nullable String arg); - - String overload(String arg1, int arg2); - - String overload(String arg1, String arg2); - - void notOverload(@Nullable Object arg); -} - -// Check return annotations - -abstract class ReturnValToNullBAD implements VariousMethods { - @Nullable - public String valBoth(String arg) { - return null; - } -} - -abstract class ReturnNullToValOK implements VariousMethods { - public abstract String nullableReturn(String arg); -} - -abstract class ReturnValFromValAndNullFromNullOK implements VariousMethods { - @Nullable - public String nullableReturn(String arg) { - return null; - } - - public String valBoth(String arg) { - return arg; - } -} - -abstract class AbstractReturnValToNullFN implements VariousMethods { - // An abstract override method with inconsistent signature is not reported - @Nullable - public abstract String valBoth(String arg); -} - -// Check parameter annotations - -abstract class ArgValToNullOK implements VariousMethods { - public String valBoth(@Nullable String arg) { - return "OK"; - } -} - -abstract class ArgNullToValBAD implements VariousMethods { - public String nullableArg(String arg) { - return arg; - } -} - -abstract class ArgNullToValForInterfaceInAnotherFileBAD - implements InconsistentSubclassAnnotationInterface { - public String implementInAnotherFile(String s) { - return "BAD"; - } -} - -abstract class ArgValToValAndNullToNullOK implements VariousMethods { - public String valBoth(String arg) { - return arg; - } - - @Nullable - public String nullableBoth(@Nullable String arg) { - return arg; - } -} - -// Check overrides + overloads - -// These are 'good' cases with real overrides -abstract class OverrideExistingCorrectlyOK implements Overloads { - public String overload(int arg) { - return "OK"; - } - - public String overload(@Nullable String arg) { - return "OK"; - } - - public String overload(String arg1, int arg2) { - return arg1; - } - - public String overload(String arg1, String arg2) { - return arg1; - } -} - -abstract class NoOverrideSinceDifferentTypesOK implements Overloads { - @Nullable - public String overload(Object arg) { - return null; - } - - public String overload(Double arg) { - return arg.toString(); - } - - // Although, String is a subtype of Object, this method is not an override - public void notOverload(String arg) { - return; - } -} - -// This is just a smoke test to check that incorrect overrides of overloaded methods get reported -abstract class OverloadExistingIncorrectBAD implements Overloads { - @Nullable - public String overload(String arg1, String arg2) { - return null; - } -} - -// Check constructors - -class ConstructorsAreExcluded { - class Base { - Base(@Nullable String s) {} - } - - class Derived extends Base { - Derived(String s) { // OK: there's no sub-typing between constructors - super(s); - } - } -} - -// Check interop with external libraries - -class ExtendsExternalLibrary extends SomeExternalClass { - - @Override - public @Nullable Object externalMethod1() { - // subtyping error on the return type not reported as we cannot - // rely on the external libraries to be correctly annotated - return null; - } - - @Override - public void externalMethod2(Object object) { - // subtyping error on the parameter type are reported - } -} - -// Check that 1) we have a special error message for lack of annotation in this method and 2) treat -// `x` as implicitly nullable -class JavaLangEquals { - @Override - public boolean equals(Object x) { - // BAD: x can not be directly dereferenced without null comparison: - // it is implicitly nullable because Java requires `x.equals(null)` to work correctly. - // It is a common enough case to make the nullsafe support this specifically. - return x.toString() == "JavaLangEquals"; - } -} - -// Check multiple interfaces in the inheritance chain -interface NullableGetter { - @Nullable - String get(); -} - -interface NonNullableInterfaceGetterOK extends NullableGetter { - String get(); -} - -class NonNullableConcreteGetterOK implements NonNullableInterfaceGetterOK { - public String get() { - return "OK"; - } -} - -class NullableConcreteGetterBAD implements NonNullableInterfaceGetterOK { - @Nullable - public String get() { - return null; - } -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/InconsistentSubclassAnnotationInterface.java b/infer/tests/codetoanalyze/java/nullsafe/InconsistentSubclassAnnotationInterface.java deleted file mode 100644 index 4057bbd6db6..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/InconsistentSubclassAnnotationInterface.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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; - -import javax.annotation.Nullable; - -public interface InconsistentSubclassAnnotationInterface { - - public String implementInAnotherFile(@Nullable String s); - - Object overloadedMethod(); - - Object overloadedMethod(Object object); -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/InheritanceForStrictMode.java b/infer/tests/codetoanalyze/java/nullsafe/InheritanceForStrictMode.java deleted file mode 100644 index f0fa650394b..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/InheritanceForStrictMode.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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. - */ - -/** Test for checking how @NullsafeStrict mode plays with inheritance rule */ -package codetoanalyze.java.nullsafe; - -import com.facebook.infer.annotation.NullsafeStrict; -import javax.annotation.Nullable; - -public class InheritanceForStrictMode { - class NonStrictBase { - public @Nullable String okToRemoveNullableInChildren() { - return null; - } - - public String badToAddNullableInChildren() { - return ""; - } - - public void params( - @Nullable String badToRemoveNullableInChildren, String okToAddNullableInChildren) {} - } - - // Exactly as NonStrictBase, except that it is marked as @NullsafeStrict - @NullsafeStrict - class StrictBase { - public @Nullable String okToRemoveNullableInChildren() { - return null; - } - - public String badToAddNullableInChildren() { - return ""; - } - - public void params( - @Nullable String badToRemoveNullableInChildren, String okToAddNullableInChildren) {} - } - - // Expecting all issues to be surfaced as ERRORs - // NOTE: we currently DON'T require the base to be strictified in order to strictify a child (see - // T60513926) - @NullsafeStrict - class StrictExtendingNonstrict extends NonStrictBase { - public @Override String okToRemoveNullableInChildren() { - return ""; - } - - public @Override @Nullable String badToAddNullableInChildren() { - return null; - } - - public @Override void params( - String badToRemoveNullableInChildren, @Nullable String okToAddNullableInChildren) {} - } - - // Expecting all issues to be surfaced as ERRORs - @NullsafeStrict - class StrictExtendingStrict extends StrictBase { - public @Override String okToRemoveNullableInChildren() { - return ""; - } - - public @Override @Nullable String badToAddNullableInChildren() { - return null; - } - - public @Override void params( - String badToRemoveNullableInChildren, @Nullable String okToAddNullableInChildren) {} - } - - // Expecting all issues to be surfaces as WARNINGs (even that we extend a strict class) - class NonStrictExtendingStrict extends StrictBase { - public @Override String okToRemoveNullableInChildren() { - return ""; - } - - public @Override @Nullable String badToAddNullableInChildren() { - return null; - } - - public @Override void params( - String badToRemoveNullableInChildren, @Nullable String okToAddNullableInChildren) {} - } -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/JunitExample.java b/infer/tests/codetoanalyze/java/nullsafe/JunitExample.java deleted file mode 100644 index c5e4d786464..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/JunitExample.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * 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. - */ -import junit.framework.TestCase; - -public class JunitExample extends TestCase { - - private Object mField; - - @Override - protected void setUp() throws Exception { - super.setUp(); - mField = new Object(); - } - - public void testSomething() { - mField.toString(); - } -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/Lambdas.java b/infer/tests/codetoanalyze/java/nullsafe/Lambdas.java deleted file mode 100644 index 1fb168ec012..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/Lambdas.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * 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; - -import com.facebook.infer.annotation.Nullsafe; -import java.lang.annotation.ElementType; -import java.lang.annotation.Target; -import java.util.Random; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.stream.Stream; -import javax.annotation.Nullable; - -public class Lambdas { - // Helper - @Nullable private String nullableStringField; - private String notNullStringField = "NotNull"; - - // Helper - static interface NullableFunction { - @Nullable - public R apply(@Nullable T t); - } - - // Helper - @Nullable - private static R nullableApply(@Nullable T t, NullableFunction f) { - return f.apply(t); - } - - // Helper - static interface NullableCallbackWithDefaultMethods { - default void onSuccess() { - // no-op - } - - void onFailure(@Nullable Integer statusCode); - } - - // Helper - private static void acceptNullableCallback(NullableCallbackWithDefaultMethods cb) { - cb.onFailure(null); - } - - // Helper - private static String acceptNullableStringSuffix(String s, @Nullable String suffix) { - return suffix == null ? s : suffix; - } - - // Helper - private static String acceptNotNullString(String s) { - return s; - } - - // Helper - @Nullable - private static String getNullableString() { - return new Random().nextBoolean() ? "NotNull" : null; - } - - private void useLambdaAllNotNull_OK(Stream stream) { - stream.map(s -> acceptNullableStringSuffix(s, "IAmNotNull")); - } - - private void useLambdaNullableFieldCapture_OK(Stream stream) { - stream.map(s -> acceptNullableStringSuffix(s, nullableStringField)); - } - - private void useLambdaNullableLocalCapture_OK(Stream stream) { - String localString = getNullableString(); - stream.map(s -> acceptNullableStringSuffix(s, localString)); - } - - private void useLambdaNullableLocalCaptureToNotNull_BAD_FN(Stream stream) { - String localString = getNullableString(); // @Nullable - stream.map(s -> acceptNotNullString(localString)); // BAM! needs NotNull - } - - private String useLambdaToSetFieldToNull_BAD_FN() { - Consumer f = - s -> { - if (s.length() < 42) { - notNullStringField = null; // BAM! - } - }; - - f.accept("TheString"); - - return notNullStringField; - } - - @Nullable - private Integer useLambdaWithNullableInterface_BAD_WRONG(String paramString) { - // should be "nullable dereference" here, while now it's "inconsistent subclass param" - return nullableApply(paramString, lambdaString -> lambdaString.length()); - } - - @Nullable - private Integer useLambdaWithExplicitParamTypes_BAD(String paramString) { - // expected "inconsistent subclass annotation" here, since parent defined param as @Nullable - return nullableApply(paramString, (String lambdaString) -> lambdaString.length()); - } - - @Nullable - private Integer useLambdaWithExplicitParamTypesAndNullability_BAD_WRONG(String paramString) { - // should flag "nullable derefernce"; instead raises "inconsistent subclass" - // because it doesn't see the @Nullable annotation on the lambda's param - return nullableApply(paramString, (@Nullable String lambdaString) -> lambdaString.length()); - } - - private void useLambdaForInterfaceWithDefaultMethods_OK_FP() { - acceptNullableCallback(statusCode -> System.out.println(statusCode)); // should be OK - } - - // Following tests demonstrates some "desired" behaviours of typechecker in - // "lambda pipelines". However, those will require significant investment to - // implement. - - // Currently, it reports nothing because we lack a model for java.util.function.Function - private void useLambdasInStreamPipeline_WRONG() { - Stream.generate(() -> getNullableString()) - .peek(s -> System.out.println(s.length())) // s is @Nullable => nullable dereference! - .flatMap( - s -> s == null ? Stream.empty() : Stream.of(s)) // should be a stream of notnull strings - .forEach(s -> System.out.println(s.length())); // fine - } - - // Helper - // Starting with Java 8 annotations can be applied to type parameters. - // Both JSR-305's @Nullable and Android's @Nullable however lack necessary - // @Target qualifier, so we define our own here. - @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) - public @interface MyNullable {}; - - @Nullsafe(Nullsafe.Mode.LOCAL) - static class NullsafeClass { - // Cannot reasonably use without some tricky internal model - private String useJavaUtilFunction_UNSUPPORTED(Function<@MyNullable String, String> f) { - return f.apply(getNullableString()); - } - } -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/LibraryCalls.java b/infer/tests/codetoanalyze/java/nullsafe/LibraryCalls.java deleted file mode 100644 index 8720587add3..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/LibraryCalls.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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; - -import java.lang.ref.PhantomReference; -import java.lang.ref.Reference; -import java.lang.ref.WeakReference; -import java.util.concurrent.atomic.AtomicReference; - -public class LibraryCalls { - - String badReferenceDereference(Reference ref) { - return ref.get().toString(); - } - - String badWeakReferenceDereference(WeakReference ref) { - return ref.get().toString(); - } - - String badPhantomReferenceDereference(PhantomReference ref) { - return ref.get().toString(); - } - - - String badAtomicReferenceDereference(AtomicReference ref) { - return ref.get().toString(); - } -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/Makefile b/infer/tests/codetoanalyze/java/nullsafe/Makefile deleted file mode 100644 index 2b8cd760db8..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -# 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. - -TESTS_DIR = ../../.. - -INFER_OPTIONS = \ - --eradicate-only \ - --eradicate-return-over-annotated \ - --eradicate-field-over-annotated \ - --eradicate-condition-redundant \ - --debug-exceptions -INFERPRINT_OPTIONS = --issues-tests -SOURCES = $(wildcard third-party-test-code/some/test/pckg/*.java) $(wildcard *.java) $(wildcard $(TESTS_DIR)/external/library/*.java) - -include $(TESTS_DIR)/javac.make diff --git a/infer/tests/codetoanalyze/java/nullsafe/MapNullability.java b/infer/tests/codetoanalyze/java/nullsafe/MapNullability.java deleted file mode 100644 index 5cd4670ae26..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/MapNullability.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * 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; - -import javax.annotation.Nullable; - -/** Check how we model the behavior of Map nullability */ -public class MapNullability { - - class TestThatGetIsAllowedOnlyAfterContainsKeyWasChecked { - - void usingGetAfterKeyWasCheckedIsOK(java.util.Map m) { - if (m.containsKey(3)) { - m.get(3).isEmpty(); - } - } - - void usingGetWithoutCheckingKeyIsBAD(java.util.Map m) { - m.get(3).isEmpty(); - } - - void usingGetAfterWrongKeyWasCheckedIsBAD(java.util.Map m) { - if (m.containsKey(3)) { - m.get(4).isEmpty(); - } - } - - void usingGetAfterKeyWasCheckedInWhileLoopIsOK(java.util.Map m) { - while (true) { - if (m.containsKey(3)) { - m.get(3).isEmpty(); - } - } - } - - void usingGetAfterWrongKeyWasCheckedInWhileLoopIsBAD(java.util.Map m) { - while (true) { - if (m.containsKey(3)) { - m.get(4).isEmpty(); - } - } - } - - void immutableMap_usingGetAfterKeyWasCheckedIsOK( - com.google.common.collect.ImmutableMap m) { - if (m.containsKey(3)) { - m.get(3).isEmpty(); - } - } - - void immutableMap_usingGetAfterWrongKeyWasCheckedIsBAD( - com.google.common.collect.ImmutableMap m) { - if (m.containsKey(3)) { - m.get(4).isEmpty(); - } - } - } - - public class TestThatGetAfterPutIsAllowed { - String dontAssignNull = ""; - - public void getAfterPutIsOK(java.util.Map map, String key) { - map.put(key, "abc"); - dontAssignNull = map.get(key); - } - - public void getWithoutPutIsBAD(java.util.Map map, String key) { - dontAssignNull = map.get(key); - } - - public void getAfterPutWrongKeyIsBAD( - java.util.Map map, String key, String wrongKey) { - map.put(key, "abc"); - dontAssignNull = map.get(wrongKey); - } - - public void getAfterPutSeveralKeysIsOK(java.util.Map map) { - map.put("key1", "value1"); - map.put("key2", "value1"); - dontAssignNull = map.get("key2"); - dontAssignNull = map.get("key1"); - dontAssignNull = map.get("key2"); - map.put("key3", "value1"); - dontAssignNull = map.get("key1"); - dontAssignNull = map.get("key2"); - dontAssignNull = map.get("key3"); - } - - public void getAfterPutSeveralKeysButGetWrongOneIsBAD(java.util.Map map) { - map.put("key1", "value1"); - map.put("key2", "value1"); - dontAssignNull = map.get("key2"); // OK - dontAssignNull = map.get("wrong_key"); // BAD - } - - public void getAfterPutNonnullIsOK(java.util.Map map, String nonnullValue) { - map.put("key", nonnullValue); - dontAssignNull = map.get("key"); - } - - public void getAfterPutNullableIsBAD( - java.util.Map map, @Nullable String nullableValue) { - map.put("key", nullableValue); - dontAssignNull = map.get("key"); - } - - public void overwriteKeyByNullIsBAD(java.util.Map map, String key) { - map.put(key, "abc"); - map.put(key, null); // Parameter not nullable - dontAssignNull = map.get(key); // BAD - } - - public void overwriteKeyByNonnullIsOK(java.util.Map map, String key) { - map.put(key, null); // Parameter not nullable - map.put(key, "abc"); - dontAssignNull = map.get(key); // OK - } - - public void getAfterConditionalPutIsOK(java.util.Map map, String key) { - if (!map.containsKey(key)) { - map.put(key, "abc"); - } - // OK: map either already contained a key, or we've just put it here! - dontAssignNull = map.get(key); - } - - public void getAfterConditionalPutWrongKeyIsBAD( - java.util.Map map, String key, String wrongKey) { - if (!map.containsKey(key)) { - map.put(wrongKey, "abc"); - } - dontAssignNull = map.get(key); - } - } -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/ModePromotions.java b/infer/tests/codetoanalyze/java/nullsafe/ModePromotions.java deleted file mode 100644 index dc8be5dc049..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/ModePromotions.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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. - */ - -/** - * A test ensuring that we correctly analyze mode promotions possibility. All classes in this file - * should be free of nullability issues (w.r.t to their mode). The goal of the test is to ensure - * that mode to promote to is correct for each class. - */ -package codetoanalyze.java.nullsafe; - -import com.facebook.infer.annotation.Nullsafe; - -// Zero issues and no dependencies - can strictify -class Default_NoDeps_CanBePromotedToStrict { - static String f() { - return ""; - } -} - -@Nullsafe(Nullsafe.Mode.LOCAL) -class Local_NoDeps_CanBePromotedToStrict { - static String f() { - return ""; - } -} - -// Nothing to promote to -@Nullsafe(Nullsafe.Mode.STRICT) -class Strict_NoDeps_NoPromos { - static String f() { - return ""; - } -} - -class Default_UsesDefault_CanBePromotedToTrustAll { - static String f() { - // We use unknown default function. Since we don't support trust some in promotions, - // the possible promotion is trust all. - return Default_NoDeps_CanBePromotedToStrict.f(); - } -} - -class Default_UsesItself_CanBePromotedToStrict { - static String f() { - // We use only the function from its own class. The class can be promoted to strict staight - // ahead. - return g(); - } - - static String g() { - return ""; - } -} - -class Default_UsesLocal_CanBePromotedToTrustNone { - static String f() { - // We depend only on a nullsafe method. - // Hence the class can be promoted to "trust none" (but not to strict). - return Local_NoDeps_CanBePromotedToStrict.f(); - } -} - -class Default_UsesStrict_CanBePromotedToStrict { - static String f() { - // We depend only on a strict class. - // Hence the class can be promoted to "trust none" (but not to strict). - return Strict_NoDeps_NoPromos.f(); - } -} - -@Nullsafe( - value = Nullsafe.Mode.LOCAL, - trustOnly = @Nullsafe.TrustList({Default_NoDeps_CanBePromotedToStrict.class})) -class TrustSome_DoesNotUseTrusted_CanBePromotedToTrustNone { - static String f() { - return Local_NoDeps_CanBePromotedToStrict.f(); - } -} - -@Nullsafe( - value = Nullsafe.Mode.LOCAL, - trustOnly = @Nullsafe.TrustList({Default_NoDeps_CanBePromotedToStrict.class})) -class TrustSome_UsesTrusted_NoPromo { - static String f() { - return Default_NoDeps_CanBePromotedToStrict.f(); - } -} - -@Nullsafe( - value = Nullsafe.Mode.LOCAL, - trustOnly = @Nullsafe.TrustList({Local_NoDeps_CanBePromotedToStrict.class})) -class TrustSome_TrustToLocalIsNotNeeded_CanBePromotedToTrustNone { - static String f() { - return Local_NoDeps_CanBePromotedToStrict.f(); - } -} - -@Nullsafe( - value = Nullsafe.Mode.LOCAL, - trustOnly = @Nullsafe.TrustList({Strict_NoDeps_NoPromos.class})) -class TrustSome_TrustStrictIsNotNeeded_CanBePromotedToStrict { - static String f() { - return Strict_NoDeps_NoPromos.f(); - } -} - -@Nullsafe(value = Nullsafe.Mode.LOCAL, trustOnly = @Nullsafe.TrustList({})) -class TrustNone_CanBePromotedToStrict { - static String f() { - return Strict_NoDeps_NoPromos.f(); - } -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/MyPreconditions.java b/infer/tests/codetoanalyze/java/nullsafe/MyPreconditions.java deleted file mode 100644 index 35b31a92be7..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/MyPreconditions.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * 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; - -import javax.annotation.Nullable; - -public class MyPreconditions { - - public static native T checkNotNull(@Nullable T t); - - public static native void checkState(boolean expression); - - public static native void checkArgument(boolean expression); -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/NestedFieldAccess.java b/infer/tests/codetoanalyze/java/nullsafe/NestedFieldAccess.java deleted file mode 100644 index fcd3ef95598..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/NestedFieldAccess.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * 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; - -import javax.annotation.Nullable; - -public class NestedFieldAccess { - - class C { - @Nullable String s; - } - - class CC { - @Nullable C c; - } - - class CCC { - @Nullable CC cc; - } - - /** - * Tests nullability check patterns for f1.f2.f3, when all components in the chain are nullable. - * (it should require checking of all components in the chain) - */ - public class TestNullableChains { - @Nullable String s; - C myc; - - TestNullableChains() { - myc = new C(); - } - - void field_AccessAfterNullCheckIsOK() { - if (s != null) { - int n = s.length(); - } - } - - void field_AccessWithoutNullCheckIsBad() { - int n = s.length(); - } - - void nestedField_AccessAfterNullCheckIsOK() { - if (myc.s != null) { - int n = myc.s.length(); - } - } - - void nestedField_AccessWithoutNullCheckIsBad() { - int n = myc.s.length(); - } - - void param_AccessAfterNullCheckIsOK(C c) { - if (c.s != null) { - int n = c.s.length(); - } - } - - void param_AccessWithoutNullCheckIsBad(C c) { - int n = c.s.length(); - } - - void local_AccessAfterNullCheckIsOK() { - C c = new C(); - if (c.s != null) { - int n = c.s.length(); - } - } - - void local_AccessWithoutNullCheckIsBad() { - C c = new C(); - int n = c.s.length(); - } - - void deep_AccessWithNullCheckIsOK(CC cc) { - if (cc.c != null && cc.c.s != null) { - int n = cc.c.s.length(); - } - } - - void deep_AccessWithoutNullCheckIsBad(CC cc) { - if (cc.c != null /* && cc.c.s != null */) { - int n = cc.c.s.length(); - } - } - - void veryDeep_AccessWithNullCheckIsOK(CCC ccc) { - if (ccc.cc != null && ccc.cc.c != null && ccc.cc.c.s != null) { - int n = ccc.cc.c.s.length(); - } - } - - void veryDeep_AccessWithoutNullCheckIsBad(CCC ccc) { - if (ccc.cc != null && ccc.cc.c != null /* && ccc.cc.c.s != null */) { - int n = ccc.cc.c.s.length(); - } - } - - void veryDeep_AccessViaOrEarlyReturnIsOK(@Nullable CCC ccc) { - if (ccc == null || ccc.cc == null || ccc.cc.c == null || ccc.cc.c.s == null) { - } else { - int n = ccc.cc.c.s.length(); - } - } - - void veryDeep_IncompleteAccessViaOrEarlyReturnIsBad(@Nullable CCC ccc) { - if (ccc == null || ccc.cc == null || ccc.cc.c == null /*|| ccc.cc.c.s == null*/) { - } else { - int n = ccc.cc.c.s.length(); - } - } - } - - /** - * Tests nullability patterns for chains a().a().a().a().nullable(). Basically nullsafe needs to - * realize that objects returned by a().a() and a().a().a() are different so it should not learn - * anything about the nullability of one based on evidence about the other one. - */ - class TestFunctionsIdempotent { - @Nullable String s; - String dontAssignNull = ""; - - @Nullable - String nullable(int n) { - return s; - } - - TestFunctionsIdempotent getSelf() { - return this; - } - - void chainOf0VsChainOf0IsOK() { - if (nullable(3) != null) { - dontAssignNull = nullable(3); - } - } - - void chainOf0VsChainOf0ParamsMismatchIsBad() { - if (nullable(3) != null) { - dontAssignNull = nullable(4); - } - } - - void otherObjVsItselfIsOK(TestFunctionsIdempotent otherObj) { - if (otherObj.nullable(3) != null) { - dontAssignNull = otherObj.nullable(3); - } - } - - void otherObjVsItselfIsOKParamsMismatchIsBAD(TestFunctionsIdempotent otherObj) { - if (otherObj.nullable(3) != null) { - dontAssignNull = otherObj.nullable(4); - } - } - - void selfVsOtherObjectIsBAD(TestFunctionsIdempotent otherObj) { - if (otherObj.nullable(3) != null) { - dontAssignNull = nullable(3); - } - } - - void chainOf0VsChainOf1IsBad() { - if (nullable(3) != null) { - dontAssignNull = getSelf().nullable(3); - } - } - - void chainOf1VsChainOf0IsBad() { - if (getSelf().nullable(3) != null) { - dontAssignNull = nullable(3); - } - } - - void chainOf1VsChainOf1IsOK() { - if (getSelf().nullable(3) != null) { - dontAssignNull = getSelf().nullable(3); - } - } - - void chainOf1VsChainOf1ParamMismatchIsBad() { - if (getSelf().nullable(3) != null) { - dontAssignNull = getSelf().nullable(4); - } - } - - void chainOf2VsChainOf2IsOK() { - if (getSelf().getSelf().nullable(3) != null) { - dontAssignNull = getSelf().getSelf().nullable(3); - } - } - - void chainOf1VsChainOf2IsBad() { - if (getSelf().nullable(3) != null) { - dontAssignNull = getSelf().getSelf().nullable(3); - } - } - - void chainOf2VsChainOf1IsBad() { - if (getSelf().getSelf().nullable(3) != null) { - dontAssignNull = getSelf().nullable(3); - } - } - } -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/NoReuseUndefFunctionValues.java b/infer/tests/codetoanalyze/java/nullsafe/NoReuseUndefFunctionValues.java deleted file mode 100644 index eea936900fc..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/NoReuseUndefFunctionValues.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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; - -import javax.annotation.Nullable; - -public class NoReuseUndefFunctionValues { - - Object mObject1; - Object mObject2; - - native Object create(); - - public NoReuseUndefFunctionValues(@Nullable Object object) { - if (object != null) { - this.mObject1 = object; - } else { - this.mObject1 = this.create(); - } - if (object != null) { - this.mObject2 = object; - } else { - this.mObject2 = this.create(); - } - } -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/NullFieldAccess.java b/infer/tests/codetoanalyze/java/nullsafe/NullFieldAccess.java deleted file mode 100644 index 25c5a9e000e..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/NullFieldAccess.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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; - -import javax.annotation.Nullable; - -public class NullFieldAccess { - - interface I { - @Nullable Object nullable = new Object(); - Object notNull = new Object(); - } - - @Nullable Object nullable; - Object notNull; - - static final @Nullable Object nullableStatic = new Object(); - static final Object notNullStatic = new Object(); - - @Nullable Object[] nullableArray; - Object[] notNullArray; - - NullFieldAccess() { - nullable = new Object(); - notNull = new Object(); - nullableArray = new Object[1]; - notNullArray = new Object[1]; - } - - void testNonStaticFields() { - Object bad = nullable; - bad.toString(); // BAD: `bad` can be null - - Object good = notNull; - good.toString(); // OK: `good` is not null - } - - void testStatic() { - Object bad = nullableStatic; - bad.toString(); // BAD: `bad` can be null - - Object good = notNullStatic; - good.toString(); // OK: `good` is not null - } - - void testInterface() { - Object bad = I.nullable; - bad.toString(); // BAD: `bad` can be null - - Object good = I.notNull; - good.toString(); // OK: `good` is not null - } - - void testArray() { - int i1 = nullableArray.length; // BAD: array can be null - Object o1 = nullableArray[0]; // BAD: array can be null - - int i2 = notNullArray.length; // OK: arrays is not null - Object o2 = notNullArray[0]; // OK: array is not null - } - -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/NullMethodCall.java b/infer/tests/codetoanalyze/java/nullsafe/NullMethodCall.java deleted file mode 100644 index 55b844a04ba..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/NullMethodCall.java +++ /dev/null @@ -1,369 +0,0 @@ -/* - * 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; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import com.facebook.infer.annotation.Assertions; -import com.google.common.base.Preconditions; -import com.google.common.base.Verify; -import java.nio.file.Paths; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import javax.annotation.Nullable; - -public class NullMethodCall { - - void callOnNull() { - String s = null; - int n = s.length(); - } - - void callOnEmptyString() { - String s = ""; - int n = s.length(); - } - - void callAfterYodaCondition(@Nullable String s) { - if (null != s) { - int n = s.length(); - } - } - - int objectLength(@Nullable Object o) { - if (o instanceof String) { - String s = (String) o; - return s.length(); // OK: s cannot be null because of instanceof - } - return 0; - } - - int testCheckState(@Nullable String s1, @Nullable String s2) { - Preconditions.checkState(s1 != null && s2 != null, "bad"); - return s1.length() + s2.length(); - } - - int testPrivateStaticInnerClassField() { - String s; - S.sfld = "abc"; - s = S.sfld; - return s.length(); - } - - private static class S { - private static @Nullable String sfld; - } - - @Nullable String fld; - private @Nullable String pfld; - - public class Inner { - int outerField() { - String s = fld; - return s.length(); - } - - int outerFieldInitialized() { - fld = "abc"; - String s = fld; - return s.length(); - } - - int outerPrivateField() { - String s = pfld; - return s.length(); - } - - int outerPrivateFieldInitialized() { - pfld = "abc"; - String s = pfld; - return s.length(); - } - - int outerPrivateFieldCheckNotNull() { - Preconditions.checkNotNull(pfld); - String s = pfld; - return s.length(); - } - - int outerPrivateFieldCheckState() { - Preconditions.checkState(pfld != null); - String s = pfld; - return s.length(); - } - - int outerPrivateFieldAssertNotNull() { - Assertions.assertNotNull(pfld); - String s = pfld; - return s.length(); - } - - int outerPrivateFieldAssumeNotNull() { - Assertions.assumeNotNull(pfld); - String s = pfld; - return s.length(); - } - - int outerPrivateFieldAssertCondition() { - Assertions.assertCondition(pfld != null, "explanation"); - String s = pfld; - return s.length(); - } - - int outerPrivateFieldAssumeCondition() { - Assertions.assumeCondition(pfld != null, "explanation"); - String s = pfld; - return s.length(); - } - - int outerPrivateFieldCheckStateYoda() { - Preconditions.checkState(null != pfld); - String s = pfld; - return s.length(); - } - - String outerFieldGuardPrivate() { - if (pfld != null) return pfld.toString(); - return ""; - } - - String outerFieldGuardPublic() { - if (fld != null) return fld.toString(); - return ""; - } - - public class InnerInner { - int outerouterPrivateFieldInitialized() { - pfld = "abc"; - String s = pfld; - return s.length(); - } - } - } - - @Nullable - String getNullable() { - return null; - } - - void testVariableAssigmentInsideConditional() { - String s = null; - if ((s = getNullable()) != null) { - int n = s.length(); - } - } - - void testFieldAssigmentInsideConditional() { - if ((fld = getNullable()) != null) { - int n = fld.length(); - } - } - - String abc = "abc"; - - void testFieldAssignmentIfThenElse(String name) { - String s = (name.length() == 0) ? null : abc; - int n = s.length(); - } - - static String throwsExn() throws java.io.IOException { - throw new java.io.IOException(); - } - - void testExceptionPerInstruction(int z) throws java.io.IOException { - String s = null; - - try { - s = throwsExn(); - } finally { - int n = s.length(); - } - } - - public class InitializeAndExceptions { - String s; - - String bad() throws java.io.IOException { - throw new java.io.IOException(); - } - - InitializeAndExceptions() throws java.io.IOException { - s = bad(); // should not report field not initialized - } - } - - public class InitializeViaPrivateMethod { - String name; - - private void reallyInitName(String s) { - name = s; - } - - private void initName(String s) { - reallyInitName(s); - } - - InitializeViaPrivateMethod() { - initName("abc"); - } - } - - class CheckNotNullVararg { - void checkNotNull(String msg, Object... objects) {} - - void testCheckNotNullVaratg(@Nullable String s1, @Nullable String s2) { - checkNotNull("hello", s1, s2); - s1.isEmpty(); - s2.isEmpty(); - } - - void testRepeatedCheckNotNull(@Nullable String s) { - checkNotNull("abc", s); - checkNotNull("abc", s.toString()); - s.toString().isEmpty(); - } - } - - public void testSystemGetPropertyReturn() { - String s = System.getProperty(""); - int n = s.length(); - } - - int testSystemGetenvBad() { - String envValue = System.getenv("WHATEVER"); - return envValue.length(); - } - - class SystemExitDoesNotReturn { - native boolean whoknows(); - - void testOK() { - String s = null; - if (whoknows()) { - s = "a"; - } else { - System.exit(1); - } - int n = s.length(); - } - } - - public void testMapGetBad( - Map m, HashMap hm, ConcurrentHashMap chm) { - m.get("foo").toString(); - hm.get("foo").toString(); - chm.get("foo").toString(); - } - - public void testMapRemoveBad( - Map m, HashMap hm, ConcurrentHashMap chm) { - m.remove("foo").toString(); - hm.remove("foo").toString(); - chm.remove("foo").toString(); - } - - @Nullable Object nullableField; - - void FP_propagatesNonNullAfterComparisonFieldOkay(Object nonNullObject) { - if (nullableField == nonNullObject) { - nullableField.toString(); - } - } - - void FP_propagatesNonNullAfterComparisonParameterOkay( - @Nullable Object nullableParameter, Object nonNullParameter) { - if (nullableParameter == nonNullParameter) { - nullableParameter.toString(); - } - } - - String customPreconditionsCheckNotNullOkay() { - MyPreconditions.checkNotNull(nullableField); - return nullableField.toString(); - } - - String customPreconditionsCheckStateOkay() { - MyPreconditions.checkState(nullableField != null); - return nullableField.toString(); - } - - String customPreconditionsCheckArgumentOkay(@Nullable Object arg) { - MyPreconditions.checkState(arg != null); - return arg.toString(); - } - - void nullMethodCallWithAlarmManager(AlarmManager manager, @Nullable PendingIntent intent) { - manager.cancel(intent); - } - - String callingSeverSideNullableGetter(ServerSideDeserializer deserializer) { - return deserializer.nullableGetter().toString(); - } - - interface AnotherI { - void withBooleanParameter(boolean test); - - void withObjectParameter(Object object); - } - - void withConjuction(@Nullable AnotherI i, boolean test1, boolean test2) { - i.withBooleanParameter(test1 && test2); - } - - void withConditionalAssignemnt( - @Nullable AnotherI i, boolean test, Object object1, Object object2) { - i.withObjectParameter(test ? object1 : object2); - } - - String assertGetOnMapOK(Map map, Integer key) { - return Assertions.assertGet(key, map).toString(); // No warning here - } - - String assertGetOnListOK(List list, int index) { - return Assertions.assertGet(index, list).toString(); // No warning here - } - - String guavaVerifyNotNullOK(@Nullable Object object) { - Verify.verifyNotNull(object); - return object.toString(); - } - - void nullabilityNotPreservedAfterAssignment() { - if (getNullable() != null) { - Object t = getNullable(); - t.toString(); // Should not warn here - } - } - - void nullabilityStoredInBooleanFP() { - boolean isNotNull = getNullable() != null; - if (isNotNull) { - getNullable().toString(); // Should not warn here - } - } - - void testInAssignmentOK(@Nullable Object object) { - Object t; - while ((t = getNullable()) != null) { - t.toString(); // Should not warn here - } - } - - String testPathGetParent() { - return Paths.get("foo").getParent().toString(); - } - - String testNotDetectingInvariantFP(@Nullable Object object1, @Nullable Object object2) { - if (object1 == null && object2 == null) { - return "both null"; - } - return object1 == null ? object2.toString() : "null"; - } -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/NullsafeMode.java b/infer/tests/codetoanalyze/java/nullsafe/NullsafeMode.java deleted file mode 100644 index 083ac8c6f22..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/NullsafeMode.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * 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; - -import com.facebook.infer.annotation.Nullsafe; -import com.facebook.infer.annotation.NullsafeStrict; -import java.util.concurrent.TimeUnit; -import javax.annotation.Nullable; -import some.test.pckg.ThirdPartyTestClass; - -public class NullsafeMode { - abstract static class VariousMethods { - public String returnVal() { - return "OK"; - } - - @Nullable - public String returnNull() { - return null; - } - } - - static class NonNullsafe extends VariousMethods { - public String valField = "OK"; - - String OK_passUncheckedToLocal(String arg) { - return new TrustAllNullsafe().acceptVal(arg); - } - - String OK_passUncheckedToStrictMode(String arg) { - return new NullsafeWithStrictMode().acceptVal(arg); - } - - String OK_passUncheckedToStrict(String arg) { - return new StrictNullsafe().acceptVal(arg); - } - - void OK_passNullableToThirdPartyParam() { - new ThirdPartyTestClass().paramUnspecified(returnNull()); - return; - } - - @Override - public String returnVal() { - return super.returnVal(); - } - } - - static class AnotherNonNullsafe extends VariousMethods {} - - static class UncheckedParams { - public long mDelay; - - public UncheckedParams(long delay) { - mDelay = delay; - } - - public UncheckedParams(UncheckedParams other) { - mDelay = other.mDelay; - } - - public UncheckedParams copy() { - return new UncheckedParams(this); - } - - public UncheckedParams(ThirdPartyTestClass.UncheckedLong delay) { - mDelay = delay.mInner; - } - } - - @Nullsafe(Nullsafe.Mode.LOCAL) - static class TrustAllNullsafe extends VariousMethods { - public String acceptVal(String arg) { - return arg; - } - - String OK_returnFromAnyNonNullsafe() { - String a = new NonNullsafe().returnVal(); - String b = new AnotherNonNullsafe().returnVal(); - return a.concat(b); - } - - String BAD_returnNullFromNonNulsafe() { - return (new NonNullsafe()).returnNull(); - } - - String BAD_returnFromUnvettedThirdParty() { - return new ThirdPartyTestClass().returnUnspecified(); - } - - String BAD_returnNullableFieldFromThirdParty() { - return new ThirdPartyTestClass().nullableField; - } - - String BAD_returnNonNullableFieldFromThirdParty() { - return new ThirdPartyTestClass().nonNullableField; - } - - String OK_passLocalToStrictMode(String arg) { - return new NullsafeWithStrictMode().acceptVal(arg); - } - - String OK_passLocalToStrict(String arg) { - return new StrictNullsafe().acceptVal(arg); - } - - UncheckedParams BAD_passThirdPartyToUnchecked() { - return new UncheckedParams(ThirdPartyTestClass.getUncheckedLong(42)); - } - - UncheckedParams OK_passUncheckedToUnchecked() { - UncheckedParams first = new UncheckedParams(42); - UncheckedParams second = new UncheckedParams(first.copy()); - return second; - } - - int OK_enumElementsAreNotNull() { - return ThirdPartyTestClass.InnerEnum.EA.ordinal(); - } - } - - @Nullsafe(value = Nullsafe.Mode.LOCAL, trustOnly = @Nullsafe.TrustList({NonNullsafe.class})) - static class TrustSomeNullsafe extends VariousMethods { - @Override - public String returnVal() { - return "OK"; - } - - String OK_returnFromTrustedNonNullsafe() { - return new NonNullsafe().returnVal(); - } - - String BAD_returnFromUntrustedNonNullsafe() { - return new AnotherNonNullsafe().returnVal(); - } - - @Nullable - String OK_returnFromUntrustedNonNullsafeAsNullable() { - return new AnotherNonNullsafe().returnVal(); - } - - String BAD_returnNullFromNonNulsafe() { - return new NonNullsafe().returnNull(); - } - - String FP_OK_accessFieldFromNonNullsafe() { - return new NonNullsafe().valField; - } - } - - @Nullsafe(value = Nullsafe.Mode.LOCAL, trustOnly = @Nullsafe.TrustList({})) - static class TrustNoneNullsafe extends VariousMethods { - String BAD_returnFromNonNullsafe() { - return new NonNullsafe().returnVal(); - } - - String OK_returnFromNullsafe() { - return new TrustSomeNullsafe().returnVal(); - } - } - - @Nullsafe(Nullsafe.Mode.STRICT) - static class NullsafeWithStrictMode extends VariousMethods { - @Override - public String returnVal() { - return "OK"; - } - - public String acceptVal(String arg) { - return arg; - } - - String BAD_returnFromNonStrict() { - return new TrustNoneNullsafe().returnVal(); - } - - String OK_returnFromNullsafeStrict() { - return new StrictNullsafe().returnVal(); - } - } - - @NullsafeStrict - static class StrictNullsafe extends VariousMethods { - private static final UncheckedParams PARAMS = - new UncheckedParams(TimeUnit.MINUTES.toMillis(42)); - - @Override - public String returnVal() { - return "OK"; - } - - public String acceptVal(String arg) { - return arg; - } - - String BAD_returnFromNonNullsafe() { - return new NonNullsafe().returnVal(); - } - - String OK_returnFromNullsafeWithStrictMode() { - return new NullsafeWithStrictMode().returnVal(); - } - - long OK_callMethodsOnThirdPartyEnumValues() { - return TimeUnit.MINUTES.toMillis(42); - } - - long OK_passResultOfCallingThirdPartyToStrict() { - return PARAMS.mDelay; - } - - UncheckedParams BAD_passThirdPartyToUnchecked() { - return new UncheckedParams(ThirdPartyTestClass.getUncheckedLong(42)); - } - - void BAD_dereferenceNotAnnotatedThirdParty() { - (new ThirdPartyTestClass()).returnUnspecified().toString(); - } - - void OK_dereferenceExplicitlyAnnotatedThirdParty() { - (new ThirdPartyTestClass()).returnExplicitlyAnnotated().toString(); - } - } -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java b/infer/tests/codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java deleted file mode 100644 index e6fa91159bd..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * 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; - -import com.facebook.infer.annotation.Nullsafe; - -/** Test to ensure we correctly evaluate mode for nested classes */ -@Nullsafe(Nullsafe.Mode.LOCAL) -class NullsafeLocal { - - public String shouldBeNullsafeModeError() { - return null; - } - - // Mode should be inherited from the parent class - class Nested { - public String shouldBeNullsafeModeError() { - return null; - } - - // Mode propagation should be transitive - class DeeplyNested { - public String shouldBeNullsafeModeError() { - return null; - } - } - - // This is Local, but not Strict mode - public String returningDefaultNotNullIsOK() { - return Default.getString(); - } - } - - // It is OK to make nested classes more strict - @Nullsafe(Nullsafe.Mode.STRICT) - class NestedStrict { - public String returningDefaultNotNullIsError() { - return Default.getString(); - } - } - - // No need to repeat the mode - it is redundant - @Nullsafe(Nullsafe.Mode.LOCAL) - class NestedExplicitLocal { - public String shouldBeNullsafeModeError() { - return null; - } - } -} - -@Nullsafe(Nullsafe.Mode.STRICT) -class NullsafeStrict { - public String returningDefaultNotNullIsError() { - return Default.getString(); - } - - // STRICT mode is propagated to the nested class - class Nested { - public String returningDefaultNotNullIsError() { - return Default.getString(); - } - - // Impossible to downgrade the level of nested class, even if the nested mode - // is implicit - @Nullsafe(Nullsafe.Mode.LOCAL) - class DeeplyNestedLocalIsStillStrict { - public String returningDefaultNotNullIsError() { - return Default.getString(); - } - } - } - - // Impossible to downgrade the level of nested class - @Nullsafe(Nullsafe.Mode.LOCAL) - class NestedLocalIsStillStrict { - public String returningDefaultNotNullIsError() { - return Default.getString(); - } - } -} - -class Default { - public static String getString() { - return ""; - } - - // OK for nested to be @Nullsafe but the outer is not - @Nullsafe(Nullsafe.Mode.LOCAL) - class NestedLocal { - public String shouldBeNullsafeModeError() { - return null; - } - - // This is Local, but not Strict mode - public String returningDefaultNotNullIsOK() { - return Default.getString(); - } - - // And we can increase strictness even more - @Nullsafe(Nullsafe.Mode.STRICT) - class DeeplyNestedStrict { - public String returningDefaultNotNullIsError() { - return Default.getString(); - } - } - } -} - -class A { - public static String getString() { - return ""; - } -} - -class B {} - -class C { - public static String getString() { - return ""; - } -} - -@Nullsafe(value = Nullsafe.Mode.LOCAL, trustOnly = @Nullsafe.TrustList({A.class, B.class})) -class TrustSome { - - public String trustA_OK() { - return A.getString(); - } - - public String dontTrustC_Bad() { - return C.getString(); - } - - // Inherits mode from the outer, the same trust - class NotAnnotatedNested { - public String trustA_OK() { - return A.getString(); - } - - public String dontTrustC_Bad() { - return C.getString(); - } - } - - // This class does not trust A anymore - @Nullsafe(value = Nullsafe.Mode.LOCAL, trustOnly = @Nullsafe.TrustList({B.class})) - class CanRemoveFromTrustList { - public String dontTrustA_BAD() { - return A.getString(); - } - } - - // Lousy attempt to add a class C to trust list - // Should have a special issue suggesting to remove C from the list. - @Nullsafe(value = Nullsafe.Mode.LOCAL, trustOnly = @Nullsafe.TrustList({A.class, C.class})) - class CanNotAddToTrustList { - public String stillDontTrustC_BAD() { - return C.getString(); - } - } -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/ParameterNotNullable.java b/infer/tests/codetoanalyze/java/nullsafe/ParameterNotNullable.java deleted file mode 100644 index cc374207593..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/ParameterNotNullable.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * 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; - -import android.annotation.SuppressLint; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import java.net.URL; -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; -import javax.annotation.Nullable; - -public class ParameterNotNullable { - - boolean field = false; - - ParameterNotNullable() { - testPrimitive(field); - } - - void testPrimitive(boolean f) {} - - void test(String s) { - int n = s.length(); - } - - void testN(@Nullable String s) { - int n = s != null ? s.length() : 0; - } - - void callNull() { - String s = null; - test(s); - } - - @SuppressLint("ERADICATE_PARAMETER_NOT_NULLABLE") - void callNullSuppressed() { - String s = null; - test(s); - } - - void callNullable(@Nullable String s) { - test(s); - } - - void callNullOK() { - String s = null; - testN(s); - } - - void callNullableOK(@Nullable String s) { - testN(s); - } - - private ParameterNotNullable(@Nullable String s) {} - - class Builder { - ParameterNotNullable getEradicateParameterNotNullable() { - return new ParameterNotNullable(null); - } - } - - public @Nullable String testSystemGetPropertyArgument() { - String s = System.getProperty(null); - return s; - } - - @Nullable - String testSystemGetenvBad() { - return System.getenv(null); - } - - static @Nullable URL testClassGetResourceArgument(Class cls) { - return cls.getResource(null); - } - - void threeParameters(String s1, String s2, String s3) {} - - void testThreeParameters() { - String s = ""; - threeParameters(null, s, s); - threeParameters(s, null, s); - threeParameters(s, s, null); - } - - class ConstructorCall { - ConstructorCall(int x, String s) {} - - ConstructorCall() { - this(3, ""); // OK - } - - ConstructorCall(int x) { - this(3, null); // NPE - } - } - - void indirectSignatureLookupOk(SomeClass c) { - c.acceptsNullableParameter(null); - } - - void doesNotAcceptNullableFirstParameter(Object object, boolean test) {} - - void callWithNullableFirstParameter(boolean t1, boolean t2) { - doesNotAcceptNullableFirstParameter(null, t1 && t2); - } - - void callWithConditionalAssignment(Object object, boolean test) { - doesNotAcceptNullableFirstParameter(test ? object : null, test); - } - - void testImmutableListOfnotNullArguments() { - - Object notNull = new Object(); - - ImmutableList.of(null); - ImmutableList.of(null, null); - ImmutableList.of(notNull, notNull); - ImmutableList.of(notNull, null); - } - - void testImmutableListCopyOfNotNullArguments() { - - Iterable nullIterable = null; - Iterator nullIterator = null; - Collection nullCollection = null; - - ImmutableList.copyOf(nullIterable); - ImmutableList.copyOf(nullIterator); - ImmutableList.copyOf(nullCollection); - } - - void testImmutableListSortedCopyOfNotNullArguments() { - ImmutableList.sortedCopyOf(null, null); - } - - void testImmutableSetOfnotNullArguments() { - - Object notNull = new Object(); - - ImmutableSet.of(null); - ImmutableSet.of(null, null); - ImmutableSet.of(notNull, notNull); - ImmutableSet.of(notNull, null); - ImmutableSet.of(notNull, null, notNull, null, notNull); - } - - void testImmutableSetCopyOfNotNullArguments() { - - Iterable nullIterable = null; - Iterator nullIterator = null; - Collection nullCollection = null; - - ImmutableSet.copyOf(nullIterable); - ImmutableSet.copyOf(nullIterator); - ImmutableSet.copyOf(nullCollection); - } - - void testImmutableMapOfnotNullArguments() { - - Object notNull = new Object(); - - ImmutableMap.of(null, null); - ImmutableMap.of(notNull, notNull); - ImmutableMap.of(notNull, null, notNull, null); - } - - void testImmutableMapCopyOfNotNullArguments() { - - Iterable nullIterable = null; - Map nullMap = null; - - ImmutableMap.copyOf(nullIterable); - ImmutableMap.copyOf(nullMap); - } - - void testParsingNullStringToNumber() { - String ns = null; - long l = Long.parseLong(ns); - int i = Integer.parseInt(ns); - } -} - -interface SomeInterface { - void acceptsNullableParameter(@Nullable Object object); -} - -abstract class SomeClass implements SomeInterface {} diff --git a/infer/tests/codetoanalyze/java/nullsafe/PropagatesNullable.java b/infer/tests/codetoanalyze/java/nullsafe/PropagatesNullable.java deleted file mode 100644 index 44d6f03af12..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/PropagatesNullable.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * 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; - -import com.facebook.infer.annotation.PropagatesNullable; -import javax.annotation.Nullable; - -// Tests for the annotation @PropagatesNullable -class TestPropagatesNullable { - - class TestOneParameter { - - // means return null iff s is null - String propagatesNullable(@PropagatesNullable String s) { - return s; - } - - @Nullable - String nullable(@Nullable String s) { - return s; - } - - void test(@Nullable String sNullable, String sNonnull) { - // null constant - propagatesNullable(null).length(); // BAD: will be NPE - nullable(null).length(); // BAD: result might be null - - // nullable - propagatesNullable(sNullable).length(); // BAD: result can be null - nullable(sNullable).length(); // BAD: result can be null - - // string literal - propagatesNullable("").length(); // OK: result can not be null - nullable("").length(); // BAD: typechecker can not figure out that this is safe - - // nonnull - propagatesNullable(sNonnull).length(); // OK: result can not be null - nullable(sNonnull).length(); // BAD: typechecker can not figure out that this is safe - - // flow sensitive nonnull FALSE POSITIVE - if (sNullable != null) { - // here we know that sNullable is not null, so it should be safe, but it is not the case - // TODO(T53770056) fix it. - propagatesNullable(sNullable).length(); - nullable(sNullable).length(); - } - } - - // limitation: we currently cannot check the body, and just trust the annotation - String cannotCheckBody(@PropagatesNullable String s) { - return null; // nothing is reported here - } - - void illustrateFalseNegativeAsCannotCheckBody() { - cannotCheckBody("").length(); // this is an NPE but is not found - } - } - - class TestSecondParameter { - // means return null iff s2 is null - String propagatesNullable(@Nullable String s1, @PropagatesNullable String s2) { - return s2; - } - - @Nullable - String nullable(@Nullable String s1, @Nullable String s2) { - return s2; - } - - // Let's ensure that @PropagatesNullable applies only to the parameter - // that was annotated with it, and not to other params. - void test(@Nullable String sNullable, String sNonnull) { - // Both nullable - propagatesNullable(sNullable, sNullable).length(); // BAD: result can be null - nullable(sNullable, sNullable).length(); // BAD: result can be null - - // First is nonnull, second is nullable - propagatesNullable(sNonnull, sNullable).length(); // BAD: result can be null - nullable(sNonnull, sNullable).length(); // BAD: result can be null - - // First is nullable, second is nonnull - propagatesNullable(sNullable, sNonnull).length(); // OK: result can not be null - nullable(sNullable, sNonnull).length(); // BAD: typechecker can not figure this out - - // Both nonnullable - propagatesNullable(sNonnull, sNonnull).length(); // OK: result can not be null - nullable(sNonnull, sNonnull).length(); // BAD: typechecker can not figure this out - } - } - - class TestBothParams { - // both parameters are annotated: - // means return null iff either s1 or s2 is null - String propagatesNullable(@PropagatesNullable String s1, @PropagatesNullable String s2) { - return s1 == null ? s1 : s2; - } - - void testBothParamsShouldBeNonnull(@Nullable String sNullable, String sNonnull) { - propagatesNullable(sNullable, sNullable).length(); // BAD: result can be null - propagatesNullable(sNonnull, sNullable).length(); // BAD: result can be null - propagatesNullable(sNullable, sNonnull).length(); // BAD: result can be null - propagatesNullable(sNonnull, sNonnull).length(); // OK: result can be null - } - } - - // For convenience, we do not require to annotate return type with @Nullable, - // make sure it is respected. - class TestReturnValueAnnotationIsAutomaticallyInferred { - - // Ensure that we do not warn with "return not nullable" even if we did not annotate the - // return with @Nullable - - String notAnnotatingReturnWhenThereIsPropagatesNullableIsOK(@PropagatesNullable String s) { - return null; // OK: treat is as implicitly nullable - } - - String notAnnotatingReturnWhenThereAreNoPropagatesNullableIsBAD(@Nullable String s) { - return null; // BAD: return not nullable - } - - // Ensure that the behavior remains the same for explicitly and implicitly annotated functions - - @Nullable - String annotatedReturn(@PropagatesNullable String s) { - return s; - } - - String notAnnotatedReturn(@PropagatesNullable String s) { - return s; - } - - // 1. Both versions equally catch non-legit usages - - void annotated_dereferencingAfterPassingNullableIsBAD(@Nullable String s) { - annotatedReturn(s).toString(); // BAD: nullable dereference - } - - void notAnnotated_dereferencingAfterPassingNullableIsBAD(@Nullable String s) { - notAnnotatedReturn(s).toString(); // BAD: nullable dereference - } - - // 2. Both versions equally allow legit usages - - void annotated_dereferencingAfterPassingNonnullIsOK(String s) { - annotatedReturn(s).toString(); // OK: inferred to be non-nullable - } - - void notAnnotated_dereferencingAfterPassingNonnullIsOK(String s) { - notAnnotatedReturn(s).toString(); // OK: inferred to be non-nullable - } - } - } diff --git a/infer/tests/codetoanalyze/java/nullsafe/ReturnNotNullable.java b/infer/tests/codetoanalyze/java/nullsafe/ReturnNotNullable.java deleted file mode 100644 index a0330e22106..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/ReturnNotNullable.java +++ /dev/null @@ -1,256 +0,0 @@ -/* - * 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; - -import com.google.common.base.Optional; -import java.io.BufferedReader; -import java.io.FileReader; -import java.io.IOException; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.stream.Stream; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class ReturnNotNullable { - - void returnvoid() { - // No warning here. - } - - Void returnVoid() { - // This is OK too. - return null; - } - - // ------------------------------------------------------------ - // Converting different things to not annotated types. - // By default, (not annotated type is treated as non nullable). - - String nullToNotAnnotatedIsBad() { - return null; - } - - String nullableToNotAnnotatedIsBad(@Nullable String s) { - return s; - } - - String notAnnotatedToNotAnnotatedIsOK(String s) { - return s; - } - - String nonNullToNotAnnotatedIsOK(@Nonnull String s) { - return s; - } - - String constantToNotAnnotatedIsOK() { - return "abc"; - } - - // ------------------------------------------------------------ - // Converting different things to @Nonnull. - // (Should be the same as converting to not annotated). - - @Nonnull - String nullToNonnullIsBad() { - return null; - } - - @Nonnull - String nullableToNonnullIsBad(@Nullable String s) { - return s; - } - - @Nonnull - String notAnnotatedToNonnullIsOK(String s) { - return s; - } - - @Nonnull - String nonNullToNonnullIsOK(@Nonnull String s) { - return s; - } - - @Nonnull - String constantToNonNullIsOK() { - return "abc"; - } - - // ------------------------------------------------------------ - // Converting different things to @Nullable. - // This is either - // 1. OK when inferred and annotated return types are both nullable, or - // 2. Leads to ERADICATE_RETURN_OVER_ANNOTATED when inferred return type - // is not nullable, but function is still annotated with @Nullable. - // This often happens when the API author decides to return @Nullable - // (anticipating future change) even though the current implementation returns non-null. - // Because of this the warning is currently turned off by default and is recommended - // to use only in specific scenarious, like code migrations. - - @Nullable - String nullToNullableIsOK() { - return null; - } - - @Nullable - String nullableToNullableIsOK(@Nullable String s) { - return s; - } - - @Nullable - String notAnnotatedNullableIsOverannotated(String s) { - return s; - } - - @Nullable - String nonNullToNullableIsOverannotated(@Nonnull String s) { - return s; - } - - @Nullable - String constantToNullableIsOverannotated() { - return "abc"; - } - - // ------------------------------------------------------- - - String throwException(@Nullable Exception e, boolean bad) throws Exception { - if (bad) { - throw (e); // no ERADICATE_RETURN_NOT_NULLABLE should be reported - } - return "OK"; - } - - @Nonnull - BufferedReader nn(BufferedReader br) { - return br; - } - - - /* - Check that orNull is modelled and RETURN_OVER_ANNOTATED is not returned. - */ - @Nullable - String testOptional(Optional os) { - return os.orNull(); - } - - class E extends Exception {} - - String return_null_in_catch() { - try { - throw new E(); - } catch (E e) { - return null; - } - } - - String return_null_in_catch_after_throw() { - try { - try { - throw new E(); - } catch (E e) { - throw e; - } - } catch (E e) { - return null; - } - } - - URL getResourceNullable(Class cls, String name) { - return cls.getResource(name); - } - - @SomeAnnotationEndingWithNullable - Object ensureWeDontConfuseSuchAnnotationsWithNullable() { - // No warnings expected - return new Object(); - } - - void testSomeAnnotationEndingWithNullable() { - // No warnings expected - ensureWeDontConfuseSuchAnnotationsWithNullable().toString(); - } - - static class ConditionalAssignment { - @Nullable Object f1; - - static Object test(boolean b) { - ConditionalAssignment x = new ConditionalAssignment(); - if (b) { - x.f1 = new Object(); - } - return x.f1; // can be null - } - } - - Stream methodUsesLambda(Stream stream) { - return stream.map(x -> null); // Intentionaly not reporting here - } - - Object $generatedReturnsNullOk() { - return null; - } - - int field; - - int returnsZero() { - field = 0; - return field; - } - - static class AssignmentResultCheck { - public Throwable nullCheckAssignmentResultAsNonnullOk(Throwable error) { - Throwable cause; - while ((cause = error.getCause()) != null) { - error = cause; - } - - return error; - } - - // This case is different from the one above in 2 ways: - // 1. The argument is a generic, - // 2. The type parameter is not {@code Object}. - // Both are important to trigger the behaviour we're checking (indirection via typecast in CFG). - public List nullCheckGenericAssignmentResultAsNonnullOk(BlockingQueue queue) { - final ArrayList records = new ArrayList<>(queue.size()); - try { - Runnable task; - // null-check should refine nullability of task - while ((task = queue.poll(0, TimeUnit.MILLISECONDS)) != null) { - records.add(task.toString()); - } - } catch (InterruptedException ie) { - // Ignore exception - } - - return records; - } - - static class NullableGetter { - @Nullable public T mInner; - - @Nullable - public T get() { - return mInner; - } - } - - public void chainedCallsWithAssignmentChecksOk(@Nullable NullableGetter c1) { - NullableGetter c2, c3; - - if (c1 != null && (c2 = c1.get()) != null && (c3 = c2.get()) != null) { - c3.get(); - } - } - } -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/ServerSideDeserializer.java b/infer/tests/codetoanalyze/java/nullsafe/ServerSideDeserializer.java deleted file mode 100644 index 6508cd96ffa..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/ServerSideDeserializer.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * 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; - -import javax.annotation.Nullable; - -@GeneratedGraphQL -public interface ServerSideDeserializer { - - public @Nullable Object nullableGetter(); -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/SomeAnnotationEndingWithNullable.java b/infer/tests/codetoanalyze/java/nullsafe/SomeAnnotationEndingWithNullable.java deleted file mode 100644 index 4c6e89c1532..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/SomeAnnotationEndingWithNullable.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * 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; - -// Test annotation to ensure Infer does not confuse it with @Nullable -@interface SomeAnnotationEndingWithNullable {} diff --git a/infer/tests/codetoanalyze/java/nullsafe/StrictMode.java b/infer/tests/codetoanalyze/java/nullsafe/StrictMode.java deleted file mode 100644 index d973411675c..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/StrictMode.java +++ /dev/null @@ -1,295 +0,0 @@ -/* - * 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; - -import com.facebook.infer.annotation.NullsafeStrict; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -@NullsafeStrict -class Strict { - - public String notInitializedIsBAD; - - public @Nullable String nullable; - public @Nonnull String nonnull = ""; - - public @Nullable String getNullable() { - return nullable; - } - - public String getNonnull() { - return nonnull; - } - - public static @Nullable String staticNullable() { - return null; - } - - public static String staticNonnull() { - return ""; - } - - // 1. Inside the same class, we trust annotations. - - private void sameClass_dereferenceNullableMethodIsBad() { - getNullable().toString(); - } - - private void sameClass_dereferenceNonnullMethodIsOK() { - getNonnull().toString(); - } - - private void sameClass_dereferenceNullableStaticMethodIsBad() { - staticNullable().toString(); - } - - private void sameClass_dereferenceNonnullStaticMethodIsOK() { - staticNonnull().toString(); - } - - private void sameClass_dereferenceNullableFieldIsBad() { - nullable.toString(); - } - - private void sameClass_dereferenceNonnullFieldIsOK() { - nonnull.toString(); - } - - private String sameClass_convertingNullableToNonnullIsBad() { - return getNullable(); - } - - private String sameClass_convertingNonnullToNonnullIsOK() { - return getNonnull(); - } - - // 2. We trust annotations that came from other classes annotated as strict - - private void strictClass_dereferenceNullableMethodIsBad() { - (new OtherStrict()).getNullable().toString(); - } - - private void strictClass_dereferenceNonnullMethodIsOK() { - (new OtherStrict()).getNonnull().toString(); - } - - private void strictClass_dereferenceNullableStaticMethodIsBad() { - OtherStrict.staticNullable().toString(); - } - - private void strictClass_dereferenceNonnullStaticMethodIsOK() { - OtherStrict.staticNonnull().toString(); - } - - private void strictClass_dereferenceNullableFieldIsBad() { - (new OtherStrict()).nullable.toString(); - } - - private void strictClass_dereferenceNonnullFieldIsOK() { - (new OtherStrict()).nonnull.toString(); - } - - private String strictClass_convertingNullableToNonnullIsBad() { - return (new OtherStrict()).getNullable(); - } - - private String strictClass_convertingNonnullToNonnullIsOK() { - return (new OtherStrict()).getNonnull(); - } - - // 3. We DON'T trust annotations that came from other classes NOT annotated as strict - // when it comes to dereferencing or converting them to nullable. - - private void nonStrictClass_dereferenceNullableMethodIsBad() { - (new NonStrict()).getNullable().toString(); - } - - private void nonStrictClass_dereferenceNonnullMethodIsBad() { - // even that it is declared as nonnull, can not dereference it without checking before - (new NonStrict()).getNonnull().toString(); - } - - private void nonStrictClass_dereferenceNullableStaticMethodIsBad() { - NonStrict.staticNullable().toString(); - } - - private void nonStrictClass_dereferenceNonnullStaticMethodIsBad() { - // even that it is declared as nonnull, can not dereference it without checking before - NonStrict.staticNonnull().toString(); - } - - private void nonStrictClass_dereferenceNullableFieldIsBad() { - (new NonStrict()).nullable.toString(); - } - - private void nonStrictClass_dereferenceNonnullFieldIsBad() { - // even that it is declared as nonnull, can not dereference it without checking before - (new NonStrict()).nonnull.toString(); - } - - private String nonStrictClass_convertingNullableToNonnullIsBad() { - return (new NonStrict()).getNullable(); - } - - public int usingPrimitiveTypesFromNonStrictIsOK() { - // Of course, primitive types can not be nullable so it - // does not matter if they are used from NonStrict - return NonStrict.getPrimitiveTypeValue(); - } - - // We don't require strictifying enums: their values are non-nullables - private SomeEnum usingEnumValuesAsNonnullIsOK() { - String str = SomeEnum.FIRST_VALUE.toString(); // can dereference - SomeEnum.FIRST_VALUE.someMethod(); // can dereference via calling a enum-specific method - return SomeEnum.SECOND_VALUE; // can convert to nonnull - } - - // FAKE_VALUE is not a value, but just a static fields so should require strictification - // (but it does not) - private SomeEnum FN_usingNonStrictifiedStaticFieldsInEnumsShouldBeBad() { - String str = SomeEnum.FAKE_VALUE.toString(); // should not be able to dereference - SomeEnum.FAKE_VALUE.someMethod(); // should not be able to dereference - return SomeEnum.FAKE_VALUE; // should not be able to convert to nonnull - } - - private SomeEnum[] enumValuesIsNonnullable() { - // values() is a special enum method which is never nullable - return SomeEnum.values(); - } - - private SomeEnum enumValueOfIsNonnullable() { - // valueOf() is a special enum method which is never nullable - return SomeEnum.valueOf("this will throw but won't return null"); - } - - private Boolean booleanFALSE_NonNullableOK() { - return Boolean.FALSE; - } - - private Boolean booleanTRUE_NonNullableOK() { - return Boolean.TRUE; - } - - private String nonStrictClass_convertingNonnullToNonnullIsBad() { - // even that it is declared as nonnull, can not convert it to nonnull it without checking before - return (new NonStrict()).getNonnull(); - } - - // 4. We don't completely prohibit using non-strict from strict, but we do require extra checks - // or adding defensive annotations. - - private void nonStrictClass_dereferenceNonnullFieldAfterCheckIsOK() { - NonStrict o = new NonStrict(); - if (o.nonnull != null) { - // This works because Nullsafe assumes that the field won't be modified between the calls - // (e.g. in multithreading context) - o.nonnull.toString(); - } - } - - private void nonStrictClass_dereferenceNonnullMethodAfterCheckIsOK() { - NonStrict o = new NonStrict(); - if (o.getNonnull() != null) { - // This works because Nullsafe assumes that all methods are determenistic and side-effect - // free, so the second call won't return null. - o.getNonnull().toString(); - } - } - - private @Nullable String nonStrictClass_convertingNonnullToNullableIsOK() { - return (new NonStrict()).getNonnull(); - } - - // 5. Main promise of strict mode: if the function is not annotated as nullable, - // it won't return null. - // So, if a function is annotated as strict, no extra check on caller's side is required. - // But strict mode DOES NOT guarantee there will be absolutely no NPE in callees: - // a) Even if strict mode calls only strict mode, there can be assertions that will throw NPE. - // b) We allow calling non-strict functions, and they internally can be inconsistent and throw - // NPE. - // c) We even allow passing values obtained from non-strict code, to other parts of non-strict - // code (e.g. we allow glueing 2 non-strict classes together inside a strict class, which might - // potentially lead to NPE if one of annotations is untrusted). - - private void propagatingNonnullFromNonStrictToStrictIsBad() { - NonStrict nonStrict = new NonStrict(); - OtherStrict strict = new OtherStrict(); - nonStrict.nonnull = strict.getNonnull(); - } - - private void propagatingNonnullBetweenTwoNonStrictObjectsIsOK() { - NonStrict nonStrict1 = new NonStrict(); - NonStrict nonStrict2 = new NonStrict(); - // Though getNonnull() is declared as non-nullable, is not strictly checked - // so there is a possibility that it can return null, e.g. if it calls to a third-party - // libraries. this null can leak to `nonnull` field, which might be a bad thing and lead to - // issues. - // This is OK for strict mode. - nonStrict1.nonnull = nonStrict2.getNonnull(); - } -} - -@NullsafeStrict -class OtherStrict { - public @Nullable String nullable; - public String nonnull = ""; - - public @Nullable String getNullable() { - return nullable; - } - - public String getNonnull() { - return nonnull; - } - - public static @Nullable String staticNullable() { - return null; - } - - public static String staticNonnull() { - return ""; - } -} - -class NonStrict { - public @Nullable String nullable; - public String nonnull = ""; - - public @Nullable String getNullable() { - return nullable; - } - - public String getNonnull() { - return nonnull; - } - - public static @Nullable String staticNullable() { - return null; - } - - public static String staticNonnull() { - return ""; - } - - public static int getPrimitiveTypeValue() { - return 0; - } -} - -enum SomeEnum { - FIRST_VALUE, - SECOND_VALUE; - - public void someMethod() {} - - // We currently have no way to distinct that guy from enum value. - // Using it will lead to NPE, but we won't detect it - // This should not occur in practice often, hopefully :) - public static SomeEnum FAKE_VALUE; -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/StrictModeForThirdParty.java b/infer/tests/codetoanalyze/java/nullsafe/StrictModeForThirdParty.java deleted file mode 100644 index fd6f3d392df..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/StrictModeForThirdParty.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * 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; - -import com.facebook.infer.annotation.NullsafeStrict; -import java.util.List; -import javax.annotation.Nullable; -import some.test.pckg.ThirdPartyTestClass; - -/** - * In this test, we test how Strict mode works for calls of 3rd party libraries, and how detection - * differs based on if the function is allow listed or not in 3rd party signatures repository. - */ -@NullsafeStrict -public class StrictModeForThirdParty { - - ThirdPartyTestClass obj; - - StrictModeForThirdParty() { - obj = new ThirdPartyTestClass(); - } - - public @Nullable String getNullable() { - return null; - } - - public String getNonnull() { - return ""; - } - - // Return values. - // In strict mode, return values should be pessimistically treated as nullable - // if the function is unspecified, and treated according to their return annotation if - // the function is allow listed in the 3rd party repo. - - public void dereferenceUnspecifiedIsBAD() { - obj.returnUnspecified().toString(); - } - - public void dereferenceSpecifiedAsNullableIsBAD() { - obj.returnSpecifiedAsNullable().toString(); - } - - public void dereferenceFieldIsBAD() { - obj.nonNullableField.toString(); - } - - public void dereferenceSpecifiedAsNonnullIsOK() { - obj.returnSpecifiedAsNonnull().toString(); - } - - // Params. - // In strict mode, params should be pessimistically treated as non-nullable if the function is - // unspecified, - // and treated based on their annotation if the function is allow listed in the 3rd party repo. - - public void passingNullableParamToUnspecifiedIsBAD() { - obj.paramUnspecified(getNullable()); - } - - public void passingNonnullParamToUnspecifiedIsOK() { - obj.paramUnspecified(getNonnull()); - } - - public void passingNullableToParamSpecifiedAsNonnullIsBAD() { - obj.secondParamSpecifiedAsNonnull(getNonnull(), getNullable()); - } - - public void passingNullableToParamSpecifiedAsNullableIsOK() { - // first param is explicitly allow listed as specified as nullable, so everything is OK - obj.secondParamSpecifiedAsNonnull(getNullable(), getNonnull()); - } - - public void passingNonnullToParamIsOK() { - // Independently of param signature, it is safe to pass non-nullables - obj.secondParamSpecifiedAsNonnull(getNonnull(), getNonnull()); - } - - // Below follow tests ensuring how we represent third party methods in the output .json file - // for interesting edge cases. - - // Expect the dependent third party signature to be correctly rendered in .json output as - // "some.test.pckg.ThirdPartyTestClass#genericObjectRepresentation(java.lang.Object, - // java.util.List)" - public String genericObjectRepresentation(String s, List l) { - return obj.generic(s, l); - } - - // Expect the dependent third party signature to be correctly rendered in .json output as - // "some.test.pckg.ThirdPartyTestClass#genericExtendsStringRepresentation(java.lang.String, - // java.util.List)" - public String genericExtendsStringRepresentation(String s, List l) { - return obj.genericString(s, l); - } - - // Expect the dependent third party signature to be correctly rendered in .json output as - // "some.test.pckg.ThirdPartyTestClass#arrayRepresentation(java.lang.String, java.lang.String[])" - public String arrayRepresentation(String s, String[] arr) { - return obj.array(s, arr); - } - - // Expect the dependent third party signature to be correctly rendered in .json output as - // "some.test.pckg.ThirdPartyTestClass#varargRepresentation(java.lang.String, java.lang.String[])" - public String varargRepresentation(String s) { - return obj.vararg(s, s, s, "Hello"); - } - - // Expect the dependent third party signature to be correctly rendered in .json output as - // "some.test.pckg.ThirdPartyTestClass#varargGenericRepresentation(java.lang.String, - // java.lang.String[])" - public String varargGenericRepresentation(String s) { - return obj.varargGeneric(s, s, s, "Hello"); - } -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/SwitchCase.java b/infer/tests/codetoanalyze/java/nullsafe/SwitchCase.java deleted file mode 100644 index 8b2f5a79ad1..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/SwitchCase.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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; - -import javax.annotation.Nullable; - -public class SwitchCase { - public String switchOnNullIsBad() { - Color color = null; - switch (color) { - case BLACK: - return "BLACK"; - case WHITE: - return "WHITE"; - default: - // the default case will never be called. - // instead, an NPE will be thrown. - return "DEFAULT"; - } - } - - public String switchOnNullableIsBad() { - Color color = getNullableColor(); - switch (color) { - case BLACK: - return "BLACK"; - case WHITE: - return "WHITE"; - default: - // in case `color` is null, this WON'T BE called. - // instead, an NPE will be thrown - return "DEFAULT"; - } - } - - public String switchOnNonNullableIsOK() { - Color color = getNonNullableColor(); - switch (color) { - case BLACK: - return "BLACK"; - case WHITE: - return "WHITE"; - default: - // default case won't happen, but this is fine - return "DEFAULT"; - } - } - - private @Nullable Color getNullableColor() { - return Color.BLACK; - } - - private Color getNonNullableColor() { - return Color.BLACK; - } -} - -enum Color { - BLACK, - WHITE; -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/SyntheticErrorSuppressions.java b/infer/tests/codetoanalyze/java/nullsafe/SyntheticErrorSuppressions.java deleted file mode 100644 index 43eba377f67..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/SyntheticErrorSuppressions.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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; - -import android.annotation.SuppressLint; -import javax.annotation.Nullable; - -public class SyntheticErrorSuppressions { - static class Fragment { - @SuppressLint("eradicate-return-over-annotated") - @Nullable - public static Object getContext() { - return new Object(); - } - - public static void setContext(final Object context) {} - } - - static class ClassWithSyntheticCode { - @Nullable private Object $ul_fieldNullable; - private Object $ul_fieldNotNull; - private Object _UL__ULSEP__fieldNotNull; - - // This method contains $ which is *extremely* rarely used in user code, but - // used extensively in autogenerated code, therefore is good marker. - private static void $ul_iAmAutogen( - final ClassWithSyntheticCode instance, final Object context) {} - - // Anothe example of autogen'd code - private static void _UL_iAmAutogen( - final ClassWithSyntheticCode instance, final Object context) {} - - @Nullable - private Object $ul_iAmNullableAutogen() { - return $ul_fieldNullable; - } - - public void passingIncorrectParamToSyntheticMethod_OK() { - $ul_iAmAutogen(this, Fragment.getContext()); - _UL_iAmAutogen(this, Fragment.getContext()); - } - - public void assigningAnythingToSyntheticField_OK() { - $ul_fieldNotNull = null; - _UL__ULSEP__fieldNotNull = null; - } - - // Following cases are hard to support since synthetic code can be a part of - // a complex expression, and we need some more sophisticated mechanisms to - // handle that. On the bright side, this is not something that happens often - // in the wild. - - public void passingSyntheticParamToAnyMethod_OK_FP() { - Fragment.setContext($ul_fieldNullable); - Fragment.setContext(_UL__ULSEP__fieldNotNull); - } - - public String dereferencingSyntheticNullableField_OK_FP() { - return $ul_fieldNullable.toString(); - } - - public String dereferencingNullableSyntheticMethodCall_OK_FP() { - return $ul_iAmNullableAutogen().toString(); - } - } -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/TrueFalseOnNull.java b/infer/tests/codetoanalyze/java/nullsafe/TrueFalseOnNull.java deleted file mode 100644 index ad60e7ac9dd..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/TrueFalseOnNull.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * 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; - -import android.text.TextUtils; -import com.facebook.infer.annotation.FalseOnNull; -import com.facebook.infer.annotation.TrueOnNull; -import com.google.common.base.Strings; -import javax.annotation.Nullable; - -/** Testing functionality related to @TrueOnNull and @FalseOnNull methods */ -public class TrueFalseOnNull { - - static class StaticOneParam { - @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 - boolean trueOnNull(@Nullable Object o) { - return o == null ? true : o.toString() == compareTo; - } - - @FalseOnNull - boolean falseOnNull(@Nullable Object o) { - return o == null ? false : o.toString() == compareTo; - } - - boolean notAnnotated(@Nullable Object o) { - return o == null ? false : o.toString() == compareTo; - } - } - - // @TrueOnNull and @FalseOnNull should expect true/false will be returned if ANY of input objects - // is null. - // In other words, they should infer that all input nullable objects are non-null in the - // 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; - } - - @FalseOnNull - boolean falseOnNull( - @Nullable Object nullable1, int primitive, Object nonnull, @Nullable Object nullable2) { - if (nullable1 == null || nullable2 == null) { - return false; - } - return nonnull == compareTo; - } - - boolean notAnnotated( - @Nullable Object nullable1, int primitive, Object nonnull, @Nullable Object nullable2) { - if (nullable1 == null || nullable2 == null) { - return false; - } - return nonnull == compareTo; - } - } - - class TestStaticOneParam { - - void trueOnNullPositiveBranchIsBAD(@Nullable String s) { - if (StaticOneParam.trueOnNull(s)) { - s.toString(); - } - } - - void trueOnNullNegativeBranchIsOK(@Nullable String s) { - if (!StaticOneParam.trueOnNull(s)) { - s.toString(); - } - } - - void falseOnNullPositiveBranchIsOK(@Nullable String s) { - if (StaticOneParam.falseOnNull(s)) { - s.toString(); - } - } - - void falseOnNullNegativeBranchIsBAD(@Nullable String s) { - if (!StaticOneParam.falseOnNull(s)) { - s.toString(); - } - } - - void notAnnotatedPositiveBranchIsBAD(@Nullable String s) { - if (StaticOneParam.notAnnotated(s)) { - s.toString(); - } - } - - 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 - if (!TextUtils.isEmpty(s)) { - s.toString(); // OK: if we are here, we know that `s` is not null - } - - // Strings.isNullOrEmpty is modelled as TrueOnNull - if (!Strings.isNullOrEmpty(s)) { - s.toString(); // OK: if we are here, we know that `s` is not null - } - } - } - - // nullsafe should assume Object.equals() and all overrides return false on `null` argument. - static class TestEqualsIsFalseOnNull { - // An example of class that overrides equals(). - static class SomeObject { - private int mContent; - - SomeObject(int content) { - mContent = content; - } - - @Override - // No nullsafe warnings are expected - public boolean equals(@Nullable Object src) { - if (!super.equals(src)) { - return false; - } - // at this point src should be a non-nullable: super.equals() would return false otherwise. - src.toString(); // should be OK to dereference now - if (!(src instanceof SomeObject)) { - return false; - } - SomeObject asSomeObject = (SomeObject) src; - return mContent == asSomeObject.mContent; - } - } - - private void testObjectEqualsIsFalseOnNull(Object x, @Nullable Object y) { - if (!x.equals(y)) { - return; - } - - // OK to dereference - y.toString(); - } - - private void testOverrideEqualsIsFalseOnNull(SomeObject x, @Nullable Object y) { - if (!x.equals(y)) { - return; - } - - // OK to dereference even that SomeObject.equals() is not annotated as @FalseOnNull - y.toString(); - } - } - - // this is a common enough pattern to be tested separately - class EarlyReturn { - - void testEarlyReturn(@Nullable CharSequence s1, @Nullable CharSequence s2) { - if (StaticOneParam.trueOnNull(s1) || StaticOneParam.notAnnotated(s2)) { - return; - } - - s1.toString(); // OK: if `s1` was null, we would no rech this point - s2.toString(); // BAD: no reason for `s2` to become non-nullable - } - } -} diff --git a/infer/tests/codetoanalyze/java/nullsafe/issues.exp b/infer/tests/codetoanalyze/java/nullsafe/issues.exp deleted file mode 100644 index e988330d524..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/issues.exp +++ /dev/null @@ -1,394 +0,0 @@ -codetoanalyze/java/nullsafe/AlternativeRecommendations.java, Linters_dummy_method, 17, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`AlternativeRecommendations` needs 5 issues to be fixed in order to be marked @Nullsafe.], AlternativeRecommendations, codetoanalyze.java.nullsafe, issues: 5, curr_mode: "Default" -codetoanalyze/java/nullsafe/AlternativeRecommendations.java, codetoanalyze.java.nullsafe.AlternativeRecommendations.dereference_ShouldSuggestAlternative(android.view.View):void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`view.findViewById(...)` is nullable and is not locally checked for null when calling `setId(...)`: call to View.findViewById(...) at line 22 (nullable according to nullsafe internal models). If this is intentional, use `androidx.core.view.ViewCompat.requireViewById()` instead.], AlternativeRecommendations, codetoanalyze.java.nullsafe, nullable_methods:android.view.View.findViewById at 22 -codetoanalyze/java/nullsafe/AlternativeRecommendations.java, codetoanalyze.java.nullsafe.AlternativeRecommendations.passingParam_ShouldSuggestAlternative(android.view.View):void, 1, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`AlternativeRecommendations.acceptsNonnullView(...)`: parameter #1(`view`) is declared non-nullable but the argument `view.findViewById(...)` is nullable: call to View.findViewById(...) at line 26 (nullable according to nullsafe internal models). If you don't expect null, use `androidx.core.view.ViewCompat.requireViewById()` instead.], AlternativeRecommendations, codetoanalyze.java.nullsafe, nullable_methods:android.view.View.findViewById at 26 -codetoanalyze/java/nullsafe/AlternativeRecommendations.java, codetoanalyze.java.nullsafe.AlternativeRecommendations.returnValue_ShouldSuggestAlternative(android.view.View):android.view.View, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`returnValue_ShouldSuggestAlternative(...)`: return type is declared non-nullable but the method returns a nullable value: call to View.findViewById(...) at line 30 (nullable according to nullsafe internal models). If you don't expect null, use `androidx.core.view.ViewCompat.requireViewById()` instead.], AlternativeRecommendations, codetoanalyze.java.nullsafe, nullable_methods:android.view.View.findViewById at 30 -codetoanalyze/java/nullsafe/AlternativeRecommendations.java, codetoanalyze.java.nullsafe.AlternativeRecommendations.assigningField_ShouldSuggestAlternative(android.view.View):void, 1, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`field` is declared non-nullable but is assigned a nullable: call to View.findViewById(...) at line 34 (nullable according to nullsafe internal models). If you don't expect null, use `androidx.core.view.ViewCompat.requireViewById()` instead.], AlternativeRecommendations, codetoanalyze.java.nullsafe, nullable_methods:android.view.View.findViewById at 34, field:codetoanalyze.java.nullsafe.AlternativeRecommendations.field -codetoanalyze/java/nullsafe/ButterKnife.java, codetoanalyze.java.nullsafe.ButterKnife.(), 0, ERADICATE_FIELD_OVER_ANNOTATED, no_bucket, ADVICE, [Field `ButterKnife.nullable` is always initialized in the constructor but is declared `@Nullable`], ButterKnife, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.ButterKnife.nullable -codetoanalyze/java/nullsafe/ButterKnife.java, Linters_dummy_method, 15, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`ButterKnife` needs 5 issues to be fixed in order to be marked @Nullsafe.], ButterKnife, codetoanalyze.java.nullsafe, issues: 5, curr_mode: "Default" -codetoanalyze/java/nullsafe/ButterKnife.java, codetoanalyze.java.nullsafe.ButterKnife.dereferencingNullableIsBAD():void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`ButterKnife.nullable` is nullable and is not locally checked for null when calling `length()`.], ButterKnife, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ButterKnife.java, codetoanalyze.java.nullsafe.ButterKnife.convertingToNotNullableForNullableIsBAD():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`convertingToNotNullableForNullableIsBAD()`: return type is declared non-nullable but the method returns a nullable value: field nullable at line 47.], ButterKnife, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ButterKnife.java, codetoanalyze.java.nullsafe.ButterKnife.passingToNullableForNullableIsBAD():void, 1, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ButterKnife.f(...)`: parameter #1(`nonNullable`) is declared non-nullable but the argument `ButterKnife.nullable` is nullable.], ButterKnife, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ButterKnife.java, codetoanalyze.java.nullsafe.ButterKnife.assignNullToNormalIsBAD():void, 1, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`normal` is declared non-nullable but is assigned `null`: null constant at line 76.], ButterKnife, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.ButterKnife.normal -codetoanalyze/java/nullsafe/ButterKnife.java, codetoanalyze.java.nullsafe.ButterKnife$TestNotInitialized.(codetoanalyze.java.nullsafe.ButterKnife), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `notInitializedNormalIsBAD` is declared non-nullable, so it should be initialized in the constructor], ButterKnife$TestNotInitialized, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.ButterKnife$TestNotInitialized.notInitializedNormalIsBAD -codetoanalyze/java/nullsafe/CapturedParam.java, Linters_dummy_method, 13, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`CapturedParam` needs 1 issues to be fixed in order to be marked @Nullsafe.], CapturedParam, codetoanalyze.java.nullsafe, issues: 1, curr_mode: "Default" -codetoanalyze/java/nullsafe/CapturedParam.java, codetoanalyze.java.nullsafe.CapturedParam.dereferencingNullableIsBAD(java.lang.Object):void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`parameter` is nullable and is not locally checked for null when calling `toString()`.], CapturedParam, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ConditionRedundant.java, codetoanalyze.java.nullsafe.ConditionRedundant.(), 0, ERADICATE_FIELD_OVER_ANNOTATED, no_bucket, ADVICE, [Field `ConditionRedundant.fieldNullable` is always initialized in the constructor but is declared `@Nullable`], ConditionRedundant, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.ConditionRedundant.fieldNullable -codetoanalyze/java/nullsafe/ConditionRedundant.java, Linters_dummy_method, 13, ERADICATE_META_CLASS_CAN_BE_NULLSAFE, no_bucket, ADVICE, [Congrats! `ConditionRedundant` is free of nullability issues. Mark it `@Nullsafe(Nullsafe.Mode.LOCAL)` to prevent regressions.], ConditionRedundant, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Default", promote_mode: "Strict" -codetoanalyze/java/nullsafe/ConditionRedundant.java, codetoanalyze.java.nullsafe.ConditionRedundant.compareNEQ_NonnullIsBAD(java.lang.String):void, 1, ERADICATE_CONDITION_REDUNDANT, no_bucket, ADVICE, [The condition s might be always true according to the existing annotations.], ConditionRedundant, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ConditionRedundant.java, codetoanalyze.java.nullsafe.ConditionRedundant.compareEQ_NonnullIsBAD(java.lang.String):void, 1, ERADICATE_CONDITION_REDUNDANT, no_bucket, ADVICE, [The condition s might be always false according to the existing annotations.], ConditionRedundant, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ConditionRedundant.java, codetoanalyze.java.nullsafe.ConditionRedundant.outsideOfIfCompareNonnullIsBAD(java.lang.String):void, 1, ERADICATE_CONDITION_REDUNDANT, no_bucket, ADVICE, [The condition s might be always true according to the existing annotations.], ConditionRedundant, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ConditionRedundant.java, codetoanalyze.java.nullsafe.ConditionRedundant.conjunctionBothNonnullIsBAD(java.lang.String,java.lang.String):void, 1, ERADICATE_CONDITION_REDUNDANT, no_bucket, ADVICE, [The condition s1 might be always true according to the existing annotations.], ConditionRedundant, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ConditionRedundant.java, codetoanalyze.java.nullsafe.ConditionRedundant.conjunctionBothNonnullIsBAD(java.lang.String,java.lang.String):void, 1, ERADICATE_CONDITION_REDUNDANT, no_bucket, ADVICE, [The condition s2 might be always true according to the existing annotations.], ConditionRedundant, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ConditionRedundant.java, codetoanalyze.java.nullsafe.ConditionRedundant.conjunctionOneNonnullIsBAD(java.lang.String,java.lang.String):void, 1, ERADICATE_CONDITION_REDUNDANT, no_bucket, ADVICE, [The condition s2 might be always true according to the existing annotations.], ConditionRedundant, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ConditionRedundant.java, codetoanalyze.java.nullsafe.ConditionRedundant.disjunctionBothNonnullIsBAD(java.lang.String,java.lang.String):void, 1, ERADICATE_CONDITION_REDUNDANT, no_bucket, ADVICE, [The condition s1 might be always false according to the existing annotations.], ConditionRedundant, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ConditionRedundant.java, codetoanalyze.java.nullsafe.ConditionRedundant.disjunctionBothNonnullIsBAD(java.lang.String,java.lang.String):void, 1, ERADICATE_CONDITION_REDUNDANT, no_bucket, ADVICE, [The condition s2 might be always true according to the existing annotations.], ConditionRedundant, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ConditionRedundant.java, codetoanalyze.java.nullsafe.ConditionRedundant.disjunctionOneNonnullIsBAD(java.lang.String,java.lang.String):void, 1, ERADICATE_CONDITION_REDUNDANT, no_bucket, ADVICE, [The condition s2 might be always true according to the existing annotations.], ConditionRedundant, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ConditionRedundant.java, codetoanalyze.java.nullsafe.ConditionRedundant.irrelevantConditionWithNonnullIsBAD(java.lang.String,java.lang.String,int):void, 1, ERADICATE_CONDITION_REDUNDANT, no_bucket, ADVICE, [The condition s1 might be always true according to the existing annotations.], ConditionRedundant, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ConditionRedundant.java, codetoanalyze.java.nullsafe.ConditionRedundant.ternary_NonnullInBothBranchesIsBAD(java.lang.String,java.lang.String,int):void, 2, ERADICATE_CONDITION_REDUNDANT, no_bucket, ADVICE, [The condition s2 might be always false according to the existing annotations.], ConditionRedundant, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ConditionRedundant.java, codetoanalyze.java.nullsafe.ConditionRedundant.FP_ternary_NonnullInOneBranch_SecondBranch_ShouldBeOK(java.lang.String,java.lang.String,int):void, 2, ERADICATE_CONDITION_REDUNDANT, no_bucket, ADVICE, [The condition s2 might be always false according to the existing annotations.], ConditionRedundant, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ConditionRedundant.java, codetoanalyze.java.nullsafe.ConditionRedundant.testFlowSensitivity(java.lang.String,java.lang.String):void, 2, ERADICATE_CONDITION_REDUNDANT, no_bucket, ADVICE, [The condition nullable1 might be always true according to the existing annotations.], ConditionRedundant, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ConditionRedundant.java, codetoanalyze.java.nullsafe.ConditionRedundant.testFlowSensitivity(java.lang.String,java.lang.String):void, 4, ERADICATE_CONDITION_REDUNDANT, no_bucket, ADVICE, [The condition nullable1 might be always true according to the existing annotations.], ConditionRedundant, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ConditionRedundant.java, codetoanalyze.java.nullsafe.ConditionRedundant.comparingNonnullFunctionIsBAD():void, 1, ERADICATE_CONDITION_REDUNDANT, no_bucket, ADVICE, [The condition lang.String(this)V might be always true: `ConditionRedundant.getNonnull()` is not annotated as `@Nullable`.], ConditionRedundant, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ConditionRedundant.java, codetoanalyze.java.nullsafe.ConditionRedundant.comparingNonnullFieldIsBAD():void, 1, ERADICATE_CONDITION_REDUNDANT, no_bucket, ADVICE, [The condition ConditionRedundant.fieldNonnull might be always true according to the existing annotations.], ConditionRedundant, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ConditionRedundant.java, codetoanalyze.java.nullsafe.ConditionRedundant.comparingNullableFieldThatIsAlreadyCheckedIsBAD():void, 2, ERADICATE_CONDITION_REDUNDANT, no_bucket, ADVICE, [The condition ConditionRedundant.fieldNullable might be always true according to the existing annotations.], ConditionRedundant, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ConditionRedundant.java, codetoanalyze.java.nullsafe.ConditionRedundant.checkNotNull_NonnullIsBAD(java.lang.String):void, 1, ERADICATE_CONDITION_REDUNDANT, no_bucket, ADVICE, [The condition (s!=null) might be always true according to the existing annotations.], ConditionRedundant, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ConditionRedundant.java, codetoanalyze.java.nullsafe.ConditionRedundant.checkArgument_NonnullIsBAd(java.lang.String):void, 1, ERADICATE_CONDITION_REDUNDANT, no_bucket, ADVICE, [The condition s might be always true according to the existing annotations.], ConditionRedundant, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ConditionRedundant.java, codetoanalyze.java.nullsafe.ConditionRedundant.assertNotNull_NonnullIsBAD(java.lang.String):void, 1, ERADICATE_CONDITION_REDUNDANT, no_bucket, ADVICE, [The condition (s!=null) might be always true according to the existing annotations.], ConditionRedundant, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ConditionRedundant.java, codetoanalyze.java.nullsafe.ConditionRedundant.comparingWithNullIfAssignedBeforeThrowableIsBAD():void, 6, ERADICATE_CONDITION_REDUNDANT, no_bucket, ADVICE, [The condition s might be always true according to the existing annotations.], ConditionRedundant, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/FieldNotInitialized.java, Linters_dummy_method, 25, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`FieldNotInitialized` needs 32 issues to be fixed in order to be marked @Nullsafe.], FieldNotInitialized, codetoanalyze.java.nullsafe, issues: 32, curr_mode: "Default" -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.FieldNotInitialized$Suppression.(codetoanalyze.java.nullsafe.FieldNotInitialized), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `nonNullIsBAD` is declared non-nullable, so it should be initialized in the constructor], FieldNotInitialized$Suppression, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotInitialized$Suppression.nonNullIsBAD -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.FieldNotInitialized$Suppression.(codetoanalyze.java.nullsafe.FieldNotInitialized), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `nonnullIsBAD` is declared non-nullable, so it should be initialized in the constructor], FieldNotInitialized$Suppression, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotInitialized$Suppression.nonnullIsBAD -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.FieldNotInitialized$Suppression.(codetoanalyze.java.nullsafe.FieldNotInitialized), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `notNullIsBAD` is declared non-nullable, so it should be initialized in the constructor], FieldNotInitialized$Suppression, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotInitialized$Suppression.notNullIsBAD -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.FieldNotInitialized$Suppression.(codetoanalyze.java.nullsafe.FieldNotInitialized), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `suppressWrongLintIsBAD` is declared non-nullable, so it should be initialized in the constructor], FieldNotInitialized$Suppression, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotInitialized$Suppression.suppressWrongLintIsBAD -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.FieldNotInitialized$Suppression.testNullifyFields():void, 3, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`nonnullIsBAD` is declared non-nullable but is assigned `null`: null constant at line 62.], FieldNotInitialized$Suppression, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotInitialized$Suppression.nonnullIsBAD -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.FieldNotInitialized$Suppression.testNullifyFields():void, 4, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`nonNullIsBAD` is declared non-nullable but is assigned `null`: null constant at line 63.], FieldNotInitialized$Suppression, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotInitialized$Suppression.nonNullIsBAD -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.FieldNotInitialized$Suppression.testNullifyFields():void, 5, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`injectIsOK` is declared non-nullable but is assigned `null`: null constant at line 64.], FieldNotInitialized$Suppression, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotInitialized$Suppression.injectIsOK -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.FieldNotInitialized$Suppression.testNullifyFields():void, 6, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`suppressAnnotationIsOK` is declared non-nullable but is assigned `null`: null constant at line 65.], FieldNotInitialized$Suppression, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotInitialized$Suppression.suppressAnnotationIsOK -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.FieldNotInitialized$Suppression.testNullifyFields():void, 7, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`suppressLintIsOK` is declared non-nullable but is assigned `null`: null constant at line 66.], FieldNotInitialized$Suppression, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotInitialized$Suppression.suppressLintIsOK -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.FieldNotInitialized$OnlyRead.(codetoanalyze.java.nullsafe.FieldNotInitialized), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `o` is declared non-nullable, so it should be initialized in the constructor], FieldNotInitialized$OnlyRead, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotInitialized$OnlyRead.o -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.FieldNotInitialized$WriteItselfIsBAD.(codetoanalyze.java.nullsafe.FieldNotInitialized), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `bad` is declared non-nullable, so it should be initialized in the constructor], FieldNotInitialized$WriteItselfIsBAD, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotInitialized$WriteItselfIsBAD.bad -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.FieldNotInitialized$InitializationOrder.(codetoanalyze.java.nullsafe.FieldNotInitialized,int), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `o1` is declared non-nullable, so it should be initialized in the constructor], FieldNotInitialized$InitializationOrder, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotInitialized$InitializationOrder.o1 -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.FieldNotInitialized$OnlyReadIndirect.(codetoanalyze.java.nullsafe.FieldNotInitialized), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `o1` is declared non-nullable, so it should be initialized in the constructor], FieldNotInitialized$OnlyReadIndirect, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotInitialized$OnlyReadIndirect.o1 -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.FieldNotInitialized$ShouldInitializeInAllBranches.(codetoanalyze.java.nullsafe.FieldNotInitialized,int), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `f2` is declared non-nullable, so it should be initialized in the constructor], FieldNotInitialized$ShouldInitializeInAllBranches, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotInitialized$ShouldInitializeInAllBranches.f2 -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.FieldNotInitialized$ShouldInitializeInAllBranches.(codetoanalyze.java.nullsafe.FieldNotInitialized,int), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `f3` is declared non-nullable, so it should be initialized in the constructor], FieldNotInitialized$ShouldInitializeInAllBranches, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotInitialized$ShouldInitializeInAllBranches.f3 -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.FieldNotInitialized$ShouldInitializeInAllBranches.(codetoanalyze.java.nullsafe.FieldNotInitialized,int), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `f5` is declared non-nullable, so it should be initialized in the constructor], FieldNotInitialized$ShouldInitializeInAllBranches, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotInitialized$ShouldInitializeInAllBranches.f5 -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.FieldNotInitialized$InitIfNull.(codetoanalyze.java.nullsafe.FieldNotInitialized), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `shouldBeGood_FIXME` is declared non-nullable, so it should be initialized in the constructor], FieldNotInitialized$InitIfNull, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotInitialized$InitIfNull.shouldBeGood_FIXME -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.FieldNotInitialized$InitCircular.(codetoanalyze.java.nullsafe.FieldNotInitialized), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `bad` is declared non-nullable, so it should be initialized in the constructor], FieldNotInitialized$InitCircular, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotInitialized$InitCircular.bad -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.FieldNotInitialized$InitCircular.(codetoanalyze.java.nullsafe.FieldNotInitialized), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `stillBad` is declared non-nullable, so it should be initialized in the constructor], FieldNotInitialized$InitCircular, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotInitialized$InitCircular.stillBad -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.FieldNotInitialized$InitWithOtherClass$OtherClass.(codetoanalyze.java.nullsafe.FieldNotInitialized$InitWithOtherClass), 0, ERADICATE_FIELD_OVER_ANNOTATED, no_bucket, ADVICE, [Field `FieldNotInitialized$InitWithOtherClass$OtherClass.nullable` is always initialized in the constructor but is declared `@Nullable`], FieldNotInitialized$InitWithOtherClass$OtherClass, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotInitialized$InitWithOtherClass$OtherClass.nullable -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.FieldNotInitialized$InitWithOtherClass.(codetoanalyze.java.nullsafe.FieldNotInitialized,codetoanalyze.java.nullsafe.FieldNotInitialized$InitWithOtherClass$OtherClass), 1, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`bad` is declared non-nullable but is assigned a nullable: field nullable at line 185.], FieldNotInitialized$InitWithOtherClass, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotInitialized$InitWithOtherClass.bad -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.FieldNotInitialized$InitWithTheSameClass.(codetoanalyze.java.nullsafe.FieldNotInitialized,codetoanalyze.java.nullsafe.FieldNotInitialized$InitWithTheSameClass), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `bad` is declared non-nullable, so it should be initialized in the constructor], FieldNotInitialized$InitWithTheSameClass, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotInitialized$InitWithTheSameClass.bad -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.FieldNotInitialized$Suppressions.(codetoanalyze.java.nullsafe.FieldNotInitialized,int), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `f2` is declared non-nullable, so it should be initialized in the constructor], FieldNotInitialized$Suppressions, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotInitialized$Suppressions.f2 -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.FieldNotInitialized$Suppressions.(codetoanalyze.java.nullsafe.FieldNotInitialized,int), 2, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`FieldNotInitialized$Suppressions.f(...)`: parameter #1(`a`) is declared non-nullable but the argument is `null`.], FieldNotInitialized$Suppressions, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.FieldNotInitialized$Suppressions.(codetoanalyze.java.nullsafe.FieldNotInitialized,int,int), 1, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`FieldNotInitialized$Suppressions.f(...)`: parameter #1(`a`) is declared non-nullable but the argument is `null`.], FieldNotInitialized$Suppressions, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.FieldNotInitialized$Suppressions.(codetoanalyze.java.nullsafe.FieldNotInitialized,int,int,int), 1, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`FieldNotInitialized$Suppressions.f(...)`: parameter #1(`a`) is declared non-nullable but the argument is `null`.], FieldNotInitialized$Suppressions, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/FieldNotInitialized.java, Linters_dummy_method, 236, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`TestKnownInitializers` needs 2 issues to be fixed in order to be marked @Nullsafe.], TestKnownInitializers, codetoanalyze.java.nullsafe, issues: 2, curr_mode: "Default" -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.TestKnownInitializers$KnownInitializers.(codetoanalyze.java.nullsafe.TestKnownInitializers), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `initInUnknownMethodIsBAD` is declared non-nullable, so it should be initialized in the constructor], TestKnownInitializers$KnownInitializers, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.TestKnownInitializers$KnownInitializers.initInUnknownMethodIsBAD -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.TestKnownInitializers$SimplyOnCreateWontDoATrick.(codetoanalyze.java.nullsafe.TestKnownInitializers), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `initInUnknownMethodIsBAD` is declared non-nullable, so it should be initialized in the constructor], TestKnownInitializers$SimplyOnCreateWontDoATrick, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.TestKnownInitializers$SimplyOnCreateWontDoATrick.initInUnknownMethodIsBAD -codetoanalyze/java/nullsafe/FieldNotInitialized.java, Linters_dummy_method, 283, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`TestInitializerAnnotation` needs 7 issues to be fixed in order to be marked @Nullsafe.], TestInitializerAnnotation, codetoanalyze.java.nullsafe, issues: 7, curr_mode: "Default" -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.TestInitializerAnnotation.(), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `dontInitAtAllIsBAD` is declared non-nullable, so it should be initialized in the constructor], TestInitializerAnnotation, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.TestInitializerAnnotation.dontInitAtAllIsBAD -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.TestInitializerAnnotation.(), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `initInAnyOtherMethodIsBAD` is declared non-nullable, so it should be initialized in the constructor], TestInitializerAnnotation, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.TestInitializerAnnotation.initInAnyOtherMethodIsBAD -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.TestInitializerAnnotation.set4(java.lang.String):void, 2, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`initByNullableInInitializedMethodIsBAD` is declared non-nullable but is assigned a nullable: method parameter value.], TestInitializerAnnotation, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.TestInitializerAnnotation.initByNullableInInitializedMethodIsBAD -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.TestInitializerAnnotation.build():java.lang.Object, 5, ERADICATE_CONDITION_REDUNDANT, no_bucket, ADVICE, [The condition TestInitializerAnnotation.initInInitilizerMethod1IsOK might be always true according to the existing annotations.], TestInitializerAnnotation, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.TestInitializerAnnotation.build():java.lang.Object, 5, ERADICATE_CONDITION_REDUNDANT, no_bucket, ADVICE, [The condition TestInitializerAnnotation.initInInitilizerMethod2IsOK might be always true according to the existing annotations.], TestInitializerAnnotation, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.TestInitializerAnnotation$TestFieldNotInitializedDerived.(codetoanalyze.java.nullsafe.TestInitializerAnnotation), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `field2_BAD` is declared non-nullable, so it should be initialized in the constructor], TestInitializerAnnotation$TestFieldNotInitializedDerived, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.TestInitializerAnnotation$TestFieldNotInitializedDerived.field2_BAD -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.TestInitializerAnnotation$TestFieldNotInitializedDerivedDerived.(codetoanalyze.java.nullsafe.TestInitializerAnnotation), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `field1_OK` is declared non-nullable, so it should be initialized in the constructor], TestInitializerAnnotation$TestFieldNotInitializedDerivedDerived, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.TestInitializerAnnotation$TestFieldNotInitializedDerivedDerived.field1_OK -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.TestInitializerAnnotation$TestFieldNotInitializedDerivedDerived.(codetoanalyze.java.nullsafe.TestInitializerAnnotation), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `field2_BAD` is declared non-nullable, so it should be initialized in the constructor], TestInitializerAnnotation$TestFieldNotInitializedDerivedDerived, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.TestInitializerAnnotation$TestFieldNotInitializedDerivedDerived.field2_BAD -codetoanalyze/java/nullsafe/FieldNotInitialized.java, codetoanalyze.java.nullsafe.TestInitializerAnnotation$TestFieldNotInitializedDerivedDerived.(codetoanalyze.java.nullsafe.TestInitializerAnnotation), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `field3_OK` is declared non-nullable, so it should be initialized in the constructor], TestInitializerAnnotation$TestFieldNotInitializedDerivedDerived, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.TestInitializerAnnotation$TestFieldNotInitializedDerivedDerived.field3_OK -codetoanalyze/java/nullsafe/FieldNotNullable.java, Linters_dummy_method, 20, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`CanAssignNullInCleanupMethods` needs 1 issues to be fixed in order to be marked @Nullsafe.], CanAssignNullInCleanupMethods, codetoanalyze.java.nullsafe, issues: 1, curr_mode: "Default" -codetoanalyze/java/nullsafe/FieldNotNullable.java, codetoanalyze.java.nullsafe.CanAssignNullInCleanupMethods.assignNullInAnyOtherMethodIsBAD():void, 1, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`someObject` is declared non-nullable but is assigned `null`: null constant at line 44.], CanAssignNullInCleanupMethods, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.CanAssignNullInCleanupMethods.someObject -codetoanalyze/java/nullsafe/FieldNotNullable.java, codetoanalyze.java.nullsafe.FieldNotNullable.(), 0, ERADICATE_FIELD_OVER_ANNOTATED, no_bucket, ADVICE, [Field `FieldNotNullable.nullable` is always initialized in the constructor but is declared `@Nullable`], FieldNotNullable, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotNullable.nullable -codetoanalyze/java/nullsafe/FieldNotNullable.java, Linters_dummy_method, 48, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`FieldNotNullable` needs 5 issues to be fixed in order to be marked @Nullsafe.], FieldNotNullable, codetoanalyze.java.nullsafe, issues: 5, curr_mode: "Default" -codetoanalyze/java/nullsafe/FieldNotNullable.java, codetoanalyze.java.nullsafe.FieldNotNullable.(), 4, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`initializeNonNullableWithNullIsBAD` is declared non-nullable but is assigned `null`: null constant at line 52.], FieldNotNullable, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotNullable.initializeNonNullableWithNullIsBAD -codetoanalyze/java/nullsafe/FieldNotNullable.java, codetoanalyze.java.nullsafe.FieldNotNullable.getNullable():java.lang.String, 0, ERADICATE_RETURN_OVER_ANNOTATED, no_bucket, ADVICE, [Method `getNullable()` is annotated with `@Nullable` but never returns null.], FieldNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/FieldNotNullable.java, codetoanalyze.java.nullsafe.FieldNotNullable.setNullableToNotNullableIsBAD(java.lang.String):void, 1, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`notNullable` is declared non-nullable but is assigned `null`: null constant at line 65.], FieldNotNullable, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotNullable.notNullable -codetoanalyze/java/nullsafe/FieldNotNullable.java, codetoanalyze.java.nullsafe.FieldNotNullable.setNullableToNotNullableIsBAD(java.lang.String):void, 2, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`notNullable` is declared non-nullable but is assigned a nullable: method parameter s.], FieldNotNullable, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldNotNullable.notNullable -codetoanalyze/java/nullsafe/FieldNotNullable.java, codetoanalyze.java.nullsafe.FieldNotNullable.setNullableToNotNullableIsBAD(java.lang.String):void, 3, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`notNullable` is declared non-nullable but is assigned a nullable: call to getNullable() at line 67.], FieldNotNullable, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.FieldNotNullable.getNullable at 67, field:codetoanalyze.java.nullsafe.FieldNotNullable.notNullable -codetoanalyze/java/nullsafe/FieldNotNullable.java, codetoanalyze.java.nullsafe.FieldNotNullable.setNullableToExternalIsBAD(java.lang.String):void, 2, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`externalNotNull` is declared non-nullable but is assigned a nullable: method parameter s.], FieldNotNullable, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.SomeExternalClass.externalNotNull -codetoanalyze/java/nullsafe/FieldNotNullable.java, codetoanalyze.java.nullsafe.SomeExternalClass.(), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, WARNING, [Field `externalNotNull` is declared non-nullable, so it should be initialized in the constructor], SomeExternalClass, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.SomeExternalClass.externalNotNull -codetoanalyze/java/nullsafe/FieldNotNullable.java, Linters_dummy_method, 98, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`SomeExternalClass` needs 1 issues to be fixed in order to be marked @Nullsafe.], SomeExternalClass, codetoanalyze.java.nullsafe, issues: 1, curr_mode: "Default" -codetoanalyze/java/nullsafe/FieldNullabilityMemoization.java, Linters_dummy_method, 27, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`FieldNullabilityMemoization` needs 2 issues to be fixed in order to be marked @Nullsafe.], FieldNullabilityMemoization, codetoanalyze.java.nullsafe, issues: 2, curr_mode: "Default" -codetoanalyze/java/nullsafe/FieldNullabilityMemoization.java, codetoanalyze.java.nullsafe.FieldNullabilityMemoization.dereferenceIsBAD():void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`FieldNullabilityMemoization.nullable` is nullable and is not locally checked for null when calling `toString()`.], FieldNullabilityMemoization, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/FieldNullabilityMemoization.java, codetoanalyze.java.nullsafe.FieldNullabilityMemoization.dereferenceViaLocalVarIsBAD():void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`a` is nullable and is not locally checked for null when calling `toString()`: field nullable at line 35.], FieldNullabilityMemoization, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/FieldOverAnnotated.java, Linters_dummy_method, 15, ERADICATE_META_CLASS_CAN_BE_NULLSAFE, no_bucket, ADVICE, [Congrats! `FieldOverAnnotated` is free of nullability issues. Mark it `@Nullsafe(Nullsafe.Mode.LOCAL)` to prevent regressions.], FieldOverAnnotated, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Default", promote_mode: "Strict" -codetoanalyze/java/nullsafe/FieldOverAnnotated.java, codetoanalyze.java.nullsafe.FieldOverAnnotated.(int), 0, ERADICATE_FIELD_OVER_ANNOTATED, no_bucket, ADVICE, [Field `FieldOverAnnotated.FP_initializedInAllConstructorsButSetToNullInAPublicMethodShouldBeOK` is always initialized in the constructor but is declared `@Nullable`], FieldOverAnnotated, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldOverAnnotated.FP_initializedInAllConstructorsButSetToNullInAPublicMethodShouldBeOK -codetoanalyze/java/nullsafe/FieldOverAnnotated.java, codetoanalyze.java.nullsafe.FieldOverAnnotated.(int), 0, ERADICATE_FIELD_OVER_ANNOTATED, no_bucket, ADVICE, [Field `FieldOverAnnotated.initializedInAllConstructorsIsBAD` is always initialized in the constructor but is declared `@Nullable`], FieldOverAnnotated, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldOverAnnotated.initializedInAllConstructorsIsBAD -codetoanalyze/java/nullsafe/FieldOverAnnotated.java, codetoanalyze.java.nullsafe.FieldOverAnnotated.(int), 0, ERADICATE_FIELD_OVER_ANNOTATED, no_bucket, ADVICE, [Field `FieldOverAnnotated.initilizedInAllConstructorsAndAllBranchesIsBAD` is always initialized in the constructor but is declared `@Nullable`], FieldOverAnnotated, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldOverAnnotated.initilizedInAllConstructorsAndAllBranchesIsBAD -codetoanalyze/java/nullsafe/FieldOverAnnotated.java, codetoanalyze.java.nullsafe.FieldOverAnnotated.(int,int), 0, ERADICATE_FIELD_OVER_ANNOTATED, no_bucket, ADVICE, [Field `FieldOverAnnotated.FP_initializedInAllConstructorsButSetToNullInAPublicMethodShouldBeOK` is always initialized in the constructor but is declared `@Nullable`], FieldOverAnnotated, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldOverAnnotated.FP_initializedInAllConstructorsButSetToNullInAPublicMethodShouldBeOK -codetoanalyze/java/nullsafe/FieldOverAnnotated.java, codetoanalyze.java.nullsafe.FieldOverAnnotated.(int,int), 0, ERADICATE_FIELD_OVER_ANNOTATED, no_bucket, ADVICE, [Field `FieldOverAnnotated.initializedInAllConstructorsIsBAD` is always initialized in the constructor but is declared `@Nullable`], FieldOverAnnotated, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldOverAnnotated.initializedInAllConstructorsIsBAD -codetoanalyze/java/nullsafe/FieldOverAnnotated.java, codetoanalyze.java.nullsafe.FieldOverAnnotated.(int,int), 0, ERADICATE_FIELD_OVER_ANNOTATED, no_bucket, ADVICE, [Field `FieldOverAnnotated.initilizedInAllConstructorsAndAllBranchesIsBAD` is always initialized in the constructor but is declared `@Nullable`], FieldOverAnnotated, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.FieldOverAnnotated.initilizedInAllConstructorsAndAllBranchesIsBAD -codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java, Linters_dummy_method, 39, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`ReturnValToNullBAD` needs 1 issues to be fixed in order to be marked @Nullsafe.], ReturnValToNullBAD, codetoanalyze.java.nullsafe, issues: 1, curr_mode: "Default" -codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java, codetoanalyze.java.nullsafe.ReturnValToNullBAD.valBoth(java.lang.String):java.lang.String, 0, ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION, no_bucket, WARNING, [Child method `ReturnValToNullBAD.valBoth(...)` is not substitution-compatible with its parent: the return type is declared as nullable, but parent method `VariousMethods.valBoth(...)` is missing `@Nullable` declaration. Either mark the parent as `@Nullable` or ensure the child does not return `null`.], ReturnValToNullBAD, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java, Linters_dummy_method, 46, ERADICATE_META_CLASS_CAN_BE_NULLSAFE, no_bucket, ADVICE, [Congrats! `ReturnNullToValOK` is free of nullability issues. Mark it `@Nullsafe(Nullsafe.Mode.LOCAL)` to prevent regressions.], ReturnNullToValOK, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Default", promote_mode: "Strict" -codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java, Linters_dummy_method, 50, ERADICATE_META_CLASS_CAN_BE_NULLSAFE, no_bucket, ADVICE, [Congrats! `ReturnValFromValAndNullFromNullOK` is free of nullability issues. Mark it `@Nullsafe(Nullsafe.Mode.LOCAL)` to prevent regressions.], ReturnValFromValAndNullFromNullOK, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Default", promote_mode: "Strict" -codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java, Linters_dummy_method, 61, ERADICATE_META_CLASS_CAN_BE_NULLSAFE, no_bucket, ADVICE, [Congrats! `AbstractReturnValToNullFN` is free of nullability issues. Mark it `@Nullsafe(Nullsafe.Mode.LOCAL)` to prevent regressions.], AbstractReturnValToNullFN, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Default", promote_mode: "Strict" -codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java, Linters_dummy_method, 69, ERADICATE_META_CLASS_CAN_BE_NULLSAFE, no_bucket, ADVICE, [Congrats! `ArgValToNullOK` is free of nullability issues. Mark it `@Nullsafe(Nullsafe.Mode.LOCAL)` to prevent regressions.], ArgValToNullOK, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Default", promote_mode: "Strict" -codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java, Linters_dummy_method, 75, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`ArgNullToValBAD` needs 1 issues to be fixed in order to be marked @Nullsafe.], ArgNullToValBAD, codetoanalyze.java.nullsafe, issues: 1, curr_mode: "Default" -codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java, codetoanalyze.java.nullsafe.ArgNullToValBAD.nullableArg(java.lang.String):java.lang.String, 0, ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION, no_bucket, WARNING, [First parameter `arg` of method `ArgNullToValBAD.nullableArg(...)` is missing `@Nullable` declaration when overriding `VariousMethods.nullableArg(...)`. The parent method declared it can handle `null` for this param, so the child should also declare that.], ArgNullToValBAD, codetoanalyze.java.nullsafe, inconsistent_param_index:0 -codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java, Linters_dummy_method, 81, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`ArgNullToValForInterfaceInAnotherFileBAD` needs 1 issues to be fixed in order to be marked @Nullsafe.], ArgNullToValForInterfaceInAnotherFileBAD, codetoanalyze.java.nullsafe, issues: 1, curr_mode: "Default" -codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java, codetoanalyze.java.nullsafe.ArgNullToValForInterfaceInAnotherFileBAD.implementInAnotherFile(java.lang.String):java.lang.String, 0, ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION, no_bucket, WARNING, [First parameter `s` of method `ArgNullToValForInterfaceInAnotherFileBAD.implementInAnotherFile(...)` is missing `@Nullable` declaration when overriding `InconsistentSubclassAnnotationInterface.implementInAnotherFile(...)`. The parent method declared it can handle `null` for this param, so the child should also declare that.], ArgNullToValForInterfaceInAnotherFileBAD, codetoanalyze.java.nullsafe, inconsistent_param_index:0 -codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java, Linters_dummy_method, 88, ERADICATE_META_CLASS_CAN_BE_NULLSAFE, no_bucket, ADVICE, [Congrats! `ArgValToValAndNullToNullOK` is free of nullability issues. Mark it `@Nullsafe(Nullsafe.Mode.LOCAL)` to prevent regressions.], ArgValToValAndNullToNullOK, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Default", promote_mode: "Strict" -codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java, Linters_dummy_method, 102, ERADICATE_META_CLASS_CAN_BE_NULLSAFE, no_bucket, ADVICE, [Congrats! `OverrideExistingCorrectlyOK` is free of nullability issues. Mark it `@Nullsafe(Nullsafe.Mode.LOCAL)` to prevent regressions.], OverrideExistingCorrectlyOK, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Default", promote_mode: "Strict" -codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java, Linters_dummy_method, 120, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`NoOverrideSinceDifferentTypesOK` needs 1 issues to be fixed in order to be marked @Nullsafe.], NoOverrideSinceDifferentTypesOK, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Default" -codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java, Linters_dummy_method, 137, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`OverloadExistingIncorrectBAD` needs 1 issues to be fixed in order to be marked @Nullsafe.], OverloadExistingIncorrectBAD, codetoanalyze.java.nullsafe, issues: 1, curr_mode: "Default" -codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java, codetoanalyze.java.nullsafe.OverloadExistingIncorrectBAD.overload(java.lang.String,java.lang.String):java.lang.String, 0, ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION, no_bucket, WARNING, [Child method `OverloadExistingIncorrectBAD.overload(...)` is not substitution-compatible with its parent: the return type is declared as nullable, but parent method `Overloads.overload(...)` is missing `@Nullable` declaration. Either mark the parent as `@Nullable` or ensure the child does not return `null`.], OverloadExistingIncorrectBAD, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java, Linters_dummy_method, 146, ERADICATE_META_CLASS_CAN_BE_NULLSAFE, no_bucket, ADVICE, [Congrats! `ConstructorsAreExcluded` is free of nullability issues. Mark it `@Nullsafe(Nullsafe.Mode.LOCAL)` to prevent regressions.], ConstructorsAreExcluded, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Default", promote_mode: "Strict" -codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java, Linters_dummy_method, 160, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`ExtendsExternalLibrary` needs 1 issues to be fixed in order to be marked @Nullsafe.], ExtendsExternalLibrary, codetoanalyze.java.nullsafe, issues: 1, curr_mode: "Default" -codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java, codetoanalyze.java.nullsafe.ExtendsExternalLibrary.externalMethod2(java.lang.Object):void, 0, ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION, no_bucket, WARNING, [First parameter `object` of method `ExtendsExternalLibrary.externalMethod2(...)` is missing `@Nullable` declaration when overriding `SomeExternalClass.externalMethod2(...)`. The parent method declared it can handle `null` for this param, so the child should also declare that.], ExtendsExternalLibrary, codetoanalyze.java.nullsafe, inconsistent_param_index:0 -codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java, Linters_dummy_method, 177, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`JavaLangEquals` needs 2 issues to be fixed in order to be marked @Nullsafe.], JavaLangEquals, codetoanalyze.java.nullsafe, issues: 2, curr_mode: "Default" -codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java, codetoanalyze.java.nullsafe.JavaLangEquals.equals(java.lang.Object):boolean, 0, ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION, no_bucket, WARNING, [Parameter `x` is missing `@Nullable` declaration: according to the Java Specification, for any object `x` call `x.equals(null)` should properly return false.], JavaLangEquals, codetoanalyze.java.nullsafe, inconsistent_param_index:0 -codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java, codetoanalyze.java.nullsafe.JavaLangEquals.equals(java.lang.Object):boolean, 4, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`x` is nullable and is not locally checked for null when calling `toString()`: Object.equals() should be able to accept `null`, according to the Java specification.], JavaLangEquals, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java, Linters_dummy_method, 197, ERADICATE_META_CLASS_CAN_BE_NULLSAFE, no_bucket, ADVICE, [Congrats! `NonNullableConcreteGetterOK` is free of nullability issues. Mark it `@Nullsafe(Nullsafe.Mode.LOCAL)` to prevent regressions.], NonNullableConcreteGetterOK, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Default", promote_mode: "Strict" -codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java, Linters_dummy_method, 203, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`NullableConcreteGetterBAD` needs 1 issues to be fixed in order to be marked @Nullsafe.], NullableConcreteGetterBAD, codetoanalyze.java.nullsafe, issues: 1, curr_mode: "Default" -codetoanalyze/java/nullsafe/InconsistentSubclassAnnotation.java, codetoanalyze.java.nullsafe.NullableConcreteGetterBAD.get():java.lang.String, 0, ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION, no_bucket, WARNING, [Child method `NullableConcreteGetterBAD.get()` is not substitution-compatible with its parent: the return type is declared as nullable, but parent method `NonNullableInterfaceGetterOK.get()` is missing `@Nullable` declaration. Either mark the parent as `@Nullable` or ensure the child does not return `null`.], NullableConcreteGetterBAD, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/InheritanceForStrictMode.java, Linters_dummy_method, 14, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`InheritanceForStrictMode` needs 6 issues to be fixed in order to be marked @Nullsafe.], InheritanceForStrictMode, codetoanalyze.java.nullsafe, issues: 6, curr_mode: "Default" -codetoanalyze/java/nullsafe/InheritanceForStrictMode.java, codetoanalyze.java.nullsafe.InheritanceForStrictMode$StrictExtendingNonstrict.badToAddNullableInChildren():java.lang.String, 0, ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION, no_bucket, ERROR, [Child method `InheritanceForStrictMode$StrictExtendingNonstrict.badToAddNullableInChildren()` is not substitution-compatible with its parent: the return type is declared as nullable, but parent method `InheritanceForStrictMode$NonStrictBase.badToAddNullableInChildren()` is missing `@Nullable` declaration. Either mark the parent as `@Nullable` or ensure the child does not return `null`.], InheritanceForStrictMode$StrictExtendingNonstrict, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/InheritanceForStrictMode.java, codetoanalyze.java.nullsafe.InheritanceForStrictMode$StrictExtendingNonstrict.params(java.lang.String,java.lang.String):void, 0, ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION, no_bucket, ERROR, [First parameter `badToRemoveNullableInChildren` of method `InheritanceForStrictMode$StrictExtendingNonstrict.params(...)` is missing `@Nullable` declaration when overriding `InheritanceForStrictMode$NonStrictBase.params(...)`. The parent method declared it can handle `null` for this param, so the child should also declare that.], InheritanceForStrictMode$StrictExtendingNonstrict, codetoanalyze.java.nullsafe, inconsistent_param_index:0 -codetoanalyze/java/nullsafe/InheritanceForStrictMode.java, codetoanalyze.java.nullsafe.InheritanceForStrictMode$StrictExtendingStrict.badToAddNullableInChildren():java.lang.String, 0, ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION, no_bucket, ERROR, [Child method `InheritanceForStrictMode$StrictExtendingStrict.badToAddNullableInChildren()` is not substitution-compatible with its parent: the return type is declared as nullable, but parent method `InheritanceForStrictMode$StrictBase.badToAddNullableInChildren()` is missing `@Nullable` declaration. Either mark the parent as `@Nullable` or ensure the child does not return `null`.], InheritanceForStrictMode$StrictExtendingStrict, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/InheritanceForStrictMode.java, codetoanalyze.java.nullsafe.InheritanceForStrictMode$StrictExtendingStrict.params(java.lang.String,java.lang.String):void, 0, ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION, no_bucket, ERROR, [First parameter `badToRemoveNullableInChildren` of method `InheritanceForStrictMode$StrictExtendingStrict.params(...)` is missing `@Nullable` declaration when overriding `InheritanceForStrictMode$StrictBase.params(...)`. The parent method declared it can handle `null` for this param, so the child should also declare that.], InheritanceForStrictMode$StrictExtendingStrict, codetoanalyze.java.nullsafe, inconsistent_param_index:0 -codetoanalyze/java/nullsafe/InheritanceForStrictMode.java, codetoanalyze.java.nullsafe.InheritanceForStrictMode$NonStrictExtendingStrict.badToAddNullableInChildren():java.lang.String, 0, ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION, no_bucket, WARNING, [Child method `InheritanceForStrictMode$NonStrictExtendingStrict.badToAddNullableInChildren()` is not substitution-compatible with its parent: the return type is declared as nullable, but parent method `InheritanceForStrictMode$StrictBase.badToAddNullableInChildren()` is missing `@Nullable` declaration. Either mark the parent as `@Nullable` or ensure the child does not return `null`.], InheritanceForStrictMode$NonStrictExtendingStrict, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/InheritanceForStrictMode.java, codetoanalyze.java.nullsafe.InheritanceForStrictMode$NonStrictExtendingStrict.params(java.lang.String,java.lang.String):void, 0, ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION, no_bucket, WARNING, [First parameter `badToRemoveNullableInChildren` of method `InheritanceForStrictMode$NonStrictExtendingStrict.params(...)` is missing `@Nullable` declaration when overriding `InheritanceForStrictMode$StrictBase.params(...)`. The parent method declared it can handle `null` for this param, so the child should also declare that.], InheritanceForStrictMode$NonStrictExtendingStrict, codetoanalyze.java.nullsafe, inconsistent_param_index:0 -codetoanalyze/java/nullsafe/JunitExample.java, Linters_dummy_method, 9, ERADICATE_META_CLASS_CAN_BE_NULLSAFE, no_bucket, ADVICE, [Congrats! `JunitExample` is free of nullability issues. Mark it `@Nullsafe(Nullsafe.Mode.LOCAL)` to prevent regressions.], JunitExample, , issues: 0, curr_mode: "Default", promote_mode: "LocalTrustAll" -codetoanalyze/java/nullsafe/Lambdas.java, Linters_dummy_method, 19, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`Lambdas` needs 9 issues to be fixed in order to be marked @Nullsafe.], Lambdas, codetoanalyze.java.nullsafe, issues: 6, curr_mode: "Default" -codetoanalyze/java/nullsafe/Lambdas.java, codetoanalyze.java.nullsafe.Lambdas$Lambda$_29_1.apply(java.lang.Object):java.lang.Object, 0, ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION, no_bucket, WARNING, [First parameter `$bcvar1` of method `Lambdas$Lambda$_29_1.apply(...)` is missing `@Nullable` declaration when overriding `Lambdas$NullableFunction.apply(...)`. The parent method declared it can handle `null` for this param, so the child should also declare that.], Lambdas$Lambda$_29_1, codetoanalyze.java.nullsafe, inconsistent_param_index:0 -codetoanalyze/java/nullsafe/Lambdas.java, codetoanalyze.java.nullsafe.Lambdas$Lambda$_28_1.apply(java.lang.Object):java.lang.Object, 0, ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION, no_bucket, WARNING, [First parameter `$bcvar1` of method `Lambdas$Lambda$_28_1.apply(...)` is missing `@Nullable` declaration when overriding `Lambdas$NullableFunction.apply(...)`. The parent method declared it can handle `null` for this param, so the child should also declare that.], Lambdas$Lambda$_28_1, codetoanalyze.java.nullsafe, inconsistent_param_index:0 -codetoanalyze/java/nullsafe/Lambdas.java, codetoanalyze.java.nullsafe.Lambdas$Lambda$_27_1.apply(java.lang.Object):java.lang.Object, 0, ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION, no_bucket, WARNING, [First parameter `$bcvar1` of method `Lambdas$Lambda$_27_1.apply(...)` is missing `@Nullable` declaration when overriding `Lambdas$NullableFunction.apply(...)`. The parent method declared it can handle `null` for this param, so the child should also declare that.], Lambdas$Lambda$_27_1, codetoanalyze.java.nullsafe, inconsistent_param_index:0 -codetoanalyze/java/nullsafe/Lambdas.java, codetoanalyze.java.nullsafe.Lambdas$Lambda$_22_0.onFailure(java.lang.Integer):void, 0, ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION, no_bucket, WARNING, [First parameter `$bcvar1` of method `Lambdas$Lambda$_22_0.onFailure(...)` is missing `@Nullable` declaration when overriding `Lambdas$NullableCallbackWithDefaultMethods.onFailure(...)`. The parent method declared it can handle `null` for this param, so the child should also declare that.], Lambdas$Lambda$_22_0, codetoanalyze.java.nullsafe, inconsistent_param_index:0 -codetoanalyze/java/nullsafe/Lambdas.java, codetoanalyze.java.nullsafe.Lambdas$NullsafeClass.useJavaUtilFunction_UNSUPPORTED(java.util.function.Function):java.lang.String, 1, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, ERROR, [Third-party `Function.apply(...)` is missing a signature that would allow passing a nullable to param #1. Actual argument `getNullableString()` is nullable. Consider adding the correct signature of `Function.apply(...)` to nullsafe/third-party-signatures/java.sig.], Lambdas$NullsafeClass, codetoanalyze.java.nullsafe, unvetted_3rd_party:[java.util.function.Function#apply(java.lang.Object)], nullable_methods:codetoanalyze.java.nullsafe.Lambdas.getNullableString at 144 -codetoanalyze/java/nullsafe/Lambdas.java, codetoanalyze.java.nullsafe.Lambdas$NullsafeClass.useJavaUtilFunction_UNSUPPORTED(java.util.function.Function):java.lang.String, 1, ERADICATE_UNVETTED_THIRD_PARTY_IN_NULLSAFE, no_bucket, ERROR, [`Function.apply(...)`: `@Nullsafe` mode prohibits using values coming from not vetted third party methods without a check. Result of this call is used at line 143. Either add a local check for null or assertion, or add the correct signature to nullsafe/third-party-signatures/java.sig.], Lambdas$NullsafeClass, codetoanalyze.java.nullsafe, unvetted_3rd_party:[java.util.function.Function#apply(java.lang.Object)] -codetoanalyze/java/nullsafe/LibraryCalls.java, Linters_dummy_method, 15, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`LibraryCalls` needs 8 issues to be fixed in order to be marked @Nullsafe.], LibraryCalls, codetoanalyze.java.nullsafe, issues: 4, curr_mode: "Default" -codetoanalyze/java/nullsafe/LibraryCalls.java, codetoanalyze.java.nullsafe.LibraryCalls.badReferenceDereference(java.lang.ref.Reference):java.lang.String, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`ref.get()` is nullable and is not locally checked for null when calling `toString()`: call to Reference.get() at line 18 (nullable according to nullsafe internal models).], LibraryCalls, codetoanalyze.java.nullsafe, nullable_methods:java.lang.ref.Reference.get at 18 -codetoanalyze/java/nullsafe/LibraryCalls.java, codetoanalyze.java.nullsafe.LibraryCalls.badWeakReferenceDereference(java.lang.ref.WeakReference):java.lang.String, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`ref.get()` is nullable and is not locally checked for null when calling `toString()`: call to Reference.get() at line 22 (nullable according to nullsafe internal models).], LibraryCalls, codetoanalyze.java.nullsafe, nullable_methods:java.lang.ref.Reference.get at 22 -codetoanalyze/java/nullsafe/LibraryCalls.java, codetoanalyze.java.nullsafe.LibraryCalls.badPhantomReferenceDereference(java.lang.ref.PhantomReference):java.lang.String, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`ref.get()` is nullable and is not locally checked for null when calling `toString()`: call to PhantomReference.get() at line 26 (nullable according to nullsafe internal models).], LibraryCalls, codetoanalyze.java.nullsafe, nullable_methods:java.lang.ref.PhantomReference.get at 26 -codetoanalyze/java/nullsafe/LibraryCalls.java, codetoanalyze.java.nullsafe.LibraryCalls.badAtomicReferenceDereference(java.util.concurrent.atomic.AtomicReference):java.lang.String, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`ref.get()` is nullable and is not locally checked for null when calling `toString()`: call to AtomicReference.get() at line 31 (nullable according to nullsafe internal models).], LibraryCalls, codetoanalyze.java.nullsafe, nullable_methods:java.util.concurrent.atomic.AtomicReference.get at 31 -codetoanalyze/java/nullsafe/MapNullability.java, Linters_dummy_method, 13, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`MapNullability` needs 20 issues to be fixed in order to be marked @Nullsafe.], MapNullability, codetoanalyze.java.nullsafe, issues: 13, curr_mode: "Default" -codetoanalyze/java/nullsafe/MapNullability.java, codetoanalyze.java.nullsafe.MapNullability$TestThatGetIsAllowedOnlyAfterContainsKeyWasChecked.usingGetWithoutCheckingKeyIsBAD(java.util.Map):void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`m.get(...)` is nullable and is not locally checked for null when calling `isEmpty()`: call to Map.get(...) at line 24 (nullable according to nullsafe internal models).], MapNullability$TestThatGetIsAllowedOnlyAfterContainsKeyWasChecked, codetoanalyze.java.nullsafe, nullable_methods:java.util.Map.get at 24 -codetoanalyze/java/nullsafe/MapNullability.java, codetoanalyze.java.nullsafe.MapNullability$TestThatGetIsAllowedOnlyAfterContainsKeyWasChecked.usingGetAfterWrongKeyWasCheckedIsBAD(java.util.Map):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`m.get(...)` is nullable and is not locally checked for null when calling `isEmpty()`: call to Map.get(...) at line 29 (nullable according to nullsafe internal models).], MapNullability$TestThatGetIsAllowedOnlyAfterContainsKeyWasChecked, codetoanalyze.java.nullsafe, nullable_methods:java.util.Map.get at 29 -codetoanalyze/java/nullsafe/MapNullability.java, codetoanalyze.java.nullsafe.MapNullability$TestThatGetIsAllowedOnlyAfterContainsKeyWasChecked.usingGetAfterWrongKeyWasCheckedInWhileLoopIsBAD(java.util.Map):void, 3, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`m.get(...)` is nullable and is not locally checked for null when calling `isEmpty()`: call to Map.get(...) at line 44 (nullable according to nullsafe internal models).], MapNullability$TestThatGetIsAllowedOnlyAfterContainsKeyWasChecked, codetoanalyze.java.nullsafe, nullable_methods:java.util.Map.get at 44 -codetoanalyze/java/nullsafe/MapNullability.java, codetoanalyze.java.nullsafe.MapNullability$TestThatGetIsAllowedOnlyAfterContainsKeyWasChecked.immutableMap_usingGetAfterWrongKeyWasCheckedIsBAD(com.google.common.collect.ImmutableMap):void, 3, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`m.get(...)` is nullable and is not locally checked for null when calling `isEmpty()`: call to ImmutableMap.get(...) at line 59 (nullable according to nullsafe internal models).], MapNullability$TestThatGetIsAllowedOnlyAfterContainsKeyWasChecked, codetoanalyze.java.nullsafe, nullable_methods:com.google.common.collect.ImmutableMap.get at 59 -codetoanalyze/java/nullsafe/MapNullability.java, codetoanalyze.java.nullsafe.MapNullability$TestThatGetAfterPutIsAllowed.getWithoutPutIsBAD(java.util.Map,java.lang.String):void, 1, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`dontAssignNull` is declared non-nullable but is assigned a nullable: call to Map.get(...) at line 73 (nullable according to nullsafe internal models).], MapNullability$TestThatGetAfterPutIsAllowed, codetoanalyze.java.nullsafe, nullable_methods:java.util.Map.get at 73, field:codetoanalyze.java.nullsafe.MapNullability$TestThatGetAfterPutIsAllowed.dontAssignNull -codetoanalyze/java/nullsafe/MapNullability.java, codetoanalyze.java.nullsafe.MapNullability$TestThatGetAfterPutIsAllowed.getAfterPutWrongKeyIsBAD(java.util.Map,java.lang.String,java.lang.String):void, 3, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`dontAssignNull` is declared non-nullable but is assigned a nullable: call to Map.get(...) at line 79 (nullable according to nullsafe internal models).], MapNullability$TestThatGetAfterPutIsAllowed, codetoanalyze.java.nullsafe, nullable_methods:java.util.Map.get at 79, field:codetoanalyze.java.nullsafe.MapNullability$TestThatGetAfterPutIsAllowed.dontAssignNull -codetoanalyze/java/nullsafe/MapNullability.java, codetoanalyze.java.nullsafe.MapNullability$TestThatGetAfterPutIsAllowed.getAfterPutSeveralKeysButGetWrongOneIsBAD(java.util.Map):void, 4, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`dontAssignNull` is declared non-nullable but is assigned a nullable: call to Map.get(...) at line 98 (nullable according to nullsafe internal models).], MapNullability$TestThatGetAfterPutIsAllowed, codetoanalyze.java.nullsafe, nullable_methods:java.util.Map.get at 98, field:codetoanalyze.java.nullsafe.MapNullability$TestThatGetAfterPutIsAllowed.dontAssignNull -codetoanalyze/java/nullsafe/MapNullability.java, codetoanalyze.java.nullsafe.MapNullability$TestThatGetAfterPutIsAllowed.getAfterPutNullableIsBAD(java.util.Map,java.lang.String):void, 2, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`Map.put(...)`: parameter #2 is declared non-nullable (according to nullsafe internal models) but the argument `nullableValue` is nullable.], MapNullability$TestThatGetAfterPutIsAllowed, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/MapNullability.java, codetoanalyze.java.nullsafe.MapNullability$TestThatGetAfterPutIsAllowed.getAfterPutNullableIsBAD(java.util.Map,java.lang.String):void, 3, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`dontAssignNull` is declared non-nullable but is assigned a nullable: method parameter nullableValue.], MapNullability$TestThatGetAfterPutIsAllowed, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.MapNullability$TestThatGetAfterPutIsAllowed.dontAssignNull -codetoanalyze/java/nullsafe/MapNullability.java, codetoanalyze.java.nullsafe.MapNullability$TestThatGetAfterPutIsAllowed.overwriteKeyByNullIsBAD(java.util.Map,java.lang.String):void, 2, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`Map.put(...)`: parameter #2 is declared non-nullable (according to nullsafe internal models) but the argument is `null`.], MapNullability$TestThatGetAfterPutIsAllowed, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/MapNullability.java, codetoanalyze.java.nullsafe.MapNullability$TestThatGetAfterPutIsAllowed.overwriteKeyByNullIsBAD(java.util.Map,java.lang.String):void, 3, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`dontAssignNull` is declared non-nullable but is assigned `null`: null constant at line 114.], MapNullability$TestThatGetAfterPutIsAllowed, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.MapNullability$TestThatGetAfterPutIsAllowed.dontAssignNull -codetoanalyze/java/nullsafe/MapNullability.java, codetoanalyze.java.nullsafe.MapNullability$TestThatGetAfterPutIsAllowed.overwriteKeyByNonnullIsOK(java.util.Map,java.lang.String):void, 1, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`Map.put(...)`: parameter #2 is declared non-nullable (according to nullsafe internal models) but the argument is `null`.], MapNullability$TestThatGetAfterPutIsAllowed, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/MapNullability.java, codetoanalyze.java.nullsafe.MapNullability$TestThatGetAfterPutIsAllowed.getAfterConditionalPutWrongKeyIsBAD(java.util.Map,java.lang.String,java.lang.String):void, 5, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`dontAssignNull` is declared non-nullable but is assigned a nullable: call to Map.get(...) at line 137 (nullable according to nullsafe internal models).], MapNullability$TestThatGetAfterPutIsAllowed, codetoanalyze.java.nullsafe, nullable_methods:java.util.Map.get at 137, field:codetoanalyze.java.nullsafe.MapNullability$TestThatGetAfterPutIsAllowed.dontAssignNull -codetoanalyze/java/nullsafe/ModePromotions.java, Linters_dummy_method, 18, ERADICATE_META_CLASS_CAN_BE_NULLSAFE, no_bucket, ADVICE, [Congrats! `Default_NoDeps_CanBePromotedToStrict` is free of nullability issues. Mark it `@Nullsafe(Nullsafe.Mode.LOCAL)` to prevent regressions.], Default_NoDeps_CanBePromotedToStrict, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Default", promote_mode: "Strict" -codetoanalyze/java/nullsafe/ModePromotions.java, Linters_dummy_method, 25, ERADICATE_META_CLASS_IS_NULLSAFE, no_bucket, INFO, [Class codetoanalyze.java.nullsafe.Local_NoDeps_CanBePromotedToStrict is free of nullability issues.], Local_NoDeps_CanBePromotedToStrict, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "LocalTrustAll", promote_mode: "Strict" -codetoanalyze/java/nullsafe/ModePromotions.java, Linters_dummy_method, 33, ERADICATE_META_CLASS_IS_NULLSAFE, no_bucket, INFO, [Class codetoanalyze.java.nullsafe.Strict_NoDeps_NoPromos is free of nullability issues.], Strict_NoDeps_NoPromos, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Strict" -codetoanalyze/java/nullsafe/ModePromotions.java, Linters_dummy_method, 39, ERADICATE_META_CLASS_CAN_BE_NULLSAFE, no_bucket, ADVICE, [Congrats! `Default_UsesDefault_CanBePromotedToTrustAll` is free of nullability issues. Mark it `@Nullsafe(Nullsafe.Mode.LOCAL)` to prevent regressions.], Default_UsesDefault_CanBePromotedToTrustAll, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Default", promote_mode: "LocalTrustAll" -codetoanalyze/java/nullsafe/ModePromotions.java, Linters_dummy_method, 47, ERADICATE_META_CLASS_CAN_BE_NULLSAFE, no_bucket, ADVICE, [Congrats! `Default_UsesItself_CanBePromotedToStrict` is free of nullability issues. Mark it `@Nullsafe(Nullsafe.Mode.LOCAL)` to prevent regressions.], Default_UsesItself_CanBePromotedToStrict, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Default", promote_mode: "Strict" -codetoanalyze/java/nullsafe/ModePromotions.java, Linters_dummy_method, 59, ERADICATE_META_CLASS_CAN_BE_NULLSAFE, no_bucket, ADVICE, [Congrats! `Default_UsesLocal_CanBePromotedToTrustNone` is free of nullability issues. Mark it `@Nullsafe(Nullsafe.Mode.LOCAL)` to prevent regressions.], Default_UsesLocal_CanBePromotedToTrustNone, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Default", promote_mode: "LocalTrustNone" -codetoanalyze/java/nullsafe/ModePromotions.java, Linters_dummy_method, 67, ERADICATE_META_CLASS_CAN_BE_NULLSAFE, no_bucket, ADVICE, [Congrats! `Default_UsesStrict_CanBePromotedToStrict` is free of nullability issues. Mark it `@Nullsafe(Nullsafe.Mode.LOCAL)` to prevent regressions.], Default_UsesStrict_CanBePromotedToStrict, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Default", promote_mode: "Strict" -codetoanalyze/java/nullsafe/ModePromotions.java, Linters_dummy_method, 78, ERADICATE_META_CLASS_IS_NULLSAFE, no_bucket, INFO, [Class codetoanalyze.java.nullsafe.TrustSome_DoesNotUseTrusted_CanBePromotedToTrustNone is free of nullability issues.], TrustSome_DoesNotUseTrusted_CanBePromotedToTrustNone, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "LocalTrustSome", promote_mode: "LocalTrustNone" -codetoanalyze/java/nullsafe/ModePromotions.java, Linters_dummy_method, 87, ERADICATE_META_CLASS_IS_NULLSAFE, no_bucket, INFO, [Class codetoanalyze.java.nullsafe.TrustSome_UsesTrusted_NoPromo is free of nullability issues.], TrustSome_UsesTrusted_NoPromo, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "LocalTrustSome" -codetoanalyze/java/nullsafe/ModePromotions.java, Linters_dummy_method, 96, ERADICATE_META_CLASS_IS_NULLSAFE, no_bucket, INFO, [Class codetoanalyze.java.nullsafe.TrustSome_TrustToLocalIsNotNeeded_CanBePromotedToTrustNone is free of nullability issues.], TrustSome_TrustToLocalIsNotNeeded_CanBePromotedToTrustNone, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "LocalTrustSome", promote_mode: "LocalTrustNone" -codetoanalyze/java/nullsafe/ModePromotions.java, Linters_dummy_method, 105, ERADICATE_META_CLASS_IS_NULLSAFE, no_bucket, INFO, [Class codetoanalyze.java.nullsafe.TrustSome_TrustStrictIsNotNeeded_CanBePromotedToStrict is free of nullability issues.], TrustSome_TrustStrictIsNotNeeded_CanBePromotedToStrict, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "LocalTrustSome", promote_mode: "Strict" -codetoanalyze/java/nullsafe/ModePromotions.java, Linters_dummy_method, 112, ERADICATE_META_CLASS_IS_NULLSAFE, no_bucket, INFO, [Class codetoanalyze.java.nullsafe.TrustNone_CanBePromotedToStrict is free of nullability issues.], TrustNone_CanBePromotedToStrict, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "LocalTrustNone", promote_mode: "Strict" -codetoanalyze/java/nullsafe/MyPreconditions.java, Linters_dummy_method, 11, ERADICATE_META_CLASS_CAN_BE_NULLSAFE, no_bucket, ADVICE, [Congrats! `MyPreconditions` is free of nullability issues. Mark it `@Nullsafe(Nullsafe.Mode.LOCAL)` to prevent regressions.], MyPreconditions, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Default", promote_mode: "Strict" -codetoanalyze/java/nullsafe/NestedFieldAccess.java, Linters_dummy_method, 12, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`NestedFieldAccess` needs 15 issues to be fixed in order to be marked @Nullsafe.], NestedFieldAccess, codetoanalyze.java.nullsafe, issues: 15, curr_mode: "Default" -codetoanalyze/java/nullsafe/NestedFieldAccess.java, codetoanalyze.java.nullsafe.NestedFieldAccess$TestNullableChains.field_AccessWithoutNullCheckIsBad():void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`NestedFieldAccess$TestNullableChains.s` is nullable and is not locally checked for null when calling `length()`.], NestedFieldAccess$TestNullableChains, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NestedFieldAccess.java, codetoanalyze.java.nullsafe.NestedFieldAccess$TestNullableChains.nestedField_AccessWithoutNullCheckIsBad():void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`NestedFieldAccess$TestNullableChains.myc.s` is nullable and is not locally checked for null when calling `length()`.], NestedFieldAccess$TestNullableChains, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NestedFieldAccess.java, codetoanalyze.java.nullsafe.NestedFieldAccess$TestNullableChains.param_AccessWithoutNullCheckIsBad(codetoanalyze.java.nullsafe.NestedFieldAccess$C):void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`c.s` is nullable and is not locally checked for null when calling `length()`.], NestedFieldAccess$TestNullableChains, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NestedFieldAccess.java, codetoanalyze.java.nullsafe.NestedFieldAccess$TestNullableChains.local_AccessWithoutNullCheckIsBad():void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`c.s` is nullable and is not locally checked for null when calling `length()`.], NestedFieldAccess$TestNullableChains, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NestedFieldAccess.java, codetoanalyze.java.nullsafe.NestedFieldAccess$TestNullableChains.deep_AccessWithoutNullCheckIsBad(codetoanalyze.java.nullsafe.NestedFieldAccess$CC):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`cc.c.s` is nullable and is not locally checked for null when calling `length()`.], NestedFieldAccess$TestNullableChains, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NestedFieldAccess.java, codetoanalyze.java.nullsafe.NestedFieldAccess$TestNullableChains.veryDeep_AccessWithoutNullCheckIsBad(codetoanalyze.java.nullsafe.NestedFieldAccess$CCC):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`ccc.cc.c.s` is nullable and is not locally checked for null when calling `length()`.], NestedFieldAccess$TestNullableChains, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NestedFieldAccess.java, codetoanalyze.java.nullsafe.NestedFieldAccess$TestNullableChains.veryDeep_IncompleteAccessViaOrEarlyReturnIsBad(codetoanalyze.java.nullsafe.NestedFieldAccess$CCC):void, 3, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`ccc.cc.c.s` is nullable and is not locally checked for null when calling `length()`.], NestedFieldAccess$TestNullableChains, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NestedFieldAccess.java, codetoanalyze.java.nullsafe.NestedFieldAccess$TestFunctionsIdempotent.chainOf0VsChainOf0ParamsMismatchIsBad():void, 2, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`dontAssignNull` is declared non-nullable but is assigned a nullable: call to nullable(...) at line 145.], NestedFieldAccess$TestFunctionsIdempotent, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.NestedFieldAccess$TestFunctionsIdempotent.nullable at 145, field:codetoanalyze.java.nullsafe.NestedFieldAccess$TestFunctionsIdempotent.dontAssignNull -codetoanalyze/java/nullsafe/NestedFieldAccess.java, codetoanalyze.java.nullsafe.NestedFieldAccess$TestFunctionsIdempotent.otherObjVsItselfIsOKParamsMismatchIsBAD(codetoanalyze.java.nullsafe.NestedFieldAccess$TestFunctionsIdempotent):void, 2, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`dontAssignNull` is declared non-nullable but is assigned a nullable: call to nullable(...) at line 157.], NestedFieldAccess$TestFunctionsIdempotent, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.NestedFieldAccess$TestFunctionsIdempotent.nullable at 157, field:codetoanalyze.java.nullsafe.NestedFieldAccess$TestFunctionsIdempotent.dontAssignNull -codetoanalyze/java/nullsafe/NestedFieldAccess.java, codetoanalyze.java.nullsafe.NestedFieldAccess$TestFunctionsIdempotent.selfVsOtherObjectIsBAD(codetoanalyze.java.nullsafe.NestedFieldAccess$TestFunctionsIdempotent):void, 2, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`dontAssignNull` is declared non-nullable but is assigned a nullable: call to nullable(...) at line 163.], NestedFieldAccess$TestFunctionsIdempotent, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.NestedFieldAccess$TestFunctionsIdempotent.nullable at 163, field:codetoanalyze.java.nullsafe.NestedFieldAccess$TestFunctionsIdempotent.dontAssignNull -codetoanalyze/java/nullsafe/NestedFieldAccess.java, codetoanalyze.java.nullsafe.NestedFieldAccess$TestFunctionsIdempotent.chainOf0VsChainOf1IsBad():void, 2, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`dontAssignNull` is declared non-nullable but is assigned a nullable: call to nullable(...) at line 169.], NestedFieldAccess$TestFunctionsIdempotent, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.NestedFieldAccess$TestFunctionsIdempotent.nullable at 169, field:codetoanalyze.java.nullsafe.NestedFieldAccess$TestFunctionsIdempotent.dontAssignNull -codetoanalyze/java/nullsafe/NestedFieldAccess.java, codetoanalyze.java.nullsafe.NestedFieldAccess$TestFunctionsIdempotent.chainOf1VsChainOf0IsBad():void, 2, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`dontAssignNull` is declared non-nullable but is assigned a nullable: call to nullable(...) at line 175.], NestedFieldAccess$TestFunctionsIdempotent, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.NestedFieldAccess$TestFunctionsIdempotent.nullable at 175, field:codetoanalyze.java.nullsafe.NestedFieldAccess$TestFunctionsIdempotent.dontAssignNull -codetoanalyze/java/nullsafe/NestedFieldAccess.java, codetoanalyze.java.nullsafe.NestedFieldAccess$TestFunctionsIdempotent.chainOf1VsChainOf1ParamMismatchIsBad():void, 2, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`dontAssignNull` is declared non-nullable but is assigned a nullable: call to nullable(...) at line 187.], NestedFieldAccess$TestFunctionsIdempotent, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.NestedFieldAccess$TestFunctionsIdempotent.nullable at 187, field:codetoanalyze.java.nullsafe.NestedFieldAccess$TestFunctionsIdempotent.dontAssignNull -codetoanalyze/java/nullsafe/NestedFieldAccess.java, codetoanalyze.java.nullsafe.NestedFieldAccess$TestFunctionsIdempotent.chainOf1VsChainOf2IsBad():void, 2, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`dontAssignNull` is declared non-nullable but is assigned a nullable: call to nullable(...) at line 199.], NestedFieldAccess$TestFunctionsIdempotent, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.NestedFieldAccess$TestFunctionsIdempotent.nullable at 199, field:codetoanalyze.java.nullsafe.NestedFieldAccess$TestFunctionsIdempotent.dontAssignNull -codetoanalyze/java/nullsafe/NestedFieldAccess.java, codetoanalyze.java.nullsafe.NestedFieldAccess$TestFunctionsIdempotent.chainOf2VsChainOf1IsBad():void, 2, ERADICATE_FIELD_NOT_NULLABLE, no_bucket, WARNING, [`dontAssignNull` is declared non-nullable but is assigned a nullable: call to nullable(...) at line 205.], NestedFieldAccess$TestFunctionsIdempotent, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.NestedFieldAccess$TestFunctionsIdempotent.nullable at 205, field:codetoanalyze.java.nullsafe.NestedFieldAccess$TestFunctionsIdempotent.dontAssignNull -codetoanalyze/java/nullsafe/NoReuseUndefFunctionValues.java, Linters_dummy_method, 12, ERADICATE_META_CLASS_CAN_BE_NULLSAFE, no_bucket, ADVICE, [Congrats! `NoReuseUndefFunctionValues` is free of nullability issues. Mark it `@Nullsafe(Nullsafe.Mode.LOCAL)` to prevent regressions.], NoReuseUndefFunctionValues, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Default", promote_mode: "Strict" -codetoanalyze/java/nullsafe/NullFieldAccess.java, Linters_dummy_method, 12, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`NullFieldAccess` needs 5 issues to be fixed in order to be marked @Nullsafe.], NullFieldAccess, codetoanalyze.java.nullsafe, issues: 5, curr_mode: "Default" -codetoanalyze/java/nullsafe/NullFieldAccess.java, codetoanalyze.java.nullsafe.NullFieldAccess.(), 0, ERADICATE_FIELD_OVER_ANNOTATED, no_bucket, ADVICE, [Field `NullFieldAccess.nullableArray` is always initialized in the constructor but is declared `@Nullable`], NullFieldAccess, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.NullFieldAccess.nullableArray -codetoanalyze/java/nullsafe/NullFieldAccess.java, codetoanalyze.java.nullsafe.NullFieldAccess.(), 0, ERADICATE_FIELD_OVER_ANNOTATED, no_bucket, ADVICE, [Field `NullFieldAccess.nullable` is always initialized in the constructor but is declared `@Nullable`], NullFieldAccess, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.NullFieldAccess.nullable -codetoanalyze/java/nullsafe/NullFieldAccess.java, codetoanalyze.java.nullsafe.NullFieldAccess.testNonStaticFields():void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`bad` is nullable and is not locally checked for null when calling `toString()`: field nullable at line 36.], NullFieldAccess, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullFieldAccess.java, codetoanalyze.java.nullsafe.NullFieldAccess.testStatic():void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`bad` is nullable and is not locally checked for null when calling `toString()`: field nullableStatic at line 44.], NullFieldAccess, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullFieldAccess.java, codetoanalyze.java.nullsafe.NullFieldAccess.testInterface():void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`bad` is nullable and is not locally checked for null when calling `toString()`: field nullable at line 52.], NullFieldAccess, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullFieldAccess.java, codetoanalyze.java.nullsafe.NullFieldAccess.testArray():void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [Array `NullFieldAccess.nullableArray` is nullable and is not locally checked for null when accessing its length.], NullFieldAccess, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullFieldAccess.java, codetoanalyze.java.nullsafe.NullFieldAccess.testArray():void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [Array `NullFieldAccess.nullableArray` is nullable and is not locally checked for null when accessing at index `0`.], NullFieldAccess, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullMethodCall.java, Linters_dummy_method, 22, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`NullMethodCall` needs 35 issues to be fixed in order to be marked @Nullsafe.], NullMethodCall, codetoanalyze.java.nullsafe, issues: 23, curr_mode: "Default" -codetoanalyze/java/nullsafe/NullMethodCall.java, codetoanalyze.java.nullsafe.NullMethodCall.callOnNull():void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [NullPointerException will be thrown at this line! `s` is `null` and is dereferenced via calling `length()`: null constant at line 25.], NullMethodCall, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullMethodCall.java, codetoanalyze.java.nullsafe.NullMethodCall$Inner.outerField():int, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `length()`: field fld at line 69.], NullMethodCall$Inner, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullMethodCall.java, codetoanalyze.java.nullsafe.NullMethodCall$Inner.outerPrivateField():int, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `length()`: field pfld at line 80.], NullMethodCall$Inner, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullMethodCall.java, codetoanalyze.java.nullsafe.NullMethodCall.testFieldAssignmentIfThenElse(java.lang.String):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `length()`: null constant at line 172.], NullMethodCall, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullMethodCall.java, codetoanalyze.java.nullsafe.NullMethodCall.testExceptionPerInstruction(int):void, 6, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [NullPointerException will be thrown at this line! `s` is `null` and is dereferenced via calling `length()`: null constant at line 181.], NullMethodCall, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullMethodCall.java, codetoanalyze.java.nullsafe.NullMethodCall.testSystemGetPropertyReturn():void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `length()`: call to System.getProperty(...) at line 235 (nullable according to nullsafe internal models).], NullMethodCall, codetoanalyze.java.nullsafe, nullable_methods:java.lang.System.getProperty at 235 -codetoanalyze/java/nullsafe/NullMethodCall.java, codetoanalyze.java.nullsafe.NullMethodCall.testSystemGetenvBad():int, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`envValue` is nullable and is not locally checked for null when calling `length()`: call to System.getenv(...) at line 240 (nullable according to nullsafe internal models).], NullMethodCall, codetoanalyze.java.nullsafe, nullable_methods:java.lang.System.getenv at 240 -codetoanalyze/java/nullsafe/NullMethodCall.java, codetoanalyze.java.nullsafe.NullMethodCall.testMapGetBad(java.util.Map,java.util.HashMap,java.util.concurrent.ConcurrentHashMap):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`m.get(...)` is nullable and is not locally checked for null when calling `toString()`: call to Map.get(...) at line 260 (nullable according to nullsafe internal models).], NullMethodCall, codetoanalyze.java.nullsafe, nullable_methods:java.util.Map.get at 260 -codetoanalyze/java/nullsafe/NullMethodCall.java, codetoanalyze.java.nullsafe.NullMethodCall.testMapGetBad(java.util.Map,java.util.HashMap,java.util.concurrent.ConcurrentHashMap):void, 3, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`hm.get(...)` is nullable and is not locally checked for null when calling `toString()`: call to HashMap.get(...) at line 261 (nullable according to nullsafe internal models).], NullMethodCall, codetoanalyze.java.nullsafe, nullable_methods:java.util.HashMap.get at 261 -codetoanalyze/java/nullsafe/NullMethodCall.java, codetoanalyze.java.nullsafe.NullMethodCall.testMapGetBad(java.util.Map,java.util.HashMap,java.util.concurrent.ConcurrentHashMap):void, 4, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`chm.get(...)` is nullable and is not locally checked for null when calling `toString()`: call to ConcurrentHashMap.get(...) at line 262 (nullable according to nullsafe internal models).], NullMethodCall, codetoanalyze.java.nullsafe, nullable_methods:java.util.concurrent.ConcurrentHashMap.get at 262 -codetoanalyze/java/nullsafe/NullMethodCall.java, codetoanalyze.java.nullsafe.NullMethodCall.testMapRemoveBad(java.util.Map,java.util.HashMap,java.util.concurrent.ConcurrentHashMap):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`m.remove(...)` is nullable and is not locally checked for null when calling `toString()`: call to Map.remove(...) at line 267 (nullable according to nullsafe internal models).], NullMethodCall, codetoanalyze.java.nullsafe, nullable_methods:java.util.Map.remove at 267 -codetoanalyze/java/nullsafe/NullMethodCall.java, codetoanalyze.java.nullsafe.NullMethodCall.testMapRemoveBad(java.util.Map,java.util.HashMap,java.util.concurrent.ConcurrentHashMap):void, 3, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`hm.remove(...)` is nullable and is not locally checked for null when calling `toString()`: call to HashMap.remove(...) at line 268 (nullable according to nullsafe internal models).], NullMethodCall, codetoanalyze.java.nullsafe, nullable_methods:java.util.HashMap.remove at 268 -codetoanalyze/java/nullsafe/NullMethodCall.java, codetoanalyze.java.nullsafe.NullMethodCall.testMapRemoveBad(java.util.Map,java.util.HashMap,java.util.concurrent.ConcurrentHashMap):void, 4, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`chm.remove(...)` is nullable and is not locally checked for null when calling `toString()`: call to ConcurrentHashMap.remove(...) at line 269 (nullable according to nullsafe internal models).], NullMethodCall, codetoanalyze.java.nullsafe, nullable_methods:java.util.concurrent.ConcurrentHashMap.remove at 269 -codetoanalyze/java/nullsafe/NullMethodCall.java, codetoanalyze.java.nullsafe.NullMethodCall.FP_propagatesNonNullAfterComparisonFieldOkay(java.lang.Object):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`NullMethodCall.nullableField` is nullable and is not locally checked for null when calling `toString()`.], NullMethodCall, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullMethodCall.java, codetoanalyze.java.nullsafe.NullMethodCall.FP_propagatesNonNullAfterComparisonParameterOkay(java.lang.Object,java.lang.Object):void, 3, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`nullableParameter` is nullable and is not locally checked for null when calling `toString()`.], NullMethodCall, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullMethodCall.java, codetoanalyze.java.nullsafe.NullMethodCall.nullMethodCallWithAlarmManager(android.app.AlarmManager,android.app.PendingIntent):void, 1, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`AlarmManager.cancel(...)`: parameter #1 is declared non-nullable (according to nullsafe internal models) but the argument `intent` is nullable.], NullMethodCall, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullMethodCall.java, codetoanalyze.java.nullsafe.NullMethodCall.callingSeverSideNullableGetter(codetoanalyze.java.nullsafe.ServerSideDeserializer):java.lang.String, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`deserializer.nullableGetter()` is nullable and is not locally checked for null when calling `toString()`.], NullMethodCall, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.ServerSideDeserializer.nullableGetter at 307 -codetoanalyze/java/nullsafe/NullMethodCall.java, codetoanalyze.java.nullsafe.NullMethodCall.withConjuction(codetoanalyze.java.nullsafe.NullMethodCall$AnotherI,boolean,boolean):void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`i` is nullable and is not locally checked for null when calling `withBooleanParameter(...)`.], NullMethodCall, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullMethodCall.java, codetoanalyze.java.nullsafe.NullMethodCall.withConditionalAssignemnt(codetoanalyze.java.nullsafe.NullMethodCall$AnotherI,boolean,java.lang.Object,java.lang.Object):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`i` is nullable and is not locally checked for null when calling `withObjectParameter(...)`.], NullMethodCall, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullMethodCall.java, codetoanalyze.java.nullsafe.NullMethodCall.nullabilityNotPreservedAfterAssignment():void, 3, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`t` is nullable and is not locally checked for null when calling `toString()`: call to getNullable() at line 340.], NullMethodCall, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.NullMethodCall.getNullable at 340 -codetoanalyze/java/nullsafe/NullMethodCall.java, codetoanalyze.java.nullsafe.NullMethodCall.nullabilityStoredInBooleanFP():void, 3, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`getNullable()` is nullable and is not locally checked for null when calling `toString()`.], NullMethodCall, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.NullMethodCall.getNullable at 346 -codetoanalyze/java/nullsafe/NullMethodCall.java, codetoanalyze.java.nullsafe.NullMethodCall.testPathGetParent():java.lang.String, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`get(...).getParent()` is nullable and is not locally checked for null when calling `toString()`: call to Path.getParent() at line 360 (nullable according to nullsafe internal models).], NullMethodCall, codetoanalyze.java.nullsafe, nullable_methods:java.nio.file.Path.getParent at 360 -codetoanalyze/java/nullsafe/NullMethodCall.java, codetoanalyze.java.nullsafe.NullMethodCall.testNotDetectingInvariantFP(java.lang.Object,java.lang.Object):java.lang.String, 4, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`object2` is nullable and is not locally checked for null when calling `toString()`.], NullMethodCall, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeMode.java, Linters_dummy_method, 16, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`NullsafeMode` needs 9 issues to be fixed in order to be marked @Nullsafe.], NullsafeMode, codetoanalyze.java.nullsafe, issues: 13, curr_mode: "Default" -codetoanalyze/java/nullsafe/NullsafeMode.java, codetoanalyze.java.nullsafe.NullsafeMode$TrustAllNullsafe.BAD_returnNullFromNonNulsafe():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, ERROR, [`BAD_returnNullFromNonNulsafe()`: return type is declared non-nullable but the method returns a nullable value: call to returnNull() at line 89.], NullsafeMode$TrustAllNullsafe, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.NullsafeMode$VariousMethods.returnNull at 89 -codetoanalyze/java/nullsafe/NullsafeMode.java, codetoanalyze.java.nullsafe.NullsafeMode$TrustAllNullsafe.BAD_returnFromUnvettedThirdParty():java.lang.String, 1, ERADICATE_UNVETTED_THIRD_PARTY_IN_NULLSAFE, no_bucket, ERROR, [`ThirdPartyTestClass.returnUnspecified()`: `@Nullsafe` mode prohibits using values coming from not vetted third party methods without a check. Result of this call is used at line 92. Either add a local check for null or assertion, or add the correct signature to nullsafe/third-party-signatures/some.test.pckg.sig.], NullsafeMode$TrustAllNullsafe, codetoanalyze.java.nullsafe, unvetted_3rd_party:[some.test.pckg.ThirdPartyTestClass#returnUnspecified()] -codetoanalyze/java/nullsafe/NullsafeMode.java, codetoanalyze.java.nullsafe.NullsafeMode$TrustAllNullsafe.BAD_returnNullableFieldFromThirdParty():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, ERROR, [`BAD_returnNullableFieldFromThirdParty()`: return type is declared non-nullable but the method returns a nullable value: field nullableField at line 97.], NullsafeMode$TrustAllNullsafe, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeMode.java, codetoanalyze.java.nullsafe.NullsafeMode$TrustAllNullsafe.BAD_returnNonNullableFieldFromThirdParty():java.lang.String, 1, ERADICATE_UNVETTED_THIRD_PARTY_IN_NULLSAFE, no_bucket, ERROR, [`ThirdPartyTestClass.nonNullableField`: `@Nullsafe` mode prohibits using values coming from not vetted third party fields without a check. This field is used at line 100. Either add a local check for null or assertion, or access `nonNullableField` via a nullsafe getter.], NullsafeMode$TrustAllNullsafe, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeMode.java, codetoanalyze.java.nullsafe.NullsafeMode$TrustAllNullsafe.BAD_passThirdPartyToUnchecked():codetoanalyze.java.nullsafe.NullsafeMode$UncheckedParams, 1, ERADICATE_UNVETTED_THIRD_PARTY_IN_NULLSAFE, no_bucket, ERROR, [`ThirdPartyTestClass.getUncheckedLong(...)`: `@Nullsafe` mode prohibits using values coming from not vetted third party methods without a check. Result of this call is used at line 113. Either add a local check for null or assertion, or add the correct signature to nullsafe/third-party-signatures/some.test.pckg.sig.], NullsafeMode$TrustAllNullsafe, codetoanalyze.java.nullsafe, unvetted_3rd_party:[some.test.pckg.ThirdPartyTestClass#getUncheckedLong(long)] -codetoanalyze/java/nullsafe/NullsafeMode.java, codetoanalyze.java.nullsafe.NullsafeMode$TrustSomeNullsafe.BAD_returnFromUntrustedNonNullsafe():java.lang.String, 1, ERADICATE_UNCHECKED_USAGE_IN_NULLSAFE, no_bucket, ERROR, [`NullsafeMode$VariousMethods.returnVal()`: `@Nullsafe(trust={...})` prohibits using values coming from non-`@Nullsafe` classes without a check, unless the class is in the trust list. Result of this call is used at line 138. Either add a local check for null or assertion, or make `NullsafeMode$VariousMethods` @Nullsafe (or add it to trust list).], NullsafeMode$TrustSomeNullsafe, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeMode.java, codetoanalyze.java.nullsafe.NullsafeMode$TrustSomeNullsafe.OK_returnFromUntrustedNonNullsafeAsNullable():java.lang.String, 0, ERADICATE_RETURN_OVER_ANNOTATED, no_bucket, ADVICE, [Method `OK_returnFromUntrustedNonNullsafeAsNullable()` is annotated with `@Nullable` but never returns null.], NullsafeMode$TrustSomeNullsafe, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeMode.java, codetoanalyze.java.nullsafe.NullsafeMode$TrustSomeNullsafe.BAD_returnNullFromNonNulsafe():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, ERROR, [`BAD_returnNullFromNonNulsafe()`: return type is declared non-nullable but the method returns a nullable value: call to returnNull() at line 148.], NullsafeMode$TrustSomeNullsafe, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.NullsafeMode$VariousMethods.returnNull at 148 -codetoanalyze/java/nullsafe/NullsafeMode.java, codetoanalyze.java.nullsafe.NullsafeMode$TrustSomeNullsafe.FP_OK_accessFieldFromNonNullsafe():java.lang.String, 1, ERADICATE_UNCHECKED_USAGE_IN_NULLSAFE, no_bucket, ERROR, [`NullsafeMode$NonNullsafe.valField`: `@Nullsafe(trust={...})` prohibits using values coming from non-`@Nullsafe` classes without a check, unless the class is in the trust list. This field is used at line 151. Either add a local check for null or assertion, or make `NullsafeMode$NonNullsafe` @Nullsafe (or add it to trust list).], NullsafeMode$TrustSomeNullsafe, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeMode.java, codetoanalyze.java.nullsafe.NullsafeMode$TrustNoneNullsafe.BAD_returnFromNonNullsafe():java.lang.String, 1, ERADICATE_UNCHECKED_USAGE_IN_NULLSAFE, no_bucket, ERROR, [`NullsafeMode$NonNullsafe.returnVal()`: `@Nullsafe(trust={...})` prohibits using values coming from non-`@Nullsafe` classes without a check, unless the class is in the trust list. Result of this call is used at line 158. Either add a local check for null or assertion, or make `NullsafeMode$NonNullsafe` @Nullsafe (or add it to trust list).], NullsafeMode$TrustNoneNullsafe, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeMode.java, codetoanalyze.java.nullsafe.NullsafeMode$NullsafeWithStrictMode.BAD_returnFromNonStrict():java.lang.String, 1, ERADICATE_UNCHECKED_USAGE_IN_NULLSAFE, no_bucket, ERROR, [`NullsafeMode$VariousMethods.returnVal()`: `@Nullsafe(STRICT)` prohibits using values coming from non-strict classes without a check. Result of this call is used at line 178. Either add a local check for null or assertion, or make `NullsafeMode$VariousMethods` nullsafe strict.], NullsafeMode$NullsafeWithStrictMode, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeMode.java, codetoanalyze.java.nullsafe.NullsafeMode$StrictNullsafe.BAD_returnFromNonNullsafe():java.lang.String, 1, ERADICATE_UNCHECKED_USAGE_IN_NULLSAFE, no_bucket, ERROR, [`NullsafeMode$NonNullsafe.returnVal()`: `@Nullsafe(STRICT)` prohibits using values coming from non-strict classes without a check. Result of this call is used at line 201. Either add a local check for null or assertion, or make `NullsafeMode$NonNullsafe` nullsafe strict.], NullsafeMode$StrictNullsafe, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeMode.java, codetoanalyze.java.nullsafe.NullsafeMode$StrictNullsafe.BAD_passThirdPartyToUnchecked():codetoanalyze.java.nullsafe.NullsafeMode$UncheckedParams, 1, ERADICATE_UNVETTED_THIRD_PARTY_IN_NULLSAFE, no_bucket, ERROR, [`ThirdPartyTestClass.getUncheckedLong(...)`: `@Nullsafe` mode prohibits using values coming from not vetted third party methods without a check. Result of this call is used at line 218. Either add a local check for null or assertion, or add the correct signature to nullsafe/third-party-signatures/some.test.pckg.sig.], NullsafeMode$StrictNullsafe, codetoanalyze.java.nullsafe, unvetted_3rd_party:[some.test.pckg.ThirdPartyTestClass#getUncheckedLong(long)] -codetoanalyze/java/nullsafe/NullsafeMode.java, codetoanalyze.java.nullsafe.NullsafeMode$StrictNullsafe.BAD_dereferenceNotAnnotatedThirdParty():void, 1, ERADICATE_UNVETTED_THIRD_PARTY_IN_NULLSAFE, no_bucket, ERROR, [`ThirdPartyTestClass.returnUnspecified()`: `@Nullsafe` mode prohibits using values coming from not vetted third party methods without a check. Result of this call is used at line 222. Either add a local check for null or assertion, or add the correct signature to nullsafe/third-party-signatures/some.test.pckg.sig.], NullsafeMode$StrictNullsafe, codetoanalyze.java.nullsafe, unvetted_3rd_party:[some.test.pckg.ThirdPartyTestClass#returnUnspecified()] -codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java, Linters_dummy_method, 14, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [@Nullsafe classes should have exactly zero nullability issues. `NullsafeLocal` has 5.], NullsafeLocal, codetoanalyze.java.nullsafe, issues: 5, curr_mode: "LocalTrustAll" -codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java, codetoanalyze.java.nullsafe.NullsafeLocal.shouldBeNullsafeModeError():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, ERROR, [`shouldBeNullsafeModeError()`: return type is declared non-nullable but the method returns `null`: null constant at line 17.], NullsafeLocal, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java, codetoanalyze.java.nullsafe.NullsafeLocal$Nested.shouldBeNullsafeModeError():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, ERROR, [`shouldBeNullsafeModeError()`: return type is declared non-nullable but the method returns `null`: null constant at line 23.], NullsafeLocal$Nested, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java, codetoanalyze.java.nullsafe.NullsafeLocal$Nested$DeeplyNested.shouldBeNullsafeModeError():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, ERROR, [`shouldBeNullsafeModeError()`: return type is declared non-nullable but the method returns `null`: null constant at line 29.], NullsafeLocal$Nested$DeeplyNested, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java, codetoanalyze.java.nullsafe.NullsafeLocal$NestedStrict.returningDefaultNotNullIsError():java.lang.String, 1, ERADICATE_UNCHECKED_USAGE_IN_NULLSAFE, no_bucket, ERROR, [`Default.getString()`: `@Nullsafe(STRICT)` prohibits using values coming from non-strict classes without a check. Result of this call is used at line 42. Either add a local check for null or assertion, or make `Default` nullsafe strict.], NullsafeLocal$NestedStrict, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java, Linters_dummy_method, 49, ERADICATE_REDUNDANT_NESTED_CLASS_ANNOTATION, no_bucket, ADVICE, [`NullsafeLocal$NestedExplicitLocal`: the same @Nullsafe mode is already specified in the outer class, so this annotation can be removed.], NullsafeLocal$NestedExplicitLocal, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java, codetoanalyze.java.nullsafe.NullsafeLocal$NestedExplicitLocal.shouldBeNullsafeModeError():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, ERROR, [`shouldBeNullsafeModeError()`: return type is declared non-nullable but the method returns `null`: null constant at line 51.], NullsafeLocal$NestedExplicitLocal, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java, Linters_dummy_method, 57, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [@Nullsafe classes should have exactly zero nullability issues. `NullsafeStrict` has 4.], NullsafeStrict, codetoanalyze.java.nullsafe, issues: 4, curr_mode: "Strict" -codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java, codetoanalyze.java.nullsafe.NullsafeStrict.returningDefaultNotNullIsError():java.lang.String, 1, ERADICATE_UNCHECKED_USAGE_IN_NULLSAFE, no_bucket, ERROR, [`Default.getString()`: `@Nullsafe(STRICT)` prohibits using values coming from non-strict classes without a check. Result of this call is used at line 58. Either add a local check for null or assertion, or make `Default` nullsafe strict.], NullsafeStrict, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java, codetoanalyze.java.nullsafe.NullsafeStrict$Nested.returningDefaultNotNullIsError():java.lang.String, 1, ERADICATE_UNCHECKED_USAGE_IN_NULLSAFE, no_bucket, ERROR, [`Default.getString()`: `@Nullsafe(STRICT)` prohibits using values coming from non-strict classes without a check. Result of this call is used at line 64. Either add a local check for null or assertion, or make `Default` nullsafe strict.], NullsafeStrict$Nested, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java, Linters_dummy_method, 71, ERADICATE_BAD_NESTED_CLASS_ANNOTATION, no_bucket, WARNING, [`NullsafeStrict$Nested$DeeplyNestedLocalIsStillStrict`: nested classes are disallowed to weaken @Nullsafe mode specified in the outer class. This annotation will be ignored.], NullsafeStrict$Nested$DeeplyNestedLocalIsStillStrict, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java, codetoanalyze.java.nullsafe.NullsafeStrict$Nested$DeeplyNestedLocalIsStillStrict.returningDefaultNotNullIsError():java.lang.String, 1, ERADICATE_UNCHECKED_USAGE_IN_NULLSAFE, no_bucket, ERROR, [`Default.getString()`: `@Nullsafe(STRICT)` prohibits using values coming from non-strict classes without a check. Result of this call is used at line 72. Either add a local check for null or assertion, or make `Default` nullsafe strict.], NullsafeStrict$Nested$DeeplyNestedLocalIsStillStrict, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java, Linters_dummy_method, 80, ERADICATE_BAD_NESTED_CLASS_ANNOTATION, no_bucket, WARNING, [`NullsafeStrict$NestedLocalIsStillStrict`: nested classes are disallowed to weaken @Nullsafe mode specified in the outer class. This annotation will be ignored.], NullsafeStrict$NestedLocalIsStillStrict, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java, codetoanalyze.java.nullsafe.NullsafeStrict$NestedLocalIsStillStrict.returningDefaultNotNullIsError():java.lang.String, 1, ERADICATE_UNCHECKED_USAGE_IN_NULLSAFE, no_bucket, ERROR, [`Default.getString()`: `@Nullsafe(STRICT)` prohibits using values coming from non-strict classes without a check. Result of this call is used at line 81. Either add a local check for null or assertion, or make `Default` nullsafe strict.], NullsafeStrict$NestedLocalIsStillStrict, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java, Linters_dummy_method, 87, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`Default` needs 1 issues to be fixed in order to be marked @Nullsafe.], Default, codetoanalyze.java.nullsafe, issues: 2, curr_mode: "Default" -codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java, codetoanalyze.java.nullsafe.Default$NestedLocal.shouldBeNullsafeModeError():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, ERROR, [`shouldBeNullsafeModeError()`: return type is declared non-nullable but the method returns `null`: null constant at line 96.], Default$NestedLocal, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java, codetoanalyze.java.nullsafe.Default$NestedLocal$DeeplyNestedStrict.returningDefaultNotNullIsError():java.lang.String, 1, ERADICATE_UNCHECKED_USAGE_IN_NULLSAFE, no_bucket, ERROR, [`Default.getString()`: `@Nullsafe(STRICT)` prohibits using values coming from non-strict classes without a check. Result of this call is used at line 107. Either add a local check for null or assertion, or make `Default` nullsafe strict.], Default$NestedLocal$DeeplyNestedStrict, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java, Linters_dummy_method, 114, ERADICATE_META_CLASS_CAN_BE_NULLSAFE, no_bucket, ADVICE, [Congrats! `A` is free of nullability issues. Mark it `@Nullsafe(Nullsafe.Mode.LOCAL)` to prevent regressions.], A, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Default", promote_mode: "Strict" -codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java, Linters_dummy_method, 120, ERADICATE_META_CLASS_CAN_BE_NULLSAFE, no_bucket, ADVICE, [Congrats! `B` is free of nullability issues. Mark it `@Nullsafe(Nullsafe.Mode.LOCAL)` to prevent regressions.], B, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Default", promote_mode: "Strict" -codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java, Linters_dummy_method, 122, ERADICATE_META_CLASS_CAN_BE_NULLSAFE, no_bucket, ADVICE, [Congrats! `C` is free of nullability issues. Mark it `@Nullsafe(Nullsafe.Mode.LOCAL)` to prevent regressions.], C, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Default", promote_mode: "Strict" -codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java, Linters_dummy_method, 129, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [@Nullsafe classes should have exactly zero nullability issues. `TrustSome` has 4.], TrustSome, codetoanalyze.java.nullsafe, issues: 4, curr_mode: "LocalTrustSome" -codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java, codetoanalyze.java.nullsafe.TrustSome.dontTrustC_Bad():java.lang.String, 1, ERADICATE_UNCHECKED_USAGE_IN_NULLSAFE, no_bucket, ERROR, [`C.getString()`: `@Nullsafe(trust={...})` prohibits using values coming from non-`@Nullsafe` classes without a check, unless the class is in the trust list. Result of this call is used at line 135. Either add a local check for null or assertion, or make `C` @Nullsafe (or add it to trust list).], TrustSome, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java, codetoanalyze.java.nullsafe.TrustSome$NotAnnotatedNested.dontTrustC_Bad():java.lang.String, 1, ERADICATE_UNCHECKED_USAGE_IN_NULLSAFE, no_bucket, ERROR, [`C.getString()`: `@Nullsafe(trust={...})` prohibits using values coming from non-`@Nullsafe` classes without a check, unless the class is in the trust list. Result of this call is used at line 145. Either add a local check for null or assertion, or make `C` @Nullsafe (or add it to trust list).], TrustSome$NotAnnotatedNested, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java, codetoanalyze.java.nullsafe.TrustSome$CanRemoveFromTrustList.dontTrustA_BAD():java.lang.String, 1, ERADICATE_UNCHECKED_USAGE_IN_NULLSAFE, no_bucket, ERROR, [`A.getString()`: `@Nullsafe(trust={...})` prohibits using values coming from non-`@Nullsafe` classes without a check, unless the class is in the trust list. Result of this call is used at line 153. Either add a local check for null or assertion, or make `A` @Nullsafe (or add it to trust list).], TrustSome$CanRemoveFromTrustList, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java, Linters_dummy_method, 161, ERADICATE_BAD_NESTED_CLASS_ANNOTATION, no_bucket, WARNING, [Nested classes cannot add classes to trust list if they are not in the outer class trust list. Remove `C` from trust list.], TrustSome$CanNotAddToTrustList, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/NullsafeModeNestedClasses.java, codetoanalyze.java.nullsafe.TrustSome$CanNotAddToTrustList.stillDontTrustC_BAD():java.lang.String, 1, ERADICATE_UNCHECKED_USAGE_IN_NULLSAFE, no_bucket, ERROR, [`C.getString()`: `@Nullsafe(trust={...})` prohibits using values coming from non-`@Nullsafe` classes without a check, unless the class is in the trust list. Result of this call is used at line 162. Either add a local check for null or assertion, or make `C` @Nullsafe (or add it to trust list).], TrustSome$CanNotAddToTrustList, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, Linters_dummy_method, 20, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`ParameterNotNullable` needs 38 issues to be fixed in order to be marked @Nullsafe.], ParameterNotNullable, codetoanalyze.java.nullsafe, issues: 38, curr_mode: "Default" -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.callNull():void, 2, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ParameterNotNullable.test(...)`: parameter #1(`s`) is declared non-nullable but the argument `s` is `null`: null constant at line 39.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.callNullable(java.lang.String):void, 1, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ParameterNotNullable.test(...)`: parameter #1(`s`) is declared non-nullable but the argument `s` is nullable.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testSystemGetPropertyArgument():java.lang.String, 1, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`System.getProperty(...)`: parameter #1 is declared non-nullable (according to nullsafe internal models) but the argument is `null`.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testSystemGetenvBad():java.lang.String, 1, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`System.getenv(...)`: parameter #1 is declared non-nullable (according to nullsafe internal models) but the argument is `null`.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testClassGetResourceArgument(java.lang.Class):java.net.URL, 1, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`Class.getResource(...)`: parameter #1 is declared non-nullable (according to nullsafe internal models) but the argument is `null`.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testThreeParameters():void, 2, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ParameterNotNullable.threeParameters(...)`: parameter #1(`s1`) is declared non-nullable but the argument is `null`.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testThreeParameters():void, 3, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ParameterNotNullable.threeParameters(...)`: parameter #2(`s2`) is declared non-nullable but the argument is `null`.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testThreeParameters():void, 4, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ParameterNotNullable.threeParameters(...)`: parameter #3(`s3`) is declared non-nullable but the argument is `null`.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable$ConstructorCall.(codetoanalyze.java.nullsafe.ParameterNotNullable,int), 0, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ParameterNotNullable$ConstructorCall(...)`: parameter #2(`s`) is declared non-nullable but the argument is `null`.], ParameterNotNullable$ConstructorCall, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.callWithNullableFirstParameter(boolean,boolean):void, 1, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ParameterNotNullable.doesNotAcceptNullableFirstParameter(...)`: parameter #1(`object`) is declared non-nullable but the argument `formal parameter object` is `null`: null constant at line 112.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.callWithConditionalAssignment(java.lang.Object,boolean):void, 1, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ParameterNotNullable.doesNotAcceptNullableFirstParameter(...)`: parameter #1(`object`) is declared non-nullable but the argument `object` is nullable: null constant at line 116.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testImmutableListOfnotNullArguments():void, 4, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ImmutableList.of(...)`: parameter #1 is declared non-nullable (according to nullsafe internal models) but the argument is `null`.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testImmutableListOfnotNullArguments():void, 5, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ImmutableList.of(...)`: parameter #1 is declared non-nullable (according to nullsafe internal models) but the argument is `null`.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testImmutableListOfnotNullArguments():void, 5, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ImmutableList.of(...)`: parameter #2 is declared non-nullable (according to nullsafe internal models) but the argument is `null`.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testImmutableListOfnotNullArguments():void, 7, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ImmutableList.of(...)`: parameter #2 is declared non-nullable (according to nullsafe internal models) but the argument is `null`.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testImmutableListCopyOfNotNullArguments():void, 6, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ImmutableList.copyOf(...)`: parameter #1 is declared non-nullable (according to nullsafe internal models) but the argument `nullIterable` is `null`: null constant at line 131.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testImmutableListCopyOfNotNullArguments():void, 7, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ImmutableList.copyOf(...)`: parameter #1 is declared non-nullable (according to nullsafe internal models) but the argument `nullIterator` is `null`: null constant at line 132.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testImmutableListCopyOfNotNullArguments():void, 8, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ImmutableList.copyOf(...)`: parameter #1 is declared non-nullable (according to nullsafe internal models) but the argument `nullCollection` is `null`: null constant at line 133.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testImmutableListSortedCopyOfNotNullArguments():void, 1, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ImmutableList.sortedCopyOf(...)`: parameter #1 is declared non-nullable (according to nullsafe internal models) but the argument is `null`.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testImmutableListSortedCopyOfNotNullArguments():void, 1, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ImmutableList.sortedCopyOf(...)`: parameter #2 is declared non-nullable (according to nullsafe internal models) but the argument is `null`.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testImmutableSetOfnotNullArguments():void, 4, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ImmutableSet.of(...)`: parameter #1 is declared non-nullable (according to nullsafe internal models) but the argument is `null`.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testImmutableSetOfnotNullArguments():void, 5, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ImmutableSet.of(...)`: parameter #1 is declared non-nullable (according to nullsafe internal models) but the argument is `null`.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testImmutableSetOfnotNullArguments():void, 5, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ImmutableSet.of(...)`: parameter #2 is declared non-nullable (according to nullsafe internal models) but the argument is `null`.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testImmutableSetOfnotNullArguments():void, 7, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ImmutableSet.of(...)`: parameter #2 is declared non-nullable (according to nullsafe internal models) but the argument is `null`.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testImmutableSetOfnotNullArguments():void, 8, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ImmutableSet.of(...)`: parameter #2 is declared non-nullable (according to nullsafe internal models) but the argument is `null`.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testImmutableSetOfnotNullArguments():void, 8, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ImmutableSet.of(...)`: parameter #4 is declared non-nullable (according to nullsafe internal models) but the argument is `null`.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testImmutableSetCopyOfNotNullArguments():void, 6, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ImmutableSet.copyOf(...)`: parameter #1 is declared non-nullable (according to nullsafe internal models) but the argument `nullIterable` is `null`: null constant at line 157.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testImmutableSetCopyOfNotNullArguments():void, 7, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ImmutableSet.copyOf(...)`: parameter #1 is declared non-nullable (according to nullsafe internal models) but the argument `nullIterator` is `null`: null constant at line 158.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testImmutableSetCopyOfNotNullArguments():void, 8, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ImmutableSet.copyOf(...)`: parameter #1 is declared non-nullable (according to nullsafe internal models) but the argument `nullCollection` is `null`: null constant at line 159.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testImmutableMapOfnotNullArguments():void, 4, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ImmutableMap.of(...)`: parameter #1 is declared non-nullable (according to nullsafe internal models) but the argument is `null`.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testImmutableMapOfnotNullArguments():void, 4, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ImmutableMap.of(...)`: parameter #2 is declared non-nullable (according to nullsafe internal models) but the argument is `null`.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testImmutableMapOfnotNullArguments():void, 6, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ImmutableMap.of(...)`: parameter #2 is declared non-nullable (according to nullsafe internal models) but the argument is `null`.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testImmutableMapOfnotNullArguments():void, 6, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ImmutableMap.of(...)`: parameter #4 is declared non-nullable (according to nullsafe internal models) but the argument is `null`.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testImmutableMapCopyOfNotNullArguments():void, 5, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ImmutableMap.copyOf(...)`: parameter #1 is declared non-nullable (according to nullsafe internal models) but the argument `nullIterable` is `null`: null constant at line 177.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testImmutableMapCopyOfNotNullArguments():void, 6, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`ImmutableMap.copyOf(...)`: parameter #1 is declared non-nullable (according to nullsafe internal models) but the argument `nullMap` is `null`: null constant at line 178.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testParsingNullStringToNumber():void, 2, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`Long.parseLong(...)`: parameter #1 is declared non-nullable (according to nullsafe internal models) but the argument `ns` is `null`: null constant at line 185.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, codetoanalyze.java.nullsafe.ParameterNotNullable.testParsingNullStringToNumber():void, 3, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`Integer.parseInt(...)`: parameter #1 is declared non-nullable (according to nullsafe internal models) but the argument `ns` is `null`: null constant at line 185.], ParameterNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ParameterNotNullable.java, Linters_dummy_method, 195, ERADICATE_META_CLASS_CAN_BE_NULLSAFE, no_bucket, ADVICE, [Congrats! `SomeClass` is free of nullability issues. Mark it `@Nullsafe(Nullsafe.Mode.LOCAL)` to prevent regressions.], SomeClass, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Default", promote_mode: "Strict" -codetoanalyze/java/nullsafe/PropagatesNullable.java, Linters_dummy_method, 14, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`TestPropagatesNullable` needs 20 issues to be fixed in order to be marked @Nullsafe.], TestPropagatesNullable, codetoanalyze.java.nullsafe, issues: 20, curr_mode: "Default" -codetoanalyze/java/nullsafe/PropagatesNullable.java, codetoanalyze.java.nullsafe.TestPropagatesNullable$TestOneParameter.test(java.lang.String,java.lang.String):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [NullPointerException will be thrown at this line! `propagatesNullable(...)` is `null` and is dereferenced via calling `length()`: null constant at line 30.], TestPropagatesNullable$TestOneParameter, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/PropagatesNullable.java, codetoanalyze.java.nullsafe.TestPropagatesNullable$TestOneParameter.test(java.lang.String,java.lang.String):void, 3, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`nullable(...)` is nullable and is not locally checked for null when calling `length()`.], TestPropagatesNullable$TestOneParameter, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.TestPropagatesNullable$TestOneParameter.nullable at 31 -codetoanalyze/java/nullsafe/PropagatesNullable.java, codetoanalyze.java.nullsafe.TestPropagatesNullable$TestOneParameter.test(java.lang.String,java.lang.String):void, 6, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`propagatesNullable(...)` is nullable and is not locally checked for null when calling `length()`: method parameter sNullable.], TestPropagatesNullable$TestOneParameter, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/PropagatesNullable.java, codetoanalyze.java.nullsafe.TestPropagatesNullable$TestOneParameter.test(java.lang.String,java.lang.String):void, 7, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`nullable(...)` is nullable and is not locally checked for null when calling `length()`.], TestPropagatesNullable$TestOneParameter, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.TestPropagatesNullable$TestOneParameter.nullable at 35 -codetoanalyze/java/nullsafe/PropagatesNullable.java, codetoanalyze.java.nullsafe.TestPropagatesNullable$TestOneParameter.test(java.lang.String,java.lang.String):void, 11, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`nullable(...)` is nullable and is not locally checked for null when calling `length()`.], TestPropagatesNullable$TestOneParameter, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.TestPropagatesNullable$TestOneParameter.nullable at 39 -codetoanalyze/java/nullsafe/PropagatesNullable.java, codetoanalyze.java.nullsafe.TestPropagatesNullable$TestOneParameter.test(java.lang.String,java.lang.String):void, 15, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`nullable(...)` is nullable and is not locally checked for null when calling `length()`.], TestPropagatesNullable$TestOneParameter, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.TestPropagatesNullable$TestOneParameter.nullable at 43 -codetoanalyze/java/nullsafe/PropagatesNullable.java, codetoanalyze.java.nullsafe.TestPropagatesNullable$TestOneParameter.test(java.lang.String,java.lang.String):void, 21, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`propagatesNullable(...)` is nullable and is not locally checked for null when calling `length()`: method parameter sNullable.], TestPropagatesNullable$TestOneParameter, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/PropagatesNullable.java, codetoanalyze.java.nullsafe.TestPropagatesNullable$TestOneParameter.test(java.lang.String,java.lang.String):void, 22, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`nullable(...)` is nullable and is not locally checked for null when calling `length()`.], TestPropagatesNullable$TestOneParameter, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.TestPropagatesNullable$TestOneParameter.nullable at 35 -codetoanalyze/java/nullsafe/PropagatesNullable.java, codetoanalyze.java.nullsafe.TestPropagatesNullable$TestSecondParameter.test(java.lang.String,java.lang.String):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`propagatesNullable(...)` is nullable and is not locally checked for null when calling `length()`: method parameter sNullable.], TestPropagatesNullable$TestSecondParameter, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/PropagatesNullable.java, codetoanalyze.java.nullsafe.TestPropagatesNullable$TestSecondParameter.test(java.lang.String,java.lang.String):void, 3, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`nullable(...)` is nullable and is not locally checked for null when calling `length()`.], TestPropagatesNullable$TestSecondParameter, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.TestPropagatesNullable$TestSecondParameter.nullable at 80 -codetoanalyze/java/nullsafe/PropagatesNullable.java, codetoanalyze.java.nullsafe.TestPropagatesNullable$TestSecondParameter.test(java.lang.String,java.lang.String):void, 6, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`propagatesNullable(...)` is nullable and is not locally checked for null when calling `length()`: method parameter sNullable.], TestPropagatesNullable$TestSecondParameter, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/PropagatesNullable.java, codetoanalyze.java.nullsafe.TestPropagatesNullable$TestSecondParameter.test(java.lang.String,java.lang.String):void, 7, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`nullable(...)` is nullable and is not locally checked for null when calling `length()`.], TestPropagatesNullable$TestSecondParameter, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.TestPropagatesNullable$TestSecondParameter.nullable at 84 -codetoanalyze/java/nullsafe/PropagatesNullable.java, codetoanalyze.java.nullsafe.TestPropagatesNullable$TestSecondParameter.test(java.lang.String,java.lang.String):void, 11, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`nullable(...)` is nullable and is not locally checked for null when calling `length()`.], TestPropagatesNullable$TestSecondParameter, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.TestPropagatesNullable$TestSecondParameter.nullable at 88 -codetoanalyze/java/nullsafe/PropagatesNullable.java, codetoanalyze.java.nullsafe.TestPropagatesNullable$TestSecondParameter.test(java.lang.String,java.lang.String):void, 15, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`nullable(...)` is nullable and is not locally checked for null when calling `length()`.], TestPropagatesNullable$TestSecondParameter, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.TestPropagatesNullable$TestSecondParameter.nullable at 92 -codetoanalyze/java/nullsafe/PropagatesNullable.java, codetoanalyze.java.nullsafe.TestPropagatesNullable$TestBothParams.testBothParamsShouldBeNonnull(java.lang.String,java.lang.String):void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`propagatesNullable(...)` is nullable and is not locally checked for null when calling `length()`: method parameter sNullable.], TestPropagatesNullable$TestBothParams, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/PropagatesNullable.java, codetoanalyze.java.nullsafe.TestPropagatesNullable$TestBothParams.testBothParamsShouldBeNonnull(java.lang.String,java.lang.String):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`propagatesNullable(...)` is nullable and is not locally checked for null when calling `length()`: method parameter sNullable.], TestPropagatesNullable$TestBothParams, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/PropagatesNullable.java, codetoanalyze.java.nullsafe.TestPropagatesNullable$TestBothParams.testBothParamsShouldBeNonnull(java.lang.String,java.lang.String):void, 3, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`propagatesNullable(...)` is nullable and is not locally checked for null when calling `length()`: method parameter sNullable.], TestPropagatesNullable$TestBothParams, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/PropagatesNullable.java, codetoanalyze.java.nullsafe.TestPropagatesNullable$TestReturnValueAnnotationIsAutomaticallyInferred.notAnnotatingReturnWhenThereAreNoPropagatesNullableIsBAD(java.lang.String):java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`notAnnotatingReturnWhenThereAreNoPropagatesNullableIsBAD(...)`: return type is declared non-nullable but the method returns `null`: null constant at line 123.], TestPropagatesNullable$TestReturnValueAnnotationIsAutomaticallyInferred, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/PropagatesNullable.java, codetoanalyze.java.nullsafe.TestPropagatesNullable$TestReturnValueAnnotationIsAutomaticallyInferred.annotated_dereferencingAfterPassingNullableIsBAD(java.lang.String):void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`annotatedReturn(...)` is nullable and is not locally checked for null when calling `toString()`: method parameter s.], TestPropagatesNullable$TestReturnValueAnnotationIsAutomaticallyInferred, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/PropagatesNullable.java, codetoanalyze.java.nullsafe.TestPropagatesNullable$TestReturnValueAnnotationIsAutomaticallyInferred.notAnnotated_dereferencingAfterPassingNullableIsBAD(java.lang.String):void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`notAnnotatedReturn(...)` is nullable and is not locally checked for null when calling `toString()`: method parameter s.], TestPropagatesNullable$TestReturnValueAnnotationIsAutomaticallyInferred, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ReturnNotNullable.java, Linters_dummy_method, 23, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`ReturnNotNullable` needs 10 issues to be fixed in order to be marked @Nullsafe.], ReturnNotNullable, codetoanalyze.java.nullsafe, issues: 8, curr_mode: "Default" -codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.nullToNotAnnotatedIsBad():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`nullToNotAnnotatedIsBad()`: return type is declared non-nullable but the method returns `null`: null constant at line 39.], ReturnNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.nullableToNotAnnotatedIsBad(java.lang.String):java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`nullableToNotAnnotatedIsBad(...)`: return type is declared non-nullable but the method returns a nullable value: method parameter s.], ReturnNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.nullToNonnullIsBad():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`nullToNonnullIsBad()`: return type is declared non-nullable but the method returns `null`: null constant at line 64.], ReturnNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.nullableToNonnullIsBad(java.lang.String):java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`nullableToNonnullIsBad(...)`: return type is declared non-nullable but the method returns a nullable value: method parameter s.], ReturnNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.notAnnotatedNullableIsOverannotated(java.lang.String):java.lang.String, 0, ERADICATE_RETURN_OVER_ANNOTATED, no_bucket, ADVICE, [Method `notAnnotatedNullableIsOverannotated(...)` is annotated with `@Nullable` but never returns null.], ReturnNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.nonNullToNullableIsOverannotated(java.lang.String):java.lang.String, 0, ERADICATE_RETURN_OVER_ANNOTATED, no_bucket, ADVICE, [Method `nonNullToNullableIsOverannotated(...)` is annotated with `@Nullable` but never returns null.], ReturnNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.constantToNullableIsOverannotated():java.lang.String, 0, ERADICATE_RETURN_OVER_ANNOTATED, no_bucket, ADVICE, [Method `constantToNullableIsOverannotated()` is annotated with `@Nullable` but never returns null.], ReturnNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.return_null_in_catch():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`return_null_in_catch()`: return type is declared non-nullable but the method returns `null`: null constant at line 152.], ReturnNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.return_null_in_catch_after_throw():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`return_null_in_catch_after_throw()`: return type is declared non-nullable but the method returns `null`: null constant at line 164.], ReturnNotNullable, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.getResourceNullable(java.lang.Class,java.lang.String):java.net.URL, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`getResourceNullable(...)`: return type is declared non-nullable but the method returns a nullable value: call to Class.getResource(...) at line 169 (nullable according to nullsafe internal models).], ReturnNotNullable, codetoanalyze.java.nullsafe, nullable_methods:java.lang.Class.getResource at 169 -codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable$ConditionalAssignment.test(boolean):java.lang.Object, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`test(...)`: return type is declared non-nullable but the method returns a nullable value: field f1 at line 191.], ReturnNotNullable$ConditionalAssignment, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/StrictMode.java, codetoanalyze.java.nullsafe.Strict.(), 0, ERADICATE_FIELD_NOT_INITIALIZED, no_bucket, ERROR, [Field `notInitializedIsBAD` is declared non-nullable, so it should be initialized in the constructor], Strict, codetoanalyze.java.nullsafe, field:codetoanalyze.java.nullsafe.Strict.notInitializedIsBAD -codetoanalyze/java/nullsafe/StrictMode.java, Linters_dummy_method, 15, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [@Nullsafe classes should have exactly zero nullability issues. `Strict` has 17.], Strict, codetoanalyze.java.nullsafe, issues: 17, curr_mode: "Strict" -codetoanalyze/java/nullsafe/StrictMode.java, codetoanalyze.java.nullsafe.Strict.sameClass_dereferenceNullableMethodIsBad():void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [`getNullable()` is nullable and is not locally checked for null when calling `toString()`.], Strict, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.Strict.getNullable at 41 -codetoanalyze/java/nullsafe/StrictMode.java, codetoanalyze.java.nullsafe.Strict.sameClass_dereferenceNullableStaticMethodIsBad():void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [`staticNullable()` is nullable and is not locally checked for null when calling `toString()`.], Strict, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.Strict.staticNullable at 49 -codetoanalyze/java/nullsafe/StrictMode.java, codetoanalyze.java.nullsafe.Strict.sameClass_dereferenceNullableFieldIsBad():void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [`Strict.nullable` is nullable and is not locally checked for null when calling `toString()`.], Strict, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/StrictMode.java, codetoanalyze.java.nullsafe.Strict.sameClass_convertingNullableToNonnullIsBad():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, ERROR, [`sameClass_convertingNullableToNonnullIsBad()`: return type is declared non-nullable but the method returns a nullable value: call to getNullable() at line 65.], Strict, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.Strict.getNullable at 65 -codetoanalyze/java/nullsafe/StrictMode.java, codetoanalyze.java.nullsafe.Strict.strictClass_dereferenceNullableMethodIsBad():void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [`getNullable(...)` is nullable and is not locally checked for null when calling `toString()`: call to getNullable() at line 75.], Strict, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.OtherStrict.getNullable at 75 -codetoanalyze/java/nullsafe/StrictMode.java, codetoanalyze.java.nullsafe.Strict.strictClass_dereferenceNullableStaticMethodIsBad():void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [`staticNullable()` is nullable and is not locally checked for null when calling `toString()`.], Strict, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.OtherStrict.staticNullable at 83 -codetoanalyze/java/nullsafe/StrictMode.java, codetoanalyze.java.nullsafe.Strict.strictClass_dereferenceNullableFieldIsBad():void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [`__new(...).nullable` is nullable and is not locally checked for null when calling `toString()`.], Strict, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/StrictMode.java, codetoanalyze.java.nullsafe.Strict.strictClass_convertingNullableToNonnullIsBad():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, ERROR, [`strictClass_convertingNullableToNonnullIsBad()`: return type is declared non-nullable but the method returns a nullable value: call to getNullable() at line 99.], Strict, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.OtherStrict.getNullable at 99 -codetoanalyze/java/nullsafe/StrictMode.java, codetoanalyze.java.nullsafe.Strict.nonStrictClass_dereferenceNullableMethodIsBad():void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [`getNullable(...)` is nullable and is not locally checked for null when calling `toString()`: call to getNullable() at line 110.], Strict, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.NonStrict.getNullable at 110 -codetoanalyze/java/nullsafe/StrictMode.java, codetoanalyze.java.nullsafe.Strict.nonStrictClass_dereferenceNonnullMethodIsBad():void, 2, ERADICATE_UNCHECKED_USAGE_IN_NULLSAFE, no_bucket, ERROR, [`NonStrict.getNonnull()`: `@Nullsafe(STRICT)` prohibits using values coming from non-strict classes without a check. Result of this call is used at line 115. Either add a local check for null or assertion, or make `NonStrict` nullsafe strict.], Strict, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/StrictMode.java, codetoanalyze.java.nullsafe.Strict.nonStrictClass_dereferenceNullableStaticMethodIsBad():void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [`staticNullable()` is nullable and is not locally checked for null when calling `toString()`.], Strict, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.NonStrict.staticNullable at 119 -codetoanalyze/java/nullsafe/StrictMode.java, codetoanalyze.java.nullsafe.Strict.nonStrictClass_dereferenceNonnullStaticMethodIsBad():void, 2, ERADICATE_UNCHECKED_USAGE_IN_NULLSAFE, no_bucket, ERROR, [`NonStrict.staticNonnull()`: `@Nullsafe(STRICT)` prohibits using values coming from non-strict classes without a check. Result of this call is used at line 124. Either add a local check for null or assertion, or make `NonStrict` nullsafe strict.], Strict, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/StrictMode.java, codetoanalyze.java.nullsafe.Strict.nonStrictClass_dereferenceNullableFieldIsBad():void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [`__new(...).nullable` is nullable and is not locally checked for null when calling `toString()`.], Strict, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/StrictMode.java, codetoanalyze.java.nullsafe.Strict.nonStrictClass_dereferenceNonnullFieldIsBad():void, 2, ERADICATE_UNCHECKED_USAGE_IN_NULLSAFE, no_bucket, ERROR, [`NonStrict.nonnull`: `@Nullsafe(STRICT)` prohibits using values coming from non-strict classes without a check. This field is used at line 133. Either add a local check for null or assertion, or make `NonStrict` nullsafe strict.], Strict, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/StrictMode.java, codetoanalyze.java.nullsafe.Strict.nonStrictClass_convertingNullableToNonnullIsBad():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, ERROR, [`nonStrictClass_convertingNullableToNonnullIsBad()`: return type is declared non-nullable but the method returns a nullable value: call to getNullable() at line 137.], Strict, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.NonStrict.getNullable at 137 -codetoanalyze/java/nullsafe/StrictMode.java, codetoanalyze.java.nullsafe.Strict.nonStrictClass_convertingNonnullToNonnullIsBad():java.lang.String, 2, ERADICATE_UNCHECKED_USAGE_IN_NULLSAFE, no_bucket, ERROR, [`NonStrict.getNonnull()`: `@Nullsafe(STRICT)` prohibits using values coming from non-strict classes without a check. Result of this call is used at line 179. Either add a local check for null or assertion, or make `NonStrict` nullsafe strict.], Strict, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/StrictMode.java, codetoanalyze.java.nullsafe.Strict.nonStrictClass_dereferenceNonnullFieldAfterCheckIsOK():void, 2, ERADICATE_CONDITION_REDUNDANT, no_bucket, ADVICE, [The condition NonStrict.nonnull might be always true according to the existing annotations.], Strict, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/StrictMode.java, codetoanalyze.java.nullsafe.Strict.nonStrictClass_dereferenceNonnullMethodAfterCheckIsOK():void, 2, ERADICATE_CONDITION_REDUNDANT, no_bucket, ADVICE, [The condition lang.String(o) might be always true: `NonStrict.getNonnull()` is not annotated as `@Nullable`.], Strict, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/StrictMode.java, codetoanalyze.java.nullsafe.Strict.nonStrictClass_convertingNonnullToNullableIsOK():java.lang.String, 0, ERADICATE_RETURN_OVER_ANNOTATED, no_bucket, ADVICE, [Method `nonStrictClass_convertingNonnullToNullableIsOK()` is annotated with `@Nullable` but never returns null.], Strict, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/StrictMode.java, Linters_dummy_method, 239, ERADICATE_META_CLASS_IS_NULLSAFE, no_bucket, INFO, [Class codetoanalyze.java.nullsafe.OtherStrict is free of nullability issues.], OtherStrict, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Strict" -codetoanalyze/java/nullsafe/StrictMode.java, Linters_dummy_method, 260, ERADICATE_META_CLASS_CAN_BE_NULLSAFE, no_bucket, ADVICE, [Congrats! `NonStrict` is free of nullability issues. Mark it `@Nullsafe(Nullsafe.Mode.LOCAL)` to prevent regressions.], NonStrict, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Default", promote_mode: "LocalTrustAll" -codetoanalyze/java/nullsafe/StrictMode.java, Linters_dummy_method, 285, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`SomeEnum` needs 2 issues to be fixed in order to be marked @Nullsafe.], SomeEnum, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Default" -codetoanalyze/java/nullsafe/StrictModeForThirdParty.java, Linters_dummy_method, 20, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [@Nullsafe classes should have exactly zero nullability issues. `StrictModeForThirdParty` has 10.], StrictModeForThirdParty, codetoanalyze.java.nullsafe, issues: 10, curr_mode: "Strict" -codetoanalyze/java/nullsafe/StrictModeForThirdParty.java, codetoanalyze.java.nullsafe.StrictModeForThirdParty.dereferenceUnspecifiedIsBAD():void, 1, ERADICATE_UNVETTED_THIRD_PARTY_IN_NULLSAFE, no_bucket, ERROR, [`ThirdPartyTestClass.returnUnspecified()`: `@Nullsafe` mode prohibits using values coming from not vetted third party methods without a check. Result of this call is used at line 42. Either add a local check for null or assertion, or add the correct signature to nullsafe/third-party-signatures/some.test.pckg.sig.], StrictModeForThirdParty, codetoanalyze.java.nullsafe, unvetted_3rd_party:[some.test.pckg.ThirdPartyTestClass#returnUnspecified()] -codetoanalyze/java/nullsafe/StrictModeForThirdParty.java, codetoanalyze.java.nullsafe.StrictModeForThirdParty.dereferenceSpecifiedAsNullableIsBAD():void, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, ERROR, [`StrictModeForThirdParty.obj.returnSpecifiedAsNullable()` is nullable and is not locally checked for null when calling `toString()`: call to ThirdPartyTestClass.returnSpecifiedAsNullable() at line 46 (declared nullable in nullsafe/third-party-signatures/some.test.pckg.sig at line 2).], StrictModeForThirdParty, codetoanalyze.java.nullsafe, nullable_methods:some.test.pckg.ThirdPartyTestClass.returnSpecifiedAsNullable at 46 -codetoanalyze/java/nullsafe/StrictModeForThirdParty.java, codetoanalyze.java.nullsafe.StrictModeForThirdParty.dereferenceFieldIsBAD():void, 1, ERADICATE_UNVETTED_THIRD_PARTY_IN_NULLSAFE, no_bucket, ERROR, [`ThirdPartyTestClass.nonNullableField`: `@Nullsafe` mode prohibits using values coming from not vetted third party fields without a check. This field is used at line 50. Either add a local check for null or assertion, or access `nonNullableField` via a nullsafe strict getter.], StrictModeForThirdParty, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/StrictModeForThirdParty.java, codetoanalyze.java.nullsafe.StrictModeForThirdParty.passingNullableParamToUnspecifiedIsBAD():void, 1, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, ERROR, [Third-party `ThirdPartyTestClass.paramUnspecified(...)` is missing a signature that would allow passing a nullable to param #1(`param`). Actual argument `getNullable()` is nullable. Consider adding the correct signature of `ThirdPartyTestClass.paramUnspecified(...)` to nullsafe/third-party-signatures/some.test.pckg.sig.], StrictModeForThirdParty, codetoanalyze.java.nullsafe, unvetted_3rd_party:[some.test.pckg.ThirdPartyTestClass#paramUnspecified(java.lang.String)], nullable_methods:codetoanalyze.java.nullsafe.StrictModeForThirdParty.getNullable at 63 -codetoanalyze/java/nullsafe/StrictModeForThirdParty.java, codetoanalyze.java.nullsafe.StrictModeForThirdParty.passingNullableToParamSpecifiedAsNonnullIsBAD():void, 1, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, ERROR, [`ThirdPartyTestClass.secondParamSpecifiedAsNonnull(...)`: parameter #2(`specifiedAsNonnull`) is declared non-nullable (see nullsafe/third-party-signatures/some.test.pckg.sig at line 3) but the argument `getNullable()` is nullable.], StrictModeForThirdParty, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.StrictModeForThirdParty.getNullable at 71 -codetoanalyze/java/nullsafe/StrictModeForThirdParty.java, codetoanalyze.java.nullsafe.StrictModeForThirdParty.genericObjectRepresentation(java.lang.String,java.util.List):java.lang.String, 1, ERADICATE_UNVETTED_THIRD_PARTY_IN_NULLSAFE, no_bucket, ERROR, [`ThirdPartyTestClass.generic(...)`: `@Nullsafe` mode prohibits using values coming from not vetted third party methods without a check. Result of this call is used at line 90. Either add a local check for null or assertion, or add the correct signature to nullsafe/third-party-signatures/some.test.pckg.sig.], StrictModeForThirdParty, codetoanalyze.java.nullsafe, unvetted_3rd_party:[some.test.pckg.ThirdPartyTestClass#generic(java.lang.Object, java.util.List)] -codetoanalyze/java/nullsafe/StrictModeForThirdParty.java, codetoanalyze.java.nullsafe.StrictModeForThirdParty.genericExtendsStringRepresentation(java.lang.String,java.util.List):java.lang.String, 1, ERADICATE_UNVETTED_THIRD_PARTY_IN_NULLSAFE, no_bucket, ERROR, [`ThirdPartyTestClass.genericString(...)`: `@Nullsafe` mode prohibits using values coming from not vetted third party methods without a check. Result of this call is used at line 97. Either add a local check for null or assertion, or add the correct signature to nullsafe/third-party-signatures/some.test.pckg.sig.], StrictModeForThirdParty, codetoanalyze.java.nullsafe, unvetted_3rd_party:[some.test.pckg.ThirdPartyTestClass#genericString(java.lang.String, java.util.List)] -codetoanalyze/java/nullsafe/StrictModeForThirdParty.java, codetoanalyze.java.nullsafe.StrictModeForThirdParty.arrayRepresentation(java.lang.String,java.lang.String[]):java.lang.String, 1, ERADICATE_UNVETTED_THIRD_PARTY_IN_NULLSAFE, no_bucket, ERROR, [`ThirdPartyTestClass.array(...)`: `@Nullsafe` mode prohibits using values coming from not vetted third party methods without a check. Result of this call is used at line 103. Either add a local check for null or assertion, or add the correct signature to nullsafe/third-party-signatures/some.test.pckg.sig.], StrictModeForThirdParty, codetoanalyze.java.nullsafe, unvetted_3rd_party:[some.test.pckg.ThirdPartyTestClass#array(java.lang.String, java.lang.String[])] -codetoanalyze/java/nullsafe/StrictModeForThirdParty.java, codetoanalyze.java.nullsafe.StrictModeForThirdParty.varargRepresentation(java.lang.String):java.lang.String, 1, ERADICATE_UNVETTED_THIRD_PARTY_IN_NULLSAFE, no_bucket, ERROR, [`ThirdPartyTestClass.vararg(...)`: `@Nullsafe` mode prohibits using values coming from not vetted third party methods without a check. Result of this call is used at line 109. Either add a local check for null or assertion, or add the correct signature to nullsafe/third-party-signatures/some.test.pckg.sig.], StrictModeForThirdParty, codetoanalyze.java.nullsafe, unvetted_3rd_party:[some.test.pckg.ThirdPartyTestClass#vararg(java.lang.String, java.lang.String[])] -codetoanalyze/java/nullsafe/StrictModeForThirdParty.java, codetoanalyze.java.nullsafe.StrictModeForThirdParty.varargGenericRepresentation(java.lang.String):java.lang.String, 1, ERADICATE_UNVETTED_THIRD_PARTY_IN_NULLSAFE, no_bucket, ERROR, [`ThirdPartyTestClass.varargGeneric(...)`: `@Nullsafe` mode prohibits using values coming from not vetted third party methods without a check. Result of this call is used at line 116. Either add a local check for null or assertion, or add the correct signature to nullsafe/third-party-signatures/some.test.pckg.sig.], StrictModeForThirdParty, codetoanalyze.java.nullsafe, unvetted_3rd_party:[some.test.pckg.ThirdPartyTestClass#varargGeneric(java.lang.Object, java.lang.Object[])] -codetoanalyze/java/nullsafe/SwitchCase.java, Linters_dummy_method, 12, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`SwitchCase` needs 2 issues to be fixed in order to be marked @Nullsafe.], SwitchCase, codetoanalyze.java.nullsafe, issues: 2, curr_mode: "Default" -codetoanalyze/java/nullsafe/SwitchCase.java, codetoanalyze.java.nullsafe.SwitchCase.switchOnNullIsBad():java.lang.String, 2, 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.], SwitchCase, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/SwitchCase.java, codetoanalyze.java.nullsafe.SwitchCase.switchOnNullableIsBad():java.lang.String, 2, 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.], SwitchCase, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.SwitchCase.getNullableColor at 28 -codetoanalyze/java/nullsafe/SwitchCase.java, codetoanalyze.java.nullsafe.SwitchCase.getNullableColor():codetoanalyze.java.nullsafe.Color, 0, ERADICATE_RETURN_OVER_ANNOTATED, no_bucket, ADVICE, [Method `getNullableColor()` is annotated with `@Nullable` but never returns null.], SwitchCase, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/SwitchCase.java, Linters_dummy_method, 63, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`Color` needs 2 issues to be fixed in order to be marked @Nullsafe.], Color, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Default" -codetoanalyze/java/nullsafe/SyntheticErrorSuppressions.java, Linters_dummy_method, 13, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`SyntheticErrorSuppressions` needs 5 issues to be fixed in order to be marked @Nullsafe.], SyntheticErrorSuppressions, codetoanalyze.java.nullsafe, issues: 3, curr_mode: "Default" -codetoanalyze/java/nullsafe/SyntheticErrorSuppressions.java, codetoanalyze.java.nullsafe.SyntheticErrorSuppressions$ClassWithSyntheticCode.passingSyntheticParamToAnyMethod_OK_FP():void, 1, ERADICATE_PARAMETER_NOT_NULLABLE, no_bucket, WARNING, [`SyntheticErrorSuppressions$Fragment.setContext(...)`: parameter #1(`context`) is declared non-nullable but the argument `SyntheticErrorSuppressions$ClassWithSyntheticCode.$ul_fieldNullable` is nullable.], SyntheticErrorSuppressions$ClassWithSyntheticCode, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/SyntheticErrorSuppressions.java, codetoanalyze.java.nullsafe.SyntheticErrorSuppressions$ClassWithSyntheticCode.dereferencingSyntheticNullableField_OK_FP():java.lang.String, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`SyntheticErrorSuppressions$ClassWithSyntheticCode.$ul_fieldNullable` is nullable and is not locally checked for null when calling `toString()`.], SyntheticErrorSuppressions$ClassWithSyntheticCode, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/SyntheticErrorSuppressions.java, codetoanalyze.java.nullsafe.SyntheticErrorSuppressions$ClassWithSyntheticCode.dereferencingNullableSyntheticMethodCall_OK_FP():java.lang.String, 1, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`$ul_iAmNullableAutogen(...)` is nullable and is not locally checked for null when calling `toString()`: call to $ul_iAmNullableAutogen() at line 68.], SyntheticErrorSuppressions$ClassWithSyntheticCode, codetoanalyze.java.nullsafe, nullable_methods:codetoanalyze.java.nullsafe.SyntheticErrorSuppressions$ClassWithSyntheticCode.$ul_iAmNullableAutogen at 68 -codetoanalyze/java/nullsafe/TrueFalseOnNull.java, Linters_dummy_method, 17, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [`TrueFalseOnNull` needs 17 issues to be fixed in order to be marked @Nullsafe.], TrueFalseOnNull, codetoanalyze.java.nullsafe, issues: 17, curr_mode: "Default" -codetoanalyze/java/nullsafe/TrueFalseOnNull.java, codetoanalyze.java.nullsafe.TrueFalseOnNull$TestStaticOneParam.trueOnNullPositiveBranchIsBAD(java.lang.String):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `toString()`.], TrueFalseOnNull$TestStaticOneParam, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/TrueFalseOnNull.java, codetoanalyze.java.nullsafe.TrueFalseOnNull$TestStaticOneParam.falseOnNullNegativeBranchIsBAD(java.lang.String):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `toString()`.], TrueFalseOnNull$TestStaticOneParam, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/TrueFalseOnNull.java, codetoanalyze.java.nullsafe.TrueFalseOnNull$TestStaticOneParam.notAnnotatedPositiveBranchIsBAD(java.lang.String):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `toString()`.], TrueFalseOnNull$TestStaticOneParam, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/TrueFalseOnNull.java, codetoanalyze.java.nullsafe.TrueFalseOnNull$TestStaticOneParam.notAnnotatedNegativeBranchIsBAD(java.lang.String):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `toString()`.], TrueFalseOnNull$TestStaticOneParam, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/TrueFalseOnNull.java, codetoanalyze.java.nullsafe.TrueFalseOnNull$TestNonStaticOneParam.trueOnNullPositiveBranchIsBAD(java.lang.String):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `toString()`.], TrueFalseOnNull$TestNonStaticOneParam, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/TrueFalseOnNull.java, codetoanalyze.java.nullsafe.TrueFalseOnNull$TestNonStaticOneParam.falseOnNullNegativeBranchIsBAD(java.lang.String):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `toString()`.], TrueFalseOnNull$TestNonStaticOneParam, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/TrueFalseOnNull.java, codetoanalyze.java.nullsafe.TrueFalseOnNull$TestNonStaticOneParam.notAnnotatedPositiveBranchIsBAD(java.lang.String):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `toString()`.], TrueFalseOnNull$TestNonStaticOneParam, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/TrueFalseOnNull.java, codetoanalyze.java.nullsafe.TrueFalseOnNull$TestNonStaticOneParam.notAnnotatedNegativeBranchIsBAD(java.lang.String):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s` is nullable and is not locally checked for null when calling `toString()`.], TrueFalseOnNull$TestNonStaticOneParam, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/TrueFalseOnNull.java, codetoanalyze.java.nullsafe.TrueFalseOnNull$TestNonStaticSeveralParams.trueOnNullPositiveBranchIsBAD(java.lang.String,java.lang.String):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s1` is nullable and is not locally checked for null when calling `toString()`.], TrueFalseOnNull$TestNonStaticSeveralParams, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/TrueFalseOnNull.java, codetoanalyze.java.nullsafe.TrueFalseOnNull$TestNonStaticSeveralParams.trueOnNullPositiveBranchIsBAD(java.lang.String,java.lang.String):void, 3, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s2` is nullable and is not locally checked for null when calling `toString()`.], TrueFalseOnNull$TestNonStaticSeveralParams, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/TrueFalseOnNull.java, codetoanalyze.java.nullsafe.TrueFalseOnNull$TestNonStaticSeveralParams.falseOnNullNegativeBranchIsBAD(java.lang.String,java.lang.String):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s1` is nullable and is not locally checked for null when calling `toString()`.], TrueFalseOnNull$TestNonStaticSeveralParams, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/TrueFalseOnNull.java, codetoanalyze.java.nullsafe.TrueFalseOnNull$TestNonStaticSeveralParams.falseOnNullNegativeBranchIsBAD(java.lang.String,java.lang.String):void, 3, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s2` is nullable and is not locally checked for null when calling `toString()`.], TrueFalseOnNull$TestNonStaticSeveralParams, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/TrueFalseOnNull.java, codetoanalyze.java.nullsafe.TrueFalseOnNull$TestNonStaticSeveralParams.notAnnotatedPositiveBranchIsBAD(java.lang.String,java.lang.String):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s1` is nullable and is not locally checked for null when calling `toString()`.], TrueFalseOnNull$TestNonStaticSeveralParams, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/TrueFalseOnNull.java, codetoanalyze.java.nullsafe.TrueFalseOnNull$TestNonStaticSeveralParams.notAnnotatedPositiveBranchIsBAD(java.lang.String,java.lang.String):void, 3, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s2` is nullable and is not locally checked for null when calling `toString()`.], TrueFalseOnNull$TestNonStaticSeveralParams, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/TrueFalseOnNull.java, codetoanalyze.java.nullsafe.TrueFalseOnNull$TestNonStaticSeveralParams.notAnnotatedNegativeBranchIsBAD(java.lang.String,java.lang.String):void, 2, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s1` is nullable and is not locally checked for null when calling `toString()`.], TrueFalseOnNull$TestNonStaticSeveralParams, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/TrueFalseOnNull.java, codetoanalyze.java.nullsafe.TrueFalseOnNull$TestNonStaticSeveralParams.notAnnotatedNegativeBranchIsBAD(java.lang.String,java.lang.String):void, 3, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s2` is nullable and is not locally checked for null when calling `toString()`.], TrueFalseOnNull$TestNonStaticSeveralParams, codetoanalyze.java.nullsafe -codetoanalyze/java/nullsafe/TrueFalseOnNull.java, codetoanalyze.java.nullsafe.TrueFalseOnNull$EarlyReturn.testEarlyReturn(java.lang.CharSequence,java.lang.CharSequence):void, 6, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`s2` is nullable and is not locally checked for null when calling `toString()`.], TrueFalseOnNull$EarlyReturn, codetoanalyze.java.nullsafe diff --git a/infer/tests/codetoanalyze/java/nullsafe/third-party-signatures/java.sig b/infer/tests/codetoanalyze/java/nullsafe/third-party-signatures/java.sig deleted file mode 100644 index 251b0d660fa..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/third-party-signatures/java.sig +++ /dev/null @@ -1,3 +0,0 @@ -java.lang.String#concat(java.lang.String) -java.lang.Throwable#getCause() @Nullable -java.util.concurrent.BlockingQueue#poll(long, java.util.concurrent.TimeUnit) @Nullable diff --git a/infer/tests/codetoanalyze/java/nullsafe/third-party-signatures/other.test.pckg.sig b/infer/tests/codetoanalyze/java/nullsafe/third-party-signatures/other.test.pckg.sig deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/infer/tests/codetoanalyze/java/nullsafe/third-party-signatures/some.test.pckg.sig b/infer/tests/codetoanalyze/java/nullsafe/third-party-signatures/some.test.pckg.sig deleted file mode 100644 index 639b5f5ac57..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/third-party-signatures/some.test.pckg.sig +++ /dev/null @@ -1,3 +0,0 @@ -some.test.pckg.ThirdPartyTestClass#returnSpecifiedAsNonnull() -some.test.pckg.ThirdPartyTestClass#returnSpecifiedAsNullable() @Nullable -some.test.pckg.ThirdPartyTestClass#secondParamSpecifiedAsNonnull(@Nullable java.lang.String, java.lang.String) diff --git a/infer/tests/codetoanalyze/java/nullsafe/third-party-test-code/some/test/pckg/ThirdPartyTestClass.java b/infer/tests/codetoanalyze/java/nullsafe/third-party-test-code/some/test/pckg/ThirdPartyTestClass.java deleted file mode 100644 index f1365edfb38..00000000000 --- a/infer/tests/codetoanalyze/java/nullsafe/third-party-test-code/some/test/pckg/ThirdPartyTestClass.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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 some.test.pckg; - -import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -/** - * A test third party class. We specify its annotations outside of this class, in a third-party - * repository. - */ -public class ThirdPartyTestClass { - - // Inner classes - - public static class UncheckedLong { - public long mInner; - - public UncheckedLong(long inner) { - mInner = inner; - } - } - - public enum InnerEnum { - EA, - EB, - } - - // Fields. - - public String nonNullableField; - - @Nullable public String nullableField; - - public ThirdPartyTestClass() { - nonNullableField = "OK"; - } - - // Static methods - - public static UncheckedLong getUncheckedLong(long l) { - return new UncheckedLong(l); - } - - // Return values. - - public @Nonnull String returnExplicitlyAnnotated() { - return ""; - } - - // No information in 3rd party repo - public String returnUnspecified() { - return ""; - } - - // 3rd party repo allow lists this function as returning non-nullable - public String returnSpecifiedAsNonnull() { - return ""; - } - - // 3rd party repo allow lists this function as returning nullable - public String returnSpecifiedAsNullable() { - return ""; - } - - // Params. - - // No information about this function in 3rd party repo - public void paramUnspecified(String param) {} - - public void secondParamSpecifiedAsNonnull( - String specifiedAsNullable, String specifiedAsNonnull) {} - - // Special case: a generic method - public T generic(T obj, List list) { - return obj; - } - - // Special case: a generic method extending String - public T genericString(T obj, List list) { - return obj; - } - - // Special case: an array - public String array(String obj, String[] arr) { - return obj; - } - - // Special case: a variable argument - public String vararg(String obj, String... arr) { - return obj; - } - - // Special case: a variable argument generic - public T varargGeneric(T obj, T... arr) { - return obj; - } -}