/*
* Copyright 2014 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.errorprone.dataflow.nullnesspropagation;
import org.checkerframework.dataflow.analysis.AbstractValue;
/**
* Represents one of the possible nullness values in our nullness analysis.
*
* @author deminguyen@google.com (Demi Nguyen)
*/
public enum Nullness implements AbstractValue<Nullness> {
/**
* The lattice for nullness looks like:
*
* Nullable
* / \
* Null Non-null
* \ /
* Bottom
*/
NULLABLE("Nullable"), // TODO(eaftan): Rename to POSSIBLY_NULL?
NULL("Null"),
NONNULL("Non-null"),
BOTTOM("Bottom");
private final String displayName;
Nullness(String displayName) {
this.displayName = displayName;
}
// The following leastUpperBound and greatestLowerBound methods were created by handwriting a
// truth table and then encoding the values into these functions. A better approach would be to
// represent the lattice directly and compute these functions from the lattice.
@Override
public Nullness leastUpperBound(Nullness other) {
if (this == other) {
return this;
}
// Bottom loses.
if (this == BOTTOM) {
return other;
}
if (other == BOTTOM) {
return this;
}
// They disagree, and neither is bottom.
return NULLABLE;
}
public Nullness greatestLowerBound(Nullness other) {
if (this == other) {
return this;
}
// Nullable loses.
if (this == NULLABLE) {
return other;
}
if (other == NULLABLE) {
return this;
}
// They disagree, and neither is nullable.
return BOTTOM;
}
/**
* Returns the {@code Nullness} that corresponds to what you can deduce by knowing that some
* expression is not equal to another expression with this {@code Nullness}.
*
* <p>A {@code Nullness} represents a set of possible values for a expression. Suppose you have
* two variables {@code var1} and {@code var2}. If {@code var1 != var2}, then {@code var1} must be
* an element of the complement of the singleton set containing the value of {@code var2}. If you
* union these complement sets over all possible values of {@code var2}, the set that results is
* what this method returns, assuming that {@code this} is the {@code Nullness} of {@code var2}.
*
* <p>Example 1: Suppose {@code nv2 == NULL}. Then {@code var2} can have exactly one value,
* {@code null}, and {@code var1} must have a value in the set of all values except {@code null}.
* That set is exactly {@code NONNULL}.
*
* <p>Example 2: Suppose {@code nv2 == NONNULL}. Then {@code var2} can have any value except
* {@code null}. Suppose {@code var2} has value {@code "foo"}. Then {@code var1} must have a value
* in the set of all values except {@code "foo"}. Now suppose {@code var2} has value {@code "bar"}
* . Then {@code var1} must have a value in set of all values except {@code "bar"}. Since we don't
* know which value in the set {@code NONNULL var2} has, we union all possible complement sets to
* get the set of all values, or {@code NULLABLE}.
*/
public Nullness deducedValueWhenNotEqual() {
switch (this) {
case NULLABLE:
return NULLABLE;
case NONNULL:
return NULLABLE;
case NULL:
return NONNULL;
case BOTTOM:
return BOTTOM;
default:
throw new AssertionError("Inverse of " + this + " not defined");
}
}
@Override
public String toString() {
return displayName;
}
}