| 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) |
| 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) |
| 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) |
| p = methodToCall2(o); |
| // :: error: (lock.not.held) |
| methodToCall2(o); |
| methodToCall2(p); |
| } |
| synchronized (lock1) { |
| o = methodToCall2(o); |
| // :: error: (assignment) |
| 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) 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) |
| 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) |
| return o; |
| } |
| |
| // :: error: (guardsatisfied.return.must.have.index) |
| @GuardSatisfied Object testReturnTypesMustMatchAndMustHaveAnIndex6(@GuardSatisfied(2) Object o) { |
| // :: error: (return) |
| return o; |
| } |
| |
| void testParamsMustMatch(@GuardSatisfied(1) Object o, @GuardSatisfied(2) Object p) { |
| // :: error: (assignment) |
| 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) {} |
| } |
| |
| void testGuardSatisfiedOnWildCardExtendsBound( |
| // :: error: (guardsatisfied.location.disallowed) |
| MyParameterizedClass1<? extends @GuardSatisfied Object> l) {} |
| |
| void testGuardSatisfiedOnWildCardSuperBound( |
| // :: error: (guardsatisfied.location.disallowed) |
| 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) |
| f.m1(); |
| } |
| |
| @MayReleaseLocks |
| void m2_2(Foo f) { |
| f.m1(); |
| } |
| |
| void m3(@GuardSatisfied Foo f) { |
| // :: error: (method.guarantee.violated) :: error: (method.invocation) |
| f.m1(); |
| } |
| |
| @MayReleaseLocks |
| void m4(Foo f) { |
| f.m1(); |
| } |
| |
| @MayReleaseLocks |
| void m5(Foo f) { |
| m3(f); |
| } |
| } |