8298943: Missing escapes for single quote marks in compiler.properties

Reviewed-by: jjg
This commit is contained in:
Archie L. Cobbs 2022-12-19 19:48:13 +00:00 committed by Jonathan Gibbons
parent 9194e91549
commit 40cb431fee
2 changed files with 131 additions and 3 deletions
src/jdk.compiler/share/classes/com/sun/tools/javac/resources
test/langtools/tools/javac/diags

@ -3337,7 +3337,7 @@ compiler.err.dc.no.content=\
no content
compiler.err.dc.no.tag.name=\
no tag name after '@'
no tag name after ''@''
compiler.err.dc.no.url=\
no URL
@ -3601,7 +3601,7 @@ compiler.warn.leaks.not.accessible.unexported=\
{0} {1} in module {2} is not exported
# 0: kind name, 1: symbol, 2: symbol
compiler.warn.leaks.not.accessible.not.required.transitive=\
{0} {1} in module {2} is not indirectly exported using 'requires transitive'
{0} {1} in module {2} is not indirectly exported using ''requires transitive''
# 0: kind name, 1: symbol, 2: symbol
compiler.warn.leaks.not.accessible.unexported.qualified=\
{0} {1} in module {2} may not be visible to all clients that require this module
@ -3663,7 +3663,7 @@ compiler.err.sealed.class.must.have.subclasses=\
# 0: symbol
compiler.err.cant.inherit.from.sealed=\
class is not allowed to extend sealed class: {0} \
(as it is not listed in its 'permits' clause)
(as it is not listed in its ''permits'' clause)
# 0: symbol
compiler.err.class.in.unnamed.module.cant.extend.sealed.in.diff.package=\

@ -32,6 +32,7 @@
import java.io.*;
import java.util.*;
import java.util.regex.*;
import javax.tools.*;
import com.sun.tools.classfile.*;
import com.sun.tools.javac.code.Lint.LintCategory;
@ -47,6 +48,8 @@ public class CheckResourceKeys {
* look for keys in resource bundles that are no longer required
* -findmissingkeys
* look for keys in resource bundles that are missing
* -checkformats
* validate MessageFormat patterns in resource bundles
*
* @throws Exception if invoked by jtreg and errors occur
*/
@ -71,16 +74,19 @@ public class CheckResourceKeys {
boolean run(String... args) throws Exception {
boolean findDeadKeys = false;
boolean findMissingKeys = false;
boolean checkFormats = false;
if (args.length == 0) {
if (is_jtreg()) {
findDeadKeys = true;
findMissingKeys = true;
checkFormats = true;
} else {
System.err.println("Usage: java CheckResourceKeys <options>");
System.err.println("where options include");
System.err.println(" -finddeadkeys find keys in resource bundles which are no longer required");
System.err.println(" -findmissingkeys find keys in resource bundles that are required but missing");
System.err.println(" -checkformats validate MessageFormat patterns in resource bundles");
return true;
}
} else {
@ -89,6 +95,8 @@ public class CheckResourceKeys {
findDeadKeys = true;
else if (arg.equalsIgnoreCase("-findmissingkeys"))
findMissingKeys = true;
else if (arg.equalsIgnoreCase("-checkformats"))
checkFormats = true;
else
error("bad option: " + arg);
}
@ -106,6 +114,9 @@ public class CheckResourceKeys {
if (findMissingKeys)
findMissingKeys(codeStrings, resourceKeys);
if (checkFormats)
checkFormats(getMessageFormatBundles());
return (errors == 0);
}
@ -312,6 +323,109 @@ public class CheckResourceKeys {
"locn."
));
void checkFormats(List<ResourceBundle> messageFormatBundles) {
for (ResourceBundle bundle : messageFormatBundles) {
for (String key : bundle.keySet()) {
final String pattern = bundle.getString(key);
try {
validateMessageFormatPattern(pattern);
} catch (IllegalArgumentException e) {
error("Invalid MessageFormat pattern for resource \""
+ key + "\": " + e.getMessage());
}
}
}
}
/**
* Do some basic validation of a {@link java.text.MessageFormat} format string.
*
* <p>
* This checks for balanced braces and unnecessary quoting.
* Code cut, pasted, &amp; simplified from {@link java.text.MessageFormat#applyPattern}.
*
* @throws IllegalArgumentException if {@code pattern} is invalid
* @throws IllegalArgumentException if {@code pattern} is null
*/
public static void validateMessageFormatPattern(String pattern) {
// Check for null
if (pattern == null)
throw new IllegalArgumentException("null pattern");
// Replicate the quirky lexical analysis of MessageFormat's parsing algorithm
final int SEG_RAW = 0;
final int SEG_INDEX = 1;
final int SEG_TYPE = 2;
final int SEG_MODIFIER = 3;
int part = SEG_RAW;
int braceStack = 0;
int quotedStartPos = -1;
for (int i = 0; i < pattern.length(); i++) {
final char ch = pattern.charAt(i);
if (part == SEG_RAW) {
if (ch == '\'') {
if (i + 1 < pattern.length() && pattern.charAt(i + 1) == '\'')
i++;
else if (quotedStartPos == -1)
quotedStartPos = i;
else {
validateMessageFormatQuoted(pattern.substring(quotedStartPos + 1, i));
quotedStartPos = -1;
}
} else if (ch == '{' && quotedStartPos == -1)
part = SEG_INDEX;
continue;
}
if (quotedStartPos != -1) {
if (ch == '\'') {
validateMessageFormatQuoted(pattern.substring(quotedStartPos + 1, i));
quotedStartPos = -1;
}
continue;
}
switch (ch) {
case ',':
if (part < SEG_MODIFIER)
part++;
break;
case '{':
braceStack++;
break;
case '}':
if (braceStack == 0)
part = SEG_RAW;
else
braceStack--;
break;
case '\'':
quotedStartPos = i;
break;
default:
break;
}
}
if (part != SEG_RAW)
throw new IllegalArgumentException("unmatched braces");
if (quotedStartPos != -1)
throw new IllegalArgumentException("unmatched quote starting at offset " + quotedStartPos);
}
/**
* Validate the content of a quoted substring in a {@link java.text.MessageFormat} pattern.
*
* <p>
* We expect this content to contain at least one special character. Otherwise,
* it was probably meant to be something in single quotes but somebody forgot
* to escape the single quotes by doulbing them; and even if intentional,
* it's still bogus because the single quotes are just going to get discarded
* and so they were unnecessary in the first place.
*/
static void validateMessageFormatQuoted(String quoted) {
if (quoted.matches("[^'{},]+"))
throw new IllegalArgumentException("unescaped single quotes around \"" + quoted + "\"");
}
/**
* Look for a resource that ends in this string fragment.
*/
@ -405,6 +519,20 @@ public class CheckResourceKeys {
return results;
}
/**
* Get resource bundles containing MessageFormat strings.
*/
List<ResourceBundle> getMessageFormatBundles() {
Module jdk_compiler = ModuleLayer.boot().findModule("jdk.compiler").get();
List<ResourceBundle> results = new ArrayList<>();
for (String name : new String[]{"compiler", "launcher"}) {
ResourceBundle b =
ResourceBundle.getBundle("com.sun.tools.javac.resources." + name, jdk_compiler);
results.add(b);
}
return results;
}
/**
* Report an error.
*/