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

public class Fields {
    class MyClass {
        public Object field;
    }

    static @GuardedBy("Fields.class") MyClass lockedStatically;

    static synchronized void ssMethod() {
        lockedStatically.field = new Object();
    }

    @GuardedBy("lockingObject") MyClass locked;

    final Object lockingObject = new Object();

    synchronized void wrongLock1() {
        // locking over wrong lock
        // :: error: (lock.not.held)
        locked.field = new Object(); // error
    }

    synchronized void wrongLock2() {
        // locking over wrong lock
        synchronized (this) {
            // :: error: (lock.not.held)
            locked.field = new Object(); // error
        }
    }

    void rightLock() {
        synchronized (lockingObject) {
            locked.field = new Object();
        }

        // accessing after the synchronized object
        // :: error: (lock.not.held)
        locked.field = new Object(); // error
    }

    @Holding("lockingObject")
    void usingHolding() {
        locked.field = new Object();
    }

    @GuardedBy("this") MyClass lockedByThis;

    void wrongLocksb() {
        // without locking
        // :: error: (lock.not.held)
        lockedByThis.field = new Object(); // error

        synchronized (Fields.class) {
            // :: error: (lock.not.held)
            lockedByThis.field = new Object(); // error
        }
    }

    void rightLockb() {
        synchronized (this) {
            lockedByThis.field = new Object();
        }

        // accessing after the synchronized object
        // :: error: (lock.not.held)
        lockedByThis.field = new Object(); // error
    }

    synchronized void synchronizedMethodb() {
        lockedByThis.field = new Object();
    }

    void test() {
        // synchronized over the right object
        final Fields a = new Fields();
        final Fields b = new Fields();

        synchronized (this) {
            lockedByThis.field = new Object();
            // :: error: (lock.not.held)
            a.lockedByThis.field = new Object(); // error
            // :: error: (lock.not.held)
            b.lockedByThis.field = new Object(); // error
        }

        synchronized (a) {
            // :: error: (lock.not.held)
            lockedByThis.field = new Object(); // error
            a.lockedByThis.field = new Object();
            // :: error: (lock.not.held)
            b.lockedByThis.field = new Object(); // error
        }

        synchronized (b) {
            // :: error: (lock.not.held)
            lockedByThis.field = new Object(); // error
            // :: error: (lock.not.held)
            a.lockedByThis.field = new Object(); // error
            b.lockedByThis.field = new Object();
        }
    }
}
