Files
allwpilib/javacPlugin/src/test/java/org/wpilib/javacplugin/CompileTestUtils.java

49 lines
1.6 KiB
Java
Raw Normal View History

[javac plugin] Add compile-time checks for unsafe or incorrect coroutine usage (#8289) CoroutineYieldInLoopDetector This checks for while loops where coroutines are in scope but without calling a blocking method on at least one of those coroutines: ``` drivetrain.run(theCoroutine -> { while (drivetrain.getDistance() < 10) { // ERROR: "Missing call to `theCoroutine.yield()` inside loop" drivetrain.setSpeed(1); } }); ``` Note that, because we assume most looping constructs in commands will use while loops, we don't check for-loops, for-each loops, or do-while loops. This check can be disabled with `@SuppressWarnings("CoroutineYieldInLoop")` CodeAfterCoroutineParkDetector Essentially acts like the Java compiler's check for code after a while (true) loop, but for coroutine.park(): ``` drivetrain.run(theCoroutine -> { drivetrain.setSpeed(1.0); theCoroutine.park(); drivetrain.setSpeed(0.0); // ERROR: "Unreachable statement: `theCoroutine.park()` will never exit" }); ``` This check can be disabled with `@SuppressWarnings("CodeAfterCoroutinePark")` IncorrectCoroutineUseDetector Checks for usage of captured (outer) coroutine parameters and assignments to fields. ``` drivetrain.run(outer -> { outer.await(arm.run(inner -> { outer.yield(); // ERROR: "Coroutine `outer` may not be in scope. Consider using `inner`" })) }); ``` This check can be disabled with `@SuppressWarnings("CoroutineMayNotBeInScope")` ``` private Coroutine coroutineField; drivetrain.run(co -> coroutineField = co); // ERROR: "Captured coroutines may not be stored in fields" ``` This check can be disabled with `@SuppressWarnings("CoroutineCapture")`
2025-11-01 20:27:08 -04:00
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package org.wpilib.javacplugin;
import java.io.IOException;
import java.util.List;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
public final class CompileTestUtils {
public static final int kJavaVersion = 17;
public static final List<Object> kJavaVersionOptions =
List.of("-source", kJavaVersion, "-target", kJavaVersion);
private CompileTestUtils() {
// Utility class
}
/**
* Gets the source code for a given compiler error.
*
* @param diagnostic The diagnostic to get the source for.
* @return The source code for the given diagnostic.
*/
public static String getErrorSource(Diagnostic<? extends JavaFileObject> diagnostic) {
try (var reader = diagnostic.getSource().openReader(true)) {
int sourceLength = (int) (diagnostic.getEndPosition() - diagnostic.getStartPosition() + 1);
char[] buf = new char[sourceLength];
long skipCnt = reader.skip(diagnostic.getStartPosition());
if (skipCnt != diagnostic.getStartPosition()) {
// Didn't skip to the expected position; bail
return "<unknown source>";
}
int readCnt = reader.read(buf);
if (readCnt != sourceLength) {
// Didn't read the expected length of text; bail
return "<unknown source>";
}
return new String(buf);
} catch (IOException e) {
return "<unknown source>";
}
}
}