8165646: (jdeprscan) adjust tool output to improve clarity

Reviewed-by: jjg, psandoz
This commit is contained in:
Stuart Marks 2016-11-01 11:28:16 -07:00
parent d23149e827
commit 2380985895
6 changed files with 147 additions and 115 deletions

View File

@ -638,6 +638,8 @@ public class Main implements DiagnosticListener<JavaFileObject> {
// now the scanning phase // now the scanning phase
boolean scanStatus = true;
switch (scanMode) { switch (scanMode) {
case LIST: case LIST:
for (DeprData dd : deprList) { for (DeprData dd : deprList) {
@ -661,24 +663,22 @@ public class Main implements DiagnosticListener<JavaFileObject> {
Scan scan = new Scan(out, err, cp, db, verbose); Scan scan = new Scan(out, err, cp, db, verbose);
for (String a : args) { for (String a : args) {
boolean success; boolean s;
if (a.endsWith(".jar")) { if (a.endsWith(".jar")) {
success = scan.scanJar(a); s = scan.scanJar(a);
} else if (a.endsWith(".class")) {
s = scan.processClassFile(a);
} else if (Files.isDirectory(Paths.get(a))) { } else if (Files.isDirectory(Paths.get(a))) {
success = scan.scanDir(a); s = scan.scanDir(a);
} else { } else {
success = scan.processClassName(a.replace('.', '/')); s = scan.processClassName(a.replace('.', '/'));
}
if (!success) {
return false;
} }
scanStatus = scanStatus && s;
} }
break; break;
} }
return true; return scanStatus;
} }
/** /**

View File

@ -34,6 +34,10 @@ import java.util.ResourceBundle;
* Message handling class for localization. * Message handling class for localization.
*/ */
public class Messages { public class Messages {
/** Indicates whether line separators in messages need replacement. */
static final boolean REPLACE_LINESEP = ! System.lineSeparator().equals("\n");
/** The resource bundle, must be non-null. */
static final ResourceBundle bundle; static final ResourceBundle bundle;
static { static {
@ -41,13 +45,25 @@ public class Messages {
try { try {
bundle = ResourceBundle.getBundle("com.sun.tools.jdeprscan.resources.jdeprscan", locale); bundle = ResourceBundle.getBundle("com.sun.tools.jdeprscan.resources.jdeprscan", locale);
} catch (MissingResourceException e) { } catch (MissingResourceException e) {
throw new InternalError("Cannot find jdeps resource bundle for locale " + locale, e); throw new InternalError("Cannot find jdeprscan resource bundle for locale " + locale, e);
} }
} }
/**
* Gets a message from the resource bundle. If necessary, translates "\n",
* the line break string used in the message file, to the system-specific
* line break string.
*
* @param key the message key
* @param args the message arguments
*/
public static String get(String key, Object... args) { public static String get(String key, Object... args) {
try { try {
return MessageFormat.format(bundle.getString(key), args); String msg = MessageFormat.format(bundle.getString(key), args);
if (REPLACE_LINESEP) {
msg = msg.replace("\n", System.lineSeparator());
}
return msg;
} catch (MissingResourceException e) { } catch (MissingResourceException e) {
throw new InternalError("Missing message: " + key, e); throw new InternalError("Missing message: " + key, e);
} }

View File

@ -92,6 +92,9 @@ Given a jar file, **jdeprscan** will scan the classes found within
that jar file and report information about how those classes use that jar file and report information about how those classes use
deprecated APIs. deprecated APIs.
Given a class file, **jdeprscan** will scan that class and report
its use of deprecated APIs.
Given a class name, **jdeprscan** will search for that class on the Given a class name, **jdeprscan** will search for that class on the
classpath, scan that class, and report information about how that classpath, scan that class, and report information about how that
class uses deprecated APIs. The class name must use the fully class uses deprecated APIs. The class name must use the fully

View File

@ -14,9 +14,9 @@ options:\n\
main.help=\ main.help=\
Scans each argument for usages of deprecated APIs. An argument\n\ Scans each argument for usages of deprecated APIs. An argument\n\
may be a directory specifying the root of a package hierarchy,\n\ may be a directory specifying the root of a package hierarchy,\n\
a JAR file, or a class name. The class name must be specified\n\ a JAR file, a class file, or a class name. The class name must be\n\
using a fully qualified class name using the $ separator character\n\ specified using a fully qualified class name using the $ separator\n\
for nested classes, for example,\n\ character for nested classes, for example,\n\
\n\ \n\
\ java.lang.Thread$State\n\ \ java.lang.Thread$State\n\
\n\ \n\
@ -73,24 +73,26 @@ Unsupported options:\n\
\ Prints a CSV file containing the loaded deprecation information\n\ \ Prints a CSV file containing the loaded deprecation information\n\
\ instead of scanning any classes or JAR files. \ instead of scanning any classes or JAR files.
error.prefix=Error:
scan.process.class=Processing class {0}... scan.process.class=Processing class {0}...
scan.dep.normal=deprecated scan.dep.normal=
scan.dep.removal=deprecated FOR REMOVAL scan.dep.removal=(forRemoval=true)
scan.out.extends={0} {1} extends class {2} {3} scan.err.exception=error: unexpected exception {0}
scan.out.implements={0} {1} implements interface {2} {3} scan.err.noclass=error: cannot find class {0}
scan.out.usestype={0} {1} uses type {2} {3} scan.err.nofile=error: cannot find file {0}
scan.out.usesmethodintype={0} {1} uses method in type {2} {3} scan.err.nomethod=error: cannot resolve Methodref {0}.{1}:{2}
scan.out.usesmethod={0} {1} uses method {2} {3} {4} {5}
scan.out.usesintfmethodintype={0} {1} uses interface method in type {2} {3} scan.head.jar=Jar file {0}:
scan.out.usesintfmethod={0} {1} uses interface method {2} {3} {4} {5} scan.head.dir=Directory {0}:
scan.out.usesfieldintype={0} {1} uses field in type {2} {3}
scan.out.usesfield={0} {1} uses field {2} {3} {4} scan.out.extends={0} {1} extends deprecated class {2} {3}
scan.out.usesfieldoftype={0} {1} uses field of type {2} {3} {4} {5} scan.out.implements={0} {1} implements deprecated interface {2} {3}
scan.out.hasfield={0} {1} has field {2} of type {3} {4} scan.out.usesclass={0} {1} uses deprecated class {2} {3}
scan.out.methodparmtype={0} {1} method {2} has parameter type {3} {4} scan.out.usesmethod={0} {1} uses deprecated method {2}::{3}{4} {5}
scan.out.methodrettype={0} {1} method {2} has return type {3} {4} scan.out.usesintfmethod={0} {1} uses deprecated method {2}::{3}{4} {5}
scan.out.methodoverride={0} {1} overrides method {2} {3} {4} {5} scan.out.usesfield={0} {1} uses deprecated field {2}::{3} {4}
scan.out.hasfield={0} {1} has field named {2} of deprecated type {3} {4}
scan.out.methodparmtype={0} {1} has method named {2} having deprecated parameter type {3} {4}
scan.out.methodrettype={0} {1} has method named {2} having deprecated return type {3} {4}
scan.out.methodoverride={0} {1} overrides deprecated method {2}::{3}{4} {5}

View File

@ -28,6 +28,7 @@ package com.sun.tools.jdeprscan.scan;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream; import java.io.PrintStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayDeque; import java.util.ArrayDeque;
@ -62,7 +63,7 @@ public class Scan {
final boolean verbose; final boolean verbose;
final ClassFinder finder; final ClassFinder finder;
boolean error = false; boolean errorOccurred = false;
public Scan(PrintStream out, public Scan(PrintStream out,
PrintStream err, PrintStream err,
@ -124,73 +125,74 @@ public class Scan {
} }
} }
void printType(String key, ClassFile cf, String cname, boolean forRemoval) String dep(boolean forRemoval) {
return Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal");
}
void printType(String key, ClassFile cf, String cname, boolean r)
throws ConstantPoolException { throws ConstantPoolException {
String dep = Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal"); out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, dep(r)));
out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, dep));
} }
void printMethod(String key, ClassFile cf, String cname, String mname, String rtype, void printMethod(String key, ClassFile cf, String cname, String mname, String rtype,
boolean forRemoval) throws ConstantPoolException { boolean r) throws ConstantPoolException {
String dep = Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal"); out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, mname, rtype, dep(r)));
out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, mname, rtype, dep));
} }
void printField(String key, ClassFile cf, String cname, String fname, void printField(String key, ClassFile cf, String cname, String fname,
boolean forRemoval) throws ConstantPoolException { boolean r) throws ConstantPoolException {
String dep = Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal"); out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, fname, dep(r)));
out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, fname, dep));
} }
void printFieldType(String key, ClassFile cf, String cname, String fname, String type, void printFieldType(String key, ClassFile cf, String cname, String fname, String type,
boolean forRemoval) throws ConstantPoolException { boolean r) throws ConstantPoolException {
String dep = Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal"); out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, fname, type, dep(r)));
out.println(Messages.get(key, typeKind(cf), cf.getName(), cname, fname, type, dep));
} }
void printHasField(ClassFile cf, String fname, String type, boolean forRemoval) void printHasField(ClassFile cf, String fname, String type, boolean r)
throws ConstantPoolException { throws ConstantPoolException {
String dep = Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal"); out.println(Messages.get("scan.out.hasfield", typeKind(cf), cf.getName(), fname, type, dep(r)));
out.println(Messages.get("scan.out.hasfield", typeKind(cf), cf.getName(), fname, type, dep));
} }
void printHasMethodParmType(ClassFile cf, String mname, String parmType, boolean forRemoval) void printHasMethodParmType(ClassFile cf, String mname, String parmType, boolean r)
throws ConstantPoolException { throws ConstantPoolException {
String dep = Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal"); out.println(Messages.get("scan.out.methodparmtype", typeKind(cf), cf.getName(), mname, parmType, dep(r)));
out.println(Messages.get("scan.out.methodparmtype", typeKind(cf), cf.getName(), mname, parmType, dep));
} }
void printHasMethodRetType(ClassFile cf, String mname, String retType, boolean forRemoval) void printHasMethodRetType(ClassFile cf, String mname, String retType, boolean r)
throws ConstantPoolException { throws ConstantPoolException {
String dep = Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal"); out.println(Messages.get("scan.out.methodrettype", typeKind(cf), cf.getName(), mname, retType, dep(r)));
out.println(Messages.get("scan.out.methodrettype", typeKind(cf), cf.getName(), mname, retType, dep));
} }
void printHasOverriddenMethod(ClassFile cf, String overridden, String mname, String desc, boolean forRemoval) void printHasOverriddenMethod(ClassFile cf, String overridden, String mname, String desc, boolean r)
throws ConstantPoolException { throws ConstantPoolException {
String dep = Messages.get(forRemoval ? "scan.dep.removal" : "scan.dep.normal");
out.println(Messages.get("scan.out.methodoverride", typeKind(cf), cf.getName(), overridden, out.println(Messages.get("scan.out.methodoverride", typeKind(cf), cf.getName(), overridden,
mname, desc, dep)); mname, desc, dep(r)));
} }
// format should not have a newline void errorException(Exception ex) {
void err(String format, Object... args) { errorOccurred = true;
error = true; err.println(Messages.get("scan.err.exception", ex.toString()));
err.print("error: ");
err.printf(format, args);
err.println();
}
void printException(Exception ex) {
err.print(Messages.get("error.prefix"));
err.print(" ");
if (verbose) { if (verbose) {
ex.printStackTrace(err); ex.printStackTrace(err);
} else {
err.print(ex);
} }
} }
void errorNoClass(String className) {
errorOccurred = true;
err.println(Messages.get("scan.err.noclass", className));
}
void errorNoFile(String fileName) {
errorOccurred = true;
err.println(Messages.get("scan.err.nofile", fileName));
}
void errorNoMethod(String className, String methodName, String desc) {
errorOccurred = true;
err.println(Messages.get("scan.err.nomethod", className, methodName, desc));
}
/** /**
* Checks whether a member (method or field) is present in a class. * Checks whether a member (method or field) is present in a class.
* The checkMethod parameter determines whether this checks for a method * The checkMethod parameter determines whether this checks for a method
@ -271,7 +273,7 @@ public class Scan {
} else { } else {
startClass = finder.find(startClassName); startClass = finder.find(startClassName);
if (startClass == null) { if (startClass == null) {
err("can't find class %s", startClassName); errorNoClass(startClassName);
return startClassName; return startClassName;
} }
} }
@ -295,7 +297,7 @@ public class Scan {
String superName = curClass.getSuperclassName(); String superName = curClass.getSuperclassName();
curClass = finder.find(superName); curClass = finder.find(superName);
if (curClass == null) { if (curClass == null) {
err("can't find class %s", superName); errorNoClass(superName);
break; break;
} }
addInterfaces(intfs, curClass); addInterfaces(intfs, curClass);
@ -310,7 +312,7 @@ public class Scan {
String intf = intfs.removeFirst(); String intf = intfs.removeFirst();
curClass = finder.find(intf); curClass = finder.find(intf);
if (curClass == null) { if (curClass == null) {
err("can't find interface %s", intf); errorNoClass(intf);
break; break;
} }
@ -324,8 +326,7 @@ public class Scan {
if (curClass == null) { if (curClass == null) {
if (checkStartClass) { if (checkStartClass) {
err("can't resolve methodref %s %s %s", errorNoMethod(startClassName, findName, findDesc);
startClassName, findName, findDesc);
return startClassName; return startClassName;
} else { } else {
// TODO: refactor this // TODO: refactor this
@ -372,18 +373,18 @@ public class Scan {
} }
/** /**
* Checks types referred to from the constant pool. * Checks Class_info entries in the constant pool.
* *
* @param cf the ClassFile of this class * @param cf the ClassFile of this class
* @param entries constant pool entries collected from this class * @param entries constant pool entries collected from this class
* @throws ConstantPoolException if a constant pool entry cannot be found * @throws ConstantPoolException if a constant pool entry cannot be found
*/ */
void checkTypes(ClassFile cf, CPEntries entries) throws ConstantPoolException { void checkClasses(ClassFile cf, CPEntries entries) throws ConstantPoolException {
for (ConstantPool.CONSTANT_Class_info ci : entries.classes) { for (ConstantPool.CONSTANT_Class_info ci : entries.classes) {
String typeName = ci.getName(); String className = ci.getName();
DeprData dd = db.getTypeDeprecated(flatten(typeName)); DeprData dd = db.getTypeDeprecated(flatten(className));
if (dd != null) { if (dd != null) {
printType("scan.out.usestype", cf, typeName, dd.isForRemoval()); printType("scan.out.usesclass", cf, className, dd.isForRemoval());
} }
} }
} }
@ -394,26 +395,19 @@ public class Scan {
* @param cf the ClassFile of this class * @param cf the ClassFile of this class
* @param nti the NameAndType_info from a MethodRef or InterfaceMethodRef entry * @param nti the NameAndType_info from a MethodRef or InterfaceMethodRef entry
* @param clname the class name * @param clname the class name
* @param typeKey key for the type message * @param msgKey message key for localization
* @param methKey key for the method message
* @throws ConstantPoolException if a constant pool entry cannot be found * @throws ConstantPoolException if a constant pool entry cannot be found
*/ */
void checkMethodRef(ClassFile cf, void checkMethodRef(ClassFile cf,
CONSTANT_NameAndType_info nti,
String clname, String clname,
String typeKey, CONSTANT_NameAndType_info nti,
String methKey) throws ConstantPoolException { String msgKey) throws ConstantPoolException {
DeprData dd = db.getTypeDeprecated(flatten(clname));
if (dd != null) {
printType(typeKey, cf, clname, dd.isForRemoval());
}
String name = nti.getName(); String name = nti.getName();
String type = nti.getType(); String type = nti.getType();
clname = resolveMember(cf, flatten(clname), name, type, true, true); clname = resolveMember(cf, flatten(clname), name, type, true, true);
dd = db.getMethodDeprecated(clname, name, type); DeprData dd = db.getMethodDeprecated(clname, name, type);
if (dd != null) { if (dd != null) {
printMethod(methKey, cf, clname, name, type, dd.isForRemoval()); printMethod(msgKey, cf, clname, name, type, dd.isForRemoval());
} }
} }
@ -425,26 +419,16 @@ public class Scan {
*/ */
void checkFieldRef(ClassFile cf, void checkFieldRef(ClassFile cf,
ConstantPool.CONSTANT_Fieldref_info fri) throws ConstantPoolException { ConstantPool.CONSTANT_Fieldref_info fri) throws ConstantPoolException {
CONSTANT_NameAndType_info nti = fri.getNameAndTypeInfo();
String clname = fri.getClassName(); String clname = fri.getClassName();
CONSTANT_NameAndType_info nti = fri.getNameAndTypeInfo();
String name = nti.getName(); String name = nti.getName();
String type = nti.getType(); String type = nti.getType();
DeprData dd = db.getTypeDeprecated(clname);
if (dd != null) {
printType("scan.out.usesfieldintype", cf, clname, dd.isForRemoval());
}
clname = resolveMember(cf, flatten(clname), name, type, false, true); clname = resolveMember(cf, flatten(clname), name, type, false, true);
dd = db.getFieldDeprecated(clname, name); DeprData dd = db.getFieldDeprecated(clname, name);
if (dd != null) { if (dd != null) {
printField("scan.out.usesfield", cf, clname, name, dd.isForRemoval()); printField("scan.out.usesfield", cf, clname, name, dd.isForRemoval());
} }
dd = db.getTypeDeprecated(flatten(type));
if (dd != null) {
printFieldType("scan.out.usesfieldoftype", cf, clname, name, type, dd.isForRemoval());
}
} }
/** /**
@ -515,18 +499,18 @@ public class Scan {
checkSuper(cf); checkSuper(cf);
checkInterfaces(cf); checkInterfaces(cf);
checkTypes(cf, entries); checkClasses(cf, entries);
for (ConstantPool.CONSTANT_Methodref_info mri : entries.methodRefs) { for (ConstantPool.CONSTANT_Methodref_info mri : entries.methodRefs) {
CONSTANT_NameAndType_info nti = mri.getNameAndTypeInfo();
String clname = mri.getClassName(); String clname = mri.getClassName();
checkMethodRef(cf, nti, clname, "scan.out.usesmethodintype", "scan.out.usesmethod"); CONSTANT_NameAndType_info nti = mri.getNameAndTypeInfo();
checkMethodRef(cf, clname, nti, "scan.out.usesmethod");
} }
for (ConstantPool.CONSTANT_InterfaceMethodref_info imri : entries.intfMethodRefs) { for (ConstantPool.CONSTANT_InterfaceMethodref_info imri : entries.intfMethodRefs) {
CONSTANT_NameAndType_info nti = imri.getNameAndTypeInfo();
String clname = imri.getClassName(); String clname = imri.getClassName();
checkMethodRef(cf, nti, clname, "scan.out.usesintfmethodintype", "scan.out.usesintfmethod"); CONSTANT_NameAndType_info nti = imri.getNameAndTypeInfo();
checkMethodRef(cf, clname, nti, "scan.out.usesintfmethod");
} }
for (ConstantPool.CONSTANT_Fieldref_info fri : entries.fieldRefs) { for (ConstantPool.CONSTANT_Fieldref_info fri : entries.fieldRefs) {
@ -545,6 +529,7 @@ public class Scan {
*/ */
public boolean scanJar(String jarname) { public boolean scanJar(String jarname) {
try (JarFile jf = new JarFile(jarname)) { try (JarFile jf = new JarFile(jarname)) {
out.println(Messages.get("scan.head.jar", jarname));
finder.addJar(jarname); finder.addJar(jarname);
Enumeration<JarEntry> entries = jf.entries(); Enumeration<JarEntry> entries = jf.entries();
while (entries.hasMoreElements()) { while (entries.hasMoreElements()) {
@ -557,10 +542,12 @@ public class Scan {
} }
} }
return true; return true;
} catch (NoSuchFileException nsfe) {
errorNoFile(jarname);
} catch (IOException | ConstantPoolException ex) { } catch (IOException | ConstantPoolException ex) {
printException(ex); errorException(ex);
return false;
} }
return false;
} }
/** /**
@ -580,12 +567,15 @@ public class Scan {
.filter(path -> !path.toString().endsWith("package-info.class")) .filter(path -> !path.toString().endsWith("package-info.class"))
.filter(path -> !path.toString().endsWith("module-info.class")) .filter(path -> !path.toString().endsWith("module-info.class"))
.collect(Collectors.toList()); .collect(Collectors.toList());
out.println(Messages.get("scan.head.dir", dirname));
for (Path p : classes) { for (Path p : classes) {
processClass(ClassFile.read(p)); processClass(ClassFile.read(p));
} }
return true; return true;
} catch (IOException | ConstantPoolException ex) { } catch (IOException | ConstantPoolException ex) {
printException(ex); errorException(ex);
return false; return false;
} }
} }
@ -600,15 +590,35 @@ public class Scan {
try { try {
ClassFile cf = finder.find(className); ClassFile cf = finder.find(className);
if (cf == null) { if (cf == null) {
err("can't find class %s", className); errorNoClass(className);
return false; return false;
} else { } else {
processClass(cf); processClass(cf);
return true; return true;
} }
} catch (ConstantPoolException ex) { } catch (ConstantPoolException ex) {
printException(ex); errorException(ex);
return false; return false;
} }
} }
/**
* Scans the named class file for uses of deprecated APIs.
*
* @param fileName the class file to scan
* @return true on success, false on failure
*/
public boolean processClassFile(String fileName) {
Path path = Paths.get(fileName);
try {
ClassFile cf = ClassFile.read(path);
processClass(cf);
return true;
} catch (NoSuchFileException nsfe) {
errorNoFile(fileName);
} catch (IOException | ConstantPoolException ex) {
errorException(ex);
}
return false;
}
} }

View File

@ -89,6 +89,7 @@ public class TestScan {
new InputStreamReader( new InputStreamReader(
new ByteArrayInputStream(bytes), StandardCharsets.UTF_8)) new ByteArrayInputStream(bytes), StandardCharsets.UTF_8))
.lines() .lines()
.filter(line -> !line.endsWith(":"))
.map(line -> line.split(" +")) .map(line -> line.split(" +"))
.map(array -> array[1]) .map(array -> array[1])
.collect(Collectors.toSet()); .collect(Collectors.toSet());