8325440: Confusing error reported for octal literals with wrong digits

Reviewed-by: vromero
This commit is contained in:
Jan Lahoda 2024-02-09 11:51:05 +00:00
parent 5daf622aea
commit 8b70b8d85a
9 changed files with 110 additions and 26 deletions

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -742,8 +742,9 @@ public class JavaTokenizer extends UnicodeReader {
private void scanNumber(int pos, int radix) {
// for octal, allow base-10 digit in case it's a float literal
this.radix = radix;
int digitRadix = (radix == 8 ? 10 : radix);
int firstDigit = digit(pos, Math.max(10, digitRadix));
boolean permitFloatingPoint = radix == 8 || radix == 10;
int digitRadix = Math.max(10, radix);
int firstDigit = digit(pos, digitRadix);
boolean seendigit = firstDigit >= 0;
boolean seenValidDigit = firstDigit >= 0 && firstDigit < digitRadix;
@ -755,10 +756,10 @@ public class JavaTokenizer extends UnicodeReader {
scanHexFractionAndSuffix(pos, seendigit);
} else if (seendigit && radix == 16 && isOneOf('p', 'P')) {
scanHexExponentAndSuffix(pos);
} else if (digitRadix == 10 && is('.')) {
} else if (permitFloatingPoint && is('.')) {
putThenNext();
scanFractionAndSuffix(pos);
} else if (digitRadix == 10 && isOneOf('e', 'E', 'f', 'F', 'd', 'D')) {
} else if (permitFloatingPoint && isOneOf('e', 'E', 'f', 'F', 'd', 'D')) {
scanFractionAndSuffix(pos);
} else {
if (!seenValidDigit) {
@ -771,13 +772,6 @@ public class JavaTokenizer extends UnicodeReader {
break;
}
}
// If it is not a floating point literal,
// the octal number should be rescanned correctly.
if (radix == 8) {
sb.setLength(0);
reset(pos);
scanDigits(pos, 8);
}
if (acceptOneOf('l', 'L')) {
tk = TokenKind.LONGLITERAL;

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -770,7 +770,7 @@ public class JavacParser implements Parser {
TypeTag.INT,
Convert.string2int(strval(prefix), token.radix()));
} catch (NumberFormatException ex) {
log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.IntNumberTooLarge(strval(prefix)));
reportIntegralLiteralError(prefix, pos);
}
break;
case LONGLITERAL:
@ -779,7 +779,7 @@ public class JavacParser implements Parser {
TypeTag.LONG,
Long.valueOf(Convert.string2long(strval(prefix), token.radix())));
} catch (NumberFormatException ex) {
log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.IntNumberTooLarge(strval(prefix)));
reportIntegralLiteralError(prefix, pos);
}
break;
case FLOATLITERAL: {
@ -862,6 +862,28 @@ public class JavacParser implements Parser {
String s = token.stringVal();
return prefix.isEmpty() ? s : prefix + s;
}
void reportIntegralLiteralError(Name prefix, int pos) {
int radix = token.radix();
if (radix == 2 || radix == 8) {
//attempt to produce more user-friendly error message for
//binary and octal literals with wrong digits:
String value = strval(prefix);
char[] cs = value.toCharArray();
for (int i = 0; i < cs.length; i++) {
char c = cs[i];
int d = Character.digit(c, radix);
if (d == (-1)) {
Error err = radix == 2 ? Errors.IllegalDigitInBinaryLiteral
: Errors.IllegalDigitInOctalLiteral;
log.error(DiagnosticFlag.SYNTAX,
token.pos + i,
err);
return ;
}
}
}
log.error(DiagnosticFlag.SYNTAX, token.pos, Errors.IntNumberTooLarge(strval(prefix)));
}
/** terms can be either expressions or types.
*/

@ -1,5 +1,5 @@
#
# Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@ -740,6 +740,12 @@ compiler.err.illegal.underscore=\
compiler.err.illegal.dot=\
illegal ''.''
compiler.err.illegal.digit.in.binary.literal=\
illegal digit in a binary literal
compiler.err.illegal.digit.in.octal.literal=\
illegal digit in an octal literal
# 0: symbol
compiler.err.illegal.qual.not.icls=\
illegal qualifier; {0} is not an inner class

@ -0,0 +1,28 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// key: compiler.err.illegal.digit.in.binary.literal
class IllegalDigitInOctalLiteral {
int i = 0b8;
}

@ -0,0 +1,28 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// key: compiler.err.illegal.digit.in.octal.literal
class IllegalDigitInOctalLiteral {
int i = 08;
}

@ -24,5 +24,5 @@
// key: compiler.err.invalid.binary.number
class InvalidBinaryNumber {
int i = 0b201000010;
int i = 0b;
}

@ -83,7 +83,6 @@ public class JavaLexerTest {
static final TestTuple[] FAILING_TESTS = {
new TestTuple(LONGLITERAL, "0bL"),
new TestTuple(LONGLITERAL, "0b20L"),
new TestTuple(LONGLITERAL, "0xL"),
new TestTuple(INTLITERAL, "0xG000L", "0x"),

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -23,8 +23,8 @@
/*
* @test
* @bug 8267361
* @summary JavaTokenizer reads octal numbers mistakenly
* @bug 8267361 8325440
* @summary Verify meaniningfull errors for broken octal literals.
* @library /tools/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main
@ -62,6 +62,9 @@ public class OctalNumberTest extends TestRunner {
int c = 02389;
int d = 028a;
int e = 02a8;
int f = 0b;
int g = 0b2;
int h = 0b12;
}""";
List<String> output = new JavacTask(tb)
.sources(code)
@ -70,13 +73,17 @@ public class OctalNumberTest extends TestRunner {
.writeAll()
.getOutputLines(Task.OutputKind.DIRECT);
List<String> expected = Arrays.asList(
"Digit.java:3:14: compiler.err.expected: ';'",
"Digit.java:4:16: compiler.err.expected: ';'",
"Digit.java:5:15: compiler.err.expected: ';'",
"Digit.java:3:14: compiler.err.illegal.digit.in.octal.literal",
"Digit.java:4:16: compiler.err.illegal.digit.in.octal.literal",
"Digit.java:5:15: compiler.err.illegal.digit.in.octal.literal",
"Digit.java:5:16: compiler.err.expected: ';'",
"Digit.java:5:17: compiler.err.expected: token.identifier",
"Digit.java:6:15: compiler.err.expected: ';'",
"Digit.java:6:17: compiler.err.expected: token.identifier",
"6 errors");
"Digit.java:7:13: compiler.err.invalid.binary.number",
"Digit.java:8:13: compiler.err.illegal.digit.in.binary.literal",
"Digit.java:9:14: compiler.err.illegal.digit.in.binary.literal",
"10 errors");
tb.checkEqual(expected, output);
}
}

@ -1,4 +1,4 @@
BadBinaryLiterals.java:10:24: compiler.err.expected: ';'
BadBinaryLiterals.java:10:22: compiler.err.illegal.digit.in.binary.literal
BadBinaryLiterals.java:12:21: compiler.err.int.number.too.large: 111111111111111111111111111111111
BadBinaryLiterals.java:14:21: compiler.err.int.number.too.large: 11111111111111111111111111111111111111111111111111111111111111111
BadBinaryLiterals.java:15:27: compiler.err.expected: ';'