Skip to content

Commit 17226ff

Browse files
authored
Add Optional method annotations
1 parent 964d027 commit 17226ff

File tree

4 files changed

+54
-84
lines changed

4 files changed

+54
-84
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.checkerframework.checker.optional.qual;
2+
3+
import java.lang.annotation.Documented;
4+
import java.lang.annotation.ElementType;
5+
import java.lang.annotation.Retention;
6+
import java.lang.annotation.RetentionPolicy;
7+
import java.lang.annotation.Target;
8+
import org.checkerframework.framework.qual.InheritedAnnotation;
9+
10+
/** An method annotation for methods that create an {@link java.util.Optional} */
11+
@Documented
12+
@Retention(RetentionPolicy.RUNTIME)
13+
@Target(ElementType.METHOD)
14+
@InheritedAnnotation
15+
public @interface OptionalCreator {}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.checkerframework.checker.optional.qual;
2+
3+
import java.lang.annotation.Documented;
4+
import java.lang.annotation.ElementType;
5+
import java.lang.annotation.Retention;
6+
import java.lang.annotation.RetentionPolicy;
7+
import java.lang.annotation.Target;
8+
import org.checkerframework.framework.qual.InheritedAnnotation;
9+
10+
/** Methods whose receiver is an {@link java.util.Optional} and return a non-optional. */
11+
@Documented
12+
@Retention(RetentionPolicy.RUNTIME)
13+
@Target(ElementType.METHOD)
14+
@InheritedAnnotation
15+
public @interface OptionalEliminator {}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.checkerframework.checker.optional.qual;
2+
3+
import java.lang.annotation.Documented;
4+
import java.lang.annotation.ElementType;
5+
import java.lang.annotation.Retention;
6+
import java.lang.annotation.RetentionPolicy;
7+
import java.lang.annotation.Target;
8+
import org.checkerframework.framework.qual.InheritedAnnotation;
9+
10+
/** Methods whose receiver is an {@link java.util.Optional} and return an {@code Optional}. */
11+
@Documented
12+
@Retention(RetentionPolicy.RUNTIME)
13+
@Target(ElementType.METHOD)
14+
@InheritedAnnotation
15+
public @interface OptionalPropagator {}

checker/src/main/java/org/checkerframework/checker/optional/OptionalVisitor.java

Lines changed: 9 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import com.sun.source.tree.UnaryTree;
1616
import com.sun.source.tree.VariableTree;
1717
import com.sun.source.util.TreePath;
18-
import java.util.Arrays;
1918
import java.util.Collection;
2019
import java.util.List;
2120
import javax.annotation.processing.ProcessingEnvironment;
@@ -27,6 +26,9 @@
2726
import javax.lang.model.type.TypeMirror;
2827
import org.checkerframework.checker.compilermsgs.qual.CompilerMessageKey;
2928
import org.checkerframework.checker.nullness.qual.Nullable;
29+
import org.checkerframework.checker.optional.qual.OptionalCreator;
30+
import org.checkerframework.checker.optional.qual.OptionalEliminator;
31+
import org.checkerframework.checker.optional.qual.OptionalPropagator;
3032
import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory;
3133
import org.checkerframework.common.basetype.BaseTypeChecker;
3234
import org.checkerframework.common.basetype.BaseTypeValidator;
@@ -51,15 +53,6 @@ public class OptionalVisitor
5153
/** The Collection type. */
5254
private final TypeMirror collectionType;
5355

54-
/** The element for java.util.Optional.empty(). */
55-
private final ExecutableElement optionalEmpty;
56-
57-
/** The element for java.util.Optional.filter(). */
58-
private final ExecutableElement optionalFilter;
59-
60-
/** The element for java.util.Optional.flatMap(). */
61-
private final ExecutableElement optionalFlatMap;
62-
6356
/** The element for java.util.Optional.get(). */
6457
private final ExecutableElement optionalGet;
6558

@@ -69,45 +62,12 @@ public class OptionalVisitor
6962
/** The element for java.util.Optional.isEmpty(), or null if running under JDK 8. */
7063
private final @Nullable ExecutableElement optionalIsEmpty;
7164

72-
/** The element for java.util.Optional.map(). */
73-
private final ExecutableElement optionalMap;
74-
75-
/** The element for java.util.Optional.of(). */
76-
private final ExecutableElement optionalOf;
77-
78-
/** The element for java.util.Optional.ofNullable(). */
79-
private final ExecutableElement optionalOfNullable;
80-
81-
/** The element for java.util.Optional.or(), or null if running under JDK 8. */
82-
private final @Nullable ExecutableElement optionalOr;
83-
84-
/** The element for java.util.Optional.orElse(). */
85-
private final ExecutableElement optionalOrElse;
86-
87-
/** The element for java.util.Optional.orElseGet(). */
88-
private final ExecutableElement optionalOrElseGet;
89-
90-
/** The element for java.util.Optional.orElseThrow(), or null if running below Java 10. */
91-
private final @Nullable ExecutableElement optionalOrElseThrow;
92-
93-
/** The element for java.util.Optional.orElseThrow(Supplier), or null if running under JDK 8. */
94-
private final @Nullable ExecutableElement optionalOrElseThrowSupplier;
95-
9665
/** The element for java.util.stream.Stream.filter(). */
9766
private final ExecutableElement streamFilter;
9867

9968
/** The element for java.util.stream.Stream.map(). */
10069
private final ExecutableElement streamMap;
10170

