Skip to content

Commit

Permalink
Add Optional method annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
smillst committed Dec 13, 2023
1 parent 964d027 commit 17226ff
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 84 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.checkerframework.checker.optional.qual;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.checkerframework.framework.qual.InheritedAnnotation;

/** An method annotation for methods that create an {@link java.util.Optional} */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@InheritedAnnotation
public @interface OptionalCreator {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.checkerframework.checker.optional.qual;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.checkerframework.framework.qual.InheritedAnnotation;

/** Methods whose receiver is an {@link java.util.Optional} and return a non-optional. */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@InheritedAnnotation
public @interface OptionalEliminator {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.checkerframework.checker.optional.qual;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.checkerframework.framework.qual.InheritedAnnotation;

/** Methods whose receiver is an {@link java.util.Optional} and return an {@code Optional}. */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@InheritedAnnotation
public @interface OptionalPropagator {}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import com.sun.source.tree.UnaryTree;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreePath;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import javax.annotation.processing.ProcessingEnvironment;
Expand All @@ -27,6 +26,9 @@
import javax.lang.model.type.TypeMirror;
import org.checkerframework.checker.compilermsgs.qual.CompilerMessageKey;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.optional.qual.OptionalCreator;
import org.checkerframework.checker.optional.qual.OptionalEliminator;
import org.checkerframework.checker.optional.qual.OptionalPropagator;
import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory;
import org.checkerframework.common.basetype.BaseTypeChecker;
import org.checkerframework.common.basetype.BaseTypeValidator;
Expand All @@ -51,15 +53,6 @@ public class OptionalVisitor
/** The Collection type. */
private final TypeMirror collectionType;

/** The element for java.util.Optional.empty(). */
private final ExecutableElement optionalEmpty;

/** The element for java.util.Optional.filter(). */
private final ExecutableElement optionalFilter;

/** The element for java.util.Optional.flatMap(). */
private final ExecutableElement optionalFlatMap;

/** The element for java.util.Optional.get(). */
private final ExecutableElement optionalGet;

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

/** The element for java.util.Optional.map(). */
private final ExecutableElement optionalMap;

/** The element for java.util.Optional.of(). */
private final ExecutableElement optionalOf;

/** The element for java.util.Optional.ofNullable(). */
private final ExecutableElement optionalOfNullable;

/** The element for java.util.Optional.or(), or null if running under JDK 8. */
private final @Nullable ExecutableElement optionalOr;

/** The element for java.util.Optional.orElse(). */
private final ExecutableElement optionalOrElse;

/** The element for java.util.Optional.orElseGet(). */
private final ExecutableElement optionalOrElseGet;

/** The element for java.util.Optional.orElseThrow(), or null if running below Java 10. */
private final @Nullable ExecutableElement optionalOrElseThrow;

/** The element for java.util.Optional.orElseThrow(Supplier), or null if running under JDK 8. */
private final @Nullable ExecutableElement optionalOrElseThrowSupplier;

/** The element for java.util.stream.Stream.filter(). */
private final ExecutableElement streamFilter;

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

/** Static methods that create an Optional. */
private final List<ExecutableElement> optionalCreators;

/** Methods whose receiver is an Optional, and return an Optional. */
private final List<ExecutableElement> optionalPropagators;

/** Methods whose receiver is an Optional, and return a non-optional. */
private final List<ExecutableElement> optionalEliminators;

/**
* Create an OptionalVisitor.
*
Expand All @@ -118,47 +78,12 @@ public OptionalVisitor(BaseTypeChecker checker) {
collectionType = types.erasure(TypesUtils.typeFromClass(Collection.class, types, elements));

ProcessingEnvironment env = checker.getProcessingEnvironment();
optionalEmpty = TreeUtils.getMethod("java.util.Optional", "empty", 0, env);
optionalFilter = TreeUtils.getMethod("java.util.Optional", "filter", 1, env);
optionalFlatMap = TreeUtils.getMethod("java.util.Optional", "flatMap", 1, env);
optionalGet = TreeUtils.getMethod("java.util.Optional", "get", 0, env);
optionalIsPresent = TreeUtils.getMethod("java.util.Optional", "isPresent", 0, env);
optionalIsEmpty = TreeUtils.getMethodOrNull("java.util.Optional", "isEmpty", 0, env);
optionalMap = TreeUtils.getMethod("java.util.Optional", "map", 1, env);
optionalOf = TreeUtils.getMethod("java.util.Optional", "of", 1, env);
optionalOr = TreeUtils.getMethodOrNull("java.util.Optional", "or", 1, env);
optionalOfNullable = TreeUtils.getMethod("java.util.Optional", "ofNullable", 1, env);
optionalOrElse = TreeUtils.getMethod("java.util.Optional", "orElse", 1, env);
optionalOrElseGet = TreeUtils.getMethod("java.util.Optional", "orElseGet", 1, env);
optionalOrElseThrow = TreeUtils.getMethodOrNull("java.util.Optional", "orElseThrow", 0, env);
optionalOrElseThrowSupplier = TreeUtils.getMethod("java.util.Optional", "orElseThrow", 1, env);

streamFilter = TreeUtils.getMethod("java.util.stream.Stream", "filter", 1, env);
streamMap = TreeUtils.getMethod("java.util.stream.Stream", "map", 1, env);

optionalCreators = Arrays.asList(optionalEmpty, optionalOf, optionalOfNullable);
optionalPropagators =
optionalOr == null
? Arrays.asList(optionalFilter, optionalFlatMap, optionalMap)
: Arrays.asList(optionalFilter, optionalFlatMap, optionalMap, optionalOr);
// TODO: add these eliminators:
// hashCode, ifPresent, ifPresentOrElse (Java 9+ only), isEmpty, isPresent, toString,
// Object.getClass
optionalEliminators =
optionalIsEmpty == null
? Arrays.asList(
optionalGet,
optionalOrElse,
optionalOrElseGet,
optionalOrElseThrow,
optionalOrElseThrowSupplier)
: Arrays.asList(
optionalGet,
optionalIsEmpty,
optionalOrElse,
optionalOrElseGet,
optionalOrElseThrow,
optionalOrElseThrowSupplier);
}

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

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

/**
Expand All @@ -247,8 +172,8 @@ private boolean isOptionalPropagation(MethodInvocationTree methInvok) {
* orElseThrow
*/
private boolean isOptionalElimination(MethodInvocationTree methInvok) {
return TreeUtils.isMethodInvocation(
methInvok, optionalEliminators, checker.getProcessingEnvironment());
ExecutableElement method = TreeUtils.elementFromUse(methInvok);
return atypeFactory.getDeclAnnotation(method, OptionalEliminator.class) != null;
}

@Override
Expand Down

0 comments on commit 17226ff

Please sign in to comment.