6415644: Make javax.lang.model.SourceVersion more informative

Reviewed-by: jjg
This commit is contained in:
Joe Darcy 2016-05-20 17:00:03 -07:00
parent e568099980
commit 8c88656e09
2 changed files with 144 additions and 35 deletions

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -55,7 +55,7 @@ public enum SourceVersion {
* 1.6: no changes * 1.6: no changes
* 1.7: diamond syntax, try-with-resources, etc. * 1.7: diamond syntax, try-with-resources, etc.
* 1.8: lambda expressions and default methods * 1.8: lambda expressions and default methods
* 9: To be determined * 9: modules, small cleanups to 1.7 and 1.8 changes
*/ */
/** /**
@ -145,6 +145,9 @@ public enum SourceVersion {
* The version recognized by the Java Platform, Standard Edition * The version recognized by the Java Platform, Standard Edition
* 9. * 9.
* *
* Additions in this release include modules and removal of a
* single underscore from the set of legal identifier names.
*
* @since 9 * @since 9
*/ */
RELEASE_9; RELEASE_9;
@ -233,10 +236,10 @@ public enum SourceVersion {
} }
/** /**
* Returns whether or not {@code name} is a syntactically valid * Returns whether or not {@code name} is a syntactically valid
* qualified name in the latest source version. Unlike {@link * qualified name in the latest source version. Unlike {@link
* #isIdentifier isIdentifier}, this method returns {@code false} * #isIdentifier isIdentifier}, this method returns {@code false}
* for keywords and literals. * for keywords, boolean literals, and the null literal.
* *
* @param name the string to check * @param name the string to check
* @return {@code true} if this string is a * @return {@code true} if this string is a
@ -244,45 +247,115 @@ public enum SourceVersion {
* @jls 6.2 Names and Identifiers * @jls 6.2 Names and Identifiers
*/ */
public static boolean isName(CharSequence name) { public static boolean isName(CharSequence name) {
return isName(name, latest());
}
/**
* Returns whether or not {@code name} is a syntactically valid
* qualified name in the given source version. Unlike {@link
* #isIdentifier isIdentifier}, this method returns {@code false}
* for keywords, boolean literals, and the null literal.
*
* @param name the string to check
* @param version the version to use
* @return {@code true} if this string is a
* syntactically valid name, {@code false} otherwise.
* @jls 6.2 Names and Identifiers
* @since 9
*/
public static boolean isName(CharSequence name, SourceVersion version) {
String id = name.toString(); String id = name.toString();
for(String s : id.split("\\.", -1)) { for(String s : id.split("\\.", -1)) {
if (!isIdentifier(s) || isKeyword(s)) if (!isIdentifier(s) || isKeyword(s, version))
return false; return false;
} }
return true; return true;
} }
private final static Set<String> keywords; /**
static { * Returns whether or not {@code s} is a keyword, boolean literal,
Set<String> s = new HashSet<>(); * or null literal in the latest source version.
String [] kws = { *
"abstract", "continue", "for", "new", "switch", * @param s the string to check
"assert", "default", "if", "package", "synchronized", * @return {@code true} if {@code s} is a keyword, or boolean
"boolean", "do", "goto", "private", "this", * literal, or null literal, {@code false} otherwise.
"break", "double", "implements", "protected", "throw", * @jls 3.9 Keywords
"byte", "else", "import", "public", "throws", * @jls 3.10.3 Boolean Literals
"case", "enum", "instanceof", "return", "transient", * @jls 3.10.7 The Null Literal
"catch", "extends", "int", "short", "try", */
"char", "final", "interface", "static", "void", public static boolean isKeyword(CharSequence s) {
"class", "finally", "long", "strictfp", "volatile", return isKeyword(s, latest());
"const", "float", "native", "super", "while",
// literals
"null", "true", "false"
};
for(String kw : kws)
s.add(kw);
keywords = Collections.unmodifiableSet(s);
} }
/** /**
* Returns whether or not {@code s} is a keyword or literal in the * Returns whether or not {@code s} is a keyword, boolean literal,
* latest source version. * or null literal in the given source version.
* *
* @param s the string to check * @param s the string to check
* @return {@code true} if {@code s} is a keyword or literal, {@code false} otherwise. * @param version the version to use
* @return {@code true} if {@code s} is a keyword, or boolean
* literal, or null literal, {@code false} otherwise.
* @jls 3.9 Keywords
* @jls 3.10.3 Boolean Literals
* @jls 3.10.7 The Null Literal
* @since 9
*/ */
public static boolean isKeyword(CharSequence s) { public static boolean isKeyword(CharSequence s, SourceVersion version) {
return keywords.contains(s.toString()); String id = s.toString();
switch(id) {
// A trip through history
case "strictfp":
return version.compareTo(RELEASE_2) >= 0;
case "assert":
return version.compareTo(RELEASE_4) >= 0;
case "enum":
return version.compareTo(RELEASE_5) >= 0;
case "_":
return version.compareTo(RELEASE_9) >= 0;
// Keywords common across versions
// Modifiers
case "public": case "protected": case "private":
case "abstract": case "static": case "final":
case "transient": case "volatile": case "synchronized":
case "native":
// Declarations
case "class": case "interface": case "extends":
case "package": case "throws": case "implements":
// Primitive types and void
case "boolean": case "byte": case "char":
case "short": case "int": case "long":
case "float": case "double":
case "void":
// Control flow
case "if": case "else":
case "try": case "catch": case "finally":
case "do": case "while":
case "for": case "continue":
case "switch": case "case": case "default":
case "break": case "throw": case "return":
// Other keywords
case "this": case "new": case "super":
case "import": case "instanceof":
// Forbidden!
case "goto": case "const":
// literals
case "null": case "true": case "false":
return true;
default:
return false;
}
} }
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -23,13 +23,14 @@
/* /*
* @test * @test
* @bug 7025809 8028543 * @bug 7025809 8028543 6415644
* @summary Test latest and latestSupported * @summary Test latest, latestSupported, underscore as keyword, etc.
* @author Joseph D. Darcy * @author Joseph D. Darcy
* @modules java.compiler * @modules java.compiler
* jdk.compiler * jdk.compiler
*/ */
import java.util.*;
import javax.lang.model.SourceVersion; import javax.lang.model.SourceVersion;
import static javax.lang.model.SourceVersion.*; import static javax.lang.model.SourceVersion.*;
@ -38,10 +39,45 @@ import static javax.lang.model.SourceVersion.*;
*/ */
public class TestSourceVersion { public class TestSourceVersion {
public static void main(String... args) { public static void main(String... args) {
testLatestSupported();
testVersionVaryingKeywords();
}
private static void testLatestSupported() {
if (SourceVersion.latest() != RELEASE_9 || if (SourceVersion.latest() != RELEASE_9 ||
SourceVersion.latestSupported() != RELEASE_9) SourceVersion.latestSupported() != RELEASE_9)
throw new RuntimeException("Unexpected release value(s) found:\n" + throw new RuntimeException("Unexpected release value(s) found:\n" +
"latest:\t" + SourceVersion.latest() + "\n" + "latest:\t" + SourceVersion.latest() + "\n" +
"latestSupported:\t" + SourceVersion.latestSupported()); "latestSupported:\t" + SourceVersion.latestSupported());
} }
private static void testVersionVaryingKeywords() {
Map<String, SourceVersion> keyWordStart =
Map.of("strictfp", RELEASE_2,
"assert", RELEASE_4,
"enum", RELEASE_5,
"_", RELEASE_9);
for (Map.Entry<String, SourceVersion> entry : keyWordStart.entrySet()) {
String key = entry.getKey();
SourceVersion value = entry.getValue();
check(true, isKeyword(key), "keyword", latest());
check(false, isName(key), "name", latest());
for(SourceVersion version : SourceVersion.values()) {
boolean isKeyword = version.compareTo(value) >= 0;
check(isKeyword, isKeyword(key, version), "keyword", version);
check(!isKeyword, isName(key, version), "name", version);
}
}
}
private static void check(boolean result, boolean expected,
String message, SourceVersion version) {
if (result != expected) {
throw new RuntimeException("Unexpected " + message + "-ness of _ on " + version);
}
}
} }