102-
/** Static methods that create an Optional. */
103-
private final List<ExecutableElement> optionalCreators;
104-
105-
/** Methods whose receiver is an Optional, and return an Optional. */
106-
private final List<ExecutableElement> optionalPropagators;
107-
108-
/** Methods whose receiver is an Optional, and return a non-optional. */
109-
private final List<ExecutableElement> optionalEliminators;
110-
11171
/**
11272
* Create an OptionalVisitor.
11373
*
@@ -118,47 +78,12 @@ public OptionalVisitor(BaseTypeChecker checker) {
11878
collectionType = types.erasure(TypesUtils.typeFromClass(Collection.class, types, elements));
11979

12080
ProcessingEnvironment env = checker.getProcessingEnvironment();
121-
optionalEmpty = TreeUtils.getMethod("java.util.Optional", "empty", 0, env);
122-
optionalFilter = TreeUtils.getMethod("java.util.Optional", "filter", 1, env);
123-
optionalFlatMap = TreeUtils.getMethod("java.util.Optional", "flatMap", 1, env);
12481
optionalGet = TreeUtils.getMethod("java.util.Optional", "get", 0, env);
12582
optionalIsPresent = TreeUtils.getMethod("java.util.Optional", "isPresent", 0, env);
12683
optionalIsEmpty = TreeUtils.getMethodOrNull("java.util.Optional", "isEmpty", 0, env);
127-
optionalMap = TreeUtils.getMethod("java.util.Optional", "map", 1, env);
128-
optionalOf = TreeUtils.getMethod("java.util.Optional", "of", 1, env);
129-
optionalOr = TreeUtils.getMethodOrNull("java.util.Optional", "or", 1, env);
130-
optionalOfNullable = TreeUtils.getMethod("java.util.Optional", "ofNullable", 1, env);
131-
optionalOrElse = TreeUtils.getMethod("java.util.Optional", "orElse", 1, env);
132-
optionalOrElseGet = TreeUtils.getMethod("java.util.Optional", "orElseGet", 1, env);
133-
optionalOrElseThrow = TreeUtils.getMethodOrNull("java.util.Optional", "orElseThrow", 0, env);
134-
optionalOrElseThrowSupplier = TreeUtils.getMethod("java.util.Optional", "orElseThrow", 1, env);
13584

13685
streamFilter = TreeUtils.getMethod("java.util.stream.Stream", "filter", 1, env);
13786
streamMap = TreeUtils.getMethod("java.util.stream.Stream", "map", 1, env);
138-
139-
optionalCreators = Arrays.asList(optionalEmpty, optionalOf, optionalOfNullable);
140-
optionalPropagators =
141-
optionalOr == null
142-
? Arrays.asList(optionalFilter, optionalFlatMap, optionalMap)
143-
: Arrays.asList(optionalFilter, optionalFlatMap, optionalMap, optionalOr);
144-
// TODO: add these eliminators:
145-
// hashCode, ifPresent, ifPresentOrElse (Java 9+ only), isEmpty, isPresent, toString,
146-
// Object.getClass
147-
optionalEliminators =
148-
optionalIsEmpty == null
149-
? Arrays.asList(
150-
optionalGet,
151-
optionalOrElse,
152-
optionalOrElseGet,
153-
optionalOrElseThrow,
154-
optionalOrElseThrowSupplier)
155-
: Arrays.asList(
156-
optionalGet,
157-
optionalIsEmpty,
158-
optionalOrElse,
159-
optionalOrElseGet,
160-
optionalOrElseThrow,
161-
optionalOrElseThrowSupplier);
16287
}
16388

16489
@Override
@@ -223,8 +148,8 @@ private boolean isCallToGet(ExpressionTree expression) {
223148
* @return true iff the method being called is Optional creation: empty, of, ofNullable
224149
*/
225150
private boolean isOptionalCreation(MethodInvocationTree methInvok) {
226-
return TreeUtils.isMethodInvocation(
227-
methInvok, optionalCreators, checker.getProcessingEnvironment());
151+
ExecutableElement method = TreeUtils.elementFromUse(methInvok);
152+
return atypeFactory.getDeclAnnotation(method, OptionalCreator.class) != null;
228153
}
229154

230155
/**
@@ -234,8 +159,8 @@ private boolean isOptionalCreation(MethodInvocationTree methInvok) {
234159
* @return true true iff the method being called is Optional propagation: filter, flatMap, map, or
235160
*/
236161
private boolean isOptionalPropagation(MethodInvocationTree methInvok) {
237-
return TreeUtils.isMethodInvocation(
238-
methInvok, optionalPropagators, checker.getProcessingEnvironment());
162+
ExecutableElement method = TreeUtils.elementFromUse(methInvok);
163+
return atypeFactory.getDeclAnnotation(method, OptionalPropagator.class) != null;
239164
}
240165

241166
/**
@@ -247,8 +172,8 @@ private boolean isOptionalPropagation(MethodInvocationTree methInvok) {
247172
* orElseThrow
248173
*/
249174
private boolean isOptionalElimination(MethodInvocationTree methInvok) {
250-
return TreeUtils.isMethodInvocation(
251-
methInvok, optionalEliminators, checker.getProcessingEnvironment());
175+
ExecutableElement method = TreeUtils.elementFromUse(methInvok);
176+
return atypeFactory.getDeclAnnotation(method, OptionalEliminator.class) != null;
252177
}
253178

254179
@Override

0 commit comments

Comments
 (0)