Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix bugs in handling of valueOf calls for map keys #1085

Merged
merged 4 commits into from
Dec 12, 2024
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
19 changes: 14 additions & 5 deletions nullaway/src/main/java/com/uber/nullaway/dataflow/AccessPath.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.VisitorState;
import com.google.errorprone.suppliers.Supplier;
import com.google.errorprone.suppliers.Suppliers;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.MethodInvocationTree;
Expand Down Expand Up @@ -75,6 +77,12 @@
*/
public final class AccessPath implements MapKey {

private static final Supplier<Type> INTEGER_TYPE_SUPPLIER =
Suppliers.typeFromString("java.lang.Integer");

private static final Supplier<Type> LONG_TYPE_SUPPLIER =
Suppliers.typeFromString("java.lang.Long");

/**
* A prefix added for elements appearing in method invocation APs which represent fields that can
* be proven to be class-initialization time constants (i.e. static final fields of a type known
Expand Down Expand Up @@ -278,14 +286,15 @@ private static Node stripCasts(Node node) {
return new NumericMapKey(((LongLiteralNode) argument).getValue());
case METHOD_INVOCATION:
MethodAccessNode target = ((MethodInvocationNode) argument).getTarget();
Node receiver = stripCasts(target.getReceiver());
List<Node> arguments = ((MethodInvocationNode) argument).getArguments();
// Check for int/long boxing.
if (target.getMethod().getSimpleName().toString().equals("valueOf")
&& arguments.size() == 1
&& castToNonNull(receiver.getTree()).getKind().equals(Tree.Kind.IDENTIFIER)
&& (receiver.toString().equals("Integer") || receiver.toString().equals("Long"))) {
return argumentToMapKeySpecifier(arguments.get(0), state, apContext);
&& arguments.size() == 1) {
Type ownerType = ((Symbol.MethodSymbol) target.getMethod()).owner.type;
if (ASTHelpers.isSameType(ownerType, INTEGER_TYPE_SUPPLIER.get(state), state)
|| ASTHelpers.isSameType(ownerType, LONG_TYPE_SUPPLIER.get(state), state)) {
return argumentToMapKeySpecifier(arguments.get(0), state, apContext);
}
}
// Fine to fallthrough:
default:
Expand Down
54 changes: 54 additions & 0 deletions nullaway/src/test/java/com/uber/nullaway/AccessPathsTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -434,4 +434,58 @@ public void testAccessUsingExplicitThis() {
"}")
.doTest();
}

@Test
public void mapKeysFromValueOf() {
defaultCompilationHelper
.addSourceLines(
"Foo.java",
"package com.uber;",
"import java.util.HashMap;",
"import java.util.Map;",
"public class Foo {",
" private final Map<Integer, Object> map = new HashMap<>();",
" private final Map<Long, Object> longMap = new HashMap<>();",
" static Integer valueOf(int i) { return 0; }",
" static Integer valueOf(int i, int j) { return i+j; }",
" public void putThenGetIntegerValueOf() {",
" map.put(Integer.valueOf(10), new Object());",
" map.get(Integer.valueOf(10)).toString();",
" }",
" public void putThenGetLongValueOf() {",
" longMap.put(Long.valueOf(10), new Object());",
" longMap.get(Long.valueOf(10)).toString();",
" }",
" public void putThenGetFooValueOf() {",
" map.put(valueOf(10), new Object());",
" // Unknown valueOf method so we report a warning",
" // BUG: Diagnostic contains: dereferenced expression map.get(valueOf(10)) is @Nullable",
" map.get(valueOf(10)).toString();",
" map.put(valueOf(10,20), new Object());",
" // Unknown valueOf method so we report a warning",
" // BUG: Diagnostic contains: dereferenced expression map.get(valueOf(10,20)) is @Nullable",
" map.get(valueOf(10,20)).toString();",
" }",
"}")
.doTest();
}

@Test
public void mapKeyFromIntegerValueOfStaticImport() {
defaultCompilationHelper
.addSourceLines(
"Foo.java",
"package com.uber;",
"import java.util.HashMap;",
"import java.util.Map;",
"import static java.lang.Integer.valueOf;",
"public class Foo {",
" private final Map<Integer, Object> map = new HashMap<>();",
" public void putThenGet() {",
" map.put(valueOf(10), new Object());",
" map.get(valueOf(10)).toString();",
" }",
"}")
.doTest();
}
}
Loading