From 59c6967e64fe2e349341614aedb2bd2ce4c8191b Mon Sep 17 00:00:00 2001 From: Artem Pianykh Date: Mon, 6 Jan 2020 02:34:59 -0800 Subject: [PATCH] [nullsafe] Redo InconsistentSubclassAnnotation tests Reviewed By: jvillard Differential Revision: D19161020 fbshipit-source-id: 06d5e5b77 --- .../InconsistentSubclassAnnotation.java | 182 ++++++++++-------- .../java/nullsafe-default/issues.exp | 16 +- 2 files changed, 117 insertions(+), 81 deletions(-) diff --git a/infer/tests/codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java b/infer/tests/codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java index 765c0fc2f..a3ac734d5 100644 --- a/infer/tests/codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java +++ b/infer/tests/codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java @@ -10,67 +10,134 @@ package codetoanalyze.java.nullsafe_default; import external.library.SomeExternalClass; import javax.annotation.Nullable; -class SubclassExample { +interface VariousMethods { + String valBoth(String arg); - class T { - public void f() {} + @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); +} + +// Check return annotations + +abstract class ReturnValToNullBAD implements VariousMethods { + @Nullable + public String valBoth(String arg) { + return null; } +} - class A { +abstract class ReturnNullToValOK implements VariousMethods { + public abstract String nullableReturn(String arg); +} - public T foo() { - return new T(); - } +abstract class ReturnValFromValAndNullFromNullOK implements VariousMethods { + @Nullable + public String nullableReturn(String arg) { + return null; + } - public @Nullable T bar() { - return null; - } + public String valBoth(String arg) { + return arg; + } +} - public void deref(@Nullable T t) { - if (t != null) { - t.f(); - } - } +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 - public void noDeref(T t) {} +abstract class ArgValToNullOK implements VariousMethods { + public String valBoth(@Nullable String arg) { + return "OK"; } +} - class B extends A { +abstract class ArgNullToValBAD implements VariousMethods { + public String nullableArg(String arg) { + return arg; + } +} - public @Nullable T foo() { - return null; - } +abstract class ArgNullToValForInterfaceInAnotherFileBAD + implements InconsistentSubclassAnnotationInterface { + public String implementInAnotherFile(String s) { + return "BAD"; + } +} - public T bar() { - return new T(); - } +abstract class ArgValToValAndNullToNullOK implements VariousMethods { + public String valBoth(String arg) { + return arg; } - interface I { - public T baz(); + @Nullable + public String nullableBoth(@Nullable String arg) { + return arg; } +} - class C implements I { +// Check overrides + overloads - public @Nullable T baz() { - return null; - } +// This is 'good' cases (should be OK except 1 FP due to broken is_override logic) +abstract class OverrideExistingCorrectlyOK implements Overloads { + // This is FP + public String overload(int arg) { + return "OK"; } - class D extends A { + public String overload(@Nullable String arg) { + return arg; + } - public void deref(T t) { - t.f(); - } + public String overload(String arg1, int arg2) { + return arg1; + } - public void noDeref(@Nullable T t) { - if (t != null) { - t.f(); - } - } + public String overload(String arg1, String arg2) { + return arg1; + } +} + +// These are FP cases that get reported due to broken is_override logic +abstract class NoOverrideSinceDifferentTypesFP implements Overloads { + @Nullable + public String overload(Object arg) { + return arg.toString(); + } + + public String overload(Double arg) { + return arg.toString(); } } +// 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) {} @@ -83,6 +150,8 @@ class ConstructorsAreExcluded { } } +// Check interop with external libraries + class ExtendsExternalLibrary extends SomeExternalClass { @Override @@ -97,40 +166,3 @@ class ExtendsExternalLibrary extends SomeExternalClass { // subtyping error on the parameter type are reported } } - -public class InconsistentSubclassAnnotation implements InconsistentSubclassAnnotationInterface { - - public static void callFromSuperclass(SubclassExample.A a) { - SubclassExample.T t = a.foo(); - t.f(); - } - - public static void callWithNullableParam(SubclassExample.A a, @Nullable SubclassExample.T t) { - a.deref(t); - } - - public String implementInAnotherFile(String s) { - return ""; - } - - public @Nullable Object overloadedMethod() { - return null; - } - - public Object overloadedMethod(Object object) { - return object; - } -} - -class Super { - String overloadingMethodLookupFP(int i) { - return Integer.toString(i); - } -} - -class Sub extends Super { - @Nullable - String overloadingMethodLookupFP(Object object) { - return null; - } -} diff --git a/infer/tests/codetoanalyze/java/nullsafe-default/issues.exp b/infer/tests/codetoanalyze/java/nullsafe-default/issues.exp index 51a465038..a502aa75e 100644 --- a/infer/tests/codetoanalyze/java/nullsafe-default/issues.exp +++ b/infer/tests/codetoanalyze/java/nullsafe-default/issues.exp @@ -96,13 +96,17 @@ codetoanalyze/java/nullsafe-default/FieldOverAnnotated.java, codetoanalyze.java. codetoanalyze/java/nullsafe-default/FieldOverAnnotated.java, codetoanalyze.java.nullsafe_default.FieldOverAnnotated.(int,int), 0, ERADICATE_FIELD_OVER_ANNOTATED, no_bucket, WARNING, [Field `FieldOverAnnotated.FP_initializedInAllConstructorsButSetToNullInAPublicMethodShouldBeOK` is always initialized in the constructor but is declared `@Nullable`] codetoanalyze/java/nullsafe-default/FieldOverAnnotated.java, codetoanalyze.java.nullsafe_default.FieldOverAnnotated.(int,int), 0, ERADICATE_FIELD_OVER_ANNOTATED, no_bucket, WARNING, [Field `FieldOverAnnotated.initializedInAllConstructorsIsBAD` is always initialized in the constructor but is declared `@Nullable`] codetoanalyze/java/nullsafe-default/FieldOverAnnotated.java, codetoanalyze.java.nullsafe_default.FieldOverAnnotated.(int,int), 0, ERADICATE_FIELD_OVER_ANNOTATED, no_bucket, WARNING, [Field `FieldOverAnnotated.initilizedInAllConstructorsAndAllBranchesIsBAD` is always initialized in the constructor but is declared `@Nullable`] +codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java, codetoanalyze.java.nullsafe_default.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 not `@Nullable` but is declared `@Nullable`in the parent class method `VariousMethods.nullableArg(...)`.] +codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java, codetoanalyze.java.nullsafe_default.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 not `@Nullable` but is declared `@Nullable`in the parent class method `InconsistentSubclassAnnotationInterface.implementInAnotherFile(...)`.] codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java, codetoanalyze.java.nullsafe_default.ExtendsExternalLibrary.externalMethod2(java.lang.Object):void, 0, ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION, no_bucket, WARNING, [First parameter `object` of method `ExtendsExternalLibrary.externalMethod2(...)` is not `@Nullable` but is declared `@Nullable`in the parent class method `SomeExternalClass.externalMethod2(...)`.] -codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java, codetoanalyze.java.nullsafe_default.InconsistentSubclassAnnotation.implementInAnotherFile(java.lang.String):java.lang.String, 0, ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION, no_bucket, WARNING, [First parameter `s` of method `InconsistentSubclassAnnotation.implementInAnotherFile(...)` is not `@Nullable` but is declared `@Nullable`in the parent class method `InconsistentSubclassAnnotationInterface.implementInAnotherFile(...)`.] -codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java, codetoanalyze.java.nullsafe_default.InconsistentSubclassAnnotation.overloadedMethod():java.lang.Object, 0, ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION, no_bucket, WARNING, [Method `InconsistentSubclassAnnotation.overloadedMethod()` is annotated with `@Nullable` but overrides unannotated method `InconsistentSubclassAnnotationInterface.overloadedMethod()`.] -codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java, codetoanalyze.java.nullsafe_default.Sub.overloadingMethodLookupFP(java.lang.Object):java.lang.String, 0, ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION, no_bucket, WARNING, [Method `Sub.overloadingMethodLookupFP(...)` is annotated with `@Nullable` but overrides unannotated method `Super.overloadingMethodLookupFP(...)`.] -codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java, codetoanalyze.java.nullsafe_default.SubclassExample$B.foo():codetoanalyze.java.nullsafe_default.SubclassExample$T, 0, ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION, no_bucket, WARNING, [Method `SubclassExample$B.foo()` is annotated with `@Nullable` but overrides unannotated method `SubclassExample$A.foo()`.] -codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java, codetoanalyze.java.nullsafe_default.SubclassExample$C.baz():codetoanalyze.java.nullsafe_default.SubclassExample$T, 0, ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION, no_bucket, WARNING, [Method `SubclassExample$C.baz()` is annotated with `@Nullable` but overrides unannotated method `SubclassExample$I.baz()`.] -codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java, codetoanalyze.java.nullsafe_default.SubclassExample$D.deref(codetoanalyze.java.nullsafe_default.SubclassExample$T):void, 0, ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION, no_bucket, WARNING, [First parameter `t` of method `SubclassExample$D.deref(...)` is not `@Nullable` but is declared `@Nullable`in the parent class method `SubclassExample$A.deref(...)`.] +codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java, codetoanalyze.java.nullsafe_default.NoOverrideSinceDifferentTypesFP.overload(java.lang.Double):java.lang.String, 0, ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION, no_bucket, WARNING, [First parameter `arg` of method `NoOverrideSinceDifferentTypesFP.overload(...)` is not `@Nullable` but is declared `@Nullable`in the parent class method `Overloads.overload(...)`.] +codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java, codetoanalyze.java.nullsafe_default.NoOverrideSinceDifferentTypesFP.overload(java.lang.Object):java.lang.String, 0, ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION, no_bucket, WARNING, [First parameter `arg` of method `NoOverrideSinceDifferentTypesFP.overload(...)` is not `@Nullable` but is declared `@Nullable`in the parent class method `Overloads.overload(...)`.] +codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java, codetoanalyze.java.nullsafe_default.NoOverrideSinceDifferentTypesFP.overload(java.lang.Object):java.lang.String, 0, ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION, no_bucket, WARNING, [Method `NoOverrideSinceDifferentTypesFP.overload(...)` is annotated with `@Nullable` but overrides unannotated method `Overloads.overload(...)`.] +codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java, codetoanalyze.java.nullsafe_default.NoOverrideSinceDifferentTypesFP.overload(java.lang.Object):java.lang.String, 0, ERADICATE_RETURN_OVER_ANNOTATED, no_bucket, WARNING, [Method `overload(...)` is annotated with `@Nullable` but never returns null.] +codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java, codetoanalyze.java.nullsafe_default.OverloadExistingIncorrectBAD.overload(java.lang.String,java.lang.String):java.lang.String, 0, ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION, no_bucket, WARNING, [Method `OverloadExistingIncorrectBAD.overload(...)` is annotated with `@Nullable` but overrides unannotated method `Overloads.overload(...)`.] +codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java, codetoanalyze.java.nullsafe_default.OverrideExistingCorrectlyOK.overload(int):java.lang.String, 0, ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION, no_bucket, WARNING, [First parameter `arg` of method `OverrideExistingCorrectlyOK.overload(...)` is not `@Nullable` but is declared `@Nullable`in the parent class method `Overloads.overload(...)`.] +codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java, codetoanalyze.java.nullsafe_default.OverrideExistingCorrectlyOK.overload(java.lang.String):java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`overload(...)`: return type is declared non-nullable but the method returns a nullable value: method parameter arg.] +codetoanalyze/java/nullsafe-default/InconsistentSubclassAnnotation.java, codetoanalyze.java.nullsafe_default.ReturnValToNullBAD.valBoth(java.lang.String):java.lang.String, 0, ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION, no_bucket, WARNING, [Method `ReturnValToNullBAD.valBoth(...)` is annotated with `@Nullable` but overrides unannotated method `VariousMethods.valBoth(...)`.] codetoanalyze/java/nullsafe-default/LibraryCalls.java, codetoanalyze.java.nullsafe_default.LibraryCalls.badAtomicReferenceDereference(java.util.concurrent.atomic.AtomicReference):java.lang.String, 0, 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 35 (nullable according to nullsafe internal models)] codetoanalyze/java/nullsafe-default/LibraryCalls.java, codetoanalyze.java.nullsafe_default.LibraryCalls.badPhantomReferenceDereference(java.lang.ref.PhantomReference):java.lang.String, 0, 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 27 (nullable according to nullsafe internal models)] codetoanalyze/java/nullsafe-default/LibraryCalls.java, codetoanalyze.java.nullsafe_default.LibraryCalls.badReferenceDereference(java.lang.ref.Reference):java.lang.String, 0, 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 19 (nullable according to nullsafe internal models)]