import org.checkerframework.checker.nullness.qual.*;

public class FlowLoop {
    void simpleWhileLoop() {
        String s = "m";

        while (s != null) {
            s.toString();
            s = null;
        }
        // :: error: (dereference.of.nullable)
        s.toString(); // error
    }

    void whileConditionError() {
        String s = "m";

        // :: error: (dereference.of.nullable)
        while (s.toString() == "m") { // error
            s.toString();
            s = null;
        }
        s.toString();
    }

    void simpleForLoop() {
        for (String s = "m"; s != null; s = null) {
            s.toString();
        }
    }

    void forLoopConditionError() {
        for (String s = "m";
                // :: error: (dereference.of.nullable)
                s.toString() != "m"; // error
                s = null) {
            s.toString();
        }
    }

    class Link {
        Object val;
        @Nullable Link next;

        public Link(Object val, @Nullable Link next) {
            this.val = val;
            this.next = next;
        }
    }

    // Both dereferences of l should succeed
    void test(@Nullable Link in) {
        for (@Nullable Link l = in; l != null; l = l.next) {
            Object o;
            o = l.val;
        }
    }

    void multipleRuns() {
        String s = "m";
        while (true) {
            // :: error: (dereference.of.nullable)
            s.toString(); // error
            s = null;
        }
    }

    void multipleRunsDo() {
        String s = "m";
        do {
            // :: error: (dereference.of.nullable)
            s.toString(); // error
            s = null;
        } while (true);
    }

    void alwaysRunForLoop() {
        String s = "m";
        for (s = null; s != null; s = "m") {
            s.toString(); // ok
        }
        // :: error: (dereference.of.nullable)
        s.toString(); // error
    }

    public void badIterator() {
        Class<?> opt_doc1 = null;
        // :: error: (dereference.of.nullable)
        opt_doc1.getInterfaces();
        Class<?> opt_doc2 = null;
        // :: error: (dereference.of.nullable)
        for (Class<? extends @Nullable Object> fd : opt_doc2.getInterfaces()) {
            // empty loop body
        }
    }

    void testContinue(@Nullable Object o) {
        for (; ; ) {
            // :: error: (dereference.of.nullable)
            o.toString();
            if (true) continue;
        }
    }

    void testBreak(@Nullable Object o) {
        while (true) {
            // :: error: (dereference.of.nullable)
            o.toString();
            if (true) break;
        }
    }

    void testSimpleNull() {
        String r1 = null;
        while (r1 != null) ;
        // :: error: (dereference.of.nullable)
        r1.toString(); // error
    }

    void testMulticheckNull() {
        String r1 = null;
        while (r1 != null && r1.equals("m")) ;
        // :: error: (dereference.of.nullable)
        r1.toString(); // error
    }

    void testAssignInLoopSimple() {
        String r1 = "";
        while (r1 != null) {
            r1 = null;
        }
        // :: error: (dereference.of.nullable)
        r1.toString(); // error
    }

    void testAssignInLoopMulti() {
        String r1 = "";
        while (r1 != null && r1.isEmpty()) {
            r1 = null;
        }
        // :: error: (dereference.of.nullable)
        r1.toString(); // error
    }

    void testBreakWithCheck() {
        String s = null;
        while (true) {
            if (s == null) break;
            s.toString();
        }
    }

    void test1() {
        while (true) {
            String s = null;
            if (s == null) {
                return;
            }
            s.toString();
        }
    }
}
