Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions nullaway/src/main/java/com/uber/nullaway/NullAway.java
Original file line number Diff line number Diff line change
Expand Up @@ -1924,9 +1924,10 @@ private Description handleInvocation(
argumentPositionNullness[i] =
Nullness.paramHasNullableAnnotation(methodSymbol, i, config)
? Nullness.NULLABLE
: ((config.isJSpecifyMode() && tree instanceof MethodInvocationTree)
: ((config.isJSpecifyMode()
&& (tree instanceof MethodInvocationTree || tree instanceof NewClassTree))
? genericsChecks.getGenericParameterNullnessAtInvocation(
i, methodSymbol, (MethodInvocationTree) tree, state, config)
i, methodSymbol, tree, state, config)
: Nullness.NONNULL);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static com.google.common.base.Verify.verify;
import static com.uber.nullaway.NullabilityUtil.castToNonNull;

import com.google.common.base.Verify;
import com.google.errorprone.VisitorState;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.AnnotatedTypeTree;
Expand Down Expand Up @@ -54,8 +55,8 @@ public final class GenericsChecks {
* from type variables for the method to their inferred type arguments (most importantly with
* inferred nullability information).
*/
private final Map<MethodInvocationTree, Map<TypeVariable, Type>>
inferredSubstitutionsForGenericMethodCalls = new LinkedHashMap<>();
private final Map<Tree, Map<TypeVariable, Type>> inferredSubstitutionsForGenericMethodCalls =
new LinkedHashMap<>();

/**
* Checks that for an instantiated generic type, {@code @Nullable} types are only used for type
Expand Down Expand Up @@ -943,31 +944,31 @@ private static com.sun.tools.javac.util.List<Type> convertTreesToTypes(
/**
* Substitutes the type arguments from a generic method invocation into the method's type.
*
* @param methodInvocationTree the method invocation tree
* @param tree the method invocation tree
* @param methodSymbol symbol for the invoked generic method
* @param state the visitor state
* @param config the NullAway config
* @return the substituted method type for the generic method
*/
private Type substituteTypeArgsInGenericMethodType(
MethodInvocationTree methodInvocationTree,
Symbol.MethodSymbol methodSymbol,
VisitorState state,
Config config) {
Tree tree, Symbol.MethodSymbol methodSymbol, VisitorState state, Config config) {
Comment thread
msridhar marked this conversation as resolved.

List<? extends Tree> typeArgumentTrees = methodInvocationTree.getTypeArguments();
List<? extends Tree> typeArgumentTrees =
(tree instanceof MethodInvocationTree)
? ((MethodInvocationTree) tree).getTypeArguments()
: ((NewClassTree) tree).getTypeArguments();
com.sun.tools.javac.util.List<Type> explicitTypeArgs = convertTreesToTypes(typeArgumentTrees);

Type.ForAll forAllType = (Type.ForAll) methodSymbol.type;
Type.MethodType underlyingMethodType = (Type.MethodType) forAllType.qtype;

// There are no explicit type arguments, so use the inferred types
if (explicitTypeArgs.isEmpty()) {
if (inferredSubstitutionsForGenericMethodCalls.containsKey(methodInvocationTree)) {
if (inferredSubstitutionsForGenericMethodCalls.containsKey(tree)) {
Comment thread
msridhar marked this conversation as resolved.
return substituteInferredTypesForTypeVariables(
state,
underlyingMethodType,
inferredSubstitutionsForGenericMethodCalls.get(methodInvocationTree),
inferredSubstitutionsForGenericMethodCalls.get(tree),
config);
}
}
Expand Down Expand Up @@ -1012,7 +1013,7 @@ private Type substituteTypeArgsInGenericMethodType(
public Nullness getGenericParameterNullnessAtInvocation(
int paramIndex,
Symbol.MethodSymbol invokedMethodSymbol,
MethodInvocationTree tree,
Tree tree,
VisitorState state,
Config config) {
boolean isVarargsParam =
Expand All @@ -1035,11 +1036,27 @@ public Nullness getGenericParameterNullnessAtInvocation(
}
}

if (!(tree.getMethodSelect() instanceof MemberSelectTree) || invokedMethodSymbol.isStatic()) {
return Nullness.NONNULL;
if (tree instanceof MethodInvocationTree) {
if (!(((MethodInvocationTree) tree).getMethodSelect() instanceof MemberSelectTree)
|| invokedMethodSymbol.isStatic()) {
return Nullness.NONNULL;
}
}
Type enclosingType =
getTreeType(((MemberSelectTree) tree.getMethodSelect()).getExpression(), config);

Type enclosingType = null;
if (tree instanceof MethodInvocationTree) {
enclosingType =
getTreeType(
((MemberSelectTree) ((MethodInvocationTree) tree).getMethodSelect()).getExpression(),
config);

} else {
Verify.verify(tree instanceof NewClassTree);
// for a constructor invocation, the type from the invocation itself is the "enclosing type"
// for the purposes of determining type arguments
enclosingType = getTreeType(tree, config);
}

return getGenericMethodParameterNullness(
paramIndex, invokedMethodSymbol, enclosingType, state, config);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2324,6 +2324,70 @@ public void issue1129() {
.doTest();
}

@Ignore("https://github.com/uber/NullAway/issues/1155")
@Test
public void callWithConstructorReceiver() {
makeHelper()
.addSourceLines(
"Test.java",
"import org.jspecify.annotations.NullMarked;",
"import org.jspecify.annotations.Nullable;",
"@NullMarked",
"public class Test {",
" private static class Inner<T extends @Nullable Object> {",
" Inner<T> identity() { return this; }",
" }",
" Inner<@Nullable Object> mThing = new Inner<@Nullable Object>().identity();",
"}")
.doTest();
}

@Test
public void newNullableWithArg() {
makeHelper()
.addSourceLines(
"Test.java",
"import org.jspecify.annotations.NullMarked;",
"import org.jspecify.annotations.Nullable;",
"@NullMarked",
"public class Test {",
" private static class Wrapper<T extends @Nullable String> {",
" private final T value;",
" Wrapper(T value) {",
" this.value = value;",
" }",
" }",
" Wrapper<@Nullable String> testConstructorCall() {",
" return new Wrapper<@Nullable String>(null);",
" }",
"}")
.doTest();
}

@Test
public void newNullableWithArgAndConstructorType() {
makeHelper()
.addSourceLines(
"Holder.java",
"import org.jspecify.annotations.NullMarked;",
"import org.jspecify.annotations.Nullable;",
"@NullMarked",
"public class Holder {",
" String s;",
" public <U extends @Nullable Object> Holder(U value) {",
" s = String.valueOf(value);",
" }",
" static Holder testNegative() {",
" return new <@Nullable String>Holder(null);",
" }",
" static Holder testPositive() {",
" // BUG: Diagnostic contains: passing @Nullable parameter 'null' where @NonNull is required",
" return new <String>Holder(null);",
" }",
"}")
.doTest();
}

@Test
public void issue1156() {
makeHelper()
Expand Down