blob: 1bedeb09ea8ee666bbc5c93a042feea38b96ef1d [file] [log] [blame]
package org.checkerframework.dataflow.expression;
import com.sun.tools.javac.code.Symbol.VarSymbol;
import java.util.Objects;
import javax.lang.model.element.Element;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.dataflow.cfg.node.LocalVariableNode;
import org.checkerframework.javacutil.AnnotationProvider;
import org.checkerframework.javacutil.ElementUtils;
import org.checkerframework.javacutil.TypeAnnotationUtils;
import org.checkerframework.javacutil.TypesUtils;
/**
* A local variable.
*
* <p>This class also represents a formal parameter expressed using its name. Class {@link
* FormalParameter} represents a formal parameter expressed using the "#2" notation.
*/
public class LocalVariable extends JavaExpression {
/** The element for this local variable. */
protected final Element element;
/**
* Creates a new LocalVariable.
*
* @param localVar a CFG local variable
*/
public LocalVariable(LocalVariableNode localVar) {
super(localVar.getType());
this.element = localVar.getElement();
}
/**
* Creates a LocalVariable
*
* @param element the element for the local variable
*/
public LocalVariable(Element element) {
super(ElementUtils.getType(element));
this.element = element;
}
@Override
public boolean equals(@Nullable Object obj) {
if (!(obj instanceof LocalVariable)) {
return false;
}
LocalVariable other = (LocalVariable) obj;
return sameElement(element, other.element);
}
/**
* Returns true if the two elements are the same.
*
* @param element1 the first element to compare
* @param element2 the second element to compare
* @return true if the two elements are the same
*/
protected static boolean sameElement(Element element1, Element element2) {
VarSymbol vs1 = (VarSymbol) element1;
VarSymbol vs2 = (VarSymbol) element2;
// The code below isn't just return vs1.equals(vs2) because an element might be
// different between subcheckers. The owner of a lambda parameter is the enclosing
// method, so a local variable and a lambda parameter might have the same name and the
// same owner. pos is used to differentiate this case.
return vs1.pos == vs2.pos
&& vs1.name.contentEquals(vs2.name)
&& vs1.owner.toString().equals(vs2.owner.toString());
}
/**
* Returns the element for this variable.
*
* @return the element for this variable
*/
public Element getElement() {
return element;
}
@Override
public int hashCode() {
VarSymbol vs = (VarSymbol) element;
return Objects.hash(
vs.name.toString(),
TypeAnnotationUtils.unannotatedType(vs.type).toString(),
vs.owner.toString());
}
@Override
public String toString() {
return element.toString();
}
@Override
public String toStringDebug() {
return super.toStringDebug() + " [owner=" + ((VarSymbol) element).owner + "]";
}
@Override
public boolean containsOfClass(Class<? extends JavaExpression> clazz) {
return getClass() == clazz;
}
@Override
public boolean isDeterministic(AnnotationProvider provider) {
return true;
}
@Override
public boolean syntacticEquals(JavaExpression je) {
if (!(je instanceof LocalVariable)) {
return false;
}
LocalVariable other = (LocalVariable) je;
return this.equals(other);
}
@Override
public boolean containsSyntacticEqualJavaExpression(JavaExpression other) {
return syntacticEquals(other);
}
@Override
public boolean isUnassignableByOtherCode() {
return true;
}
@Override
public boolean isUnmodifiableByOtherCode() {
return TypesUtils.isImmutableTypeInJdk(((VarSymbol) element).type);
}
@Override
public <R, P> R accept(JavaExpressionVisitor<R, P> visitor, P p) {
return visitor.visitLocalVariable(this, p);
}
}