diff --git a/epilogue-processor/src/main/java/edu/wpi/first/epilogue/processor/AnnotationProcessor.java b/epilogue-processor/src/main/java/edu/wpi/first/epilogue/processor/AnnotationProcessor.java index 98d3abb872..22f0e89459 100644 --- a/epilogue-processor/src/main/java/edu/wpi/first/epilogue/processor/AnnotationProcessor.java +++ b/epilogue-processor/src/main/java/edu/wpi/first/epilogue/processor/AnnotationProcessor.java @@ -86,7 +86,7 @@ public class AnnotationProcessor extends AbstractProcessor { .getMessager() .printMessage( Diagnostic.Kind.ERROR, - "Custom logger classes should have a @CustomLoggerFor annotation", + "[EPILOGUE] Custom logger classes should have a @CustomLoggerFor annotation", e); }); @@ -284,7 +284,7 @@ public class AnnotationProcessor extends AbstractProcessor { .getMessager() .printMessage( Diagnostic.Kind.ERROR, - "Logger classes must have a public no-argument constructor", + "[EPILOGUE] Logger classes must have a public no-argument constructor", annotatedElement); continue; } @@ -306,7 +306,17 @@ public class AnnotationProcessor extends AbstractProcessor { .getMessager() .printMessage( Diagnostic.Kind.ERROR, - "Multiple custom loggers detected for type " + targetType, + "[EPILOGUE] Multiple custom loggers detected for type " + targetType, + annotatedElement); + continue; + } + + if (annotatedElement instanceof TypeElement t && !t.getTypeParameters().isEmpty()) { + processingEnv + .getMessager() + .printMessage( + Diagnostic.Kind.ERROR, + "[EPILOGUE] Custom logger classes cannot take generic type arguments", annotatedElement); continue; } @@ -318,7 +328,7 @@ public class AnnotationProcessor extends AbstractProcessor { .getMessager() .printMessage( Diagnostic.Kind.ERROR, - "Not a subclass of ClassSpecificLogger<" + targetType + ">", + "[EPILOGUE] Not a subclass of ClassSpecificLogger<" + targetType + ">", annotatedElement); continue; } @@ -368,7 +378,7 @@ public class AnnotationProcessor extends AbstractProcessor { .getMessager() .printMessage( Diagnostic.Kind.ERROR, - "Could not write logger file for " + clazz.getQualifiedName(), + "[EPILOGUE] Could not write logger file for " + clazz.getQualifiedName(), clazz); e.printStackTrace(System.err); } diff --git a/epilogue-processor/src/main/java/edu/wpi/first/epilogue/processor/ConfiguredLoggerHandler.java b/epilogue-processor/src/main/java/edu/wpi/first/epilogue/processor/ConfiguredLoggerHandler.java index 98b4def04f..f7e6744edf 100644 --- a/epilogue-processor/src/main/java/edu/wpi/first/epilogue/processor/ConfiguredLoggerHandler.java +++ b/epilogue-processor/src/main/java/edu/wpi/first/epilogue/processor/ConfiguredLoggerHandler.java @@ -22,13 +22,22 @@ public class ConfiguredLoggerHandler extends ElementHandler { @Override public boolean isLoggable(Element element) { - return m_customLoggers.containsKey(dataType(element)); + return m_customLoggers.keySet().stream() + .anyMatch(m -> m_processingEnv.getTypeUtils().isAssignable(dataType(element), m)); } @Override public String logInvocation(Element element) { var dataType = dataType(element); - var loggerType = m_customLoggers.get(dataType); + var loggerType = + m_customLoggers.entrySet().stream() + .filter( + e -> { + return m_processingEnv.getTypeUtils().isAssignable(dataType, e.getKey()); + }) + .findFirst() + .orElseThrow() + .getValue(); return "Epilogue." + StringUtils.lowerCamelCase(loggerType.asElement().getSimpleName()) diff --git a/epilogue-processor/src/test/java/edu/wpi/first/epilogue/processor/AnnotationProcessorTest.java b/epilogue-processor/src/test/java/edu/wpi/first/epilogue/processor/AnnotationProcessorTest.java index 603ec39d65..e5d67a2c22 100644 --- a/epilogue-processor/src/test/java/edu/wpi/first/epilogue/processor/AnnotationProcessorTest.java +++ b/epilogue-processor/src/test/java/edu/wpi/first/epilogue/processor/AnnotationProcessorTest.java @@ -1470,6 +1470,106 @@ class AnnotationProcessorTest { assertLoggerGenerates(source, expectedGeneratedSource); } + @Test + void customGenericLogger() { + String source = + """ + package edu.wpi.first.epilogue; + + import edu.wpi.first.epilogue.logging.*; + import edu.wpi.first.math.numbers.*; + import edu.wpi.first.math.Num; + import edu.wpi.first.math.Vector; + + @CustomLoggerFor(Vector.class) + class VectorLogger extends ClassSpecificLogger> { + public VectorLogger() { + super((Class) Vector.class); + } + + @Override + public void update(DataLogger dataLogger, Vector object) { + // Implementation is irrelevant + } + } + + @Logged + class Example { + Vector vec; + } + """; + + String expectedGeneratedSource = + """ + package edu.wpi.first.epilogue; + + import edu.wpi.first.epilogue.Logged; + import edu.wpi.first.epilogue.Epilogue; + import edu.wpi.first.epilogue.logging.ClassSpecificLogger; + import edu.wpi.first.epilogue.logging.DataLogger; + + public class ExampleLogger extends ClassSpecificLogger { + public ExampleLogger() { + super(Example.class); + } + + @Override + public void update(DataLogger dataLogger, Example object) { + if (Epilogue.shouldLog(Logged.Importance.DEBUG)) { + Epilogue.vectorLogger.tryUpdate(dataLogger.getSubLogger("vec"), object.vec, Epilogue.getConfig().errorHandler); + } + } + } + """; + + assertLoggerGenerates(source, expectedGeneratedSource); + } + + @Test + void genericLoggerForGenericType() { + String source = + """ + package edu.wpi.first.epilogue; + + import edu.wpi.first.epilogue.logging.*; + + class Generic { } + + @CustomLoggerFor(Generic.class) + // Invalid: loggers cannot take type arguments + class GenericLogger extends ClassSpecificLogger> { + public GenericLogger() { + super((Class) Generic.class); + } + + @Override + public void update(DataLogger dataLogger, Generic object) { + // Implementation is irrelevant + } + } + + @Logged + class Example { + Generic genericField; + } + """; + + Compilation compilation = + javac() + .withOptions(kJavaVersionOptions) + .withProcessors(new AnnotationProcessor()) + .compile(JavaFileObjects.forSourceString("edu.wpi.first.epilogue.Example", source)); + + assertThat(compilation).failed(); + assertThat(compilation).hadErrorCount(1); + + assertCompilationError( + "[EPILOGUE] Custom logger classes cannot take generic type arguments", + 9, + 1, + compilation.errors().get(0)); + } + @Test void warnsAboutNonLoggableFields() { String source =