Source:
<code>void collectIncludesRecursively( TypeElement module, Map<String, TypeElement> result, Deque<String> path) { Map<String, Object> annotation = CodeGen.getAnnotation(Module.class, module); if (annotation == null) { throw new ModuleValidationException("No @Module on " + module, module); } String name = module.getQualifiedName().toString(); if (path.contains(name)) { StringBuilder message = new StringBuilder("Cycle: ") .append(name) .append(" includes itself"); <START> if (path.size() == 1) { <END> message.append(" directly."); } else { message.append(" by way of:"); Iterator<String> pathIterator = path.descendingIterator(); while (pathIterator.hasNext()) { String pathElement = pathIterator.next(); message.append("\n->").append(pathElement).append(" includes"); } message.append("\n====>").append(name); } throw new ModuleValidationException(message.toString(), module); } else { path.push(name); } result.put(name, module); Types typeUtils = processingEnv.getTypeUtils(); List<Object> seedModules = new ArrayList<Object>(); seedModules.addAll(Arrays.asList((Object[]) annotation.get("includes"))); if (!annotation.get("addsTo").equals(Void.class)) seedModules.add(annotation.get("addsTo")); for (Object include : seedModules) { if (!(include instanceof TypeMirror)) { processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "Unexpected value for include: " + include + " in " + module, module); continue; } TypeElement includedModule = (TypeElement) typeUtils.asElement((TypeMirror) include); collectIncludesRecursively(includedModule, result, path); } }</code><technical_language>message syntax ProblemDetector? Dependency cycle: 0. dagger.ProblemDetectorTest$Rock bound dagger.ProblemDetectorTest$Rock 1. dagger.ProblemDetectorTest$Scissors bound dagger.ProblemDetectorTest$Scissors 2. dagger.ProblemDetectorTest$Paper bound dagger.ProblemDetectorTest$Paper 0. dagger.ProblemDetectorTest$Rock</technical_language>

Target:
void collectIncludesRecursively( TypeElement module, Map<String, TypeElement> result, Deque<String> path) { Map<String, Object> annotation = CodeGen.getAnnotation(Module.class, module); if (annotation == null) { throw new ModuleValidationException("No @Module on " + module, module); } String name = module.getQualifiedName().toString(); if (path.contains(name)) { StringBuilder message = new StringBuilder("Module Inclusion Cycle: "); if (path.size() == 1) { message.append(name).append(" includes itself directly."); } else { String current = null; String includer = name; for (int i = 0; path.size() > 0; i++) { current = includer; includer = path.pop(); message.append("\n").append(i).append(". ") .append(current).append(" included by ").append(includer); } message.append("\n0. ").append(name); } throw new ModuleValidationException(message.toString(), module); } result.put(name, module); Types typeUtils = processingEnv.getTypeUtils(); List<Object> seedModules = new ArrayList<Object>(); seedModules.addAll(Arrays.asList((Object[]) annotation.get("includes"))); if (!annotation.get("addsTo").equals(Void.class)) seedModules.add(annotation.get("addsTo")); for (Object include : seedModules) { if (!(include instanceof TypeMirror)) { processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "Unexpected value for include: " + include + " in " + module, module); continue; } TypeElement includedModule = (TypeElement) typeUtils.asElement((TypeMirror) include); path.push(name); collectIncludesRecursively(includedModule, result, path); path.pop(); } }