Skip to content

Commit

Permalink
Followup changes
Browse files Browse the repository at this point in the history
  * Add ASTHelpers.hasAnnotation(Symbol, String, VisitorState), use it in Matchers.hasAnnotation.
  * Update LocalStore to accept more things.

RELNOTES: none
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=101156354
  • Loading branch information
sameb authored and cushon committed Sep 14, 2015
1 parent 86828e4 commit 7c0438d
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 30 deletions.
52 changes: 40 additions & 12 deletions core/src/main/java/com/google/errorprone/dataflow/LocalStore.java
Expand Up @@ -19,12 +19,14 @@
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Sets.intersection; import static com.google.common.collect.Sets.intersection;


import com.google.common.base.Equivalence;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;


import org.checkerframework.dataflow.analysis.AbstractValue; import org.checkerframework.dataflow.analysis.AbstractValue;
import org.checkerframework.dataflow.analysis.FlowExpressions; import org.checkerframework.dataflow.analysis.FlowExpressions;
import org.checkerframework.dataflow.analysis.Store; import org.checkerframework.dataflow.analysis.Store;
import org.checkerframework.dataflow.cfg.node.LocalVariableNode; import org.checkerframework.dataflow.cfg.node.LocalVariableNode;
import org.checkerframework.dataflow.cfg.node.Node;


