core/src/com/google/inject/internal/Errors.java
<T> Errors missingImplementationWithHint(Key<T> key, Injector injector) {
    StringBuilder sb = new StringBuilder();

    sb.append(format("No implementation for %s was bound.", key));

    // Keys which have similar strings as the desired key
    List<String> possibleMatches = new ArrayList<>();

    // Check for other keys that may have the same type,
    // but not the same annotation
    TypeLiteral<T> type = key.getTypeLiteral();
    List<Binding<T>> sameTypes = injector.findBindingsByType(type);
    if (!sameTypes.isEmpty()) {
      sb.append(format("%n  Did you mean?"));
      int howMany = Math.min(sameTypes.size(), MAX_MATCHING_TYPES_REPORTED);
      for (int i = 0; i < howMany; ++i) {
        // TODO: Look into a better way to prioritize suggestions. For example, possbily
        // use levenshtein distance of the given annotation vs actual annotation.
        sb.append(format("%n    * %s", sameTypes.get(i).getKey()));
      }
      int remaining = sameTypes.size() - MAX_MATCHING_TYPES_REPORTED;
      if (remaining > 0) {
        String plural = (remaining == 1) ? "" : "s";
        sb.append(format("%n    %d more binding%s with other annotations.", remaining, plural));
      }
    } else {
      // For now, do a simple substring search for possibilities. This can help spot
      // issues when there are generics being used (such as a wrapper class) and the
      // user has forgotten they need to bind based on the wrapper, not the underlying
      // class. In the future, consider doing a strict in-depth type search.
      // TODO: Look into a better way to prioritize suggestions. For example, possbily
      // use levenshtein distance of the type literal strings.
      String want = type.toString();
      Map<Key<?>, Binding<?>> bindingMap = injector.getAllBindings();
      for (Key<?> bindingKey : bindingMap.keySet()) {
        String have = bindingKey.getTypeLiteral().toString();
        if (have.contains(want) || want.contains(have)) {
          Formatter fmt = new Formatter();
          Messages.formatSource(fmt, bindingMap.get(bindingKey).getSource());
          String match = String.format("%s bound%s", convert(bindingKey), fmt.toString());
          possibleMatches.add(match);
          // TODO: Consider a check that if there are more than some number of results,
          // don't suggest any.
          if (possibleMatches.size() > MAX_RELATED_TYPES_REPORTED) {
            // Early exit if we have found more than we need.
            break;
          }
        }
      }

      if ((possibleMatches.size() > 0) && (possibleMatches.size() <= MAX_RELATED_TYPES_REPORTED)) {
        sb.append(format("%n  Did you mean?"));
        for (String possibleMatch : possibleMatches) {
          sb.append(format("%n    %s", possibleMatch));
        }
      }
    }

    // If where are no possibilities to suggest, then handle the case of missing
    // annotations on simple types. This is usually a bad idea.
    if (sameTypes.isEmpty()
        && possibleMatches.isEmpty()
        && key.getAnnotation() == null
        && COMMON_AMBIGUOUS_TYPES.contains(key.getTypeLiteral().getRawType())) {
      // We don't recommend using such simple types without annotations.
      sb.append(format("%nThe key seems very generic, did you forget an annotation?"));
    }

    return addMessage(sb.toString());
  }
<T> Errors missingImplementationWithHint(Key<T> key, Injector injector) {
    StringBuilder sb = new StringBuilder();

    sb.append(format("No implementation for %s was bound.", key));

    // Keys which have similar strings as the desired key
    List<String> possibleMatches = new ArrayList<>();

    // Check for other keys that may have the same type,
    // but not the same annotation
    TypeLiteral<T> type = key.getTypeLiteral();
    List<Binding<T>> sameTypes = injector.findBindingsByType(type);
    if (!sameTypes.isEmpty()) {
      sb.append(format("%n  Did you mean?"));
      int howMany = Math.min(sameTypes.size(), MAX_MATCHING_TYPES_REPORTED);
      for (int i = 0; i < howMany; ++i) {
        // TODO: Look into a better way to prioritize suggestions. For example, possbily
        // use levenshtein distance of the given annotation vs actual annotation.
        sb.append(format("%n    * %s", sameTypes.get(i).getKey()));
      }
      int remaining = sameTypes.size() - MAX_MATCHING_TYPES_REPORTED;
      if (remaining > 0) {
        String plural = (remaining == 1) ? "" : "s";
        sb.append(format("%n    %d more binding%s with other annotations.", remaining, plural));
      }
    } else {
      // For now, do a simple substring search for possibilities. This can help spot
      // issues when there are generics being used (such as a wrapper class) and the
      // user has forgotten they need to bind based on the wrapper, not the underlying
      // class. In the future, consider doing a strict in-depth type search.
      // TODO: Look into a better way to prioritize suggestions. For example, possbily
      // use levenshtein distance of the type literal strings.
      String want = type.toString();
      Map<Key<?>, Binding<?>> bindingMap = injector.getAllBindings();
      for (Key<?> bindingKey : bindingMap.keySet()) {
        String have = bindingKey.getTypeLiteral().toString();
        if (have.contains(want) || want.contains(have)) {
          Formatter fmt = new Formatter();
          Messages.formatSource(fmt, bindingMap.get(bindingKey).getSource());
          String match = String.format("%s bound%s", convert(bindingKey), fmt.toString());
          possibleMatches.add(match);
          // TODO: Consider a check that if there are more than some number of results,
          // don't suggest any.
          if (possibleMatches.size() > MAX_RELATED_TYPES_REPORTED) {
            // Early exit if we have found more than we need.
            break;
          }
        }
      }

      if ((possibleMatches.size() > 0) && (possibleMatches.size() <= MAX_RELATED_TYPES_REPORTED)) {
        sb.append(format("%n  Did you mean?"));
        for (String possibleMatch : possibleMatches) {
          sb.append(format("%n    %s", possibleMatch));
        }
      }
    }

    // If where are no possibilities to suggest, then handle the case of missing
    // annotations on simple types. This is usually a bad idea.
    if (sameTypes.isEmpty()
        && possibleMatches.isEmpty()
        && key.getAnnotation() == null
        && COMMON_AMBIGUOUS_TYPES.contains(key.getTypeLiteral().getRawType())) {
      // We don't recommend using such simple types without annotations.
      sb.append(format("%nThe key seems very generic, did you forget an annotation?"));
    }

    return addMessage(sb.toString());
  }
Within guice's core, allow for better missing binding messages