import org.checkerframework.checker.nullness.qual.*; import org.checkerframework.dataflow.qual.*; public class AssertIfClient { @RequiresNonNull("#1.rpcResponse()") void rpcResponseNonNull(Proxy proxy) { @NonNull Object response = proxy.rpcResponse(); } void rpcResponseNullable(Proxy proxy) { @Nullable Object response = proxy.rpcResponse(); } void rpcResponseTypestate() { Proxy proxy = new Proxy(); //:: error: (assignment.type.incompatible) @NonNull Object response1 = proxy.rpcResponse(); //:: error: (contracts.precondition.not.satisfied) rpcResponseNonNull(proxy); rpcResponseNullable(proxy); proxy.issueRpc(); @NonNull Object response2 = proxy.rpcResponse(); @NonNull Object response3 = proxy.rpcResponse(); rpcResponseNonNull(proxy); rpcResponseNullable(proxy); } } class Proxy { // the RPC response, or null if not yet received @MonotonicNonNull Object response = null; @SuppressWarnings("contracts.postcondition.not.satisfied") @EnsuresNonNull({"response", "rpcResponse()"}) void issueRpc() { response = new Object(); } // If this method returns true, // then response is non-null and rpcResponse() returns non-null @SuppressWarnings("contracts.conditional.postcondition.not.satisfied") @EnsuresNonNullIf( expression = {"response", "rpcResponse()"}, result = true ) boolean rpcResponseReceived() { return response != null; } // Returns non-null if the response has been received, null otherwise; // but an @AssertNonNullIfNonNull annotation would states the converse, // that if the result is non-null then the response hs been received. // See rpcResponseReceived. @Pure @Nullable Object rpcResponse() { return response; } }