import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
Expand All @@ -42,6 +44,7 @@
* @author deminguyen@google.com (Demi Nguyen) * @author deminguyen@google.com (Demi Nguyen)
*/ */
public final class LocalStore<V extends AbstractValue<V>> implements Store<LocalStore<V>> { public final class LocalStore<V extends AbstractValue<V>> implements Store<LocalStore<V>> {

@SuppressWarnings({"unchecked", "rawtypes"}) // fully variant @SuppressWarnings({"unchecked", "rawtypes"}) // fully variant
private static final LocalStore<?> EMPTY = new LocalStore(ImmutableMap.of()); private static final LocalStore<?> EMPTY = new LocalStore(ImmutableMap.of());


Expand All @@ -50,18 +53,14 @@ public static <V extends AbstractValue<V>> LocalStore<V> empty() {
return (LocalStore<V>) EMPTY; return (LocalStore<V>) EMPTY;
} }


/* private final ImmutableMap<Equivalence.Wrapper<Node>, V> contents;
* TODO(cpovirk): Return to LocalVariableNode keys if LocalVariableNode.equals is fixed to use the
* variable's declaring element instead of its name.
*/
private final ImmutableMap<Element, V> contents;


private LocalStore(Map<Element, V> contents) { private LocalStore(Map<Equivalence.Wrapper<Node>, V> contents) {
this.contents = ImmutableMap.copyOf(contents); this.contents = ImmutableMap.copyOf(contents);
} }


public V getInformation(LocalVariableNode node) { public V getInformation(Node node) {
return contents.get(node.getElement()); return contents.get(NodeEquivalance.INSTANCE.wrap(node));
} }


public Builder<V> toBuilder() { public Builder<V> toBuilder() {
Expand All @@ -74,14 +73,19 @@ public Builder<V> toBuilder() {
* it. * it.
*/ */
public static final class Builder<V extends AbstractValue<V>> { public static final class Builder<V extends AbstractValue<V>> {
private final Map<Element, V> contents; private final Map<Equivalence.Wrapper<Node>, V> contents;


Builder(LocalStore<V> prototype) { Builder(LocalStore<V> prototype) {
contents = new HashMap<>(prototype.contents); contents = new HashMap<>(prototype.contents);
} }


public void setInformation(LocalVariableNode node, V value) { public Builder<V> setInformation(Node node, V value) {
contents.put(node.getElement(), checkNotNull(value)); contents.put(NodeEquivalance.INSTANCE.wrap(node), checkNotNull(value));
return this;
}

public V getInformation(Node node) {
return contents.get(NodeEquivalance.INSTANCE.wrap(node));
} }


public LocalStore<V> build() { public LocalStore<V> build() {
Expand All @@ -98,7 +102,7 @@ public LocalStore<V> copy() {
@Override @Override
public LocalStore<V> leastUpperBound(LocalStore<V> other) { public LocalStore<V> leastUpperBound(LocalStore<V> other) {
Builder<V> result = LocalStore.<V>empty().toBuilder(); Builder<V> result = LocalStore.<V>empty().toBuilder();
for (Element var : intersection(contents.keySet(), other.contents.keySet())) { for (Equivalence.Wrapper<Node> var : intersection(contents.keySet(), other.contents.keySet())) {
result.contents.put(var, contents.get(var).leastUpperBound(other.contents.get(var))); result.contents.put(var, contents.get(var).leastUpperBound(other.contents.get(var)));
} }
return result.build(); return result.build();
Expand Down Expand Up @@ -137,4 +141,28 @@ public boolean hasDOToutput() {
public String toDOToutput() { public String toDOToutput() {
throw new UnsupportedOperationException("DOT output not supported"); throw new UnsupportedOperationException("DOT output not supported");
} }

private static class NodeEquivalance extends Equivalence<Node> {
static final NodeEquivalance INSTANCE = new NodeEquivalance();

@Override
protected boolean doEquivalent(Node a, Node b) {
// TODO(cpovirk): Remove equiv wrapper & hack if LocalVariableNode.equals is fixed to use
// the variable's declaring element instead of its name.
if (a instanceof LocalVariableNode && b instanceof LocalVariableNode) {
Element aEl = ((LocalVariableNode) a).getElement();
Element bEl = ((LocalVariableNode) b).getElement();
return aEl.equals(bEl);
}
return a.equals(b);
}

@Override
protected int doHash(Node n) {
if (n instanceof LocalVariableNode) {
return ((LocalVariableNode) n).getElement().hashCode();
}
return n.hashCode();
}
}
} }
19 changes: 1 addition & 18 deletions core/src/main/java/com/google/errorprone/matchers/Matchers.java
Expand Up @@ -770,24 +770,7 @@ public static <T extends Tree> Matcher<T> hasAnnotation(final String annotationT
return new Matcher<T>() { return new Matcher<T>() {
@Override @Override
public boolean matches (T tree, VisitorState state) { public boolean matches (T tree, VisitorState state) {
Symbol sym = ASTHelpers.getSymbol(tree); return ASTHelpers.hasAnnotation(ASTHelpers.getSymbol(tree), annotationType, state);
Symbol annotationSym = state.getSymbolFromString(annotationType);
Symbol inheritedSym = state.getSymtab().inheritedType.tsym;

if ((sym == null) || (annotationSym == null)) {
return false;
}
if ((sym instanceof ClassSymbol) && (annotationSym.attribute(inheritedSym) != null)) {
while (sym != null) {
if (sym.attribute(annotationSym) != null) {
return true;
}
sym = ((ClassSymbol) sym).getSuperclass().tsym;
}
return false;
} else {
return sym.attribute(annotationSym) != null;
}
} }
}; };
} }
Expand Down
26 changes: 26 additions & 0 deletions core/src/main/java/com/google/errorprone/util/ASTHelpers.java
Expand Up @@ -393,6 +393,32 @@ public static MethodSymbol findSuperMethod(MethodSymbol method, Types types) {
return null; return null;
} }


/**
* Determines whether a symbol has an annotation of the given type.
* This includes annotations inherited from superclasses due to @Inherited.
*
* @param annotationType The type of the annotation to look for (e.g, "javax.annotation.Nullable")
*/
public static boolean hasAnnotation(Symbol sym, String annotationType, VisitorState state) {
Symbol annotationSym = state.getSymbolFromString(annotationType);
Symbol inheritedSym = state.getSymtab().inheritedType.tsym;

if ((sym == null) || (annotationSym == null)) {
return false;
}
if ((sym instanceof ClassSymbol) && (annotationSym.attribute(inheritedSym) != null)) {
while (sym != null) {
if (sym.attribute(annotationSym) != null) {
return true;
}
sym = ((ClassSymbol) sym).getSuperclass().tsym;
}
return false;
} else {
return sym.attribute(annotationSym) != null;
}
}

/** /**
* Check for the presence of an annotation, considering annotation inheritance. * Check for the presence of an annotation, considering annotation inheritance.
* *
Expand Down

0 comments on commit 7c0438d

Please sign in to comment.