docs: private Map<String, List<ExecutableElement>> providerMethodsByClass(RoundEnvironment env) {
      Elements elementUtils = processingEnv.getElementUtils();
      Types types = processingEnv.getTypeUtils();
  
      Map<String, List<ExecutableElement>> result = new HashMap<String, List<ExecutableElement>>();
  
      provides:
      for (Element providerMethod : findProvidesMethods(env)) {
        switch (providerMethod.getEnclosingElement().getKind()) {
          case CLASS:
            break; // valid, move along
          default:
            // TODO(tbroyer): pass annotation information
            error("Unexpected @Provides on " + elementToString(providerMethod), providerMethod);
            continue;
        }
        TypeElement type = (TypeElement) providerMethod.getEnclosingElement();
        Set<Modifier> typeModifiers = type.getModifiers();
        if (typeModifiers.contains(PRIVATE)
            || typeModifiers.contains(ABSTRACT)) {
          error("Classes declaring @Provides methods must not be private or abstract: "
                  + type.getQualifiedName(), type);
          continue;
        }
  
        Set<Modifier> methodModifiers = providerMethod.getModifiers();
        if (methodModifiers.contains(PRIVATE)
            || methodModifiers.contains(ABSTRACT)
            || methodModifiers.contains(STATIC)) {
          error("@Provides methods must not be private, abstract or static: "
                  + type.getQualifiedName() + "." + providerMethod, providerMethod);
          continue;
        }
  
        ExecutableElement providerMethodAsExecutable = (ExecutableElement) providerMethod;
        if (!providerMethodAsExecutable.getThrownTypes().isEmpty()) {
          error("@Provides methods must not have a throws clause: "
              + type.getQualifiedName() + "." + providerMethod, providerMethod);
          continue;
        }
  
        // Invalidate return types.
        TypeMirror returnType = types.erasure(providerMethodAsExecutable.getReturnType());
        if (!returnType.getKind().equals(TypeKind.ERROR)) {
          // Validate if we have a type to validate (a type yet to be generated by other
          // processors is not "invalid" in this way, so ignore).
          for (String invalidTypeName : INVALID_RETURN_TYPES) {
            TypeElement invalidTypeElement = elementUtils.getTypeElement(invalidTypeName);
            if (invalidTypeElement != null && types.isSameType(returnType,
                types.erasure(invalidTypeElement.asType()))) {
              error(String.format("@Provides method must not return %s directly: %s.%s",
                  invalidTypeElement, type.getQualifiedName(), providerMethod), providerMethod);
              continue provides; // Skip to next provides method.
            }
          }
        }
  
        List<ExecutableElement> methods = result.get(type.getQualifiedName().toString());
        if (methods == null) {
          methods = new ArrayList<ExecutableElement>();
          result.put(type.getQualifiedName().toString(), methods);
        }
        methods.add(providerMethodAsExecutable);
      }
  
      TypeMirror objectType = elementUtils.getTypeElement("java.lang.Object").asType();
  
      // Catch any stray modules without @Provides since their injectable types
      // should still be registered and a ModuleAdapter should still be written.
      for (Element module : env.getElementsAnnotatedWith(Module.class)) {
        if (!module.getKind().equals(ElementKind.CLASS)) {
          error("Modules must be classes: " + elementToString(module), module);
          continue;
        }
  
        TypeElement moduleType = (TypeElement) module;
  
        // Verify that all modules do not extend from non-Object types.
        if (!types.isSameType(moduleType.getSuperclass(), objectType)) {
          error("Modules must not extend from other classes: " + elementToString(module), module);
        }
  
        String moduleName = moduleType.getQualifiedName().toString();
        if (result.containsKey(moduleName)) continue;
        result.put(moduleName, new ArrayList<ExecutableElement>());
      }
      return result;
    }