From bf97abfbd345c06237972ae8cefce86d733bacad Mon Sep 17 00:00:00 2001 From: Jules Villard Date: Fri, 15 May 2020 08:30:55 -0700 Subject: [PATCH] [pulse] add Java tests Summary: Start with tests about dynamic dispatch to test the upcoming pre-analysis. Reviewed By: ezgicicek Differential Revision: D21594496 fbshipit-source-id: 1771ea968 --- Makefile | 1 + .../java/pulse/DynamicDispatch.java | 157 ++++++++++++++++++ infer/tests/codetoanalyze/java/pulse/Makefile | 12 ++ .../tests/codetoanalyze/java/pulse/issues.exp | 6 + 4 files changed, 176 insertions(+) create mode 100644 infer/tests/codetoanalyze/java/pulse/DynamicDispatch.java create mode 100644 infer/tests/codetoanalyze/java/pulse/Makefile create mode 100644 infer/tests/codetoanalyze/java/pulse/issues.exp diff --git a/Makefile b/Makefile index a57f9ff8a..09c516680 100644 --- a/Makefile +++ b/Makefile @@ -165,6 +165,7 @@ DIRECT_TESTS += \ java_litho-required-props \ java_performance \ java_performance-exclusive \ + java_pulse \ java_purity \ java_quandary \ java_racerd \ diff --git a/infer/tests/codetoanalyze/java/pulse/DynamicDispatch.java b/infer/tests/codetoanalyze/java/pulse/DynamicDispatch.java new file mode 100644 index 000000000..5d80b4aa7 --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/DynamicDispatch.java @@ -0,0 +1,157 @@ +/* + * 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.infer; + +public class DynamicDispatch { + + static interface Interface { + public Object foo(); + } + + static class Impl implements Interface { + @Override + public Object foo() { + return null; + } + } + + static void interfaceShouldNotCauseFalseNegativeEasyBad() { + Interface i = new Impl(); + // should be a warning since Impl's implementation of foo returns null + i.foo().toString(); + } + + static void interfaceShouldNotCauseFalseNegativeHardOK(Interface i) { + i.foo().toString(); + } + + static void callWithBadImplementationBad_FN(Impl impl) { + interfaceShouldNotCauseFalseNegativeHardOK(impl); + } + + static class Supertype { + Object foo() { + return new Object(); + } + + Object bar() { + return null; + } + } + + static class Subtype extends Supertype { + @Override + Object foo() { + return null; + } + + @Override + Object bar() { + return new Object(); + } + } + + static void dynamicDispatchShouldNotCauseFalseNegativeEasyBad() { + Supertype o = new Subtype(); + // should report a warning because we know the dynamic type of o is Subtype + o.foo().toString(); + } + + static void dynamicDispatchShouldNotCauseFalsePositiveEasyOK() { + Supertype o = new Subtype(); + // should not report a warning because we know the dynamic type of o is Subtype + o.bar().toString(); + } + + static void dynamicDispatchShouldNotReportWhenCallingSupertypeOK(Supertype o) { + // should not report a warning because the Supertype implementation + // of foo() does not return null + o.foo().toString(); + } + + static void dynamicDispatchShouldReportWhenCalledWithSubtypeParameterBad_FN(Subtype o) { + // should report a warning because the Subtype implementation + // of foo() returns null + dynamicDispatchShouldNotReportWhenCallingSupertypeOK(o); + } + + static Object dynamicDispatchWrapperFoo(Supertype o) { + return o.foo(); + } + + static Object dynamicDispatchWrapperBar(Supertype o) { + return o.bar(); + } + + static void dynamicDispatchCallsWrapperWithSupertypeOK() { + // Should not report because Supertype.foo() does not return null + Supertype o = new Supertype(); + dynamicDispatchWrapperFoo(o).toString(); + } + + static void dynamicDispatchCallsWrapperWithSupertypeBad() { + // Should report because Supertype.bar() returns null + Supertype o = new Supertype(); + dynamicDispatchWrapperBar(o).toString(); + } + + static void dynamicDispatchCallsWrapperWithSubtypeBad_FN() { + // Should report because Subtype.foo() returns null + Supertype o = new Subtype(); + dynamicDispatchWrapperFoo(o).toString(); + } + + static void dynamicDispatchCallsWrapperWithSubtypeOK_FP() { + // Should not report because Subtype.bar() does not returns null + Supertype o = new Subtype(); + dynamicDispatchWrapperBar(o).toString(); + } + + static class WithField { + + Supertype mField; + + WithField(Supertype t) { + mField = t; + } + + static void dispatchOnFieldOK_FP() { + Supertype subtype = new Subtype(); + WithField object = new WithField(subtype); + object.mField.bar().toString(); + } + + static void dispatchOnFieldBad_FN() { + Supertype subtype = new Subtype(); + WithField object = new WithField(subtype); + object.mField.foo().toString(); + } + } + + private Object callFoo(Supertype o) { + return o.foo(); + } + + void dynamicResolutionWithPrivateMethodBad_FN() { + Supertype subtype = new Subtype(); + callFoo(subtype).toString(); + } + + Object variadicMethod(Supertype... args) { + if (args.length == 0) { + return null; + } else { + return args[0].foo(); + } + } + + void dynamicResolutionWithVariadicMethodBad() { + Supertype subtype = new Subtype(); + variadicMethod(subtype, null, null).toString(); + } +} diff --git a/infer/tests/codetoanalyze/java/pulse/Makefile b/infer/tests/codetoanalyze/java/pulse/Makefile new file mode 100644 index 000000000..636a8e4ba --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/Makefile @@ -0,0 +1,12 @@ +# 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 = --pulse-only --debug-exceptions +INFERPRINT_OPTIONS = --issues-tests +SOURCES = $(wildcard *.java) + +include $(TESTS_DIR)/javac.make diff --git a/infer/tests/codetoanalyze/java/pulse/issues.exp b/infer/tests/codetoanalyze/java/pulse/issues.exp new file mode 100644 index 000000000..13bce5bf0 --- /dev/null +++ b/infer/tests/codetoanalyze/java/pulse/issues.exp @@ -0,0 +1,6 @@ +codetoanalyze/java/pulse/DynamicDispatch.java, codetoanalyze.java.infer.DynamicDispatch$WithField.dispatchOnFieldOK_FP():void, 3, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `Object DynamicDispatch$Supertype.bar()` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `Object DynamicDispatch$Supertype.bar()`,return from call to `Object DynamicDispatch$Supertype.bar()`,assigned,invalid access occurs here] +codetoanalyze/java/pulse/DynamicDispatch.java, codetoanalyze.java.infer.DynamicDispatch.dynamicDispatchCallsWrapperWithSubtypeOK_FP():void, 3, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `Object DynamicDispatch.dynamicDispatchWrapperBar(DynamicDispatch$Supertype)` here,when calling `Object DynamicDispatch$Supertype.bar()` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `Object DynamicDispatch.dynamicDispatchWrapperBar(DynamicDispatch$Supertype)`,return from call to `Object DynamicDispatch.dynamicDispatchWrapperBar(DynamicDispatch$Supertype)`,assigned,invalid access occurs here] +codetoanalyze/java/pulse/DynamicDispatch.java, codetoanalyze.java.infer.DynamicDispatch.dynamicDispatchCallsWrapperWithSupertypeBad():void, 3, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `Object DynamicDispatch.dynamicDispatchWrapperBar(DynamicDispatch$Supertype)` here,when calling `Object DynamicDispatch$Supertype.bar()` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `Object DynamicDispatch.dynamicDispatchWrapperBar(DynamicDispatch$Supertype)`,return from call to `Object DynamicDispatch.dynamicDispatchWrapperBar(DynamicDispatch$Supertype)`,assigned,invalid access occurs here] +codetoanalyze/java/pulse/DynamicDispatch.java, codetoanalyze.java.infer.DynamicDispatch.dynamicDispatchShouldNotCauseFalseNegativeEasyBad():void, 3, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `Object DynamicDispatch$Subtype.foo()` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `Object DynamicDispatch$Subtype.foo()`,return from call to `Object DynamicDispatch$Subtype.foo()`,assigned,invalid access occurs here] +codetoanalyze/java/pulse/DynamicDispatch.java, codetoanalyze.java.infer.DynamicDispatch.dynamicResolutionWithVariadicMethodBad():void, 2, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `Object DynamicDispatch.variadicMethod(DynamicDispatch$Supertype[])` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `Object DynamicDispatch.variadicMethod(DynamicDispatch$Supertype[])`,return from call to `Object DynamicDispatch.variadicMethod(DynamicDispatch$Supertype[])`,assigned,invalid access occurs here] +codetoanalyze/java/pulse/DynamicDispatch.java, codetoanalyze.java.infer.DynamicDispatch.interfaceShouldNotCauseFalseNegativeEasyBad():void, 3, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `Object DynamicDispatch$Impl.foo()` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `Object DynamicDispatch$Impl.foo()`,return from call to `Object DynamicDispatch$Impl.foo()`,assigned,invalid access occurs here]