import org.checkerframework.dataflow.qual.*;
import org.checkerframework.framework.qual.EnsuresQualifier;
import testlib.util.*;

class MethodCallFlowExpr {

    @Pure
    String p1(int i) {
        return "";
    }

    @Pure
    String p1b(Long i) {
        return "";
    }

    @Pure
    String p1(String s) {
        return "";
    }

    String nonpure() {
        return "";
    }

    @Pure
    String p2(String s, long l, String s2) {
        return "";
    }

    @Pure
    <T> String p1c(T i) {
        return "";
    }

    @Pure
    static String p1d(int i) {
        return "";
    }

    @EnsuresQualifier(expression = "p1c(\"abc\")", qualifier = Odd.class)
    // :: error: (contracts.postcondition.not.satisfied)
    void e0() {
        // don't bother with implementation
    }

    @EnsuresQualifier(expression = "p1(1)", qualifier = Odd.class)
    // :: error: (contracts.postcondition.not.satisfied)
    void e1() {
        // don't bother with implementation
    }

    @EnsuresQualifier(expression = "p1(\"abc\")", qualifier = Odd.class)
    // :: error: (contracts.postcondition.not.satisfied)
    void e2() {
        // don't bother with implementation
    }

    @EnsuresQualifier(expression = "p2(\"abc\", 2L, p1(1))", qualifier = Odd.class)
    // :: error: (contracts.postcondition.not.satisfied)
    void e4() {
        // don't bother with implementation
    }

    @EnsuresQualifier(expression = "p1b(2L)", qualifier = Odd.class)
    // :: error: (contracts.postcondition.not.satisfied)
    void e5() {
        // don't bother with implementation
    }

    @EnsuresQualifier(expression = "p1b(null)", qualifier = Odd.class)
    // :: error: (contracts.postcondition.not.satisfied)
    void e6() {
        // don't bother with implementation
    }

    @EnsuresQualifier(expression = "p1d(1)", qualifier = Odd.class)
    // :: error: (contracts.postcondition.not.satisfied)
    void e7a() {
        // don't bother with implementation
    }

    @EnsuresQualifier(expression = "this.p1d(1)", qualifier = Odd.class)
    // :: error: (contracts.postcondition.not.satisfied)
    void e7b() {
        // don't bother with implementation
    }

    @EnsuresQualifier(expression = "MethodCallFlowExpr.p1d(1)", qualifier = Odd.class)
    // :: error: (contracts.postcondition.not.satisfied)
    void e7c() {
        // don't bother with implementation
    }

    void t1() {
        // :: error: (assignment.type.incompatible)
        @Odd String l1 = p1(1);
        e1();
        // :: error: (assignment.type.incompatible)
        @Odd String l2 = p1(2);
        @Odd String l3 = p1(1);
    }

    void t2() {
        // :: error: (assignment.type.incompatible)
        @Odd String l1 = p1("abc");
        e2();
        // :: error: (assignment.type.incompatible)
        @Odd String l2 = p1("def");
        // :: error: (assignment.type.incompatible)
        @Odd String l2b = p1(1);
        @Odd String l3 = p1("abc");
    }

    void t3() {
        // :: error: (assignment.type.incompatible)
        @Odd String l1 = p2("abc", 2L, p1(1));
        e4();
        // :: error: (assignment.type.incompatible)
        @Odd String l2 = p2("abc", 2L, p1(2));
        // :: error: (assignment.type.incompatible)
        @Odd String l2b = p2("abc", 1L, p1(1));
        @Odd String l3 = p2("abc", 2L, p1(1));
    }

    void t4() {
        // :: error: (assignment.type.incompatible)
        @Odd String l1 = p1b(2L);
        e5();
        // :: error: (assignment.type.incompatible)
        @Odd String l2 = p1b(null);
        // :: error: (assignment.type.incompatible)
        @Odd String l2b = p1b(1L);
        // FIXME: the following line shouldn't give an error
        // (or at least it didn't give an error earlier)
        // @Odd String l3 = p1b(2L);
    }

    void t5() {
        // :: error: (assignment.type.incompatible)
        @Odd String l1 = p1b(null);
        e6();
        // :: error: (assignment.type.incompatible)
        @Odd String l2 = p1b(1L);
        // FIXME: the following line shouldn't give an error
        // (or at least it didn't give an error earlier)
        // @Odd String l3 = p1b(null);
    }

    void t6() {
        // :: error: (assignment.type.incompatible)
        @Odd String l1 = p1c("abc");
        e0();
        // :: error: (assignment.type.incompatible)
        @Odd String l2 = p1c("def");
        @Odd String l3 = p1c("abc");
    }

    void t7() {
        // :: error: (assignment.type.incompatible)
        @Odd String l1 = p1d(1);
        // :: error: (assignment.type.incompatible)
        @Odd String l1b = MethodCallFlowExpr.p1d(1);
        // :: error: (assignment.type.incompatible)
        @Odd String l1c = this.p1d(1);
        e7a();
        @Odd String l2 = p1d(1);
        @Odd String l2b = MethodCallFlowExpr.p1d(1);
        @Odd String l2c = this.p1d(1);
    }

    void t8() {
        // :: error: (assignment.type.incompatible)
        @Odd String l1 = p1d(1);
        // :: error: (assignment.type.incompatible)
        @Odd String l1b = MethodCallFlowExpr.p1d(1);
        // :: error: (assignment.type.incompatible)
        @Odd String l1c = this.p1d(1);
        e7b();
        @Odd String l2 = p1d(1);
        @Odd String l2b = MethodCallFlowExpr.p1d(1);
        @Odd String l2c = this.p1d(1);
    }

    void t9() {
        // :: error: (assignment.type.incompatible)
        @Odd String l1 = p1d(1);
        // :: error: (assignment.type.incompatible)
        @Odd String l1b = MethodCallFlowExpr.p1d(1);
        // :: error: (assignment.type.incompatible)
        @Odd String l1c = this.p1d(1);
        e7c();
        @Odd String l2 = p1d(1);
        @Odd String l2b = MethodCallFlowExpr.p1d(1);
        @Odd String l2c = this.p1d(1);
    }
}
