Home | History | Annotate | Download | only in internal
      1 /*
      2  * Copyright (C) 2006 Google Inc.
      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 com.google.inject.internal;
     18 
     19 import com.google.common.collect.ImmutableList;
     20 import com.google.common.collect.ImmutableSet;
     21 import com.google.common.collect.Lists;
     22 import com.google.common.collect.Ordering;
     23 import com.google.common.primitives.Primitives;
     24 import com.google.inject.Binding;
     25 import com.google.inject.ConfigurationException;
     26 import com.google.inject.CreationException;
     27 import com.google.inject.Injector;
     28 import com.google.inject.Key;
     29 import com.google.inject.ProvisionException;
     30 import com.google.inject.Scope;
     31 import com.google.inject.TypeLiteral;
     32 import com.google.inject.internal.util.SourceProvider;
     33 import com.google.inject.spi.ElementSource;
     34 import com.google.inject.spi.Message;
     35 import com.google.inject.spi.ScopeBinding;
     36 import com.google.inject.spi.TypeConverterBinding;
     37 import com.google.inject.spi.TypeListenerBinding;
     38 import java.io.Serializable;
     39 import java.lang.annotation.Annotation;
     40 import java.lang.reflect.Constructor;
     41 import java.lang.reflect.Field;
     42 import java.lang.reflect.Member;
     43 import java.lang.reflect.Method;
     44 import java.lang.reflect.Type;
     45 import java.util.ArrayList;
     46 import java.util.Collection;
     47 import java.util.Formatter;
     48 import java.util.List;
     49 import java.util.Map;
     50 import java.util.Set;
     51 
     52 /**
     53  * A collection of error messages. If this type is passed as a method parameter, the method is
     54  * considered to have executed successfully only if new errors were not added to this collection.
     55  *
     56  * <p>Errors can be chained to provide additional context. To add context, call {@link #withSource}
     57  * to create a new Errors instance that contains additional context. All messages added to the
     58  * returned instance will contain full context.
     59  *
     60  * <p>To avoid messages with redundant context, {@link #withSource} should be added sparingly. A
     61  * good rule of thumb is to assume a method's caller has already specified enough context to
     62  * identify that method. When calling a method that's defined in a different context, call that
     63  * method with an errors object that includes its context.
     64  *
     65  * @author jessewilson (at) google.com (Jesse Wilson)
     66  */
     67 public final class Errors implements Serializable {
     68 
     69   /** When a binding is not found, show at most this many bindings with the same type */
     70   private static final int MAX_MATCHING_TYPES_REPORTED = 3;
     71 
     72   /** When a binding is not found, show at most this many bindings that have some similarities */
     73   private static final int MAX_RELATED_TYPES_REPORTED = 3;
     74 
     75   /**
     76    * Throws a ConfigurationException with an NullPointerExceptions as the cause if the given
     77    * reference is {@code null}.
     78    */
     79   static <T> T checkNotNull(T reference, String name) {
     80     if (reference != null) {
     81       return reference;
     82     }
     83 
     84     NullPointerException npe = new NullPointerException(name);
     85     throw new ConfigurationException(ImmutableSet.of(new Message(npe.toString(), npe)));
     86   }
     87 
     88   /**
     89    * Throws a ConfigurationException with a formatted {@link Message} if this condition is {@code
     90    * false}.
     91    */
     92   static void checkConfiguration(boolean condition, String format, Object... args) {
     93     if (condition) {
     94       return;
     95     }
     96 
     97     throw new ConfigurationException(ImmutableSet.of(new Message(Errors.format(format, args))));
     98   }
     99 
    100   /**
    101    * If the key is unknown and it is one of these types, it generally means there is a missing
    102    * annotation.
    103    */
    104   private static final ImmutableSet<Class<?>> COMMON_AMBIGUOUS_TYPES =
    105       ImmutableSet.<Class<?>>builder()
    106           .add(Object.class)
    107           .add(String.class)
    108           .addAll(Primitives.allWrapperTypes())
    109           .build();
    110 
    111   /** The root errors object. Used to access the list of error messages. */
    112   private final Errors root;
    113 
    114   /** The parent errors object. Used to obtain the chain of source objects. */
    115   private final Errors parent;
    116 
    117   /** The leaf source for errors added here. */
    118   private final Object source;
    119 
    120   /** null unless (root == this) and error messages exist. Never an empty list. */
    121   private List<Message> errors; // lazy, use getErrorsForAdd()
    122 
    123   public Errors() {
    124     this.root = this;
    125     this.parent = null;
    126     this.source = SourceProvider.UNKNOWN_SOURCE;
    127   }
    128 
    129   public Errors(Object source) {
    130     this.root = this;
    131     this.parent = null;
    132     this.source = source;
    133   }
    134 
    135   private Errors(Errors parent, Object source) {
    136     this.root = parent.root;
    137     this.parent = parent;
    138     this.source = source;
    139   }
    140 
    141   /** Returns an instance that uses {@code source} as a reference point for newly added errors. */
    142   public Errors withSource(Object source) {
    143     return source == this.source || source == SourceProvider.UNKNOWN_SOURCE
    144         ? this
    145         : new Errors(this, source);
    146   }
    147 
    148   /**
    149    * We use a fairly generic error message here. The motivation is to share the same message for
    150    * both bind time errors:
    151    *
    152    * <pre><code>Guice.createInjector(new AbstractModule() {
    153    *   public void configure() {
    154    *     bind(Runnable.class);
    155    *   }
    156    * }</code></pre>
    157    *
    158    * ...and at provide-time errors:
    159    *
    160    * <pre><code>Guice.createInjector().getInstance(Runnable.class);</code></pre>
    161    *
    162    * Otherwise we need to know who's calling when resolving a just-in-time binding, which makes
    163    * things unnecessarily complex.
    164    */
    165   public Errors missingImplementation(Key key) {
    166     return addMessage("No implementation for %s was bound.", key);
    167   }
    168 
    169   /** Within guice's core, allow for better missing binding messages */
    170   <T> Errors missingImplementationWithHint(Key<T> key, Injector injector) {
    171     StringBuilder sb = new StringBuilder();
    172 
    173     sb.append(format("No implementation for %s was bound.", key));
    174 
    175     // Keys which have similar strings as the desired key
    176     List<String> possibleMatches = new ArrayList<>();
    177 
    178     // Check for other keys that may have the same type,
    179     // but not the same annotation
    180     TypeLiteral<T> type = key.getTypeLiteral();
    181     List<Binding<T>> sameTypes = injector.findBindingsByType(type);
    182     if (!sameTypes.isEmpty()) {
    183       sb.append(format("%n  Did you mean?"));
    184       int howMany = Math.min(sameTypes.size(), MAX_MATCHING_TYPES_REPORTED);
    185       for (int i = 0; i < howMany; ++i) {
    186         // TODO: Look into a better way to prioritize suggestions. For example, possbily
    187         // use levenshtein distance of the given annotation vs actual annotation.
    188         sb.append(format("%n    * %s", sameTypes.get(i).getKey()));
    189       }
    190       int remaining = sameTypes.size() - MAX_MATCHING_TYPES_REPORTED;
    191       if (remaining > 0) {
    192         String plural = (remaining == 1) ? "" : "s";
    193         sb.append(format("%n    %d more binding%s with other annotations.", remaining, plural));
    194       }
    195     } else {
    196       // For now, do a simple substring search for possibilities. This can help spot
    197       // issues when there are generics being used (such as a wrapper class) and the
    198       // user has forgotten they need to bind based on the wrapper, not the underlying
    199       // class. In the future, consider doing a strict in-depth type search.
    200       // TODO: Look into a better way to prioritize suggestions. For example, possbily
    201       // use levenshtein distance of the type literal strings.
    202       String want = type.toString();
    203       Map<Key<?>, Binding<?>> bindingMap = injector.getAllBindings();
    204       for (Key<?> bindingKey : bindingMap.keySet()) {
    205         String have = bindingKey.getTypeLiteral().toString();
    206         if (have.contains(want) || want.contains(have)) {
    207           Formatter fmt = new Formatter();
    208           Messages.formatSource(fmt, bindingMap.get(bindingKey).getSource());
    209           String match = String.format("%s bound%s", convert(bindingKey), fmt.toString());
    210           possibleMatches.add(match);
    211           // TODO: Consider a check that if there are more than some number of results,
    212           // don't suggest any.
    213           if (possibleMatches.size() > MAX_RELATED_TYPES_REPORTED) {
    214             // Early exit if we have found more than we need.
    215             break;
    216           }
    217         }
    218       }
    219 
    220       if ((possibleMatches.size() > 0) && (possibleMatches.size() <= MAX_RELATED_TYPES_REPORTED)) {
    221         sb.append(format("%n  Did you mean?"));
    222         for (String possibleMatch : possibleMatches) {
    223           sb.append(format("%n    %s", possibleMatch));
    224         }
    225       }
    226     }
    227 
    228     // If where are no possibilities to suggest, then handle the case of missing
    229     // annotations on simple types. This is usually a bad idea.
    230     if (sameTypes.isEmpty()
    231         && possibleMatches.isEmpty()
    232         && key.getAnnotation() == null
    233         && COMMON_AMBIGUOUS_TYPES.contains(key.getTypeLiteral().getRawType())) {
    234       // We don't recommend using such simple types without annotations.
    235       sb.append(format("%nThe key seems very generic, did you forget an annotation?"));
    236     }
    237 
    238     return addMessage(sb.toString());
    239   }
    240 
    241   public Errors jitDisabled(Key<?> key) {
    242     return addMessage("Explicit bindings are required and %s is not explicitly bound.", key);
    243   }
    244 
    245   public Errors jitDisabledInParent(Key<?> key) {
    246     return addMessage(
    247         "Explicit bindings are required and %s would be bound in a parent injector.%n"
    248             + "Please add an explicit binding for it, either in the child or the parent.",
    249         key);
    250   }
    251 
    252   public Errors atInjectRequired(Class clazz) {
    253     return addMessage(
    254         "Explicit @Inject annotations are required on constructors,"
    255             + " but %s has no constructors annotated with @Inject.",
    256         clazz);
    257   }
    258 
    259   public Errors converterReturnedNull(
    260       String stringValue,
    261       Object source,
    262       TypeLiteral<?> type,
    263       TypeConverterBinding typeConverterBinding) {
    264     return addMessage(
    265         "Received null converting '%s' (bound at %s) to %s%n using %s.",
    266         stringValue, convert(source), type, typeConverterBinding);
    267   }
    268 
    269   public Errors conversionTypeError(
    270       String stringValue,
    271       Object source,
    272       TypeLiteral<?> type,
    273       TypeConverterBinding typeConverterBinding,
    274       Object converted) {
    275     return addMessage(
    276         "Type mismatch converting '%s' (bound at %s) to %s%n"
    277             + " using %s.%n"
    278             + " Converter returned %s.",
    279         stringValue, convert(source), type, typeConverterBinding, converted);
    280   }
    281 
    282   public Errors conversionError(
    283       String stringValue,
    284       Object source,
    285       TypeLiteral<?> type,
    286       TypeConverterBinding typeConverterBinding,
    287       RuntimeException cause) {
    288     return errorInUserCode(
    289         cause,
    290         "Error converting '%s' (bound at %s) to %s%n using %s.%n Reason: %s",
    291         stringValue,
    292         convert(source),
    293         type,
    294         typeConverterBinding,
    295         cause);
    296   }
    297 
    298   public Errors ambiguousTypeConversion(
    299       String stringValue,
    300       Object source,
    301       TypeLiteral<?> type,
    302       TypeConverterBinding a,
    303       TypeConverterBinding b) {
    304     return addMessage(
    305         "Multiple converters can convert '%s' (bound at %s) to %s:%n"
    306             + " %s and%n"
    307             + " %s.%n"
    308             + " Please adjust your type converter configuration to avoid overlapping matches.",
    309         stringValue, convert(source), type, a, b);
    310   }
    311 
    312   public Errors bindingToProvider() {
    313     return addMessage("Binding to Provider is not allowed.");
    314   }
    315 
    316   public Errors notASubtype(Class<?> implementationType, Class<?> type) {
    317     return addMessage("%s doesn't extend %s.", implementationType, type);
    318   }
    319 
    320   public Errors recursiveImplementationType() {
    321     return addMessage("@ImplementedBy points to the same class it annotates.");
    322   }
    323 
    324   public Errors recursiveProviderType() {
    325     return addMessage("@ProvidedBy points to the same class it annotates.");
    326   }
    327 
    328   public Errors missingRuntimeRetention(Class<? extends Annotation> annotation) {
    329     return addMessage(format("Please annotate %s with @Retention(RUNTIME).", annotation));
    330   }
    331 
    332   public Errors missingScopeAnnotation(Class<? extends Annotation> annotation) {
    333     return addMessage(format("Please annotate %s with @ScopeAnnotation.", annotation));
    334   }
    335 
    336   public Errors optionalConstructor(Constructor constructor) {
    337     return addMessage(
    338         "%s is annotated @Inject(optional=true), but constructors cannot be optional.",
    339         constructor);
    340   }
    341 
    342   public Errors cannotBindToGuiceType(String simpleName) {
    343     return addMessage("Binding to core guice framework type is not allowed: %s.", simpleName);
    344   }
    345 
    346   public Errors scopeNotFound(Class<? extends Annotation> scopeAnnotation) {
    347     return addMessage("No scope is bound to %s.", scopeAnnotation);
    348   }
    349 
    350   public Errors scopeAnnotationOnAbstractType(
    351       Class<? extends Annotation> scopeAnnotation, Class<?> type, Object source) {
    352     return addMessage(
    353         "%s is annotated with %s, but scope annotations are not supported "
    354             + "for abstract types.%n Bound at %s.",
    355         type, scopeAnnotation, convert(source));
    356   }
    357 
    358   public Errors misplacedBindingAnnotation(Member member, Annotation bindingAnnotation) {
    359     return addMessage(
    360         "%s is annotated with %s, but binding annotations should be applied "
    361             + "to its parameters instead.",
    362         member, bindingAnnotation);
    363   }
    364 
    365   private static final String CONSTRUCTOR_RULES =
    366       "Classes must have either one (and only one) constructor "
    367           + "annotated with @Inject or a zero-argument constructor that is not private.";
    368 
    369   public Errors missingConstructor(Class<?> implementation) {
    370     return addMessage(
    371         "Could not find a suitable constructor in %s. " + CONSTRUCTOR_RULES, implementation);
    372   }
    373 
    374   public Errors tooManyConstructors(Class<?> implementation) {
    375     return addMessage(
    376         "%s has more than one constructor annotated with @Inject. " + CONSTRUCTOR_RULES,
    377         implementation);
    378   }
    379 
    380   public Errors constructorNotDefinedByType(Constructor<?> constructor, TypeLiteral<?> type) {
    381     return addMessage("%s does not define %s", type, constructor);
    382   }
    383 
    384   public Errors duplicateScopes(
    385       ScopeBinding existing, Class<? extends Annotation> annotationType, Scope scope) {
    386     return addMessage(
    387         "Scope %s is already bound to %s at %s.%n Cannot bind %s.",
    388         existing.getScope(), annotationType, existing.getSource(), scope);
    389   }
    390 
    391   public Errors voidProviderMethod() {
    392     return addMessage("Provider methods must return a value. Do not return void.");
    393   }
    394 
    395   public Errors missingConstantValues() {
    396     return addMessage("Missing constant value. Please call to(...).");
    397   }
    398 
    399   public Errors cannotInjectInnerClass(Class<?> type) {
    400     return addMessage(
    401         "Injecting into inner classes is not supported.  "
    402             + "Please use a 'static' class (top-level or nested) instead of %s.",
    403         type);
    404   }
    405 
    406   public Errors duplicateBindingAnnotations(
    407       Member member, Class<? extends Annotation> a, Class<? extends Annotation> b) {
    408     return addMessage(
    409         "%s has more than one annotation annotated with @BindingAnnotation: %s and %s",
    410         member, a, b);
    411   }
    412 
    413   public Errors staticInjectionOnInterface(Class<?> clazz) {
    414     return addMessage("%s is an interface, but interfaces have no static injection points.", clazz);
    415   }
    416 
    417   public Errors cannotInjectFinalField(Field field) {
    418     return addMessage("Injected field %s cannot be final.", field);
    419   }
    420 
    421   public Errors cannotInjectAbstractMethod(Method method) {
    422     return addMessage("Injected method %s cannot be abstract.", method);
    423   }
    424 
    425   public Errors cannotInjectNonVoidMethod(Method method) {
    426     return addMessage("Injected method %s must return void.", method);
    427   }
    428 
    429   public Errors cannotInjectMethodWithTypeParameters(Method method) {
    430     return addMessage("Injected method %s cannot declare type parameters of its own.", method);
    431   }
    432 
    433   public Errors duplicateScopeAnnotations(
    434       Class<? extends Annotation> a, Class<? extends Annotation> b) {
    435     return addMessage("More than one scope annotation was found: %s and %s.", a, b);
    436   }
    437 
    438   public Errors recursiveBinding() {
    439     return addMessage("Binding points to itself.");
    440   }
    441 
    442   public Errors bindingAlreadySet(Key<?> key, Object source) {
    443     return addMessage("A binding to %s was already configured at %s.", key, convert(source));
    444   }
    445 
    446   public Errors jitBindingAlreadySet(Key<?> key) {
    447     return addMessage(
    448         "A just-in-time binding to %s was already configured on a parent injector.", key);
    449   }
    450 
    451   public Errors childBindingAlreadySet(Key<?> key, Set<Object> sources) {
    452     Formatter allSources = new Formatter();
    453     for (Object source : sources) {
    454       if (source == null) {
    455         allSources.format("%n    (bound by a just-in-time binding)");
    456       } else {
    457         allSources.format("%n    bound at %s", source);
    458       }
    459     }
    460     Errors errors =
    461         addMessage(
    462             "Unable to create binding for %s."
    463                 + " It was already configured on one or more child injectors or private modules"
    464                 + "%s%n"
    465                 + "  If it was in a PrivateModule, did you forget to expose the binding?",
    466             key, allSources.out());
    467     return errors;
    468   }
    469 
    470   public Errors errorCheckingDuplicateBinding(Key<?> key, Object source, Throwable t) {
    471     return addMessage(
    472         "A binding to %s was already configured at %s and an error was thrown "
    473             + "while checking duplicate bindings.  Error: %s",
    474         key, convert(source), t);
    475   }
    476 
    477   public Errors errorNotifyingTypeListener(
    478       TypeListenerBinding listener, TypeLiteral<?> type, Throwable cause) {
    479     return errorInUserCode(
    480         cause,
    481         "Error notifying TypeListener %s (bound at %s) of %s.%n Reason: %s",
    482         listener.getListener(),
    483         convert(listener.getSource()),
    484         type,
    485         cause);
    486   }
    487 
    488   public Errors exposedButNotBound(Key<?> key) {
    489     return addMessage("Could not expose() %s, it must be explicitly bound.", key);
    490   }
    491 
    492   public Errors keyNotFullySpecified(TypeLiteral<?> typeLiteral) {
    493     return addMessage("%s cannot be used as a key; It is not fully specified.", typeLiteral);
    494   }
    495 
    496   public Errors errorEnhancingClass(Class<?> clazz, Throwable cause) {
    497     return errorInUserCode(cause, "Unable to method intercept: %s", clazz);
    498   }
    499 
    500   public static Collection<Message> getMessagesFromThrowable(Throwable throwable) {
    501     if (throwable instanceof ProvisionException) {
    502       return ((ProvisionException) throwable).getErrorMessages();
    503     } else if (throwable instanceof ConfigurationException) {
    504       return ((ConfigurationException) throwable).getErrorMessages();
    505     } else if (throwable instanceof CreationException) {
    506       return ((CreationException) throwable).getErrorMessages();
    507     } else {
    508       return ImmutableSet.of();
    509     }
    510   }
    511 
    512   public Errors errorInUserCode(Throwable cause, String messageFormat, Object... arguments) {
    513     Collection<Message> messages = getMessagesFromThrowable(cause);
    514 
    515     if (!messages.isEmpty()) {
    516       return merge(messages);
    517     } else {
    518       return addMessage(cause, messageFormat, arguments);
    519     }
    520   }
    521 
    522   public Errors cannotInjectRawProvider() {
    523     return addMessage("Cannot inject a Provider that has no type parameter");
    524   }
    525 
    526   public Errors cannotInjectRawMembersInjector() {
    527     return addMessage("Cannot inject a MembersInjector that has no type parameter");
    528   }
    529 
    530   public Errors cannotInjectTypeLiteralOf(Type unsupportedType) {
    531     return addMessage("Cannot inject a TypeLiteral of %s", unsupportedType);
    532   }
    533 
    534   public Errors cannotInjectRawTypeLiteral() {
    535     return addMessage("Cannot inject a TypeLiteral that has no type parameter");
    536   }
    537 
    538   public void throwCreationExceptionIfErrorsExist() {
    539     if (!hasErrors()) {
    540       return;
    541     }
    542 
    543     throw new CreationException(getMessages());
    544   }
    545 
    546   public void throwConfigurationExceptionIfErrorsExist() {
    547     if (!hasErrors()) {
    548       return;
    549     }
    550 
    551     throw new ConfigurationException(getMessages());
    552   }
    553 
    554   // Guice no longer calls this, but external callers do
    555   public void throwProvisionExceptionIfErrorsExist() {
    556     if (!hasErrors()) {
    557       return;
    558     }
    559 
    560     throw new ProvisionException(getMessages());
    561   }
    562 
    563   public Errors merge(Collection<Message> messages) {
    564     List<Object> sources = getSources();
    565     for (Message message : messages) {
    566       addMessage(Messages.mergeSources(sources, message));
    567     }
    568     return this;
    569   }
    570 
    571   public Errors merge(Errors moreErrors) {
    572     if (moreErrors.root == root || moreErrors.root.errors == null) {
    573       return this;
    574     }
    575 
    576     merge(moreErrors.root.errors);
    577     return this;
    578   }
    579 
    580   public Errors merge(InternalProvisionException ipe) {
    581     merge(ipe.getErrors());
    582     return this;
    583   }
    584 
    585   private List<Object> getSources() {
    586     List<Object> sources = Lists.newArrayList();
    587     for (Errors e = this; e != null; e = e.parent) {
    588       if (e.source != SourceProvider.UNKNOWN_SOURCE) {
    589         sources.add(0, e.source);
    590       }
    591     }
    592     return sources;
    593   }
    594 
    595   public void throwIfNewErrors(int expectedSize) throws ErrorsException {
    596     if (size() == expectedSize) {
    597       return;
    598     }
    599 
    600     throw toException();
    601   }
    602 
    603   public ErrorsException toException() {
    604     return new ErrorsException(this);
    605   }
    606 
    607   public boolean hasErrors() {
    608     return root.errors != null;
    609   }
    610 
    611   public Errors addMessage(String messageFormat, Object... arguments) {
    612     return addMessage(null, messageFormat, arguments);
    613   }
    614 
    615   private Errors addMessage(Throwable cause, String messageFormat, Object... arguments) {
    616     addMessage(Messages.create(cause, getSources(), messageFormat, arguments));
    617     return this;
    618   }
    619 
    620   public Errors addMessage(Message message) {
    621     if (root.errors == null) {
    622       root.errors = Lists.newArrayList();
    623     }
    624     root.errors.add(message);
    625     return this;
    626   }
    627 
    628   // TODO(lukes): inline into callers
    629   public static String format(String messageFormat, Object... arguments) {
    630     return Messages.format(messageFormat, arguments);
    631   }
    632 
    633   public List<Message> getMessages() {
    634     if (root.errors == null) {
    635       return ImmutableList.of();
    636     }
    637 
    638     return new Ordering<Message>() {
    639       @Override
    640       public int compare(Message a, Message b) {
    641         return a.getSource().compareTo(b.getSource());
    642       }
    643     }.sortedCopy(root.errors);
    644   }
    645 
    646   public int size() {
    647     return root.errors == null ? 0 : root.errors.size();
    648   }
    649 
    650   // TODO(lukes): inline in callers.  There are some callers outside of guice, so this is difficult
    651   public static Object convert(Object o) {
    652     return Messages.convert(o);
    653   }
    654 
    655   // TODO(lukes): inline in callers.  There are some callers outside of guice, so this is difficult
    656   public static Object convert(Object o, ElementSource source) {
    657     return Messages.convert(o, source);
    658   }
    659 
    660   // TODO(lukes): inline in callers.  There are some callers outside of guice, so this is difficult
    661   public static void formatSource(Formatter formatter, Object source) {
    662     Messages.formatSource(formatter, source);
    663   }
    664 
    665 }
    666