/*
* 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.bugpatterns;
import com.google.errorprone.CompilationTestHelper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** @author cushon@google.com (Liam Miller-Cushon) */
@RunWith(JUnit4.class)
public class NarrowingCompoundAssignmentTest {
private CompilationTestHelper compilationHelper;
@Before
public void setUp() {
compilationHelper =
CompilationTestHelper.newInstance(NarrowingCompoundAssignment.class, getClass());
}
@Test
public void testPositiveCase() throws Exception {
compilationHelper
.addSourceLines(
"Test.java",
"class Test {",
" void m() {",
" short s = 0;",
" char t = 0;",
" byte u = 0;",
" float v = 0;",
" // BUG: Diagnostic contains: s = (short) (s * 1)",
" s *= 1;",
" // BUG: Diagnostic contains: t = (char) (t * 1)",
" t *= 1;",
" // BUG: Diagnostic contains: u = (byte) (u * 1)",
" u *= 1;",
" // BUG: Diagnostic contains: u = (byte) (u * 1L)",
" u *= 1L;",
" // BUG: Diagnostic contains: v = (float) (v * 1.0)",
" v *= 1.0;",
" // BUG: Diagnostic contains: v = (float) (v * 1.0d)",
" v *= 1.0d;",
" }",
"}")
.doTest();
}
@Test
public void testAllOps() throws Exception {
compilationHelper
.addSourceLines(
"Test.java",
"class Test {",
" void m() {",
" short s = 0;",
" // BUG: Diagnostic contains: s = (short) (s * 1)",
" s *= 1;",
" // BUG: Diagnostic contains: s = (short) (s / 1)",
" s /= 1;",
" // BUG: Diagnostic contains: s = (short) (s % 1)",
" s %= 1;",
" // BUG: Diagnostic contains: s = (short) (s + 1)",
" s += 1;",
" // BUG: Diagnostic contains: s = (short) (s - 1)",
" s -= 1;",
" // BUG: Diagnostic contains: s = (short) (s << 1)",
" s <<= 1;",
" // Signed right shifts are OK",
" s >>= 1;",
" // BUG: Diagnostic contains: s = (short) (s >>> 1)",
" s >>>= 1;",
" }",
"}")
.doTest();
}
@Test
public void testDeficientRightShift() throws Exception {
compilationHelper
.addSourceLines(
"Test.java",
"class Test {",
" void m() {",
" // BUG: Diagnostic contains: i = (short) (i >>> 1)",
" for (short i = -1; i != 0; i >>>= 1);",
" }",
"}")
.doTest();
}
@Test
public void testNegativeCase() throws Exception {
compilationHelper
.addSourceLines(
"Test.java",
"class Test {",
" void m() {",
" int s = 0;",
" long t = 0;",
" double u = 0;",
" s *= 1;",
" t *= 1;",
" u *= 1;",
" }",
"}")
.doTest();
}
@Test
public void testFloatFloat() throws Exception {
compilationHelper
.addSourceLines(
"Test.java",
"class Test {",
" void m() {",
" float a = 0;",
" float b = 0;",
" Float c = Float.valueOf(0);",
" a += b;",
" a += c;",
" }",
"}")
.doTest();
}
// bit twiddling deficient types with masks of the same width is fine
@Test
public void testBitTwiddle() throws Exception {
compilationHelper
.addSourceLines(
"Test.java",
"class Test {",
" void m() {",
" short smask = 0b1;",
" byte bmask = 0b1;",
"",
" short s = 0;",
" byte b = 0;",
"",
" s &= smask;",
" s |= smask;",
" s ^= smask;",
"",
" s &= bmask;",
" s |= bmask;",
" s ^= bmask;",
"",
" b &= bmask;",
" b |= bmask;",
" b ^= bmask;",
" }",
"}")
.doTest();
}
@Test
public void allowsBinopsOfDeficientTypes() throws Exception {
compilationHelper
.addSourceLines(
"Test.java",
"class Test {",
" void m() {",
" short smask = 0b1;",
" byte bmask = 0b1;",
"",
" short s = 0;",
" byte b = 0;",
"",
" s += smask;",
" s -= smask;",
" s *= smask;",
"",
" s += bmask;",
" s -= bmask;",
" s *= bmask;",
"",
" b -= bmask;",
" b += bmask;",
" b /= bmask;",
" }",
"}")
.doTest();
}
@Test
public void testPreservePrecedence() throws Exception {
compilationHelper
.addSourceLines(
"Test.java",
"class Test {",
" void m() {",
" float f = 0;",
" // BUG: Diagnostic contains: f = (float) (f - (3.0 - 2.0))",
" f -= 3.0 - 2.0;",
" }",
"}")
.doTest();
}
@Test
public void testPreservePrecedence2() throws Exception {
compilationHelper
.addSourceLines(
"Test.java",
"class Test {",
" void m() {",
" float f = 0;",
" // BUG: Diagnostic contains: f = (float) (f - 3.0 * 2.0)",
" f -= 3.0 * 2.0;",
" }",
"}")
.doTest();
}
@Test
public void testPreservePrecedenceExhaustive() throws Exception {
testPrecedence("*", "*", true);
testPrecedence("*", "+", true);
testPrecedence("*", "<<", true);
testPrecedence("*", "&", true);
testPrecedence("*", "|", true);
testPrecedence("+", "*", false);
testPrecedence("+", "+", true);
testPrecedence("+", "<<", true);
testPrecedence("+", "&", true);
testPrecedence("+", "|", true);
testPrecedence("<<", "*", false);
testPrecedence("<<", "+", false);
testPrecedence("<<", "<<", true);
testPrecedence("<<", "&", true);
testPrecedence("<<", "|", true);
testPrecedence("&", "*", false);
testPrecedence("&", "+", false);
testPrecedence("&", "<<", false);
testPrecedence("&", "&", true);
testPrecedence("&", "|", true);
testPrecedence("|", "*", false);
testPrecedence("|", "+", false);
testPrecedence("|", "<<", false);
testPrecedence("|", "&", false);
testPrecedence("|", "|", true);
}
private void testPrecedence(String opA, String opB, boolean parens) throws Exception {
String rhs = String.format("1 %s 2", opB);
if (parens) {
rhs = "(" + rhs + ")";
}
String expect = String.format("s = (short) (s %s %s", opA, rhs);
String compoundAssignment = String.format(" s %s= 1 %s 2;", opA, opB);
compilationHelper
.addSourceLines(
"Test.java",
"class Test {",
" void m() {",
" short s = 0;",
" // BUG: Diagnostic contains: " + expect,
compoundAssignment,
" }",
"}")
.doTest();
}
@Test
public void testDoubleLong() throws Exception {
compilationHelper
.addSourceLines(
"Test.java",
"class Test {",
" void m() {",
" long a = 1;",
" double b = 2;",
" // BUG: Diagnostic contains: Compound assignments from double to long",
" a *= b;",
" }",
"}")
.doTest();
}
@Test
public void testDoubleInt() throws Exception {
compilationHelper
.addSourceLines(
"Test.java",
"class Test {",
" void m() {",
" int a = 1;",
" double b = 2;",
" // BUG: Diagnostic contains: Compound assignments from double to int",
" a *= b;",
" }",
"}")
.doTest();
}
@Test
public void testFloatLong() throws Exception {
compilationHelper
.addSourceLines(
"Test.java",
"class Test {",
" void m() {",
" long a = 1;",
" float b = 2;",
" // BUG: Diagnostic contains: Compound assignments from float to long",
" a *= b;",
" }",
"}")
.doTest();
}
@Test
public void testFloatInt() throws Exception {
compilationHelper
.addSourceLines(
"Test.java",
"class Test {",
" void m() {",
" int a = 1;",
" float b = 2;",
" // BUG: Diagnostic contains:" + " Compound assignments from float to int",
" a *= b;",
" }",
"}")
.doTest();
}
@Test
public void exhaustiveTypes() throws Exception {
compilationHelper
.addSourceLines(
"Test.java",
"class Test {",
" void f(short s, byte b, char c, int i, long l, float f, double d) {",
" s += s;",
" s += b;",
" // BUG: Diagnostic contains:",
" s += c;",
" // BUG: Diagnostic contains:",
" s += i;",
" // BUG: Diagnostic contains:",
" s += l;",
" // BUG: Diagnostic contains:",
" s += f;",
" // BUG: Diagnostic contains:",
" s += d;",
" // BUG: Diagnostic contains:",
" b += s;",
" b += b;",
" // BUG: Diagnostic contains:",
" b += c;",
" // BUG: Diagnostic contains:",
" b += i;",
" // BUG: Diagnostic contains:",
" b += l;",
" // BUG: Diagnostic contains:",
" b += f;",
" // BUG: Diagnostic contains:",
" b += d;",
" // BUG: Diagnostic contains:",
" c += s;",
" // BUG: Diagnostic contains:",
" c += b;",
" c += c;",
" // BUG: Diagnostic contains:",
" c += i;",
" // BUG: Diagnostic contains:",
" c += l;",
" // BUG: Diagnostic contains:",
" c += f;",
" // BUG: Diagnostic contains:",
" c += d;",
" i += s;",
" i += b;",
" i += c;",
" i += i;",
" // BUG: Diagnostic contains:",
" i += l;",
" // BUG: Diagnostic contains:",
" i += f;",
" // BUG: Diagnostic contains:",
" i += d;",
" l += s;",
" l += b;",
" l += c;",
" l += i;",
" l += l;",
" // BUG: Diagnostic contains:",
" l += f;",
" // BUG: Diagnostic contains:",
" l += d;",
" f += s;",
" f += b;",
" f += c;",
" f += i;",
" f += l;",
" f += f;",
" // BUG: Diagnostic contains:",
" f += d;",
" d += s;",
" d += b;",
" d += c;",
" d += i;",
" d += l;",
" d += f;",
" d += d;",
" }",
"}")
.doTest();
}
@Test
public void testBoxing() throws Exception {
compilationHelper
.addSourceLines(
"Test.java",
"class Test {",
" void m() {",
" int a = 1;",
" // BUG: Diagnostic contains: from Long to int",
" a += (Long) 0L;",
" }",
"}")
.doTest();
}
@Test
public void testStringConcat() throws Exception {
compilationHelper
.addSourceLines(
"Test.java",
"class Test {",
" void m() {",
" String a = \"\";",
" a += (char) 0;",
" }",
"}")
.doTest();
}
}