• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Dagger Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dagger.internal.codegen.compileroption;
18 
19 import static com.google.common.base.CaseFormat.LOWER_CAMEL;
20 import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
21 import static com.google.common.base.Preconditions.checkArgument;
22 import static com.google.common.collect.Sets.immutableEnumSet;
23 import static dagger.internal.codegen.compileroption.FeatureStatus.DISABLED;
24 import static dagger.internal.codegen.compileroption.FeatureStatus.ENABLED;
25 import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.EXPERIMENTAL_AHEAD_OF_TIME_SUBCOMPONENTS;
26 import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.EXPERIMENTAL_ANDROID_MODE;
27 import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.EXPERIMENTAL_DAGGER_ERROR_MESSAGES;
28 import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.FAST_INIT;
29 import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.FLOATING_BINDS_METHODS;
30 import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.FORMAT_GENERATED_SOURCE;
31 import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.GENERATED_CLASS_EXTENDS_COMPONENT;
32 import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.IGNORE_PRIVATE_AND_STATIC_INJECTION_FOR_COMPONENT;
33 import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.IGNORE_PROVISION_KEY_WILDCARDS;
34 import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.INCLUDE_STACKTRACE_WITH_DEFERRED_ERROR_MESSAGES;
35 import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.PLUGINS_VISIT_FULL_BINDING_GRAPHS;
36 import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.STRICT_MULTIBINDING_VALIDATION;
37 import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.STRICT_SUPERFICIAL_VALIDATION;
38 import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.VALIDATE_TRANSITIVE_COMPONENT_DEPENDENCIES;
39 import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.WARN_IF_INJECTION_FACTORY_NOT_GENERATED_UPSTREAM;
40 import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.WRITE_PRODUCER_NAME_IN_TOKEN;
41 import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.KeyOnlyOption.HEADER_COMPILATION;
42 import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.KeyOnlyOption.USE_GRADLE_INCREMENTAL_PROCESSING;
43 import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Validation.DISABLE_INTER_COMPONENT_SCOPE_VALIDATION;
44 import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Validation.EXPLICIT_BINDING_CONFLICTS_WITH_INJECT;
45 import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Validation.FULL_BINDING_GRAPH_VALIDATION;
46 import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Validation.MODULE_HAS_DIFFERENT_SCOPES_VALIDATION;
47 import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Validation.NULLABLE_VALIDATION;
48 import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Validation.PRIVATE_MEMBER_VALIDATION;
49 import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Validation.STATIC_MEMBER_VALIDATION;
50 import static dagger.internal.codegen.compileroption.ValidationType.ERROR;
51 import static dagger.internal.codegen.compileroption.ValidationType.NONE;
52 import static dagger.internal.codegen.compileroption.ValidationType.WARNING;
53 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
54 import static java.util.stream.Collectors.joining;
55 import static java.util.stream.Stream.concat;
56 
57 import androidx.room.compiler.processing.XMessager;
58 import androidx.room.compiler.processing.XProcessingEnv;
59 import androidx.room.compiler.processing.XTypeElement;
60 import com.google.common.base.Ascii;
61 import com.google.common.collect.ImmutableList;
62 import com.google.common.collect.ImmutableMap;
63 import com.google.common.collect.ImmutableSet;
64 import dagger.internal.codegen.javapoet.TypeNames;
65 import java.util.Arrays;
66 import java.util.EnumSet;
67 import java.util.HashMap;
68 import java.util.Map;
69 import java.util.Optional;
70 import java.util.Set;
71 import java.util.stream.Stream;
72 import javax.inject.Inject;
73 import javax.tools.Diagnostic;
74 
75 /** {@link CompilerOptions} for the given processor. */
76 public final class ProcessingEnvironmentCompilerOptions extends CompilerOptions {
77   // EnumOption<T> doesn't support integer inputs so just doing this as a 1-off for now.
78   private static final String KEYS_PER_COMPONENT_SHARD = "dagger.keysPerComponentShard";
79 
80   private final XProcessingEnv processingEnv;
81   private final XMessager messager;
82   private final Map<String, String> options;
83   private final Map<EnumOption<?>, Object> enumOptions = new HashMap<>();
84   private final Map<EnumOption<?>, ImmutableMap<String, ? extends Enum<?>>> allCommandLineOptions =
85       new HashMap<>();
86 
87   @Inject
ProcessingEnvironmentCompilerOptions( XProcessingEnv processingEnv, XMessager messager, @ProcessingOptions Map<String, String> options)88   ProcessingEnvironmentCompilerOptions(
89       XProcessingEnv processingEnv,
90       XMessager messager,
91       @ProcessingOptions Map<String, String> options) {
92     this.processingEnv = processingEnv;
93     this.messager = messager;
94     this.options = options;
95     checkValid();
96   }
97 
98   @Override
usesProducers()99   public boolean usesProducers() {
100     return processingEnv.findTypeElement(TypeNames.PRODUCES) != null;
101   }
102 
103   @Override
headerCompilation()104   public boolean headerCompilation() {
105     return isEnabled(HEADER_COMPILATION);
106   }
107 
108   @Override
fastInit(XTypeElement component)109   public boolean fastInit(XTypeElement component) {
110     return fastInitInternal(component);
111   }
112 
fastInitInternal(XTypeElement component)113   private boolean fastInitInternal(XTypeElement component) {
114     return isEnabled(FAST_INIT);
115   }
116 
117   @Override
formatGeneratedSource()118   public boolean formatGeneratedSource() {
119     return isEnabled(FORMAT_GENERATED_SOURCE);
120   }
121 
122   @Override
writeProducerNameInToken()123   public boolean writeProducerNameInToken() {
124     return isEnabled(WRITE_PRODUCER_NAME_IN_TOKEN);
125   }
126 
127   @Override
nullableValidationKind()128   public Diagnostic.Kind nullableValidationKind() {
129     return diagnosticKind(NULLABLE_VALIDATION);
130   }
131 
132   @Override
privateMemberValidationKind()133   public Diagnostic.Kind privateMemberValidationKind() {
134     return diagnosticKind(PRIVATE_MEMBER_VALIDATION);
135   }
136 
137   @Override
staticMemberValidationKind()138   public Diagnostic.Kind staticMemberValidationKind() {
139     return diagnosticKind(STATIC_MEMBER_VALIDATION);
140   }
141 
142   @Override
includeStacktraceWithDeferredErrorMessages()143   public boolean includeStacktraceWithDeferredErrorMessages() {
144     return isEnabled(INCLUDE_STACKTRACE_WITH_DEFERRED_ERROR_MESSAGES);
145   }
146 
147   @Override
ignorePrivateAndStaticInjectionForComponent()148   public boolean ignorePrivateAndStaticInjectionForComponent() {
149     return isEnabled(IGNORE_PRIVATE_AND_STATIC_INJECTION_FOR_COMPONENT);
150   }
151 
152   @Override
scopeCycleValidationType()153   public ValidationType scopeCycleValidationType() {
154     return parseOption(DISABLE_INTER_COMPONENT_SCOPE_VALIDATION);
155   }
156 
157   @Override
validateTransitiveComponentDependencies()158   public boolean validateTransitiveComponentDependencies() {
159     return isEnabled(VALIDATE_TRANSITIVE_COMPONENT_DEPENDENCIES);
160   }
161 
162   @Override
warnIfInjectionFactoryNotGeneratedUpstream()163   public boolean warnIfInjectionFactoryNotGeneratedUpstream() {
164     return isEnabled(WARN_IF_INJECTION_FACTORY_NOT_GENERATED_UPSTREAM);
165   }
166 
167   @Override
fullBindingGraphValidationType()168   public ValidationType fullBindingGraphValidationType() {
169     return parseOption(FULL_BINDING_GRAPH_VALIDATION);
170   }
171 
172   @Override
pluginsVisitFullBindingGraphs(XTypeElement component)173   public boolean pluginsVisitFullBindingGraphs(XTypeElement component) {
174     return isEnabled(PLUGINS_VISIT_FULL_BINDING_GRAPHS);
175   }
176 
177   @Override
moduleHasDifferentScopesDiagnosticKind()178   public Diagnostic.Kind moduleHasDifferentScopesDiagnosticKind() {
179     return diagnosticKind(MODULE_HAS_DIFFERENT_SCOPES_VALIDATION);
180   }
181 
182   @Override
explicitBindingConflictsWithInjectValidationType()183   public ValidationType explicitBindingConflictsWithInjectValidationType() {
184     return parseOption(EXPLICIT_BINDING_CONFLICTS_WITH_INJECT);
185   }
186 
187   @Override
experimentalDaggerErrorMessages()188   public boolean experimentalDaggerErrorMessages() {
189     return isEnabled(EXPERIMENTAL_DAGGER_ERROR_MESSAGES);
190   }
191 
192   @Override
ignoreProvisionKeyWildcards()193   public boolean ignoreProvisionKeyWildcards() {
194     return isEnabled(IGNORE_PROVISION_KEY_WILDCARDS);
195   }
196 
197   @Override
strictMultibindingValidation()198   public boolean strictMultibindingValidation() {
199     return isEnabled(STRICT_MULTIBINDING_VALIDATION);
200   }
201 
202   @Override
strictSuperficialValidation()203   public boolean strictSuperficialValidation() {
204     return isEnabled(STRICT_SUPERFICIAL_VALIDATION);
205   }
206 
207   @Override
generatedClassExtendsComponent()208   public boolean generatedClassExtendsComponent() {
209     return isEnabled(GENERATED_CLASS_EXTENDS_COMPONENT);
210   }
211 
212   @Override
keysPerComponentShard(XTypeElement component)213   public int keysPerComponentShard(XTypeElement component) {
214     if (options.containsKey(KEYS_PER_COMPONENT_SHARD)) {
215       checkArgument(
216           component.getClassName().packageName().startsWith("dagger."),
217           "Cannot set %s. It is only meant for internal testing.", KEYS_PER_COMPONENT_SHARD);
218       return Integer.parseInt(options.get(KEYS_PER_COMPONENT_SHARD));
219     }
220     return super.keysPerComponentShard(component);
221   }
222 
isEnabled(KeyOnlyOption keyOnlyOption)223   private boolean isEnabled(KeyOnlyOption keyOnlyOption) {
224     return options.containsKey(keyOnlyOption.toString());
225   }
226 
isEnabled(Feature feature)227   private boolean isEnabled(Feature feature) {
228     return parseOption(feature).equals(ENABLED);
229   }
230 
diagnosticKind(Validation validation)231   private Diagnostic.Kind diagnosticKind(Validation validation) {
232     return parseOption(validation).diagnosticKind().get();
233   }
234 
235   @SuppressWarnings("CheckReturnValue")
checkValid()236   private ProcessingEnvironmentCompilerOptions checkValid() {
237     for (Feature feature : Feature.values()) {
238       parseOption(feature);
239     }
240     for (Validation validation : Validation.values()) {
241       parseOption(validation);
242     }
243     noLongerRecognized(EXPERIMENTAL_ANDROID_MODE);
244     noLongerRecognized(FLOATING_BINDS_METHODS);
245     noLongerRecognized(EXPERIMENTAL_AHEAD_OF_TIME_SUBCOMPONENTS);
246     noLongerRecognized(USE_GRADLE_INCREMENTAL_PROCESSING);
247     if (!isEnabled(IGNORE_PROVISION_KEY_WILDCARDS)) {
248       if (processingEnv.getBackend() == XProcessingEnv.Backend.KSP) {
249         processingEnv.getMessager().printMessage(
250             Diagnostic.Kind.ERROR,
251             String.format(
252                 "When using KSP, you must also enable the '%s' compiler option (see %s).",
253                 "dagger.ignoreProvisionKeyWildcards",
254                 "https://dagger.dev/dev-guide/compiler-options#ignore-provision-key-wildcards"));
255       }
256     }
257     return this;
258   }
259 
noLongerRecognized(CommandLineOption commandLineOption)260   private void noLongerRecognized(CommandLineOption commandLineOption) {
261     if (options.containsKey(commandLineOption.toString())) {
262       messager.printMessage(
263           Diagnostic.Kind.WARNING, commandLineOption + " is no longer recognized by Dagger");
264     }
265   }
266 
267   private interface CommandLineOption {
268     /** The key of the option (appears after "-A"). */
269     @Override
toString()270     String toString();
271 
272     /**
273      * Returns all aliases besides {@link #toString()}, such as old names for an option, in order of
274      * precedence.
275      */
aliases()276     default ImmutableList<String> aliases() {
277       return ImmutableList.of();
278     }
279 
280     /** All the command-line names for this option, in order of precedence. */
allNames()281     default Stream<String> allNames() {
282       return concat(Stream.of(toString()), aliases().stream());
283     }
284   }
285 
286   /** An option that can be set on the command line. */
287   private interface EnumOption<E extends Enum<E>> extends CommandLineOption {
288     /** The default value for this option. */
defaultValue()289     E defaultValue();
290 
291     /** The valid values for this option. */
validValues()292     Set<E> validValues();
293   }
294 
295   enum KeyOnlyOption implements CommandLineOption {
296     HEADER_COMPILATION {
297       @Override
toString()298       public String toString() {
299         return "experimental_turbine_hjar";
300       }
301     },
302 
303     USE_GRADLE_INCREMENTAL_PROCESSING {
304       @Override
toString()305       public String toString() {
306         return "dagger.gradle.incremental";
307       }
308     },
309   }
310 
311   /**
312    * A feature that can be enabled or disabled on the command line by setting {@code -Akey=ENABLED}
313    * or {@code -Akey=DISABLED}.
314    */
315   enum Feature implements EnumOption<FeatureStatus> {
316     FAST_INIT,
317 
318     EXPERIMENTAL_ANDROID_MODE,
319 
320     FORMAT_GENERATED_SOURCE,
321 
322     WRITE_PRODUCER_NAME_IN_TOKEN,
323 
324     WARN_IF_INJECTION_FACTORY_NOT_GENERATED_UPSTREAM,
325 
326     INCLUDE_STACKTRACE_WITH_DEFERRED_ERROR_MESSAGES,
327 
328     IGNORE_PRIVATE_AND_STATIC_INJECTION_FOR_COMPONENT,
329 
330     EXPERIMENTAL_AHEAD_OF_TIME_SUBCOMPONENTS,
331 
332     FORCE_USE_SERIALIZED_COMPONENT_IMPLEMENTATIONS,
333 
334     EMIT_MODIFIABLE_METADATA_ANNOTATIONS(ENABLED),
335 
336     PLUGINS_VISIT_FULL_BINDING_GRAPHS,
337 
338     FLOATING_BINDS_METHODS,
339 
340     EXPERIMENTAL_DAGGER_ERROR_MESSAGES,
341 
342     STRICT_MULTIBINDING_VALIDATION,
343 
344     STRICT_SUPERFICIAL_VALIDATION(ENABLED),
345 
346     GENERATED_CLASS_EXTENDS_COMPONENT,
347 
348     IGNORE_PROVISION_KEY_WILDCARDS(ENABLED),
349 
350     VALIDATE_TRANSITIVE_COMPONENT_DEPENDENCIES(ENABLED)
351     ;
352 
353     final FeatureStatus defaultValue;
354 
Feature()355     Feature() {
356       this(DISABLED);
357     }
358 
Feature(FeatureStatus defaultValue)359     Feature(FeatureStatus defaultValue) {
360       this.defaultValue = defaultValue;
361     }
362 
363     @Override
defaultValue()364     public FeatureStatus defaultValue() {
365       return defaultValue;
366     }
367 
368     @Override
validValues()369     public Set<FeatureStatus> validValues() {
370       return EnumSet.allOf(FeatureStatus.class);
371     }
372 
373     @Override
toString()374     public String toString() {
375       return optionName(this);
376     }
377   }
378 
379   /** The diagnostic kind or validation type for a kind of validation. */
380   enum Validation implements EnumOption<ValidationType> {
381     DISABLE_INTER_COMPONENT_SCOPE_VALIDATION(),
382 
383     NULLABLE_VALIDATION(ERROR, WARNING),
384 
385     PRIVATE_MEMBER_VALIDATION(ERROR, WARNING),
386 
387     STATIC_MEMBER_VALIDATION(ERROR, WARNING),
388 
389     /** Whether to validate full binding graphs for components, subcomponents, and modules. */
FULL_BINDING_GRAPH_VALIDATION(NONE, ERROR, WARNING)390     FULL_BINDING_GRAPH_VALIDATION(NONE, ERROR, WARNING) {
391       @Override
392       public ImmutableList<String> aliases() {
393         return ImmutableList.of("dagger.moduleBindingValidation");
394       }
395     },
396 
397     /**
398      * How to report conflicting scoped bindings when validating partial binding graphs associated
399      * with modules.
400      */
401     MODULE_HAS_DIFFERENT_SCOPES_VALIDATION(ERROR, WARNING),
402 
403     /**
404      * How to report that an explicit binding in a subcomponent conflicts with an {@code @Inject}
405      * constructor used in an ancestor component.
406      */
407     EXPLICIT_BINDING_CONFLICTS_WITH_INJECT(ERROR, WARNING, NONE),
408     ;
409 
410     final ValidationType defaultType;
411     final ImmutableSet<ValidationType> validTypes;
412 
Validation()413     Validation() {
414       this(ERROR, WARNING, NONE);
415     }
416 
Validation(ValidationType defaultType, ValidationType... moreValidTypes)417     Validation(ValidationType defaultType, ValidationType... moreValidTypes) {
418       this.defaultType = defaultType;
419       this.validTypes = immutableEnumSet(defaultType, moreValidTypes);
420     }
421 
422     @Override
defaultValue()423     public ValidationType defaultValue() {
424       return defaultType;
425     }
426 
427     @Override
validValues()428     public Set<ValidationType> validValues() {
429       return validTypes;
430     }
431 
432     @Override
toString()433     public String toString() {
434       return optionName(this);
435     }
436   }
437 
optionName(Enum<? extends EnumOption<?>> option)438   private static String optionName(Enum<? extends EnumOption<?>> option) {
439     return "dagger." + UPPER_UNDERSCORE.to(LOWER_CAMEL, option.name());
440   }
441 
442   /** The supported command-line options. */
supportedOptions()443   public static ImmutableSet<String> supportedOptions() {
444     // need explicit type parameter to avoid a runtime stream error
445     return ImmutableSet.<String>builder()
446         .addAll(
447             Stream.<CommandLineOption[]>of(
448                 KeyOnlyOption.values(), Feature.values(), Validation.values())
449             .flatMap(Arrays::stream)
450             .flatMap(CommandLineOption::allNames)
451             .collect(toImmutableSet()))
452         .add(KEYS_PER_COMPONENT_SHARD)
453         .build();
454   }
455 
456   /**
457    * Returns the value for the option as set on the command line by any name, or the default value
458    * if not set.
459    *
460    * <p>If more than one name is used to set the value, but all names specify the same value,
461    * reports a warning and returns that value.
462    *
463    * <p>If more than one name is used to set the value, and not all names specify the same value,
464    * reports an error and returns the default value.
465    */
parseOption(EnumOption<T> option)466   private <T extends Enum<T>> T parseOption(EnumOption<T> option) {
467     @SuppressWarnings("unchecked") // we only put covariant values into the map
468     T value = (T) enumOptions.computeIfAbsent(option, this::parseOptionUncached);
469     return value;
470   }
471 
isSetOnCommandLine(Feature feature)472   private boolean isSetOnCommandLine(Feature feature) {
473     return getUsedNames(feature).count() > 0;
474   }
475 
parseOptionUncached(EnumOption<T> option)476   private <T extends Enum<T>> T parseOptionUncached(EnumOption<T> option) {
477     ImmutableMap<String, T> values = parseOptionWithAllNames(option);
478 
479     // If no value is specified, return the default value.
480     if (values.isEmpty()) {
481       return option.defaultValue();
482     }
483 
484     // If all names have the same value, return that.
485     if (values.asMultimap().inverse().keySet().size() == 1) {
486       // Warn if an option was set with more than one name. That would be an error if the values
487       // differed.
488       if (values.size() > 1) {
489         reportUseOfDifferentNamesForOption(Diagnostic.Kind.WARNING, option, values.keySet());
490       }
491       return values.values().asList().get(0);
492     }
493 
494     // If different names have different values, report an error and return the default
495     // value.
496     reportUseOfDifferentNamesForOption(Diagnostic.Kind.ERROR, option, values.keySet());
497     return option.defaultValue();
498   }
499 
reportUseOfDifferentNamesForOption( Diagnostic.Kind diagnosticKind, EnumOption<?> option, ImmutableSet<String> usedNames)500   private void reportUseOfDifferentNamesForOption(
501       Diagnostic.Kind diagnosticKind, EnumOption<?> option, ImmutableSet<String> usedNames) {
502     messager.printMessage(
503         diagnosticKind,
504         String.format(
505             "Only one of the equivalent options (%s) should be used; prefer -A%s",
506             usedNames.stream().map(name -> "-A" + name).collect(joining(", ")), option));
507   }
508 
parseOptionWithAllNames( EnumOption<T> option)509   private <T extends Enum<T>> ImmutableMap<String, T> parseOptionWithAllNames(
510       EnumOption<T> option) {
511     @SuppressWarnings("unchecked") // map is covariant
512     ImmutableMap<String, T> aliasValues =
513         (ImmutableMap<String, T>)
514             allCommandLineOptions.computeIfAbsent(option, this::parseOptionWithAllNamesUncached);
515     return aliasValues;
516   }
517 
parseOptionWithAllNamesUncached( EnumOption<T> option)518   private <T extends Enum<T>> ImmutableMap<String, T> parseOptionWithAllNamesUncached(
519       EnumOption<T> option) {
520     ImmutableMap.Builder<String, T> values = ImmutableMap.builder();
521     getUsedNames(option)
522         .forEach(
523             name -> parseOptionWithName(option, name).ifPresent(value -> values.put(name, value)));
524     return values.build();
525   }
526 
parseOptionWithName(EnumOption<T> option, String key)527   private <T extends Enum<T>> Optional<T> parseOptionWithName(EnumOption<T> option, String key) {
528     checkArgument(options.containsKey(key), "key %s not found", key);
529     String stringValue = options.get(key);
530     if (stringValue == null) {
531       messager.printMessage(Diagnostic.Kind.ERROR, "Processor option -A" + key + " needs a value");
532     } else {
533       try {
534         T value =
535             Enum.valueOf(option.defaultValue().getDeclaringClass(), Ascii.toUpperCase(stringValue));
536         if (option.validValues().contains(value)) {
537           return Optional.of(value);
538         }
539       } catch (IllegalArgumentException e) {
540         // handled below
541       }
542       messager.printMessage(
543           Diagnostic.Kind.ERROR,
544           String.format(
545               "Processor option -A%s may only have the values %s (case insensitive), found: %s",
546               key, option.validValues(), stringValue));
547     }
548     return Optional.empty();
549   }
550 
getUsedNames(CommandLineOption option)551   private Stream<String> getUsedNames(CommandLineOption option) {
552     return option.allNames().filter(options::containsKey);
553   }
554 }
555