8169676: boolean result of Option.process is often ignored
authorjjg
Wed, 16 Nov 2016 11:53:24 -0800
changeset 42260 2b4da2bf6660
parent 42259 e4d04fcd0826
child 42261 bb52b5514ad5
8169676: boolean result of Option.process is often ignored Reviewed-by: ksrini, jlahoda
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/BaseFileManager.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Main.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/OptionHelper.java
langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/main/Start.java
langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/main/ToolOption.java
langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/resources/javadoc.properties
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Start.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ToolOption.java
langtools/test/jdk/javadoc/tool/BadOptionsTest.java
langtools/test/tools/javadoc/BadOptionsTest.java
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/BaseFileManager.java	Wed Nov 16 10:45:23 2016 -0800
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/BaseFileManager.java	Wed Nov 16 11:53:24 2016 -0800
@@ -249,8 +249,11 @@
             return false;
         }
 
-        if (!o.handleOption(helper, current, remaining))
-            throw new IllegalArgumentException(current);
+        try {
+            o.handleOption(helper, current, remaining);
+        } catch (Option.InvalidValueException e) {
+            throw new IllegalArgumentException(e.getMessage(), e);
+        }
 
         return true;
     }
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java	Wed Nov 16 10:45:23 2016 -0800
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java	Wed Nov 16 11:53:24 2016 -0800
@@ -161,16 +161,6 @@
         }
 
         @Override
-        public void error(String key, Object... args) {
-            Arguments.this.error(key, args);
-        }
-
-        @Override
-        public void error(JCDiagnostic.Error error) {
-            Arguments.this.error(error);
-        }
-
-        @Override
         public void addFile(Path p) {
             files.add(p);
         }
@@ -222,11 +212,6 @@
         }
 
         @Override
-        public void error(String key, Object... args) {
-            Arguments.this.error(key, args);
-        }
-
-        @Override
         public Log getLog() {
             return Arguments.this.log;
         }
@@ -263,6 +248,17 @@
     }
 
     /**
+     * Minimal initialization for tools, like javadoc,
+     * to be able to process javac options for themselves,
+     * and then call validate.
+     * @param ownName  the name of this tool; used to prefix messages
+     */
+    public void init(String ownName) {
+        this.ownName = ownName;
+        errorMode = ErrorMode.LOG;
+    }
+
+    /**
      * Gets the files to be compiled.
      * @return the files to be compiled
      */
@@ -379,7 +375,10 @@
             }
 
             if (option != null) {
-                if (!option.handleOption(helper, arg, argIter)) {
+                try {
+                    option.handleOption(helper, arg, argIter);
+                } catch (Option.InvalidValueException e) {
+                    error(e);
                     return false;
                 }
                 continue;
@@ -446,24 +445,23 @@
             // It is allowed to compile nothing if just asking for help or version info.
             // But also note that none of these options are supported in API mode.
             if (options.isSet(Option.HELP)
-                || options.isSet(Option.X)
-                || options.isSet(Option.VERSION)
-                || options.isSet(Option.FULLVERSION)
-                || options.isSet(Option.MODULE))
-                return true;
-
-            if (emptyAllowed)
+                    || options.isSet(Option.X)
+                    || options.isSet(Option.VERSION)
+                    || options.isSet(Option.FULLVERSION)
+                    || options.isSet(Option.MODULE)) {
                 return true;
-
-            if (!errors) {
-                if (JavaCompiler.explicitAnnotationProcessingRequested(options)) {
-                    error("err.no.source.files.classes");
-                } else {
-                    error("err.no.source.files");
-                }
             }
 
-            return false;
+            if (!emptyAllowed) {
+                if (!errors) {
+                    if (JavaCompiler.explicitAnnotationProcessingRequested(options)) {
+                        error("err.no.source.files.classes");
+                    } else {
+                        error("err.no.source.files");
+                    }
+                }
+                return false;
+            }
         }
 
         if (!checkDirectory(Option.D)) {
@@ -555,7 +553,6 @@
         }
 
         boolean lintOptions = options.isUnset(Option.XLINT_CUSTOM, "-" + LintCategory.OPTIONS.option);
-
         if (lintOptions && source.compareTo(Source.DEFAULT) < 0 && !options.isSet(Option.RELEASE)) {
             if (fm instanceof BaseFileManager) {
                 if (((BaseFileManager) fm).isDefaultBootClassPath())
@@ -757,7 +754,7 @@
     public boolean isEmpty() {
         return ((files == null) || files.isEmpty())
                 && ((fileObjects == null) || fileObjects.isEmpty())
-                && classNames.isEmpty();
+                && (classNames == null || classNames.isEmpty());
     }
 
     public void allowEmpty() {
@@ -886,6 +883,21 @@
         }
     }
 
+    void error(Option.InvalidValueException f) {
+        String msg = f.getMessage();
+        errors = true;
+        switch (errorMode) {
+            case ILLEGAL_ARGUMENT: {
+                throw new PropagatedException(new IllegalArgumentException(msg, f.getCause()));
+            }
+            case ILLEGAL_STATE: {
+                throw new PropagatedException(new IllegalStateException(msg, f.getCause()));
+            }
+            case LOG:
+                log.printRawLines(ownName + ": " + msg);
+        }
+    }
+
     void warning(String key, Object... args) {
         report(key, args);
     }
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Main.java	Wed Nov 16 10:45:23 2016 -0800
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Main.java	Wed Nov 16 11:53:24 2016 -0800
@@ -194,7 +194,10 @@
                 @Override
                 public void put(String name, String value) { }
             };
-            Option.HELP.process(h, "-help");
+            try {
+                Option.HELP.process(h, "-help");
+            } catch (Option.InvalidValueException ignore) {
+            }
             return Result.CMDERR;
         }
 
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java	Wed Nov 16 10:45:23 2016 -0800
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java	Wed Nov 16 11:53:24 2016 -0800
@@ -93,9 +93,8 @@
 
     G_NONE("-g:none", "opt.g.none", STANDARD, BASIC) {
         @Override
-        public boolean process(OptionHelper helper, String option) {
+        public void process(OptionHelper helper, String option) {
             helper.put("-g:", "none");
-            return false;
         }
     },
 
@@ -138,11 +137,10 @@
         }
 
         @Override
-        public boolean process(OptionHelper helper, String option) {
+        public void process(OptionHelper helper, String option) {
             String prev = helper.get(XDOCLINT_CUSTOM);
             String next = (prev == null) ? option : (prev + " " + option);
             helper.put(XDOCLINT_CUSTOM.primaryName, next);
-            return false;
         }
     },
 
@@ -154,20 +152,18 @@
         }
 
         @Override
