@ -62,6 +62,8 @@ at <https://pmd.github.io/latest/>.
|
||||
* [#817](https://github.com/pmd/pmd/issues/817): \[java] UnnecessaryModifierRule crashes on valid code
|
||||
* java-design
|
||||
* [#785](https://github.com/pmd/pmd/issues/785): \[java] NPE in DataClass rule
|
||||
* java-performance
|
||||
* [#841](https://github.com/pmd/pmd/issues/841): \[java] InsufficientStringBufferDeclaration NumberFormatException
|
||||
|
||||
### API Changes
|
||||
|
||||
|
@ -97,6 +97,69 @@ public class ASTLiteral extends AbstractJavaTypeNode {
|
||||
return false;
|
||||
}
|
||||
|
||||
private String stripIntValue() {
|
||||
String image = getImage().toLowerCase().replaceAll("_", "");
|
||||
|
||||
boolean isNegative = false;
|
||||
if (image.charAt(0) == '-') {
|
||||
isNegative = true;
|
||||
image = image.substring(1);
|
||||
}
|
||||
|
||||
if (image.endsWith("l")) {
|
||||
image = image.substring(0, image.length() - 1);
|
||||
}
|
||||
|
||||
// ignore base prefix if any
|
||||
if (image.charAt(0) == '0' && image.length() > 1) {
|
||||
if (image.charAt(1) == 'x' || image.charAt(1) == 'b') {
|
||||
image = image.substring(2);
|
||||
} else {
|
||||
image = image.substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (isNegative) {
|
||||
return "-" + image;
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
private String stripFloatValue() {
|
||||
return getImage().toLowerCase().replaceAll("_", "");
|
||||
}
|
||||
|
||||
private int getIntBase() {
|
||||
final String image = getImage().toLowerCase();
|
||||
final int offset = image.charAt(0) == '-' ? 1 : 0;
|
||||
if (image.startsWith("0x", offset)) {
|
||||
return 16;
|
||||
}
|
||||
if (image.startsWith("0b", offset)) {
|
||||
return 2;
|
||||
}
|
||||
if (image.startsWith("0", offset) && image.length() > 1) {
|
||||
return 8;
|
||||
}
|
||||
return 10;
|
||||
}
|
||||
|
||||
public int getValueAsInt() {
|
||||
return (int) getValueAsLong(); // the downcast allows to parse 0x80000000+ numbers as negative instead of a NumberFormatException
|
||||
}
|
||||
|
||||
public long getValueAsLong() {
|
||||
return Long.parseLong(stripIntValue(), getIntBase());
|
||||
}
|
||||
|
||||
public float getValueAsFloat() {
|
||||
return Float.parseFloat(stripFloatValue());
|
||||
}
|
||||
|
||||
public double getValueAsDouble() {
|
||||
return Double.parseDouble(stripFloatValue());
|
||||
}
|
||||
|
||||
public void setCharLiteral() {
|
||||
this.isChar = true;
|
||||
}
|
||||
|
@ -199,16 +199,17 @@ public class InsufficientStringBufferDeclarationRule extends AbstractJavaRule {
|
||||
anticipatedLength += str.length() - 2;
|
||||
} else if (literal.isCharLiteral()) {
|
||||
anticipatedLength += 1;
|
||||
} else if (literal.isIntLiteral() && str.startsWith("0x")) {
|
||||
} else if (literal.isIntLiteral()) {
|
||||
// but only if we are not inside a cast expression
|
||||
Node parentNode = literal.jjtGetParent().jjtGetParent().jjtGetParent();
|
||||
if (parentNode instanceof ASTCastExpression
|
||||
&& ((ASTCastExpression) parentNode).getType() == char.class) {
|
||||
anticipatedLength += 1;
|
||||
} else {
|
||||
// any number, regardless of the base will be converted to base 10
|
||||
// e.g. 0xdeadbeef -> will be converted to a
|
||||
// base 10 integer string: 3735928559
|
||||
anticipatedLength += String.valueOf(Long.parseLong(str.substring(2), 16)).length();
|
||||
anticipatedLength += String.valueOf(literal.getValueAsLong()).length();
|
||||
}
|
||||
} else {
|
||||
anticipatedLength += str.length();
|
||||
@ -273,11 +274,8 @@ public class InsufficientStringBufferDeclarationRule extends AbstractJavaRule {
|
||||
// characters
|
||||
// don't add the constructor's length
|
||||
iConstructorLength = 14 + str.length();
|
||||
} else if (literal.isIntLiteral() && str.startsWith("0x")) {
|
||||
// bug 3516101 - the string could be a hex number
|
||||
iConstructorLength = Integer.parseInt(str.substring(2), 16);
|
||||
} else {
|
||||
iConstructorLength = Integer.parseInt(str);
|
||||
} else if (literal.isIntLiteral()) {
|
||||
iConstructorLength = literal.getValueAsInt();
|
||||
}
|
||||
} else {
|
||||
iConstructorLength = -1;
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.rule.performance;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import net.sourceforge.pmd.lang.ast.Node;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTBooleanLiteral;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTCastExpression;
|
||||
@ -83,21 +81,13 @@ public class RedundantFieldInitializerRule extends AbstractJavaRule {
|
||||
// code.
|
||||
Number value = -1;
|
||||
if (literal.isIntLiteral()) {
|
||||
value = parseInteger(literal.getImage());
|
||||
value = literal.getValueAsInt();
|
||||
} else if (literal.isLongLiteral()) {
|
||||
String s = literal.getImage();
|
||||
// remove the ending "l" or "L" for long
|
||||
// values
|
||||
s = s.substring(0, s.length() - 1);
|
||||
value = parseInteger(s);
|
||||
value = literal.getValueAsLong();
|
||||
} else if (literal.isFloatLiteral()) {
|
||||
String s = literal.getImage();
|
||||
// remove the ending "f" or "F" for float
|
||||
// values
|
||||
s = s.substring(0, s.length() - 1);
|
||||
value = Float.valueOf(s.replaceAll("_", ""));
|
||||
value = literal.getValueAsFloat();
|
||||
} else if (literal.isDoubleLiteral()) {
|
||||
value = Double.valueOf(literal.getImage().replaceAll("_", ""));
|
||||
value = literal.getValueAsDouble();
|
||||
} else if (literal.isCharLiteral()) {
|
||||
value = (int) literal.getImage().charAt(1);
|
||||
}
|
||||
@ -152,27 +142,4 @@ public class RedundantFieldInitializerRule extends AbstractJavaRule {
|
||||
private void addViolation(Object data, ASTVariableDeclarator variableDeclarator) {
|
||||
super.addViolation(data, variableDeclarator, variableDeclarator.jjtGetChild(0).getImage());
|
||||
}
|
||||
|
||||
private Number parseInteger(String s) {
|
||||
boolean negative = false;
|
||||
String number = s;
|
||||
if (number.charAt(0) == '-') {
|
||||
negative = true;
|
||||
number = number.substring(1);
|
||||
}
|
||||
BigInteger result;
|
||||
if (number.startsWith("0x") || number.startsWith("0X")) {
|
||||
result = new BigInteger(number.substring(2).replaceAll("_", ""), 16);
|
||||
} else if (number.startsWith("0b") || number.startsWith("0B")) {
|
||||
result = new BigInteger(number.substring(2).replaceAll("_", ""), 8);
|
||||
} else if (number.charAt(0) == '0' && number.length() > 1) {
|
||||
result = new BigInteger(number.substring(1).replaceAll("_", ""), 8);
|
||||
} else {
|
||||
result = new BigInteger(number.replaceAll("_", ""));
|
||||
}
|
||||
if (negative) {
|
||||
result = result.negate();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -59,6 +59,46 @@ public class ASTLiteralTest {
|
||||
assertTrue((literals.iterator().next()).isCharLiteral());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntValueParsing() {
|
||||
ASTLiteral literal = new ASTLiteral(1);
|
||||
literal.setIntLiteral();
|
||||
literal.setImage("1___234");
|
||||
literal.testingOnlySetBeginColumn(1);
|
||||
literal.testingOnlySetEndColumn(7);
|
||||
assertEquals(1234, literal.getValueAsInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntValueParsingBinary() {
|
||||
ASTLiteral literal = new ASTLiteral(1);
|
||||
literal.setIntLiteral();
|
||||
literal.setImage("0b0000_0010");
|
||||
literal.testingOnlySetBeginColumn(1);
|
||||
literal.testingOnlySetEndColumn(7);
|
||||
assertEquals(2, literal.getValueAsInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntValueParsingNegativeHexa() {
|
||||
ASTLiteral literal = new ASTLiteral(1);
|
||||
literal.setIntLiteral();
|
||||
literal.setImage("-0X0000_000f");
|
||||
literal.testingOnlySetBeginColumn(1);
|
||||
literal.testingOnlySetEndColumn(7);
|
||||
assertEquals(-15, literal.getValueAsInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFloatValueParsingNegative() {
|
||||
ASTLiteral literal = new ASTLiteral(1);
|
||||
literal.setIntLiteral();
|
||||
literal.setImage("-3_456.123_456");
|
||||
literal.testingOnlySetBeginColumn(1);
|
||||
literal.testingOnlySetEndColumn(7);
|
||||
assertEquals(-3456.123456f, literal.getValueAsFloat(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringUnicodeEscapesNotEscaped() {
|
||||
ASTLiteral literal = new ASTLiteral(1);
|
||||
|
@ -310,7 +310,7 @@ public class Foo {
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description><![CDATA[
|
||||
15, Append int, incorrect presize
|
||||
15, Append long, incorrect presize
|
||||
]]></description>
|
||||
<expected-problems>2</expected-problems>
|
||||
<code><![CDATA[
|
||||
@ -318,11 +318,11 @@ public class Foo {
|
||||
private static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(Foo.class);
|
||||
public void bar() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append(12345678901234567890);
|
||||
sb.append(12345678901234567890L);
|
||||
}
|
||||
public void bar2() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(12345678901234567890);
|
||||
sb.append(12345678901234567890L);
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
@ -1059,6 +1059,19 @@ public final class test {
|
||||
"# Testing" + NEWLINE +
|
||||
"# More Contents" + NEWLINE);
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>#841 InsufficientStringBufferDeclaration NumberFormatException</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
public class Test {
|
||||
|
||||
public static void main(final String ... args) {
|
||||
StringBuilder report = new StringBuilder(10_000).append("test");
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
Reference in New Issue
Block a user