-
Notifications
You must be signed in to change notification settings - Fork 291
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Handles unexpected error cases in JSpecify mode where an index is asserted `@Nonnull` (`fizz[x]!=null`) but the array elements itself could be null (`@Nullable String [] fizz`). **Example** ``` @nullable String [] fizz ={"1"} if (fizz[i] != null) { fizz[i].toString(); } ``` **Current Behavior** This throws an error due to dereference of `fizz[i]` **New Behavior** There should be no error here unless it's outside the block. So only the latter dereference throws up an error in the below case. ``` @nullable String [] fizz ={"1"} if (fizz[i] != null) { fizz[i].toString(); } fizz[i].toString(); ``` --------- Co-authored-by: Manu Sridharan <msridhar@gmail.com>
- Loading branch information
1 parent
a4ce249
commit 76115d4
Showing
7 changed files
with
408 additions
and
63 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
76 changes: 22 additions & 54 deletions
76
nullaway/src/main/java/com/uber/nullaway/dataflow/AccessPathElement.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,62 +1,30 @@ | ||
package com.uber.nullaway.dataflow; | ||
|
||
import com.google.common.collect.ImmutableList; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.Objects; | ||
import javax.annotation.Nullable; | ||
import javax.lang.model.element.Element; | ||
|
||
/** | ||
* Represents a (non-root) element of an AccessPath. | ||
* Represents a generic element in an access path used for nullability analysis. | ||
* | ||
* <p>This is just a java Element (field, method, etc) in the access-path chain (e.g. f or g() in | ||
* x.f.g()). Plus, optionally, a list of constant arguments, allowing access path elements for | ||
* method calls with constant values (e.g. h(3) or k("STR_KEY") in x.h(3).g().k("STR_KEY")). | ||
* <p>This interface abstracts over different kinds of path elements that can be part of an access | ||
* path, including fields and methods, or array indices. Implementations of this interface should | ||
* specify the type of the access path element: | ||
* | ||
* <ul> | ||
* <li>{@code FieldOrMethodCallElement} - Represents access to a field or the invocation of a | ||
* method, potentially with constant arguments. | ||
* <li>{@code ArrayIndexElement} - Represents access to an array element either by a constant | ||
* index or via an index that is calculated dynamically. | ||
* </ul> | ||
* | ||
* <p>The {@code getJavaElement()} method returns the corresponding Java {@link Element} that the | ||
* access path element refers to. | ||
*/ | ||
public final class AccessPathElement { | ||
private final Element javaElement; | ||
@Nullable private final ImmutableList<String> constantArguments; | ||
|
||
public AccessPathElement(Element javaElement, List<String> constantArguments) { | ||
this.javaElement = javaElement; | ||
this.constantArguments = ImmutableList.copyOf(constantArguments); | ||
} | ||
|
||
public AccessPathElement(Element javaElement) { | ||
this.javaElement = javaElement; | ||
this.constantArguments = null; | ||
} | ||
|
||
public Element getJavaElement() { | ||
return this.javaElement; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object obj) { | ||
if (obj instanceof AccessPathElement) { | ||
AccessPathElement otherNode = (AccessPathElement) obj; | ||
return this.javaElement.equals(otherNode.javaElement) | ||
&& Objects.equals(constantArguments, otherNode.constantArguments); | ||
} else { | ||
return false; | ||
} | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
int result = javaElement.hashCode(); | ||
result = 31 * result + (constantArguments != null ? constantArguments.hashCode() : 0); | ||
return result; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "APElement{" | ||
+ "javaElement=" | ||
+ javaElement.toString() | ||
+ ", constantArguments=" | ||
+ Arrays.deepToString(constantArguments != null ? constantArguments.toArray() : null) | ||
+ '}'; | ||
} | ||
public interface AccessPathElement { | ||
/** | ||
* Returns the Java element associated with this access path element. | ||
* | ||
* @return the Java {@link Element} related to this path element, such as a field, method, or the | ||
* array itself. | ||
*/ | ||
Element getJavaElement(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
75 changes: 75 additions & 0 deletions
75
nullaway/src/main/java/com/uber/nullaway/dataflow/ArrayIndexElement.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package com.uber.nullaway.dataflow; | ||
|
||
import java.util.Objects; | ||
import javax.lang.model.element.Element; | ||
|
||
/** | ||
* Represents an array index element of an AccessPath, encapsulating access to array elements either | ||
* via constant or variable indices. | ||
* | ||
* <p>This class holds an element that represents the array itself and an index that specifies the | ||
* position within the array. The index can be a constant (Integer) if it's statically known, or an | ||
* Element representing a variable index. | ||
*/ | ||
public class ArrayIndexElement implements AccessPathElement { | ||
private final Element javaElement; | ||
private final Object index; | ||
|
||
private ArrayIndexElement(Element javaElement, Object index) { | ||
this.javaElement = javaElement; | ||
this.index = index; | ||
} | ||
|
||
/** | ||
* Creates an ArrayIndexElement with an integer index. | ||
* | ||
* @param javaElement the element of the array | ||
* @param index the integer index to access the array | ||
* @return an instance of ArrayIndexElement | ||
*/ | ||
public static ArrayIndexElement withIntegerIndex(Element javaElement, Integer index) { | ||
return new ArrayIndexElement(javaElement, index); | ||
} | ||
|
||
/** | ||
* Creates an ArrayIndexElement with a local variable or field index. | ||
* | ||
* @param javaElement the element of the array | ||
* @param indexElement the local variable or field element representing the index | ||
* @return an instance of ArrayIndexElement | ||
*/ | ||
public static ArrayIndexElement withVariableIndex(Element javaElement, Element indexElement) { | ||
return new ArrayIndexElement(javaElement, indexElement); | ||
} | ||
|
||
@Override | ||
public Element getJavaElement() { | ||
return this.javaElement; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "ArrayIndexElement{" | ||
+ "javaElement=" | ||
+ javaElement | ||
+ ", index=" | ||
+ (index instanceof Element ? ((Element) index).getSimpleName() : index) | ||
+ '}'; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object obj) { | ||
if (obj instanceof ArrayIndexElement) { | ||
ArrayIndexElement other = (ArrayIndexElement) obj; | ||
return Objects.equals(javaElement, other.javaElement) && Objects.equals(index, other.index); | ||
} | ||
return false; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
int result = javaElement.hashCode(); | ||
result = 31 * result + (index != null ? index.hashCode() : 0); | ||
return result; | ||
} | ||
} |
62 changes: 62 additions & 0 deletions
62
nullaway/src/main/java/com/uber/nullaway/dataflow/FieldOrMethodCallElement.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package com.uber.nullaway.dataflow; | ||
|
||
import com.google.common.collect.ImmutableList; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.Objects; | ||
import javax.annotation.Nullable; | ||
import javax.lang.model.element.Element; | ||
|
||
/** | ||
* Represents a (non-root) field or method call element of an AccessPath. | ||
* | ||
* <p>This is just a java Element (field or method call) in the access-path chain (e.g. f or g() in | ||
* x.f.g()). Plus, optionally, a list of constant arguments, allowing access path elements for | ||
* method calls with constant values (e.g. h(3) or k("STR_KEY") in x.h(3).g().k("STR_KEY")). | ||
*/ | ||
public class FieldOrMethodCallElement implements AccessPathElement { | ||
private final Element javaElement; | ||
@Nullable private final ImmutableList<String> constantArguments; | ||
|
||
public FieldOrMethodCallElement(Element javaElement, List<String> constantArguments) { | ||
this.javaElement = javaElement; | ||
this.constantArguments = ImmutableList.copyOf(constantArguments); | ||
} | ||
|
||
public FieldOrMethodCallElement(Element javaElement) { | ||
this.javaElement = javaElement; | ||
this.constantArguments = null; | ||
} | ||
|
||
@Override | ||
public Element getJavaElement() { | ||
return this.javaElement; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object obj) { | ||
if (obj instanceof FieldOrMethodCallElement) { | ||
FieldOrMethodCallElement other = (FieldOrMethodCallElement) obj; | ||
return this.javaElement.equals(other.javaElement) | ||
&& Objects.equals(this.constantArguments, other.constantArguments); | ||
} | ||
return false; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
int result = javaElement.hashCode(); | ||
result = 31 * result + (constantArguments != null ? constantArguments.hashCode() : 0); | ||
return result; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "FieldOrMethodCallElement{" | ||
+ "javaElement=" | ||
+ javaElement | ||
+ ", constantArguments=" | ||
+ (constantArguments != null ? Arrays.toString(constantArguments.toArray()) : "null") | ||
+ '}'; | ||
} | ||
} |
Oops, something went wrong.