-        public boolean process(OptionHelper helper, String option) {
+        public void process(OptionHelper helper, String option) {
             String prev = helper.get(XDOCLINT_PACKAGE);
             String next = (prev == null) ? option : (prev + " " + option);
             helper.put(XDOCLINT_PACKAGE.primaryName, next);
-            return false;
         }
     },
 
     // -nowarn is retained for command-line backward compatibility
     NOWARN("-nowarn", "opt.nowarn", STANDARD, BASIC) {
         @Override
-        public boolean process(OptionHelper helper, String option) {
+        public void process(OptionHelper helper, String option) {
             helper.put("-Xlint:none", option);
-            return false;
         }
     },
 
@@ -176,9 +172,8 @@
     // -deprecation is retained for command-line backward compatibility
     DEPRECATION("-deprecation", "opt.deprecation", STANDARD, BASIC) {
         @Override
-        public boolean process(OptionHelper helper, String option) {
+        public void process(OptionHelper helper, String option) {
             helper.put("-Xlint:deprecation", option);
-            return false;
         }
     },
 
@@ -201,15 +196,13 @@
         // the the module=path pairs by an invalid path character, NULL.
         // The standard file manager code knows to split apart the NULL-separated components.
         @Override
-        public boolean process(OptionHelper helper, String option, String arg) {
+        public void process(OptionHelper helper, String option, String arg) throws InvalidValueException {
             if (arg.isEmpty()) {
-                helper.error("err.no.value.for.option", option);
-                return true;
+                throw helper.newInvalidValueException("err.no.value.for.option", option);
             } else if (getPattern().matcher(arg).matches()) {
                 String prev = helper.get(PATCH_MODULE);
                 if (prev == null) {
                     super.process(helper, option, arg);
-                    return false;
                 } else {
                     String argModulePackage = arg.substring(0, arg.indexOf('='));
                     boolean isRepeated = Arrays.stream(prev.split("\0"))
@@ -217,16 +210,13 @@
                             .collect(Collectors.toSet())
                             .contains(argModulePackage);
                     if (isRepeated) {
-                        helper.error("err.repeated.value.for.patch.module", argModulePackage);
-                        return true;
+                        throw helper.newInvalidValueException("err.repeated.value.for.patch.module", argModulePackage);
                     } else {
                         super.process(helper, option, prev + '\0' + arg);
-                        return false;
                     }
                 }
             } else {
-                helper.error("err.bad.value.for.option", option, arg);
-                return true;
+                throw helper.newInvalidValueException("err.bad.value.for.option", option, arg);
             }
         }
 
@@ -238,10 +228,10 @@
 
     BOOT_CLASS_PATH("--boot-class-path -bootclasspath", "opt.arg.path", "opt.bootclasspath", STANDARD, FILEMANAGER) {
         @Override
-        public boolean process(OptionHelper helper, String option, String arg) {
+        public void process(OptionHelper helper, String option, String arg) throws InvalidValueException {
             helper.remove("-Xbootclasspath/p:");
             helper.remove("-Xbootclasspath/a:");
-            return super.process(helper, option, arg);
+            super.process(helper, option, arg);
         }
     },
 
@@ -251,10 +241,10 @@
 
     XBOOTCLASSPATH("-Xbootclasspath:", "opt.arg.path", "opt.bootclasspath", EXTENDED, FILEMANAGER) {
         @Override
-        public boolean process(OptionHelper helper, String option, String arg) {
+        public void process(OptionHelper helper, String option, String arg) throws InvalidValueException {
             helper.remove("-Xbootclasspath/p:");
             helper.remove("-Xbootclasspath/a:");
-            return super.process(helper, "-bootclasspath", arg);
+            super.process(helper, "-bootclasspath", arg);
         }
     },
 
@@ -262,8 +252,8 @@
 
     DJAVA_EXT_DIRS("-Djava.ext.dirs=", "opt.arg.dirs", "opt.extdirs", EXTENDED, FILEMANAGER) {
         @Override
-        public boolean process(OptionHelper helper, String option, String arg) {
-            return EXTDIRS.process(helper, "-extdirs", arg);
+        public void process(OptionHelper helper, String option, String arg) throws InvalidValueException {
+            EXTDIRS.process(helper, "-extdirs", arg);
         }
     },
 
@@ -271,8 +261,8 @@
 
     DJAVA_ENDORSED_DIRS("-Djava.endorsed.dirs=", "opt.arg.dirs", "opt.endorseddirs", EXTENDED, FILEMANAGER) {
         @Override
-        public boolean process(OptionHelper helper, String option, String arg) {
-            return ENDORSEDDIRS.process(helper, "-endorseddirs", arg);
+        public void process(OptionHelper helper, String option, String arg) throws InvalidValueException {
+            ENDORSEDDIRS.process(helper, "-endorseddirs", arg);
         }
     },
 
@@ -298,25 +288,23 @@
 
     SOURCE("-source", "opt.arg.release", "opt.source", STANDARD, BASIC) {
         @Override
-        public boolean process(OptionHelper helper, String option, String operand) {
+        public void process(OptionHelper helper, String option, String operand) throws InvalidValueException {
             Source source = Source.lookup(operand);
             if (source == null) {
-                helper.error("err.invalid.source", operand);
-                return true;
+                throw helper.newInvalidValueException("err.invalid.source", operand);
             }
-            return super.process(helper, option, operand);
+            super.process(helper, option, operand);
         }
     },
 
     TARGET("-target", "opt.arg.release", "opt.target", STANDARD, BASIC) {
         @Override
-        public boolean process(OptionHelper helper, String option, String operand) {
+        public void process(OptionHelper helper, String option, String operand) throws InvalidValueException {
             Target target = Target.lookup(operand);
             if (target == null) {
-                helper.error("err.invalid.target", operand);
-                return true;
+                throw helper.newInvalidValueException("err.invalid.target", operand);
             }
-            return super.process(helper, option, operand);
+            super.process(helper, option, operand);
         }
     },
 
@@ -345,46 +333,45 @@
 
     PROFILE("-profile", "opt.arg.profile", "opt.profile", STANDARD, BASIC) {
         @Override
-        public boolean process(OptionHelper helper, String option, String operand) {
+        public void process(OptionHelper helper, String option, String operand) throws InvalidValueException {
             Profile profile = Profile.lookup(operand);
             if (profile == null) {
-                helper.error("err.invalid.profile", operand);
-                return true;
+                throw helper.newInvalidValueException("err.invalid.profile", operand);
             }
-            return super.process(helper, option, operand);
+            super.process(helper, option, operand);
         }
     },
 
     VERSION("-version", "opt.version", STANDARD, INFO) {
         @Override
-        public boolean process(OptionHelper helper, String option) {
+        public void process(OptionHelper helper, String option) throws InvalidValueException {
             Log log = helper.getLog();
             String ownName = helper.getOwnName();
             log.printLines(WriterKind.STDOUT, PrefixKind.JAVAC, "version", ownName,  JavaCompiler.version());
-            return super.process(helper, option);
+            super.process(helper, option);
         }
     },
 
     FULLVERSION("-fullversion", null, HIDDEN, INFO) {
         @Override
-        public boolean process(OptionHelper helper, String option) {
+        public void process(OptionHelper helper, String option) throws InvalidValueException {
             Log log = helper.getLog();
             String ownName = helper.getOwnName();
             log.printLines(WriterKind.STDOUT, PrefixKind.JAVAC, "fullVersion", ownName,  JavaCompiler.fullVersion());
-            return super.process(helper, option);
+            super.process(helper, option);
         }
     },
 
     // Note: -h is already taken for "native header output directory".
     HELP("--help -help", "opt.help", STANDARD, INFO) {
         @Override
-        public boolean process(OptionHelper helper, String option) {
+        public void process(OptionHelper helper, String option) throws InvalidValueException {
             Log log = helper.getLog();
             String ownName = helper.getOwnName();
             log.printLines(WriterKind.STDOUT, PrefixKind.JAVAC, "msg.usage.header", ownName);
             showHelp(log, OptionKind.STANDARD);
             log.printNewline(WriterKind.STDOUT);
-            return super.process(helper, option);
+            super.process(helper, option);
         }
     },
 
@@ -401,31 +388,28 @@
         // Mapping for processor options created in
         // JavacProcessingEnvironment
         @Override
-        public boolean process(OptionHelper helper, String option) {
+        public void process(OptionHelper helper, String option) throws InvalidValueException {
             int argLength = option.length();
             if (argLength == 2) {
-                helper.error("err.empty.A.argument");
-                return true;
+                throw helper.newInvalidValueException("err.empty.A.argument");
             }
             int sepIndex = option.indexOf('=');
             String key = option.substring(2, (sepIndex != -1 ? sepIndex : argLength) );
             if (!JavacProcessingEnvironment.isValidOptionName(key)) {
-                helper.error("err.invalid.A.key", option);
-                return true;
+                throw helper.newInvalidValueException("err.invalid.A.key", option);
             }
             helper.put(option, option);
-            return false;
         }
     },
 
     X("-X", "opt.X", STANDARD, INFO) {
         @Override
-        public boolean process(OptionHelper helper, String option) {
+        public void process(OptionHelper helper, String option) throws InvalidValueException {
             Log log = helper.getLog();
             showHelp(log, OptionKind.EXTENDED);
             log.printNewline(WriterKind.STDOUT);
             log.printLines(WriterKind.STDOUT, PrefixKind.JAVAC, "msg.usage.nonstandard.footer");
-            return super.process(helper, option);
+            super.process(helper, option);
         }
     },
 
@@ -433,16 +417,16 @@
     // It's actually implemented by the launcher.
     J("-J", "opt.arg.flag", "opt.J", STANDARD, INFO, ArgKind.ADJACENT) {
         @Override
-        public boolean process(OptionHelper helper, String option) {
+        public void process(OptionHelper helper, String option) {
             throw new AssertionError("the -J flag should be caught by the launcher.");
         }
     },
 
     MOREINFO("-moreinfo", null, HIDDEN, BASIC) {
         @Override
-        public boolean process(OptionHelper helper, String option) {
+        public void process(OptionHelper helper, String option) throws InvalidValueException {
             Type.moreInfo = true;
-            return super.process(helper, option);
+            super.process(helper, option);
         }
     },
 
@@ -462,9 +446,8 @@
     // display warnings for generic unchecked operations
     WARNUNCHECKED("-warnunchecked", null, HIDDEN, BASIC) {
         @Override
-        public boolean process(OptionHelper helper, String option) {
+        public void process(OptionHelper helper, String option) {
             helper.put("-Xlint:unchecked", option);
-            return false;
         }
     },
 
@@ -474,15 +457,14 @@
 
     XSTDOUT("-Xstdout", "opt.arg.file", "opt.Xstdout", EXTENDED, INFO) {
         @Override
-        public boolean process(OptionHelper helper, String option, String arg) {
+        public void process(OptionHelper helper, String option, String arg) throws InvalidValueException {
             try {
                 Log log = helper.getLog();
                 log.setWriters(new PrintWriter(new FileWriter(arg), true));
             } catch (java.io.IOException e) {
-                helper.error("err.error.writing.file", arg, e);
-                return true;
+                throw helper.newInvalidValueException("err.error.writing.file", arg, e);
             }
-            return super.process(helper, option, arg);
+            super.process(helper, option, arg);
         }
     },
 
@@ -507,11 +489,10 @@
 
     PLUGIN("-Xplugin:", "opt.arg.plugin", "opt.plugin", EXTENDED, BASIC) {
         @Override
-        public boolean process(OptionHelper helper, String option) {
+        public void process(OptionHelper helper, String option) {
             String p = option.substring(option.indexOf(':') + 1).trim();
             String prev = helper.get(PLUGIN);
             helper.put(PLUGIN.primaryName, (prev == null) ? p : prev + '\0' + p);
-            return false;
         }
     },
 
@@ -519,22 +500,22 @@
 
     DEBUG("--debug:", null, HIDDEN, BASIC) {
         @Override
-        public boolean process(OptionHelper helper, String option) {
-            return HiddenGroup.DEBUG.process(helper, option);
+        public void process(OptionHelper helper, String option) throws InvalidValueException {
+            HiddenGroup.DEBUG.process(helper, option);
         }
     },
 
     SHOULDSTOP("--should-stop:", null, HIDDEN, BASIC) {
         @Override
-        public boolean process(OptionHelper helper, String option) {
-            return HiddenGroup.SHOULDSTOP.process(helper, option);
+        public void process(OptionHelper helper, String option) throws InvalidValueException {
+            HiddenGroup.SHOULDSTOP.process(helper, option);
         }
     },
 
     DIAGS("--diags:", null, HIDDEN, BASIC) {
         @Override
-        public boolean process(OptionHelper helper, String option) {
-            return HiddenGroup.DIAGS.process(helper, option);
+        public void process(OptionHelper helper, String option) throws InvalidValueException {
+            HiddenGroup.DIAGS.process(helper, option);
         }
     },
 
@@ -548,33 +529,29 @@
             return s.startsWith(primaryName);
         }
         @Override
-        public boolean process(OptionHelper helper, String option) {
-            return process(helper, option, option.substring(primaryName.length()));
+        public void process(OptionHelper helper, String option) {
+            process(helper, option, option.substring(primaryName.length()));
         }
 
         @Override
-        public boolean process(OptionHelper helper, String option, String arg) {
+        public void process(OptionHelper helper, String option, String arg) {
             int eq = arg.indexOf('=');
             String key = (eq < 0) ? arg : arg.substring(0, eq);
             String value = (eq < 0) ? arg : arg.substring(eq+1);
             helper.put(key, value);
-            return false;
         }
     },
 
     ADD_EXPORTS("--add-exports", "opt.arg.addExports", "opt.addExports", EXTENDED, BASIC) {
         @Override
-        public boolean process(OptionHelper helper, String option, String arg) {
+        public void process(OptionHelper helper, String option, String arg) throws InvalidValueException {
             if (arg.isEmpty()) {
-                helper.error("err.no.value.for.option", option);
-                return true;
+                throw helper.newInvalidValueException("err.no.value.for.option", option);
             } else if (getPattern().matcher(arg).matches()) {
                 String prev = helper.get(ADD_EXPORTS);
                 helper.put(ADD_EXPORTS.primaryName, (prev == null) ? arg : prev + '\0' + arg);
-                return false;
             } else {
-                helper.error("err.bad.value.for.option", option, arg);
-                return true;
+                throw helper.newInvalidValueException("err.bad.value.for.option", option, arg);
             }
         }
 
@@ -586,17 +563,14 @@
 
     ADD_READS("--add-reads", "opt.arg.addReads", "opt.addReads", EXTENDED, BASIC) {
         @Override
-        public boolean process(OptionHelper helper, String option, String arg) {
+        public void process(OptionHelper helper, String option, String arg) throws InvalidValueException {
             if (arg.isEmpty()) {
-                helper.error("err.no.value.for.option", option);
-                return true;
+                throw helper.newInvalidValueException("err.no.value.for.option", option);
             } else if (getPattern().matcher(arg).matches()) {
                 String prev = helper.get(ADD_READS);
                 helper.put(ADD_READS.primaryName, (prev == null) ? arg : prev + '\0' + arg);
-                return false;
             } else {
-                helper.error("err.bad.value.for.option", option, arg);
-                return true;
+                throw helper.newInvalidValueException("err.bad.value.for.option", option, arg);
             }
         }
 
@@ -608,14 +582,12 @@
 
     XMODULE("-Xmodule:", "opt.arg.module", "opt.module", EXTENDED, BASIC) {
         @Override
-        public boolean process(OptionHelper helper, String option, String arg) {
+        public void process(OptionHelper helper, String option, String arg) throws InvalidValueException {
             String prev = helper.get(XMODULE);
             if (prev != null) {
-                helper.error("err.option.too.many", XMODULE.primaryName);
-                return true;
+                throw helper.newInvalidValueException("err.option.too.many", XMODULE.primaryName);
             }
             helper.put(XMODULE.primaryName, arg);
-            return false;
         }
     },
 
@@ -623,19 +595,16 @@
 
     ADD_MODULES("--add-modules", "opt.arg.addmods", "opt.addmods", STANDARD, BASIC) {
         @Override
-        public boolean process(OptionHelper helper, String option, String arg) {
+        public void process(OptionHelper helper, String option, String arg) throws InvalidValueException {
             if (arg.isEmpty()) {
-                helper.error("err.no.value.for.option", option);
-                return true;
+                throw helper.newInvalidValueException("err.no.value.for.option", option);
             } else if (getPattern().matcher(arg).matches()) {
                 String prev = helper.get(ADD_MODULES);
                 // since the individual values are simple names, we can simply join the
                 // values of multiple --add-modules options with ','
                 helper.put(ADD_MODULES.primaryName, (prev == null) ? arg : prev + ',' + arg);
-                return false;
             } else {
-                helper.error("err.bad.value.for.option", option, arg);
-                return true;
+                throw helper.newInvalidValueException("err.bad.value.for.option", option, arg);
             }
         }
 
@@ -647,16 +616,13 @@
 
     LIMIT_MODULES("--limit-modules", "opt.arg.limitmods", "opt.limitmods", STANDARD, BASIC) {
         @Override
-        public boolean process(OptionHelper helper, String option, String arg) {
+        public void process(OptionHelper helper, String option, String arg) throws InvalidValueException {
             if (arg.isEmpty()) {
-                helper.error("err.no.value.for.option", option);
-                return true;
+                throw helper.newInvalidValueException("err.no.value.for.option", option);
             } else if (getPattern().matcher(arg).matches()) {
                 helper.put(LIMIT_MODULES.primaryName, arg); // last one wins
-                return false;
             } else {
-                helper.error("err.bad.value.for.option", option, arg);
-                return true;
+                throw helper.newInvalidValueException("err.bad.value.for.option", option, arg);
             }
         }
 
@@ -670,7 +636,7 @@
     // It's actually implemented by the CommandLine class.
     AT("@", "opt.arg.file", "opt.AT", STANDARD, INFO, ArgKind.ADJACENT) {
         @Override
-        public boolean process(OptionHelper helper, String option) {
+        public void process(OptionHelper helper, String option) {
             throw new AssertionError("the @ flag should be caught by CommandLine.");
         }
     },
@@ -690,22 +656,19 @@
             }
         }
         @Override
-        public boolean process(OptionHelper helper, String option) {
+        public void process(OptionHelper helper, String option) throws InvalidValueException {
             if (option.endsWith(".java") ) {
                 Path p = Paths.get(option);
                 if (!Files.exists(p)) {
-                    helper.error("err.file.not.found", p);
-                    return true;
+                    throw helper.newInvalidValueException("err.file.not.found", p);
                 }
                 if (!Files.isRegularFile(p)) {
-                    helper.error("err.file.not.file", p);
-                    return true;
+                    throw helper.newInvalidValueException("err.file.not.file", p);
                 }
                 helper.addFile(p);
             } else {
                 helper.addClassName(option);
             }
-            return false;
         }
     },
 
@@ -714,7 +677,7 @@
     INHERIT_RUNTIME_ENVIRONMENT("--inherit-runtime-environment", "opt.inherit_runtime_environment",
             EXTENDED, BASIC) {
         @Override
-        public boolean process(OptionHelper helper, String option) {
+        public void process(OptionHelper helper, String option) throws InvalidValueException {
             try {
                 Class.forName(JDK9Wrappers.VMHelper.VM_CLASSNAME);
                 String[] runtimeArgs = JDK9Wrappers.VMHelper.getRuntimeArguments();
@@ -729,9 +692,8 @@
                     }
                 }
             } catch (ClassNotFoundException | SecurityException e) {
-                helper.error("err.cannot.access.runtime.env");
+                throw helper.newInvalidValueException("err.cannot.access.runtime.env");
             }
-            return false;
         }
 
         private Option[] getSupportedRuntimeOptions() {
@@ -748,6 +710,23 @@
     };
 
     /**
+     * This exception is thrown when an invalid value is given for an option.
+     * The detail string gives a detailed, localized message, suitable for use
+     * in error messages reported to the user.
+     */
+    public static class InvalidValueException extends Exception {
+        private static final long serialVersionUID = -1;
+
+        public InvalidValueException(String msg) {
+            super(msg);
+        }
+
+        public InvalidValueException(String msg, Throwable cause) {
+            super(msg, cause);
+        }
+    }
+
+    /**
      * The kind of argument, if any, accepted by this option. The kind is augmented
      * by characters in the name of the option.
      */
@@ -831,14 +810,13 @@
             this.text = text;
         }
 
-        public boolean process(OptionHelper helper, String option) {
+        public void process(OptionHelper helper, String option) throws InvalidValueException {
             String p = option.substring(option.indexOf(':') + 1).trim();
             String[] subOptions = p.split(";");
             for (String subOption : subOptions) {
                 subOption = text + "." + subOption.trim();
                 XD.process(helper, subOption, subOption);
             }
-            return false;
         }
 
         static boolean skip(String name) {
@@ -1038,7 +1016,7 @@
      * @return true if the operation was successful, and false otherwise
      * @implNote The return value is the opposite of that used by {@link #process}.
      */
-    public boolean handleOption(OptionHelper helper, String arg, Iterator<String> rest) {
+    public void handleOption(OptionHelper helper, String arg, Iterator<String> rest) throws InvalidValueException {
         if (hasArg()) {
             String option;
             String operand;
@@ -1051,15 +1029,14 @@
                 operand = arg.substring(sep + 1);
             } else {
                 if (!rest.hasNext()) {
-                    helper.error("err.req.arg", arg);
-                    return false;
+                    throw helper.newInvalidValueException("err.req.arg", arg);
                 }
                 option = arg;
                 operand = rest.next();
             }
-            return !process(helper, option, operand);
+            process(helper, option, operand);
         } else {
-            return !process(helper, arg);
+            process(helper, arg);
         }
     }
 
@@ -1068,14 +1045,14 @@
      * or which contains an argument within it, following a separator.
      * @param helper a helper to provide access to the environment
      * @param option the option to be processed
-     * @return true if an error occurred
+     * @throws InvalidValueException if an error occurred
      */
-    public boolean process(OptionHelper helper, String option) {
+    public void process(OptionHelper helper, String option) throws InvalidValueException {
         if (argKind == ArgKind.NONE) {
-            return process(helper, primaryName, option);
+            process(helper, primaryName, option);
         } else {
             int sep = findSeparator(option);
-            return process(helper, primaryName, option.substring(sep + 1));
+            process(helper, primaryName, option.substring(sep + 1));
         }
     }
 
@@ -1085,9 +1062,8 @@
      * @param option the option to be processed
      * @param arg the value to associate with the option, or a default value
      *  to be used if the option does not otherwise take an argument.
-     * @return true if an error occurred
      */
-    public boolean process(OptionHelper helper, String option, String arg) {
+    public void process(OptionHelper helper, String option, String arg) throws InvalidValueException {
         if (choices != null) {
             if (choiceKind == ChoiceKind.ONEOF) {
                 // some clients like to see just one of option+choice set
@@ -1110,7 +1086,6 @@
         helper.put(primaryName, arg);
         if (group == OptionGroup.FILEMANAGER)
             helper.handleFileManagerOption(this, arg);
-        return false;
     }
 
     /**
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/OptionHelper.java	Wed Nov 16 10:45:23 2016 -0800
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/OptionHelper.java	Wed Nov 16 11:53:24 2016 -0800
@@ -43,29 +43,55 @@
 
 public abstract class OptionHelper {
 
-    /** Get the current value of an option. */
+    /**
+     * Get the current value of an option.
+     * @param option the option
+     * @return the value of the option
+     */
     public abstract String get(Option option);
 
-    /** Set the value of an option. */
+    /**
+     * Set the value of an option.
+     * @param name the primary name of the option
+     * @param value the value for the option
+     */
     public abstract void put(String name, String value);
 
-    /** Remove any prior value for an option. */
+    /**
+     * Remove any prior value for an option.
+     * @param name the primary name of the option
+     */
     public abstract void remove(String name);
 
-    /** Handle a file manager option. */
+    /**
+     * Handle a file manager option.
+     * @param option the option
+     * @param value the value for the option
+     * @return true if the option was handled successfully, and false otherwise
+     */
     public abstract boolean handleFileManagerOption(Option option, String value);
 
-    /** Get access to the Log for the compilation. */
+    /**
+     * Get access to the Log for the compilation.
+     * @return the log
+     */
     public abstract Log getLog();
 
-    /** Get the name of the tool, such as "javac", to be used in info like -help. */
+    /**
+     * Get the name of the tool, such as "javac", to be used in info like -help.
+     * @return the name of the tool
+     */
     public abstract String getOwnName();
 
-    /** Report an error. */
-    abstract void error(String key, Object... args);
-
-    /** Report an error. */
-    abstract void error(JCDiagnostic.Error error);
+    /**
+     * Returns a new InvalidValueException, with a localized detail message.
+     * @param key the resource key for the message
+     * @param args the arguments, if any, for the resource string
+     * @return the InvalidValueException
+     */
+    Option.InvalidValueException newInvalidValueException(String key, Object... args) {
+        return new Option.InvalidValueException(getLog().localize(PrefixKind.JAVAC, key, args));
+    }
 
     /** Record a file to be compiled. */
     abstract void addFile(Path p);
@@ -112,16 +138,6 @@
         }
 
         @Override
-        void error(String key, Object... args) {
-            throw new IllegalArgumentException(log.localize(PrefixKind.JAVAC, key, args));
-        }
-
-        @Override
-        void error(JCDiagnostic.Error error) {
-            throw new IllegalArgumentException(log.localize(error));
-        }
-
-        @Override
         public void addFile(Path p) {
             throw new IllegalArgumentException(p.toString());
         }
--- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/main/Start.java	Wed Nov 16 10:45:23 2016 -0800
+++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/main/Start.java	Wed Nov 16 11:53:24 2016 -0800
@@ -45,6 +45,7 @@
 import com.sun.tools.javac.main.CommandLine;
 import com.sun.tools.javac.main.Option;
 import com.sun.tools.javac.file.BaseFileManager;
+import com.sun.tools.javac.main.Arguments;
 import com.sun.tools.javac.main.OptionHelper;
 import com.sun.tools.javac.main.OptionHelper.GrumpyHelper;
 import com.sun.tools.javac.platform.PlatformDescription;
@@ -309,12 +310,16 @@
                 if (o == ToolOption.LOCALE && i > 0)
                     usageError("main.locale_first");
 
-                if (o.hasArg) {
-                    oneArg(argv, i++);
-                    o.process(this, argv[i]);
-                } else {
-                    setOption(arg);
-                    o.process(this);
+                try {
+                    if (o.hasArg) {
+                        oneArg(argv, i++);
+                        o.process(this, argv[i]);
+                    } else {
+                        setOption(arg);
+                        o.process(this);
+                    }
+                } catch (Option.InvalidValueException e) {
+                    usageError("main.option.invalid.value", e.getMessage());
                 }
             } else if (arg.equals("-XDaccessInternalAPI")) {
                 // pass this hidden option down to the doclet, if it wants it
@@ -367,6 +372,11 @@
             ((BaseFileManager) fileManager).handleOptions(fileManagerOpts);
         }
 
+        Arguments arguments = Arguments.instance(context);
+        arguments.init(messager.programName);
+        arguments.allowEmpty();
+        arguments.validate();
+
         String platformString = compOpts.get("--release");
 
         if (platformString != null) {
@@ -560,7 +570,7 @@
 
     @Override
     OptionHelper getOptionHelper() {
-        return new GrumpyHelper(null) {
+        return new GrumpyHelper(messager) {
             @Override
             public String get(com.sun.tools.javac.main.Option option) {
                 return compOpts.get(option);
--- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/main/ToolOption.java	Wed Nov 16 10:45:23 2016 -0800
+++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/main/ToolOption.java	Wed Nov 16 11:53:24 2016 -0800
@@ -31,6 +31,7 @@
 
 import com.sun.tools.javac.code.Flags;
 import com.sun.tools.javac.main.Option;
+import com.sun.tools.javac.main.Option.InvalidValueException;
 import com.sun.tools.javac.main.OptionHelper;
 import com.sun.tools.javac.util.ListBuffer;
 import com.sun.tools.javac.util.Options;
@@ -141,14 +142,14 @@
 
     ADD_MODULES("--add-modules", true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws InvalidValueException {
             Option.ADD_MODULES.process(helper.getOptionHelper(), opt, arg);
         }
     },
 
     LIMIT_MODULES("--limit-modules", true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws InvalidValueException {
             Option.LIMIT_MODULES.process(helper.getOptionHelper(), opt, arg);
         }
     },
@@ -191,28 +192,28 @@
 
     ADD_READS("--add-reads", true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws InvalidValueException {
             Option.ADD_READS.process(helper.getOptionHelper(), opt, arg);
         }
     },
 
     ADD_EXPORTS("--add-exports", true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws InvalidValueException {
             Option.ADD_EXPORTS.process(helper.getOptionHelper(), opt, arg);
         }
     },
 
     XMODULE("-Xmodule:", false) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws InvalidValueException {
             Option.XMODULE.process(helper.getOptionHelper(), arg);
         }
     },
 
     PATCH_MODULE("--patch-module", true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws InvalidValueException {
             Option.PATCH_MODULE.process(helper.getOptionHelper(), opt, arg);
         }
     },
@@ -356,7 +357,7 @@
         this.hasArg = hasArg;
     }
 
-    void process(Helper helper, String arg) { }
+    void process(Helper helper, String arg) throws Option.InvalidValueException { }
 
     void process(Helper helper) { }
 
--- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/resources/javadoc.properties	Wed Nov 16 10:45:23 2016 -0800
+++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/resources/javadoc.properties	Wed Nov 16 11:53:24 2016 -0800
@@ -126,6 +126,7 @@
 main.release.bootclasspath.conflict=option {0} cannot be used together with -release
 main.unsupported.release.version=release version {0} not supported
 main.release.not.standard.file.manager=-release option specified, but the provided JavaFileManager is not a StandardJavaFileManager.
+main.option.invalid.value={0}
 tag.illegal_char_in_arr_dim=Tag {0}: Syntax Error in array dimension, method parameters: {1}
 tag.illegal_see_tag=Tag {0}: Syntax Error in method parameters: {1}
 tag.missing_comma_space=Tag {0}: Missing comma or space in method parameters: {1}
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Start.java	Wed Nov 16 10:45:23 2016 -0800
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Start.java	Wed Nov 16 11:53:24 2016 -0800
@@ -51,6 +51,7 @@
 import com.sun.tools.javac.api.JavacTrees;
 import com.sun.tools.javac.file.BaseFileManager;
 import com.sun.tools.javac.file.JavacFileManager;
+import com.sun.tools.javac.main.Arguments;
 import com.sun.tools.javac.main.CommandLine;
 import com.sun.tools.javac.main.OptionHelper;
 import com.sun.tools.javac.main.OptionHelper.GrumpyHelper;
@@ -59,6 +60,7 @@
 import com.sun.tools.javac.util.ClientCodeException;
 import com.sun.tools.javac.util.Context;
 import com.sun.tools.javac.util.Log;
+import com.sun.tools.javac.util.Log.WriterKind;
 import com.sun.tools.javac.util.Options;
 
 import jdk.javadoc.doclet.Doclet;
@@ -325,9 +327,14 @@
         if (argv.length > 0 && "-Xold".equals(argv[0])) {
             warn("main.legacy_api");
             String[] nargv = Arrays.copyOfRange(argv, 1, argv.length);
-            return com.sun.tools.javadoc.Main.execute(nargv) == 0
-                    ? OK
-                    : ERROR;
+            int rc = com.sun.tools.javadoc.Main.execute(
+                    messager.programName,
+                    messager.getWriter(WriterKind.ERROR),
+                    messager.getWriter(WriterKind.WARNING),
+                    messager.getWriter(WriterKind.NOTICE),
+                    "com.sun.tools.doclets.standard.Standard",
+                    nargv);
+            return (rc == 0) ? OK : ERROR;
         }
         return begin(Arrays.asList(argv), Collections.<JavaFileObject> emptySet());
     }
@@ -400,9 +407,14 @@
             }
             warn("main.legacy_api");
             String[] array = options.toArray(new String[options.size()]);
-            return com.sun.tools.javadoc.Main.execute(array) == 0
-                    ? OK
-                    : ERROR;
+            int rc = com.sun.tools.javadoc.Main.execute(
+                    messager.programName,
+                    messager.getWriter(WriterKind.ERROR),
+                    messager.getWriter(WriterKind.WARNING),
+                    messager.getWriter(WriterKind.NOTICE),
+                    "com.sun.tools.doclets.standard.Standard",
+                    array);
+            return (rc == 0) ? OK : ERROR;
         }
 
         Result result = OK;
@@ -410,6 +422,11 @@
             result = parseAndExecute(options, fileObjects)
                     ? OK
                     : ERROR;
+        } catch (com.sun.tools.javac.main.Option.InvalidValueException e) {
+            messager.printError(e.getMessage());
+            Throwable t = e.getCause();
+            dumpStack(t == null ? e : t);
+            return ERROR;
         } catch (OptionException toe) {
             if (toe.message != null)
                 messager.printError(toe.message);
@@ -482,8 +499,8 @@
      * Main program - internal
      */
     @SuppressWarnings("unchecked")
-    private boolean parseAndExecute(List<String> argList,
-            Iterable<? extends JavaFileObject> fileObjects) throws ToolException, OptionException {
+    private boolean parseAndExecute(List<String> argList, Iterable<? extends JavaFileObject> fileObjects)
+            throws ToolException, OptionException, com.sun.tools.javac.main.Option.InvalidValueException {
         long tm = System.currentTimeMillis();
 
         List<String> javaNames = new ArrayList<>();
@@ -491,11 +508,19 @@
         compOpts = Options.instance(context);
 
         // Make sure no obsolete source/target messages are reported
-        com.sun.tools.javac.main.Option.XLINT.process(getOptionHelper(), "-Xlint:-options");
+        try {
+            com.sun.tools.javac.main.Option.XLINT_CUSTOM.process(getOptionHelper(), "-Xlint:-options");
+        } catch (com.sun.tools.javac.main.Option.InvalidValueException ignore) {
+        }
 
         doclet.init(locale, messager);
         parseArgs(argList, javaNames);
 
+        Arguments arguments = Arguments.instance(context);
+        arguments.init(ProgramName);
+        arguments.allowEmpty();
+        arguments.validate();
+
         if (fileManager instanceof BaseFileManager) {
             ((BaseFileManager) fileManager).handleOptions(fileManagerOpts);
         }
@@ -805,7 +830,7 @@
     }
 
     private void parseArgs(List<String> args, List<String> javaNames) throws ToolException,
-            OptionException {
+            OptionException, com.sun.tools.javac.main.Option.InvalidValueException {
         for (int i = 0 ; i < args.size() ; i++) {
             String arg = args.get(i);
             ToolOption o = ToolOption.get(arg);
@@ -929,7 +954,7 @@
 
     @Override
     OptionHelper getOptionHelper() {
-        return new GrumpyHelper(null) {
+        return new GrumpyHelper(messager) {
             @Override
             public String get(com.sun.tools.javac.main.Option option) {
                 return compOpts.get(option);
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ToolOption.java	Wed Nov 16 10:45:23 2016 -0800
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ToolOption.java	Wed Nov 16 11:53:24 2016 -0800
@@ -35,6 +35,7 @@
 import javax.lang.model.element.ElementKind;
 
 import com.sun.tools.javac.main.Option;
+import com.sun.tools.javac.main.Option.InvalidValueException;
 import com.sun.tools.javac.main.Option.OptionKind;
 import com.sun.tools.javac.main.OptionHelper;
 import com.sun.tools.javac.util.Options;
@@ -56,70 +57,70 @@
 
     BOOTCLASSPATH("-bootclasspath", STANDARD, true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws InvalidValueException {
             Option.BOOT_CLASS_PATH.process(helper.getOptionHelper(), primaryName, arg);
         }
     },
 
     CLASS_PATH("--class-path -classpath -cp", STANDARD, true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws InvalidValueException {
             Option.CLASS_PATH.process(helper.getOptionHelper(), primaryName, arg);
         }
     },
 
     EXTDIRS("-extdirs", STANDARD, true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws InvalidValueException {
             Option.EXTDIRS.process(helper.getOptionHelper(), primaryName, arg);
         }
     },
 
     SOURCE_PATH("--source-path -sourcepath", STANDARD, true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws InvalidValueException {
             Option.SOURCE_PATH.process(helper.getOptionHelper(), primaryName, arg);
         }
     },
 
     MODULE_SOURCE_PATH("--module-source-path", STANDARD, true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws InvalidValueException {
             Option.MODULE_SOURCE_PATH.process(helper.getOptionHelper(), primaryName, arg);
         }
     },
 
     UPGRADE_MODULE_PATH("--upgrade-module-path", STANDARD, true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws InvalidValueException {
             Option.UPGRADE_MODULE_PATH.process(helper.getOptionHelper(), primaryName, arg);
         }
     },
 
     SYSTEM("--system", STANDARD, true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws InvalidValueException {
             Option.SYSTEM.process(helper.getOptionHelper(), primaryName, arg);
         }
     },
 
     MODULE_PATH("--module-path -p", STANDARD, true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws InvalidValueException {
             Option.MODULE_PATH.process(helper.getOptionHelper(), primaryName, arg);
         }
     },
 
     ADD_MODULES("--add-modules", STANDARD, true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws InvalidValueException {
             Option.ADD_MODULES.process(helper.getOptionHelper(), primaryName, arg);
         }
     },
 
     LIMIT_MODULES("--limit-modules", STANDARD, true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws InvalidValueException {
             Option.LIMIT_MODULES.process(helper.getOptionHelper(), primaryName, arg);
         }
     },
@@ -133,63 +134,63 @@
 
     ENCODING("-encoding", STANDARD, true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws InvalidValueException {
             Option.ENCODING.process(helper.getOptionHelper(), primaryName, arg);
         }
     },
 
     RELEASE("--release", STANDARD, true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws InvalidValueException {
             Option.RELEASE.process(helper.getOptionHelper(), primaryName, arg);
         }
     },
 
     SOURCE("-source", STANDARD, true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws InvalidValueException {
             Option.SOURCE.process(helper.getOptionHelper(), primaryName, arg);
         }
     },
 
     XMAXERRS("-Xmaxerrs", EXTENDED, true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws InvalidValueException {
             Option.XMAXERRS.process(helper.getOptionHelper(), primaryName, arg);
         }
     },
 
     XMAXWARNS("-Xmaxwarns", EXTENDED, true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws InvalidValueException {
             Option.XMAXWARNS.process(helper.getOptionHelper(), primaryName, arg);
         }
     },
 
     ADD_READS("--add-reads", EXTENDED, true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws InvalidValueException {
             Option.ADD_READS.process(helper.getOptionHelper(), primaryName, arg);
         }
     },
 
     ADD_EXPORTS("--add-exports", EXTENDED, true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws InvalidValueException {
             Option.ADD_EXPORTS.process(helper.getOptionHelper(), primaryName, arg);
         }
     },
 
     XMODULE("-Xmodule:", EXTENDED, false) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws InvalidValueException {
             Option.XMODULE.process(helper.getOptionHelper(), arg);
         }
     },
 
     PATCH_MODULE("--patch-module", EXTENDED, true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws InvalidValueException {
             Option.PATCH_MODULE.process(helper.getOptionHelper(), primaryName, arg);
         }
     },
@@ -388,7 +389,7 @@
         this.hasSuffix = lastChar == ':' || lastChar == '=';
     }
 
-    void process(Helper helper, String arg) throws OptionException { }
+    void process(Helper helper, String arg) throws OptionException, Option.InvalidValueException { }
 
     void process(Helper helper) throws OptionException { }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/javadoc/tool/BadOptionsTest.java	Wed Nov 16 11:53:24 2016 -0800
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2002, 2016, 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.
+ */
+
+/*
+ * @test
+ * @bug 8169676
+ * @summary boolean result of Option.process is often ignored
+ * @modules jdk.compiler/com.sun.tools.javac.api
+ * @modules jdk.compiler/com.sun.tools.javac.main
+ * @modules jdk.javadoc/jdk.javadoc.internal.api
+ * @modules jdk.javadoc/jdk.javadoc.internal.tool
+ * @library /tools/lib
+ * @build toolbox.JavacTask toolbox.JavadocTask toolbox.TestRunner toolbox.ToolBox
+ * @run main BadOptionsTest
+ */
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.Set;
+
+import javax.lang.model.SourceVersion;
+
+import jdk.javadoc.doclet.Doclet;
+import jdk.javadoc.doclet.DocletEnvironment;
+import jdk.javadoc.doclet.Reporter;
+
+import toolbox.JavadocTask;
+import toolbox.ModuleBuilder;
+import toolbox.Task;
+import toolbox.TestRunner;
+import toolbox.ToolBox;
+
+/*
+ * This is primarily a test of the error reporting mechanisms
+ * for bad options provided by javac and utilized by javadoc.
+ * It is not an exhaustive test of all bad option forms detected
+ * by javac/javadoc.
+ */
+public class BadOptionsTest extends TestRunner {
+
+    public static void main(String... args) throws Exception {
+        BadOptionsTest t = new BadOptionsTest();
+        t.runTests();
+    }
+
+    private final ToolBox tb = new ToolBox();
+    private final Path src = Paths.get("src");
+
+    BadOptionsTest() throws IOException {
+        super(System.err);
+        init();
+    }
+
+    void init() throws IOException {
+        tb.writeJavaFiles(src,
+                "public class C { }");
+
+    }
+
+    @Test
+    public void testAddModulesEmptyArg() {
+        Task.Result result = new JavadocTask(tb, Task.Mode.CMDLINE)
+                .options("--add-modules=")
+                .files(src.resolve("C.java"))
+                .run(Task.Expect.FAIL)
+                .writeAll();
+        checkFound(result.getOutput(Task.OutputKind.DIRECT),
+                "javadoc: error - no value for --add-modules option");
+        checkNotFound(result, "Exception", "at jdk.javadoc/");
+    }
+
+    @Test
+    public void testAddModulesBadName() {
+        Task.Result result = new JavadocTask(tb, Task.Mode.CMDLINE)
+                .options("-quiet",
+                        "--add-modules", "123")
+                .files(src.resolve("C.java"))
+                .run()
+                .writeAll();
+        checkFound(result.getOutput(Task.OutputKind.DIRECT),
+                "warning: bad name in value for --add-modules option: '123'");
+        checkNotFound(result, "Exception", "at jdk.javadoc/");
+    }
+
+    @Test
+    public void testAddExportsEmptyArg() {
+        Task.Result result = new JavadocTask(tb, Task.Mode.CMDLINE)
+                .options("--add-exports=")
+                .files(src.resolve("C.java"))
+                .run(Task.Expect.FAIL)
+                .writeAll();
+        checkFound(result.getOutput(Task.OutputKind.DIRECT),
+                "javadoc: error - no value for --add-exports option");
+        checkNotFound(result, "Exception", "at jdk.javadoc/");
+    }
+
+    @Test
+    public void testAddExportsBadArg() {
+        Task.Result result = new JavadocTask(tb, Task.Mode.CMDLINE)
+                .options("--add-exports=m/p")
+                .files(src.resolve("C.java"))
+                .run(Task.Expect.FAIL)
+                .writeAll();
+        checkFound(result.getOutput(Task.OutputKind.DIRECT),
+                "javadoc: error - bad value for --add-exports option");
+        checkNotFound(result, "Exception", "at jdk.javadoc/");
+    }
+
+    @Test
+    public void testAddExportsBadName() {
+        Task.Result result = new JavadocTask(tb, Task.Mode.CMDLINE)
+                .options("--add-exports", "m!/p1=m2")
+                .files(src.resolve("C.java"))
+                .run()
+                .writeAll();
+        checkFound(result.getOutput(Task.OutputKind.DIRECT),
+                "warning: bad name in value for --add-exports option: 'm!'");
+        checkNotFound(result, "Exception", "at jdk.javadoc/");
+    }
+
+    private void checkFound(String log, String... expect) {
+        for (String e : expect) {
+            if (!log.contains(e)) {
+                error("Expected string not found: '" + e + "'");
+            }
+        }
+    }
+
+    private void checkNotFound(Task.Result result, String... unexpected) {
+        for (Task.OutputKind k : Task.OutputKind.values()) {
+            String r = result.getOutput(k);
+            for (String u : unexpected) {
+                if (r.contains(u)) {
+                    error("Unexpected string found: '" + u + "'");
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javadoc/BadOptionsTest.java	Wed Nov 16 11:53:24 2016 -0800
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2002, 2016, 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.
+ */
+
+/*
+ * @test
+ * @bug 8169676
+ * @summary boolean result of Option.process is often ignored
+ * @modules jdk.compiler/com.sun.tools.javac.api
+ * @modules jdk.compiler/com.sun.tools.javac.main
+ * @modules jdk.javadoc/jdk.javadoc.internal.api
+ * @modules jdk.javadoc/jdk.javadoc.internal.tool
+ * @library /tools/lib
+ * @build toolbox.JavacTask toolbox.JavadocTask toolbox.TestRunner toolbox.ToolBox
+ * @run main BadOptionsTest
+ */
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.Set;
+
+import javax.lang.model.SourceVersion;
+
+import jdk.javadoc.doclet.Doclet;
+import jdk.javadoc.doclet.DocletEnvironment;
+import jdk.javadoc.doclet.Reporter;
+
+import toolbox.JavadocTask;
+import toolbox.ModuleBuilder;
+import toolbox.Task;
+import toolbox.TestRunner;
+import toolbox.ToolBox;
+
+/*
+ * This is primarily a test of the error reporting mechanisms
+ * for bad options provided by javac and utilized by javadoc.
+ * It is not an exhaustive test of all bad option forms detected
+ * by javac/javadoc.
+ */
+public class BadOptionsTest extends TestRunner {
+
+    public static void main(String... args) throws Exception {
+        BadOptionsTest t = new BadOptionsTest();
+        t.runTests();
+    }
+
+    private final ToolBox tb = new ToolBox();
+    private final Path src = Paths.get("src");
+
+    BadOptionsTest() throws IOException {
+        super(System.err);
+        init();
+    }
+
+    void init() throws IOException {
+        tb.writeJavaFiles(src,
+                "public class C { }");
+
+    }
+
+    @Test
+    public void testAddModulesEmptyArg() {
+        Task.Result result = new JavadocTask(tb, Task.Mode.CMDLINE)
+                .options("-Xold",
+                        "--add-modules", "")
+                .files(src.resolve("C.java"))
+                .run(Task.Expect.FAIL)
+                .writeAll();
+        checkFound(result.getOutput(Task.OutputKind.DIRECT),
+                "javadoc: error - no value for --add-modules option");
+        checkNotFound(result, "Exception", "at jdk.javadoc/");
+    }
+
+    @Test
+    public void testAddModulesBadName() {
+        Task.Result result = new JavadocTask(tb, Task.Mode.CMDLINE)
+                .options("-Xold", "-quiet",
+                        "--add-modules", "123")
+                .files(src.resolve("C.java"))
+                .run()
+                .writeAll();
+        checkFound(result.getOutput(Task.OutputKind.DIRECT),
+                "warning: bad name in value for --add-modules option: '123'");
+        checkNotFound(result, "Exception", "at jdk.javadoc/");
+    }
+
+    @Test
+    public void testAddExportsEmptyArg() {
+        Task.Result result = new JavadocTask(tb, Task.Mode.CMDLINE)
+                .options("-Xold",
+                        "--add-exports", "")
+                .files(src.resolve("C.java"))
+                .run(Task.Expect.FAIL)
+                .writeAll();
+        checkFound(result.getOutput(Task.OutputKind.DIRECT),
+                "javadoc: error - no value for --add-exports option");
+        checkNotFound(result, "Exception", "at jdk.javadoc/");
+    }
+
+    @Test
+    public void testAddExportsBadArg() {
+        Task.Result result = new JavadocTask(tb, Task.Mode.CMDLINE)
+                .options("-Xold",
+                        "--add-exports", "m/p")
+                .files(src.resolve("C.java"))
+                .run(Task.Expect.FAIL)
+                .writeAll();
+        checkFound(result.getOutput(Task.OutputKind.DIRECT),
+                "javadoc: error - bad value for --add-exports option");
+        checkNotFound(result, "Exception", "at jdk.javadoc/");
+    }
+
+    @Test
+    public void testAddExportsBadName() {
+        Task.Result result = new JavadocTask(tb, Task.Mode.CMDLINE)
+                .options("-Xold",
+                        "--add-exports", "m!/p1=m2")
+                .files(src.resolve("C.java"))
+                .run()
+                .writeAll();
+        checkFound(result.getOutput(Task.OutputKind.DIRECT),
+                "warning: bad name in value for --add-exports option: 'm!'");
+        checkNotFound(result, "Exception", "at jdk.javadoc/");
+    }
+
+    private void checkFound(String log, String... expect) {
+        for (String e : expect) {
+            if (!log.contains(e)) {
+                error("Expected string not found: '" + e + "'");
+            }
+        }
+    }
+
+    private void checkNotFound(Task.Result result, String... unexpected) {
+        for (Task.OutputKind k : Task.OutputKind.values()) {
+            String r = result.getOutput(k);
+            for (String u : unexpected) {
+                if (r.contains(u)) {
+                    error("Unexpected string found: '" + u + "'");
+                }
+            }
+        }
+    }
+}