import org.checkerframework.dataflow.qual.Deterministic;
import org.checkerframework.dataflow.qual.Pure;
import org.checkerframework.framework.test.*;
import testlib.util.*;

// various tests about keeping information in the store about pure method calls
class StorePure {

    String f1, f2;

    // various pure methods

    @Pure
    String pure1() {
        return null;
    }

    @Pure
    String pure1b() {
        return null;
    }

    @Deterministic
    String pure1c() {
        return null;
    }

    @Pure
    String pure2(int i) {
        return null;
    }

    @Pure
    String pure3(boolean b) {
        return null;
    }

    @Pure
    String pure4(String o) {
        return null;
    }

    void nonpure() {}

    void t1(@Odd String p1, String p2, boolean b1) {
        String l0 = "";
        if (pure1() == p1) {
            @Odd String l1 = pure1();
            l0 = "a"; // does not remove information
            @Odd String l1b = pure1();
            // :: error: (assignment.type.incompatible)
            @Odd String l2 = pure1b();
            nonpure(); // non-pure method call might change the return value of pure1
            // :: error: (assignment.type.incompatible)
            @Odd String l3 = pure1();
        }
    }

    // check that it only works for deterministic methods
    void t1b(@Odd String p1, String p2, boolean b1) {
        if (pure1c() == p1) {
            @Odd String l1 = pure1c();
        }
    }

    void t2(@Odd String p1, String p2, boolean b1) {
        String l0 = "";
        if (pure1() == p1) {
            @Odd String l1 = pure1();
            l0 = "a"; // does not remove information
            @Odd String l1b = pure1();
            // :: error: (assignment.type.incompatible)
            @Odd String l2 = pure1b();
            f1 = ""; // field update might change the return value of pure1
            // :: error: (assignment.type.incompatible)
            @Odd String l3 = pure1();
        }
    }

    void t3(@Odd String p1, String p2, boolean b1) {
        String l0 = "";
        if (pure2(1) == p1) {
            // :: error: (assignment.type.incompatible)
            @Odd String l4 = pure2(0);
            @Odd String l1 = pure2(1);
            l0 = "a"; // does not remove information
            @Odd String l1b = pure2(1);
            nonpure(); // non-pure method call might change the return value of pure2
            // :: error: (assignment.type.incompatible)
            @Odd String l3 = pure2(1);
        }
    }

    void t4(@Odd String p1, String p2, boolean b1) {
        String l0 = "";
        if (pure2(1) == p1) {
            // :: error: (assignment.type.incompatible)
            @Odd String l4 = pure2(0);
            @Odd String l1 = pure2(1);
            l0 = "a"; // does not remove information
            @Odd String l1b = pure2(1);
            f1 = ""; // field update might change the return value of pure2
            // :: error: (assignment.type.incompatible)
            @Odd String l3 = pure2(1);
        }
    }

    void t5(@Odd String p1, String p2, boolean b1) {
        String l0 = "";
        if (pure3(true) == p1) {
            // :: error: (assignment.type.incompatible)
            @Odd String l4 = pure3(false);
            @Odd String l1 = pure3(true);
            l0 = "a"; // does not remove information
            @Odd String l1b = pure3(true);
            nonpure(); // non-pure method call might change the return value of pure2
            // :: error: (assignment.type.incompatible)
            @Odd String l3 = pure3(true);
        }
    }

    void t6(@Odd String p1, String p2, boolean b1) {
        String l0 = "";
        if (pure3(true) == p1) {
            // :: error: (assignment.type.incompatible)
            @Odd String l4 = pure3(false);
            @Odd String l1 = pure3(true);
            l0 = "a"; // does not remove information
            @Odd String l1b = pure3(true);
            f1 = ""; // field update might change the return value of pure2
            // :: error: (assignment.type.incompatible)
            @Odd String l3 = pure3(true);
        }
    }

    // local variable as argument
    void t7(@Odd String p1, String p2, boolean b1) {
        String l0 = "";
        if (pure4(l0) == p1) {
            // :: error: (assignment.type.incompatible)
            @Odd String l4 = pure4("jk");
            @Odd String l1 = pure4(l0);
            l0 = "a"; // remove information (!)
            // :: error: (assignment.type.incompatible)
            @Odd String l1b = pure4(l0);
        }
    }
}
