/*
* Copyright 2016 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.bugpatterns;
import com.google.common.io.ByteStreams;
import com.google.errorprone.CompilationTestHelper;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** {@link BoxedPrimitiveConstructor}Test */
@RunWith(JUnit4.class)
public class BoxedPrimitiveConstructorTest {
CompilationTestHelper compilationHelper;
@Before
public void setUp() {
compilationHelper =
CompilationTestHelper.newInstance(BoxedPrimitiveConstructor.class, getClass());
}
@Test
public void positive() {
compilationHelper
.addSourceLines(
"Test.java",
"public class Test {",
" {",
" // BUG: Diagnostic contains: byte b = (byte) 0;",
" byte b = new Byte((byte) 0);",
" // BUG: Diagnostic contains: char c = (char) 0;",
" char c = new Character((char) 0);",
" // BUG: Diagnostic contains: double d = 0;",
" double d = new Double(0);",
" // BUG: Diagnostic contains: float f = 0;",
" float f = new Float(0);",
" // BUG: Diagnostic contains: int i = 0;",
" int i = new Integer(0);",
" // BUG: Diagnostic contains: long j = 0;",
" long j = new Long(0);",
" // BUG: Diagnostic contains: short s = (short) 0;",
" short s = new Short((short) 0);",
" Double dd = d;",
" // BUG: Diagnostic contains: float f2 = dd.floatValue();",
" float f2 = new Float(dd);",
" // BUG: Diagnostic contains: float f3 = (float) d;",
" float f3 = new Float(d);",
" // BUG: Diagnostic contains: foo(Float.valueOf((float) d));",
" foo(new Float(d));",
" }",
" public void foo(Float f) {}",
"}")
.doTest();
}
@Test
public void positiveStrings() {
compilationHelper
.addSourceLines(
"Test.java",
"public class Test {",
" {",
" // BUG: Diagnostic contains: byte b = Byte.valueOf(\"0\");",
" byte b = new Byte(\"0\");",
" // BUG: Diagnostic contains: double d = Double.valueOf(\"0\");",
" double d = new Double(\"0\");",
" // BUG: Diagnostic contains: float f = Float.valueOf(\"0\");",
" float f = new Float(\"0\");",
" // BUG: Diagnostic contains: int i = Integer.valueOf(\"0\");",
" int i = new Integer(\"0\");",
" // BUG: Diagnostic contains: long j = Long.valueOf(\"0\");",
" long j = new Long(\"0\");",
" // BUG: Diagnostic contains: short s = Short.valueOf(\"0\");",
" short s = new Short(\"0\");",
" }",
"}")
.doTest();
}
@Test
public void booleanConstant() {
compilationHelper
.addSourceLines(
"Test.java",
"public class Test {",
" static final Boolean CONST = true;",
" static final String CONST2 = null;",
" {",
" // BUG: Diagnostic contains: boolean a = true;",
" boolean a = new Boolean(true);",
" // BUG: Diagnostic contains: boolean b = false;",
" boolean b = new Boolean(false);",
" // BUG: Diagnostic contains: boolean c = Boolean.valueOf(CONST);",
" boolean c = new Boolean(CONST);",
" // BUG: Diagnostic contains: boolean e = true;",
" boolean e = new Boolean(\"true\");",
" // BUG: Diagnostic contains: boolean f = false;",
" boolean f = new Boolean(\"nope\");",
" // BUG: Diagnostic contains: boolean g = Boolean.valueOf(CONST2);",
" boolean g = new Boolean(CONST2);",
" // BUG: Diagnostic contains: System.err.println(Boolean.TRUE);",
" System.err.println(new Boolean(\"true\"));",
" // BUG: Diagnostic contains: System.err.println(Boolean.FALSE);",
" System.err.println(new Boolean(\"false\"));",
" }",
"}")
.doTest();
}
@Test
public void negative() {
compilationHelper
.addSourceLines(
"Test.java",
"public class Test {",
" {",
" String s = new String((String) null);",
" }",
"}")
.doTest();
}
@Test
public void autoboxing() {
compilationHelper
.addSourceLines(
"Test.java",
"public abstract class Test {",
" abstract int g(Integer x);",
" void f(int x) {",
" // BUG: Diagnostic contains: int i = x;",
" int i = new Integer(x);",
" // BUG: Diagnostic contains: i = g(Integer.valueOf(x));",
" i = g(new Integer(x));",
" // BUG: Diagnostic contains: i = (short) 0;",
" i = new Integer((short) 0);",
" }",
"}")
.doTest();
}
// Tests that `new Integer(x).memberSelect` isn't unboxed to x.memberSelect
// TODO(cushon): we could provide a better fix for byteValue(), but hopefully no one does that?
@Test
public void methodCall() {
compilationHelper
.addSourceLines(
"Test.java",
"public abstract class Test {",
" abstract int g(Integer x);",
" void f(int x) {",
" // BUG: Diagnostic contains: int i = Integer.valueOf(x).byteValue();",
" int i = new Integer(x).byteValue();",
" }",
"}")
.doTest();
}
@Test
public void stringValue() {
compilationHelper
.addSourceLines(
"Test.java",
"public abstract class Test {",
" abstract int g(Integer x);",
" void f(int x) {",
" // BUG: Diagnostic contains: String s = String.valueOf(x);",
" String s = new Integer(x).toString();",
" }",
"}")
.doTest();
}
@Test
public void compareTo() {
compilationHelper
.addSourceLines(
"Test.java",
"public abstract class Test {",
" abstract int g(Integer x);",
" void f(int x, Integer y, double d, Double dd, Float f) {",
" // BUG: Diagnostic contains: int c1 = Integer.compare(x, y);",
" int c1 = new Integer(x).compareTo(y);",
" // BUG: Diagnostic contains: int c2 = y.compareTo(Integer.valueOf(x));",
" int c2 = y.compareTo(new Integer(x));",
" // BUG: Diagnostic contains: int c3 = Float.compare((float) d, f);",
" int c3 = new Float(d).compareTo(f);",
" // BUG: Diagnostic contains: int c4 = Float.compare(dd.floatValue(), f);",
" int c4 = new Float(dd).compareTo(f);",
" }",
"}")
.doTest();
}
@Test
public void testHashCode() {
compilationHelper
.addSourceLines(
"Test.java",
"public abstract class Test {",
" abstract int g(Integer x);",
" void f(int x, Integer y) {",
" // BUG: Diagnostic contains: int h = Integer.hashCode(x);",
" int h = new Integer(x).hashCode();",
" }",
"}")
.doTest();
}
@Test
public void longHashCode() {
compilationHelper
.addSourceLines(
"Test.java",
"public abstract class Test {",
" abstract int g(Integer x);",
" int f(long x) {",
" // BUG: Diagnostic contains: return Longs.hashCode(x);",
" return new Long(x).hashCode();",
" }",
"}")
.doTest();
}
@Rule public final TemporaryFolder tempFolder = new TemporaryFolder();
public static class Super {}
public static class Inner extends Super {}
// TODO(b/30478325): create a better way to write this style of test
static void addClassToJar(JarOutputStream jos, Class<?> clazz) throws IOException {
String entryPath = clazz.getName().replace('.', '/') + ".class";
try (InputStream is = clazz.getClassLoader().getResourceAsStream(entryPath)) {
jos.putNextEntry(new JarEntry(entryPath));
ByteStreams.copy(is, jos);
}
}
@Test
public void incompleteClasspath() throws Exception {
File libJar = tempFolder.newFile("lib.jar");
try (FileOutputStream fis = new FileOutputStream(libJar);
JarOutputStream jos = new JarOutputStream(fis)) {
addClassToJar(jos, BoxedPrimitiveConstructorTest.class);
addClassToJar(jos, Inner.class);
}
compilationHelper
.addSourceLines(
"Test.java",
"import " + Inner.class.getCanonicalName() + ";",
"class Test {",
" void m() {",
" new Inner();",
" }",
"}")
.setArgs(Arrays.asList("-cp", libJar.toString()))
.doTest();
}
@Test
public void autoboxWidening() {
compilationHelper
.addSourceLines(
"Test.java",
"class Test {",
" void f(float f) {",
" // BUG: Diagnostic contains: (double) f;",
" Double d = new Double(f);",
" // BUG: Diagnostic contains: (short) (byte) 0;",
" Short s = new Short((byte) 0);",
" }",
"}")
.doTest();
}
}