-
Notifications
You must be signed in to change notification settings - Fork 39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce MoreTypes
utility class
#234
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,132 @@ | ||||||
package tech.picnic.errorprone.bugpatterns.util; | ||||||
|
||||||
import static java.util.stream.Collectors.toCollection; | ||||||
|
||||||
import com.google.errorprone.VisitorState; | ||||||
import com.google.errorprone.suppliers.Supplier; | ||||||
import com.google.errorprone.suppliers.Suppliers; | ||||||
import com.sun.tools.javac.code.BoundKind; | ||||||
import com.sun.tools.javac.code.Type; | ||||||
import java.util.ArrayList; | ||||||
import java.util.Arrays; | ||||||
import java.util.List; | ||||||
import java.util.Objects; | ||||||
import java.util.Optional; | ||||||
import java.util.function.BiFunction; | ||||||
|
||||||
/** | ||||||
* A set of helper methods which together define a DSL for defining {@link Type types}. | ||||||
* | ||||||
* <p>These methods are meant to be statically imported. Example usage: | ||||||
* | ||||||
* <pre>{@code | ||||||
* Supplier<Type> type = | ||||||
* VisitorState.memoize( | ||||||
* generic( | ||||||
* type("reactor.core.publisher.Flux"), | ||||||
* subOf(generic(type("org.reactivestreams.Publisher"), unbound())))); | ||||||
* }</pre> | ||||||
* | ||||||
* This statement produces a memoized supplier of the type {@code Flux<? extends Publisher<?>>}. | ||||||
*/ | ||||||
public final class MoreTypes { | ||||||
private MoreTypes() {} | ||||||
|
||||||
/** | ||||||
* Creates a supplier of the type with the given fully qualified name. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any reason for not using |
||||||
* | ||||||
* <p>This method should only be used when building more complex types in combination with other | ||||||
* {@link MoreTypes} methods. In other cases prefer directly calling {@link | ||||||
* Suppliers#typeFromString(String)}. | ||||||
* | ||||||
* @param typeName The type of interest. | ||||||
* @return A supplier which returns the described type if available in the given state, and {@code | ||||||
* null} otherwise. | ||||||
*/ | ||||||
public static Supplier<Type> type(String typeName) { | ||||||
return Suppliers.typeFromString(typeName); | ||||||
} | ||||||
|
||||||
/** | ||||||
* Creates a supplier of the described generic type. | ||||||
* | ||||||
* @param type The base type of interest. | ||||||
* @param typeArgs The desired type arguments. | ||||||
* @return A supplier which returns the described type if available in the given state, and {@code | ||||||
* null} otherwise. | ||||||
*/ | ||||||
// XXX: The given `type` should be a generic type, so perhaps `withParams` would be a better | ||||||
// method name. But the DSL wouldn't look as nice that way. | ||||||
@SafeVarargs | ||||||
@SuppressWarnings("varargs") | ||||||
public static Supplier<Type> generic(Supplier<Type> type, Supplier<Type>... typeArgs) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To me, I know
IMHO the DSL is very concise (even if we would make some of the method names a little longer). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In EP I see checks for this:
Maybe it'd be nice to add a check for that in this method? Using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not yet feeling the first suggestion: in practice the code will look like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. W.r.t. the second suggestion: since the type evaluation is deferred, I'm not sure an |
||||||
return propagateNull( | ||||||
type, | ||||||
(state, baseType) -> { | ||||||
List<Type> params = | ||||||
Arrays.stream(typeArgs).map(s -> s.get(state)).collect(toCollection(ArrayList::new)); | ||||||
if (params.stream().anyMatch(Objects::isNull)) { | ||||||
return null; | ||||||
} | ||||||
|
||||||
return state.getType(baseType, /* isArray= */ false, params); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's been a while since I wrote this, but indeed, I think |
||||||
}); | ||||||
} | ||||||
|
||||||
/** | ||||||
* Creates a raw (erased, non-generic) variant of the given type. | ||||||
* | ||||||
* @param type The base type of interest. | ||||||
* @return A supplier which returns the described type if available in the given state, and {@code | ||||||
* null} otherwise. | ||||||
*/ | ||||||
public static Supplier<Type> raw(Supplier<Type> type) { | ||||||
return propagateNull(type, (state, baseType) -> baseType.tsym.erasure(state.getTypes())); | ||||||
} | ||||||
|
||||||
/** | ||||||
* Creates a {@code ? super T} wildcard type, with {@code T} bound to the given type. | ||||||
* | ||||||
* @param type The base type of interest. | ||||||
* @return A supplier which returns the described type if available in the given state, and {@code | ||||||
* null} otherwise. | ||||||
*/ | ||||||
public static Supplier<Type> superOf(Supplier<Type> type) { | ||||||
return propagateNull( | ||||||
type, | ||||||
(state, baseType) -> | ||||||
new Type.WildcardType(baseType, BoundKind.SUPER, state.getSymtab().boundClass)); | ||||||
} | ||||||
|
||||||
/** | ||||||
* Creates a {@code ? extends T} wildcard type, with {@code T} bound to the given type. | ||||||
* | ||||||
* @param type The base type of interest. | ||||||
* @return A supplier which returns the described type if available in the given state, and {@code | ||||||
* null} otherwise. | ||||||
*/ | ||||||
public static Supplier<Type> subOf(Supplier<Type> type) { | ||||||
return propagateNull( | ||||||
type, | ||||||
(state, baseType) -> | ||||||
new Type.WildcardType( | ||||||
type.get(state), BoundKind.EXTENDS, state.getSymtab().boundClass)); | ||||||
} | ||||||
|
||||||
/** | ||||||
* Creates an unbound wildcard type ({@code ?}). | ||||||
* | ||||||
* @return A supplier which returns the described type. | ||||||
*/ | ||||||
public static Supplier<Type> unbound() { | ||||||
return state -> | ||||||
new Type.WildcardType( | ||||||
state.getSymtab().objectType, BoundKind.UNBOUND, state.getSymtab().boundClass); | ||||||
} | ||||||
|
||||||
private static Supplier<Type> propagateNull( | ||||||
Supplier<Type> type, BiFunction<VisitorState, Type, Type> transformer) { | ||||||
return state -> | ||||||
Optional.ofNullable(type.get(state)).map(t -> transformer.apply(state, t)).orElse(null); | ||||||
} | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add this class to
StaticImport
.