import org.checkerframework.checker.lock.qual.GuardSatisfied; import org.checkerframework.checker.lock.qual.GuardedBy; import org.checkerframework.checker.lock.qual.GuardedByUnknown; import org.checkerframework.checker.lock.qual.MayReleaseLocks; public class GuardSatisfiedTest { void testGuardSatisfiedIndexMatching( @GuardSatisfied GuardSatisfiedTest this, @GuardSatisfied(1) Object o, @GuardSatisfied(2) Object p, @GuardSatisfied Object q) { methodToCall1(o, o); methodToCall1(p, p); //:: error: (guardsatisfied.parameters.must.match) methodToCall1(o, p); //:: error: (guardsatisfied.parameters.must.match) methodToCall1(p, o); } // Test defaulting of parameters - they must default to @GuardedBy({}), not @GuardSatisfied void testDefaulting(Object mustDefaultToGuardedByNothing, @GuardSatisfied Object p) { // Must assign in this direction to test the defaulting because assigning a RHS of @GuardedBy({}) to a LHS @GuardSatisfied is legal. //:: error: (assignment.type.incompatible) mustDefaultToGuardedByNothing = p; @GuardedBy({}) Object q = mustDefaultToGuardedByNothing; } void testMethodCall( @GuardSatisfied GuardSatisfiedTest this, @GuardedBy("lock1") Object o, @GuardedBy("lock2") Object p, @GuardSatisfied Object q) { // Test matching parameters //:: error: (lock.not.held) methodToCall1(o, o); //:: error: (lock.not.held) :: error: (guardsatisfied.parameters.must.match) methodToCall1(o, p); //:: error: (lock.not.held) methodToCall1(p, p); synchronized (lock2) { //:: error: (lock.not.held) methodToCall1(o, o); //:: error: (guardsatisfied.parameters.must.match) :: error: (lock.not.held) methodToCall1(o, p); methodToCall1(p, p); synchronized (lock1) { methodToCall1(o, o); //:: error: (guardsatisfied.parameters.must.match) methodToCall1(o, p); methodToCall1(p, p); } } // Test a return type matching a parameter. //:: error: (lock.not.held) o = methodToCall2(o); //:: error: (lock.not.held) :: error: (assignment.type.incompatible) p = methodToCall2(o); //:: error: (lock.not.held) methodToCall2(o); //:: error: (lock.not.held) methodToCall2(p); synchronized (lock2) { //:: error: (lock.not.held) o = methodToCall2(o); //:: error: (lock.not.held) :: error: (assignment.type.incompatible) p = methodToCall2(o); //:: error: (lock.not.held) methodToCall2(o); methodToCall2(p); } synchronized (lock1) { o = methodToCall2(o); //:: error: (assignment.type.incompatible) p = methodToCall2(o); methodToCall2(o); //:: error: (lock.not.held) methodToCall2(p); } // Test the receiver type matching a parameter // Two @GS parameters with no index are incomparable (as is the case for 'this' and 'q') //:: error: (guardsatisfied.parameters.must.match) methodToCall3(q); //:: error: (guardsatisfied.parameters.must.match) :: error: (lock.not.held) methodToCall3(p); synchronized (lock1) { // Two @GS parameters with no index are incomparable (as is the case for 'this' and 'q') //:: error: (guardsatisfied.parameters.must.match) methodToCall3(q); //:: error: (guardsatisfied.parameters.must.match) :: error: (lock.not.held) methodToCall3(p); synchronized (lock2) { // Two @GS parameters with no index are incomparable (as is the case for 'this' and 'q') //:: error: (guardsatisfied.parameters.must.match) methodToCall3(q); //:: error: (guardsatisfied.parameters.must.match) methodToCall3(p); } } // Test the return type matching the receiver type methodToCall4(); } // Test the return type NOT matching the receiver type void testMethodCall(@GuardedBy("lock1") GuardSatisfiedTest this) { @GuardedBy("lock2") Object g; //:: error: (lock.not.held) methodToCall4(); // TODO: lock.not.held is getting swallowed below // error (assignment.type.incompatible) error (lock.not.held) // g = methodToCall4(); // Separate the above test case into two for now //:: error: (lock.not.held) methodToCall4(); // The following error is due to the fact that you cannot access "this.lock1" without first having acquired "lock1". // The right fix in a user scenario would be to not guard "this" with "this.lock1". The current object could instead // be guarded by "<self>" or by some other lock expression that is not one of its fields. We are keeping this test // case here to make sure this scenario issues a warning. //:: error: (lock.not.held) synchronized (lock1) { //:: error: (assignment.type.incompatible) g = methodToCall4(); } } //:: error: (guardsatisfied.return.must.have.index) @GuardSatisfied Object testReturnTypesMustMatchAndMustHaveAnIndex1(@GuardSatisfied Object o) { // If the two @GuardSatisfied had an index, this error would not be issued: //:: error: (guardsatisfied.assignment.disallowed) return o; } @GuardSatisfied(1) Object testReturnTypesMustMatchAndMustHaveAnIndex2(@GuardSatisfied(1) Object o) { return o; } @GuardSatisfied(0) Object testReturnTypesMustMatchAndMustHaveAnIndex3(@GuardSatisfied(0) Object o) { return o; } // @GuardSatisfied is equivalent to @GuardSatisfied(-1). //:: error: (guardsatisfied.return.must.have.index) @GuardSatisfied Object testReturnTypesMustMatchAndMustHaveAnIndex4(@GuardSatisfied(-1) Object o) { // If the two @GuardSatisfied had an index, this error would not be issued: //:: error: (guardsatisfied.assignment.disallowed) return o; } @GuardSatisfied(1) Object testReturnTypesMustMatchAndMustHaveAnIndex5(@GuardSatisfied(2) Object o) { //:: error: (return.type.incompatible) return o; } //:: error: (guardsatisfied.return.must.have.index) @GuardSatisfied Object testReturnTypesMustMatchAndMustHaveAnIndex6(@GuardSatisfied(2) Object o) { //:: error: (return.type.incompatible) return o; } void testParamsMustMatch(@GuardSatisfied(1) Object o, @GuardSatisfied(2) Object p) { //:: error: (assignment.type.incompatible) o = p; } void methodToCall1( @GuardSatisfied GuardSatisfiedTest this, @GuardSatisfied(1) Object o, @GuardSatisfied(1) Object p) {} @GuardSatisfied(1) Object methodToCall2(@GuardSatisfied GuardSatisfiedTest this, @GuardSatisfied(1) Object o) { return o; } void methodToCall3(@GuardSatisfied(1) GuardSatisfiedTest this, @GuardSatisfied(1) Object o) {} @GuardSatisfied(1) Object methodToCall4(@GuardSatisfied(1) GuardSatisfiedTest this) { return this; } final Object lock1 = new Object(), lock2 = new Object(); void testAssignment(@GuardSatisfied Object o) { @GuardedBy({"lock1", "lock2"}) Object p = new Object(); //:: error: (lock.not.held) o = p; synchronized (lock1) { //:: error: (lock.not.held) o = p; synchronized (lock2) { o = p; } } } // Test disallowed @GuardSatisfied locations. // Whenever a disallowed location can be located within a method return type, // receiver or parameter, test it there, because it's important to check // that those are not mistakenly allowed, since annotations // on method return types, receivers and parameters are allowed. // By definition, fields and non-parameter local variables cannot be // in one of these locations on a method declaration, but other // locations can be. //:: error: (guardsatisfied.location.disallowed) @GuardSatisfied Object field; //:: error: (guardsatisfied.location.disallowed) void testGuardSatisfiedOnArrayElementAndLocalVariable(@GuardSatisfied Object[] array) { //:: error: (guardsatisfied.location.disallowed) @GuardSatisfied Object local; } //:: error: (guardsatisfied.location.disallowed) <T extends @GuardSatisfied Object> T testGuardSatisfiedOnBound(T t) { return t; } class MyParameterizedClass1<T extends @GuardedByUnknown Object> { void testGuardSatisfiedOnReceiverOfParameterizedClass( @GuardSatisfied MyParameterizedClass1<T> this) {} void testGuardSatisfiedOnArrayOfParameterizedType( MyParameterizedClass1<T> @GuardSatisfied [] array) {} void testGuardSatisfiedOnArrayComponentOfParameterizedType( //:: error: (guardsatisfied.location.disallowed) @GuardSatisfied MyParameterizedClass1<T>[] array) {} }; // TODO: Enable :: error: (guardsatisfied.location.disallowed) void testGuardSatisfiedOnWildCardExtendsBound( MyParameterizedClass1<? extends @GuardSatisfied Object> l) {} // TODO: Enable :: error: (guardsatisfied.location.disallowed) void testGuardSatisfiedOnWildCardSuperBound( MyParameterizedClass1<? super @GuardSatisfied Object> l) {} @GuardSatisfied(1) Object testGuardSatisfiedOnParameters( @GuardSatisfied GuardSatisfiedTest this, Object @GuardSatisfied [] array, @GuardSatisfied Object param, @GuardSatisfied(1) Object param2) { return param2; } void testGuardSatisfiedOnArray1(Object @GuardSatisfied [][][] array) {} //:: error: (guardsatisfied.location.disallowed) void testGuardSatisfiedOnArray2(@GuardSatisfied Object[][][] array) {} //:: error: (guardsatisfied.location.disallowed) void testGuardSatisfiedOnArray3(Object[] @GuardSatisfied [][] array) {} //:: error: (guardsatisfied.location.disallowed) void testGuardSatisfiedOnArray4(Object[][] @GuardSatisfied [] array) {} } class Foo { @MayReleaseLocks void m1() {} @MayReleaseLocks //:: error: (guardsatisfied.with.mayreleaselocks) void m2(@GuardSatisfied Foo f) { //:: error: (method.invocation.invalid) f.m1(); } @MayReleaseLocks void m2_2(Foo f) { f.m1(); } void m3(@GuardSatisfied Foo f) { //:: error: (method.guarantee.violated) :: error: (method.invocation.invalid) f.m1(); } @MayReleaseLocks void m4(Foo f) { f.m1(); } @MayReleaseLocks void m5(Foo f) { m3(f); } }