mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-19 00:41:43 +00:00
Merge branch 'main' into 2027
This commit is contained in:
72
.github/workflows/documentation.yml
vendored
72
.github/workflows/documentation.yml
vendored
@@ -1,72 +0,0 @@
|
||||
name: Documentation
|
||||
|
||||
on: [push, workflow_dispatch]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
BASE_PATH: allwpilib/docs
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: "Documentation - Publish"
|
||||
runs-on: ubuntu-24.04
|
||||
if: github.repository == 'wpilibsuite/allwpilib' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
|
||||
concurrency: ci-docs-publish
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- uses: gradle/actions/wrapper-validation@v4
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 21
|
||||
- name: Set environment variables (Development)
|
||||
run: |
|
||||
echo "BRANCH=development" >> $GITHUB_ENV
|
||||
if: github.ref == 'refs/heads/main'
|
||||
- name: Set environment variables (Tag)
|
||||
run: |
|
||||
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
|
||||
echo "BRANCH=beta" >> $GITHUB_ENV
|
||||
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
|
||||
- name: Set environment variables (Release)
|
||||
run: |
|
||||
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
|
||||
echo "BRANCH=release" >> $GITHUB_ENV
|
||||
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, 'alpha') && !contains(github.ref, 'beta') && !contains(github.ref, '2027')
|
||||
- name: Set environment variables (2027)
|
||||
run: |
|
||||
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
|
||||
echo "BRANCH=2027" >> $GITHUB_ENV
|
||||
if: startsWith(github.ref, 'refs/tags/v') && contains(github.ref, '2027')
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew docs:generateJavaDocs docs:doxygen -PbuildServer ${{ env.EXTRA_GRADLE_ARGS }}
|
||||
- name: Install SSH Client 🔑
|
||||
uses: webfactory/ssh-agent@v0.9.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.GH_DEPLOY_KEY }}
|
||||
- name: Deploy 🚀
|
||||
uses: JamesIves/github-pages-deploy-action@v4.6.1
|
||||
with:
|
||||
ssh-key: true
|
||||
repository-name: wpilibsuite/wpilibsuite.github.io
|
||||
branch: allwpilib-${{ env.BRANCH }}
|
||||
clean: true
|
||||
single-commit: true
|
||||
folder: docs/build/docs
|
||||
- name: Trigger Workflow
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.DISPATCH_PAT_TOKEN }}
|
||||
script: |
|
||||
github.rest.actions.createWorkflowDispatch({
|
||||
owner: context.repo.owner,
|
||||
repo: 'wpilibsuite.github.io',
|
||||
workflow_id: 'static.yml',
|
||||
ref: 'main',
|
||||
})
|
||||
3
.github/workflows/fix_compile_commands.py
vendored
3
.github/workflows/fix_compile_commands.py
vendored
@@ -31,6 +31,9 @@ def main():
|
||||
# Replace GCC warning argument with one Clang recognizes
|
||||
elif arg == "-Wno-maybe-uninitialized":
|
||||
out_args.append("-Wno-uninitialized")
|
||||
# Skip GCC-specific warning argument
|
||||
elif arg == "-Wno-error=restrict":
|
||||
pass
|
||||
else:
|
||||
out_args.append(arg)
|
||||
|
||||
|
||||
69
.github/workflows/gradle.yml
vendored
69
.github/workflows/gradle.yml
vendored
@@ -194,7 +194,7 @@ jobs:
|
||||
run: echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
|
||||
if: startsWith(github.ref, 'refs/tags/v2027')
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew docs:zipDocs --build-cache -PbuildServer ${{ env.EXTRA_GRADLE_ARGS }}
|
||||
run: ./gradlew docs:zipDocs --build-cache -PbuildServer -PdocWarningsAsErrors ${{ env.EXTRA_GRADLE_ARGS }}
|
||||
env:
|
||||
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
@@ -203,6 +203,73 @@ jobs:
|
||||
name: Documentation
|
||||
path: docs/build/outputs
|
||||
|
||||
publish:
|
||||
name: "Documentation - Publish"
|
||||
runs-on: ubuntu-22.04
|
||||
if: github.repository == 'wpilibsuite/allwpilib' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
|
||||
needs: [build-documentation]
|
||||
concurrency: ci-docs-publish
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- name: Download docs artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: Documentation
|
||||
- name: Make output directories
|
||||
run: |
|
||||
mkdir -p docs/tmp/doxygen/html
|
||||
mkdir -p docs/tmp/javadoc
|
||||
- name: Extract docs
|
||||
run: |
|
||||
unzip _GROUP_edu_wpi_first_wpilibc_ID_documentation_CLS.zip -d docs/tmp/doxygen/html
|
||||
unzip _GROUP_edu_wpi_first_wpilibj_ID_documentation_CLS.zip -d docs/tmp/javadoc
|
||||
- name: Set environment variables (Development)
|
||||
run: |
|
||||
echo "BRANCH=development" >> $GITHUB_ENV
|
||||
if: github.ref == 'refs/heads/main'
|
||||
- name: Set environment variables (Tag)
|
||||
run: |
|
||||
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
|
||||
echo "BRANCH=beta" >> $GITHUB_ENV
|
||||
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '2027')
|
||||
- name: Set environment variables (Release)
|
||||
run: |
|
||||
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
|
||||
echo "BRANCH=release" >> $GITHUB_ENV
|
||||
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, 'alpha') && !contains(github.ref, 'beta') && !contains(github.ref, '2027')
|
||||
- name: Set environment variables (2027)
|
||||
run: |
|
||||
echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
|
||||
echo "BRANCH=2027" >> $GITHUB_ENV
|
||||
if: startsWith(github.ref, 'refs/tags/v') && contains(github.ref, '2027')
|
||||
- name: Install SSH Client 🔑
|
||||
uses: webfactory/ssh-agent@v0.9.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.GH_DEPLOY_KEY }}
|
||||
- name: Deploy 🚀
|
||||
uses: JamesIves/github-pages-deploy-action@v4.6.1
|
||||
with:
|
||||
ssh-key: true
|
||||
repository-name: wpilibsuite/wpilibsuite.github.io
|
||||
branch: allwpilib-${{ env.BRANCH }}
|
||||
clean: true
|
||||
single-commit: true
|
||||
folder: docs/tmp
|
||||
- name: Trigger Workflow
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.DISPATCH_PAT_TOKEN }}
|
||||
script: |
|
||||
github.rest.actions.createWorkflowDispatch({
|
||||
owner: context.repo.owner,
|
||||
repo: 'wpilibsuite.github.io',
|
||||
workflow_id: 'static.yml',
|
||||
ref: 'main',
|
||||
})
|
||||
|
||||
combine:
|
||||
name: Combine
|
||||
needs: [build-docker, build-host, build-documentation]
|
||||
|
||||
15
.github/workflows/lint-format.yml
vendored
15
.github/workflows/lint-format.yml
vendored
@@ -123,18 +123,3 @@ jobs:
|
||||
echo '' >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
if: ${{ failure() }}
|
||||
|
||||
documentation:
|
||||
name: "Documentation"
|
||||
runs-on: ubuntu-24.04
|
||||
needs: [validation]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 21
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew docs:zipDocs -PbuildServer -PdocWarningsAsErrors ${{ env.EXTRA_GRADLE_ARGS }}
|
||||
|
||||
@@ -283,6 +283,7 @@ endif()
|
||||
set(FILENAME_DEP_REPLACE "get_filename_component(SELF_DIR \"$\{CMAKE_CURRENT_LIST_FILE\}\" PATH)")
|
||||
set(SELF_DIR "$\{SELF_DIR\}")
|
||||
set(WPIUNITS_DEP_REPLACE_IMPL "find_dependency(wpiunits)")
|
||||
set(WPIANNOTATIONS_DEP_REPLACE_IMPL "find_dependency(wpiannotations)")
|
||||
set(WPIUTIL_DEP_REPLACE "find_dependency(wpiutil)")
|
||||
set(DATALOG_DEP_REPLACE "find_dependency(datalog)")
|
||||
add_subdirectory(wpiutil)
|
||||
@@ -305,6 +306,10 @@ if(WITH_WPIMATH)
|
||||
add_subdirectory(wpimath)
|
||||
endif()
|
||||
|
||||
if(WITH_JAVA)
|
||||
add_subdirectory(wpiannotations)
|
||||
endif()
|
||||
|
||||
if(WITH_WPIUNITS AND NOT WITH_WPIMATH)
|
||||
# In case of building wpiunits standalone
|
||||
set(WPIUNITS_DEP_REPLACE ${WPIUNITS_DEP_REPLACE_IMPL})
|
||||
|
||||
@@ -181,7 +181,11 @@ tasks.register('deployStatic') {
|
||||
model {
|
||||
components {
|
||||
benchmarkCpp(NativeExecutableSpec) {
|
||||
targetBuildTypes 'debug'
|
||||
if (project.hasProperty('ciDebugOnly')) {
|
||||
targetBuildTypes 'debug'
|
||||
} else {
|
||||
targetBuildTypes 'release'
|
||||
}
|
||||
sources {
|
||||
cpp {
|
||||
source {
|
||||
@@ -235,7 +239,11 @@ model {
|
||||
}
|
||||
}
|
||||
benchmarkCppStatic(NativeExecutableSpec) {
|
||||
targetBuildTypes 'debug'
|
||||
if (project.hasProperty('ciDebugOnly')) {
|
||||
targetBuildTypes 'debug'
|
||||
} else {
|
||||
targetBuildTypes 'release'
|
||||
}
|
||||
nativeUtils.excludeBinariesFromStrip(it)
|
||||
sources {
|
||||
cpp {
|
||||
|
||||
@@ -9,5 +9,5 @@ repositories {
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
implementation "edu.wpi.first:native-utils:2025.12.1"
|
||||
implementation "edu.wpi.first:native-utils:2026.0.0"
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ evaluationDependsOn(':cscore')
|
||||
evaluationDependsOn(':epilogue-runtime')
|
||||
evaluationDependsOn(':hal')
|
||||
evaluationDependsOn(':ntcore')
|
||||
evaluationDependsOn(':wpiannotations')
|
||||
evaluationDependsOn(':wpilibNewCommands')
|
||||
evaluationDependsOn(':wpilibc')
|
||||
evaluationDependsOn(':wpilibj')
|
||||
@@ -77,64 +78,19 @@ doxygen.sourceSets.main {
|
||||
// apriltag
|
||||
exclude 'apriltag_pose.h'
|
||||
|
||||
// Eigen
|
||||
exclude 'Eigen/**'
|
||||
exclude 'unsupported/**'
|
||||
|
||||
// LLVM
|
||||
exclude 'wpi/AlignOf.h'
|
||||
exclude 'wpi/Casting.h'
|
||||
exclude 'wpi/Chrono.h'
|
||||
exclude 'wpi/Compiler.h'
|
||||
exclude 'wpi/ConvertUTF.h'
|
||||
exclude 'wpi/DenseMap.h'
|
||||
exclude 'wpi/DenseMapInfo.h'
|
||||
exclude 'wpi/Endian.h'
|
||||
exclude 'wpi/EpochTracker.h'
|
||||
exclude 'wpi/Errc.h'
|
||||
exclude 'wpi/Errno.h'
|
||||
exclude 'wpi/ErrorHandling.h'
|
||||
exclude 'wpi/bit.h'
|
||||
exclude 'wpi/fs.h'
|
||||
exclude 'wpi/FunctionExtras.h'
|
||||
exclude 'wpi/function_ref.h'
|
||||
exclude 'wpi/iterator.h'
|
||||
exclude 'wpi/iterator_range.h'
|
||||
exclude 'wpi/ManagedStatic.h'
|
||||
exclude 'wpi/MathExtras.h'
|
||||
exclude 'wpi/MemAlloc.h'
|
||||
exclude 'wpi/PointerIntPair.h'
|
||||
exclude 'wpi/PointerLikeTypeTraits.h'
|
||||
exclude 'wpi/PointerUnion.h'
|
||||
exclude 'wpi/raw_os_ostream.h'
|
||||
exclude 'wpi/raw_ostream.h'
|
||||
exclude 'wpi/SmallPtrSet.h'
|
||||
exclude 'wpi/SmallSet.h'
|
||||
exclude 'wpi/SmallString.h'
|
||||
exclude 'wpi/SmallVector.h'
|
||||
exclude 'wpi/StringExtras.h'
|
||||
exclude 'wpi/StringMap.h'
|
||||
exclude 'wpi/SwapByteOrder.h'
|
||||
exclude 'wpi/type_traits.h'
|
||||
exclude 'wpi/VersionTuple.h'
|
||||
exclude 'wpi/WindowsError.h'
|
||||
|
||||
// fmtlib
|
||||
exclude 'fmt/**'
|
||||
|
||||
// libuv
|
||||
exclude 'uv.h'
|
||||
exclude 'uv/**'
|
||||
exclude 'wpinet/uv/**'
|
||||
|
||||
// json
|
||||
exclude 'wpi/adl_serializer.h'
|
||||
exclude 'wpi/byte_container_with_subtype.h'
|
||||
exclude 'wpi/detail/**'
|
||||
exclude 'wpi/json.h'
|
||||
exclude 'wpi/json_fwd.h'
|
||||
exclude 'wpi/ordered_map.h'
|
||||
exclude 'wpi/thirdparty/**'
|
||||
|
||||
// mpack
|
||||
exclude 'wpi/mpack.h'
|
||||
@@ -218,6 +174,7 @@ task generateJavaDocs(type: Javadoc) {
|
||||
source project(':epilogue-runtime').sourceSets.main.java
|
||||
source project(':hal').sourceSets.main.java
|
||||
source project(':ntcore').sourceSets.main.java
|
||||
source project(':wpiannotations').sourceSets.main.java
|
||||
source project(':wpilibNewCommands').sourceSets.main.java
|
||||
source project(':wpilibj').sourceSets.main.java
|
||||
source project(':wpimath').sourceSets.main.java
|
||||
|
||||
@@ -149,9 +149,14 @@ void glass::DisplayFMSReadOnly(FMSModel* model) {
|
||||
}
|
||||
}
|
||||
if (auto data = model->GetGameSpecificMessageData()) {
|
||||
wpi::SmallString<64> gsmBuf;
|
||||
ImGui::Text("Game Specific: %s",
|
||||
exists ? data->GetValue(gsmBuf).data() : "?");
|
||||
if (exists) {
|
||||
wpi::SmallString<64> gsmBuf;
|
||||
std::string_view gsm = data->GetValue(gsmBuf);
|
||||
ImGui::Text("Game Specific: %.*s", static_cast<int>(gsm.size()),
|
||||
gsm.data());
|
||||
} else {
|
||||
ImGui::TextUnformatted("Game Specific: ?");
|
||||
}
|
||||
}
|
||||
|
||||
if (!exists) {
|
||||
|
||||
11
javacPlugin/BUILD.bazel
Normal file
11
javacPlugin/BUILD.bazel
Normal file
@@ -0,0 +1,11 @@
|
||||
load("@rules_java//java:defs.bzl", "java_plugin")
|
||||
|
||||
java_plugin(
|
||||
name = "plugin",
|
||||
srcs = glob(["src/main/java/**/*.java"]),
|
||||
resources = glob(["src/main/resources/**"]),
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//wpiannotations",
|
||||
],
|
||||
)
|
||||
17
javacPlugin/build.gradle
Normal file
17
javacPlugin/build.gradle
Normal file
@@ -0,0 +1,17 @@
|
||||
ext {
|
||||
useJava = true
|
||||
useCpp = false
|
||||
baseId = 'wpilibj-javac-plugin'
|
||||
groupId = 'org.wpilib'
|
||||
|
||||
nativeName = ''
|
||||
devMain = ''
|
||||
}
|
||||
|
||||
apply from: "${rootDir}/shared/java/javacommon.gradle"
|
||||
|
||||
dependencies {
|
||||
implementation project(':wpiannotations')
|
||||
testImplementation 'com.google.testing.compile:compile-testing:+'
|
||||
testImplementation project(':wpilibNewCommands')
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
// 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 com.sun.source.tree.CompilationUnitTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import com.sun.source.tree.NewClassTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import com.sun.source.util.JavacTask;
|
||||
import com.sun.source.util.TaskEvent;
|
||||
import com.sun.source.util.TaskListener;
|
||||
import com.sun.source.util.TreeScanner;
|
||||
import com.sun.source.util.Trees;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.tools.Diagnostic;
|
||||
import org.wpilib.annotation.NoDiscard;
|
||||
|
||||
/** Checks for usages of methods that require their return values to be used. */
|
||||
public class ReturnValueUsedListener implements TaskListener {
|
||||
private final JavacTask m_task;
|
||||
private final Set<CompilationUnitTree> m_visitedCUs = new HashSet<>();
|
||||
|
||||
public ReturnValueUsedListener(JavacTask task) {
|
||||
m_task = task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finished(TaskEvent e) {
|
||||
// We override `finished` instead of `started` because we want to run after the
|
||||
// ANALYZE attribution phase has completed and assigned types to elements in the AST
|
||||
// Track the visited CUs to avoid re-processing the same CU multiple times when we call
|
||||
// `Trees.getElement()` on a tree path.
|
||||
if (e.getKind() == TaskEvent.Kind.ANALYZE && m_visitedCUs.add(e.getCompilationUnit())) {
|
||||
e.getCompilationUnit().accept(new Scanner(e.getCompilationUnit()), null);
|
||||
}
|
||||
}
|
||||
|
||||
private final class Scanner extends TreeScanner<Void, Void> {
|
||||
private final CompilationUnitTree m_root;
|
||||
private final Trees m_trees;
|
||||
|
||||
Scanner(CompilationUnitTree compilationUnit) {
|
||||
m_root = compilationUnit;
|
||||
m_trees = Trees.instance(m_task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitMethodInvocation(MethodInvocationTree node, Void unused) {
|
||||
checkIgnoredExpression(node);
|
||||
return super.visitMethodInvocation(node, unused);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitNewClass(NewClassTree node, Void unused) {
|
||||
checkIgnoredExpression(node);
|
||||
return super.visitNewClass(node, unused);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common logic for both method invocations and constructor calls when they appear as
|
||||
* stand-alone expression statements (i.e., their result is ignored).
|
||||
*/
|
||||
private void checkIgnoredExpression(Tree node) {
|
||||
var path = m_trees.getPath(m_root, node);
|
||||
|
||||
// Walk the tree upwards to see if the node is directly or indirectly annotated with
|
||||
// @SuppressWarnings("NoDiscard") or @SuppressWarnings("all"). If so, then we ignore any
|
||||
// @NoDiscard messages for this node
|
||||
for (var currentPath = path; currentPath != null; currentPath = currentPath.getParentPath()) {
|
||||
var element = m_trees.getElement(currentPath);
|
||||
if (element == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (element.getAnnotation(SuppressWarnings.class) != null) {
|
||||
String[] suppressions = element.getAnnotation(SuppressWarnings.class).value();
|
||||
for (String suppression : suppressions) {
|
||||
if ("NoDiscard".equals(suppression) || "all".equals(suppression)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var parentPath = (path == null) ? null : path.getParentPath();
|
||||
if (parentPath == null || parentPath.getLeaf().getKind() != Tree.Kind.EXPRESSION_STATEMENT) {
|
||||
// If the parent node is an expression statement, then the value is ignored.
|
||||
// Otherwise, the value is used and we can ignore this site.
|
||||
return;
|
||||
}
|
||||
|
||||
// Resolve the static type of the expression
|
||||
TypeMirror type = getType(node);
|
||||
if (type == null || type.getKind() == TypeKind.VOID) {
|
||||
// Skip void (e.g., void-returning methods)
|
||||
return;
|
||||
}
|
||||
|
||||
// Check @NoDiscard on the invoked executable (method or constructor)
|
||||
var invoked = getInvokedExecutable(node);
|
||||
if (invoked != null) {
|
||||
List<String> messages = getNoDiscardMessages(invoked);
|
||||
for (String msg : messages) {
|
||||
m_trees.printMessage(Diagnostic.Kind.ERROR, msg, node, m_root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TypeMirror getType(Tree node) {
|
||||
var path = m_trees.getPath(m_root, node);
|
||||
if (path == null) {
|
||||
return null;
|
||||
}
|
||||
// Requires running after ANALYZE attribution has completed for this CU.
|
||||
return m_trees.getTypeMirror(path);
|
||||
}
|
||||
|
||||
private ExecutableElement getInvokedExecutable(Tree node) {
|
||||
var path = m_trees.getPath(m_root, node);
|
||||
if (path == null) {
|
||||
return null;
|
||||
}
|
||||
var el = m_trees.getElement(path);
|
||||
return (el instanceof ExecutableElement ee) ? ee : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects all @NoDiscard messages applicable to the given executable: - The method/constructor
|
||||
* itself (if annotated) - The return type (if declared) including its superclasses and all
|
||||
* implemented interfaces Returns formatted diagnostics messages ready to print.
|
||||
*/
|
||||
private List<String> getNoDiscardMessages(ExecutableElement method) {
|
||||
List<String> messages = new ArrayList<>();
|
||||
|
||||
// 1) Method-level @NoDiscard
|
||||
var methodNoDiscard = method.getAnnotation(NoDiscard.class);
|
||||
if (methodNoDiscard != null) {
|
||||
String msg = methodNoDiscard.value();
|
||||
if (msg.isEmpty()) {
|
||||
messages.add("Result of @NoDiscard method is ignored");
|
||||
} else {
|
||||
messages.add(msg);
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Type-level @NoDiscard (classes + interfaces recursively)
|
||||
TypeElement targetType = null;
|
||||
if (method.getKind() == ElementKind.CONSTRUCTOR) {
|
||||
// For constructors, the "return type" is the enclosing type
|
||||
var enclosing = method.getEnclosingElement();
|
||||
if (enclosing instanceof TypeElement te) {
|
||||
targetType = te;
|
||||
}
|
||||
} else {
|
||||
var returnType = method.getReturnType();
|
||||
if (returnType instanceof DeclaredType dt && dt.asElement() instanceof TypeElement te) {
|
||||
targetType = te;
|
||||
}
|
||||
}
|
||||
if (targetType != null) {
|
||||
Set<TypeElement> seen = new HashSet<>();
|
||||
collectNoDiscardMessagesFromTypeHierarchy(targetType, seen, messages);
|
||||
}
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for @NoDiscard on the provided type element, its superclasses, and all implemented
|
||||
* interfaces (recursively). Appends formatted messages to the provided list for every match.
|
||||
*
|
||||
* @param type The type element to search
|
||||
* @param seen A set of type elements that have already been searched
|
||||
* @param out The list to append messages to
|
||||
*/
|
||||
private void collectNoDiscardMessagesFromTypeHierarchy(
|
||||
TypeElement type, Set<TypeElement> seen, List<String> out) {
|
||||
if (type == null || !seen.add(type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check this type directly
|
||||
var typeNoDiscard = type.getAnnotation(NoDiscard.class);
|
||||
if (typeNoDiscard != null) {
|
||||
String message = typeNoDiscard.value();
|
||||
if (message.isEmpty()) {
|
||||
out.add(
|
||||
"Result of method returning @NoDiscard type %s is ignored"
|
||||
.formatted(type.getQualifiedName()));
|
||||
} else {
|
||||
out.add(message);
|
||||
}
|
||||
}
|
||||
|
||||
// Check superclass chain
|
||||
var superMirror = type.getSuperclass();
|
||||
if (superMirror != null && superMirror.getKind() != TypeKind.NONE) {
|
||||
var superEl = m_task.getTypes().asElement(superMirror);
|
||||
if (superEl instanceof TypeElement ste) {
|
||||
collectNoDiscardMessagesFromTypeHierarchy(ste, seen, out);
|
||||
}
|
||||
}
|
||||
|
||||
// Check all implemented interfaces (recursively)
|
||||
for (var iface : type.getInterfaces()) {
|
||||
var ifaceEl = m_task.getTypes().asElement(iface);
|
||||
if (ifaceEl instanceof TypeElement ite) {
|
||||
collectNoDiscardMessagesFromTypeHierarchy(ite, seen, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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 com.sun.source.util.JavacTask;
|
||||
import com.sun.source.util.Plugin;
|
||||
|
||||
/**
|
||||
* A javac compiler plugin that adds compiler warnings for incorrect usage of WPILib types. Also
|
||||
* supports WPILib's custom annotations like @NoDiscard.
|
||||
*/
|
||||
public class WPILibJavacPlugin implements Plugin {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "WPILib";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(JavacTask task, String... args) {
|
||||
task.addTaskListener(new ReturnValueUsedListener(task));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean autoStart() {
|
||||
// autoStart means we don't need to manually pass -Xplugin:WPILib to the javac compiler args
|
||||
// for the plugin to run
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.wpilib.javacplugin.WPILibJavacPlugin
|
||||
@@ -0,0 +1,13 @@
|
||||
// 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.util.List;
|
||||
|
||||
public class CompileTestOptions {
|
||||
public static final int kJavaVersion = 17;
|
||||
public static final List<Object> kJavaVersionOptions =
|
||||
List.of("-source", kJavaVersion, "-target", kJavaVersion);
|
||||
}
|
||||
@@ -0,0 +1,638 @@
|
||||
// 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 static com.google.testing.compile.CompilationSubject.assertThat;
|
||||
import static com.google.testing.compile.Compiler.javac;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.wpilib.javacplugin.CompileTestOptions.kJavaVersionOptions;
|
||||
|
||||
import com.google.testing.compile.Compilation;
|
||||
import com.google.testing.compile.JavaFileObjects;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class ReturnValueUsedListenerTest {
|
||||
@Test
|
||||
void nodiscardReturnValueIsUsed() {
|
||||
String source =
|
||||
"""
|
||||
package frc.robot;
|
||||
|
||||
import org.wpilib.annotation.NoDiscard;
|
||||
|
||||
class Example {
|
||||
@NoDiscard
|
||||
int getI() { return 0; }
|
||||
|
||||
void usage() {
|
||||
int i = getI();
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
Compilation compilation =
|
||||
javac()
|
||||
.withOptions(kJavaVersionOptions)
|
||||
.compile(JavaFileObjects.forSourceString("frc.robot.Example", source));
|
||||
|
||||
assertThat(compilation).succeededWithoutWarnings();
|
||||
}
|
||||
|
||||
@Test
|
||||
void nodiscardReturnValueUnused() {
|
||||
String source =
|
||||
"""
|
||||
package frc.robot;
|
||||
|
||||
import org.wpilib.annotation.NoDiscard;
|
||||
|
||||
class Example {
|
||||
@NoDiscard
|
||||
int getI() { return 0; }
|
||||
|
||||
void usage() {
|
||||
getI();
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
Compilation compilation =
|
||||
javac()
|
||||
.withOptions(kJavaVersionOptions)
|
||||
.compile(JavaFileObjects.forSourceString("frc.robot.Example", source));
|
||||
|
||||
assertThat(compilation).failed();
|
||||
assertEquals(1, compilation.errors().size());
|
||||
var error = compilation.errors().get(0);
|
||||
assertEquals("Result of @NoDiscard method is ignored", error.getMessage(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nodiscardOnClass() {
|
||||
String source =
|
||||
"""
|
||||
package frc.robot;
|
||||
|
||||
import org.wpilib.annotation.NoDiscard;
|
||||
|
||||
@NoDiscard
|
||||
class Example {
|
||||
Example getExample() { return new Example(); }
|
||||
|
||||
void usage() {
|
||||
getExample();
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
Compilation compilation =
|
||||
javac()
|
||||
.withOptions(kJavaVersionOptions)
|
||||
.compile(JavaFileObjects.forSourceString("frc.robot.Example", source));
|
||||
|
||||
assertThat(compilation).failed();
|
||||
assertEquals(1, compilation.errors().size());
|
||||
var error = compilation.errors().get(0);
|
||||
assertEquals(
|
||||
"Result of method returning @NoDiscard type frc.robot.Example is ignored",
|
||||
error.getMessage(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nodiscardOnClassCustomMessage() {
|
||||
String source =
|
||||
"""
|
||||
package frc.robot;
|
||||
|
||||
import org.wpilib.annotation.NoDiscard;
|
||||
|
||||
@NoDiscard("Custom message")
|
||||
class Example {
|
||||
Example getExample() { return new Example(); }
|
||||
|
||||
void usage() {
|
||||
getExample();
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
Compilation compilation =
|
||||
javac()
|
||||
.withOptions(kJavaVersionOptions)
|
||||
.compile(JavaFileObjects.forSourceString("frc.robot.Example", source));
|
||||
|
||||
assertThat(compilation).failed();
|
||||
assertEquals(1, compilation.errors().size());
|
||||
var error = compilation.errors().get(0);
|
||||
assertEquals("Custom message", error.getMessage(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nodiscardOnClassAndMethod() {
|
||||
String source =
|
||||
"""
|
||||
package frc.robot;
|
||||
|
||||
import org.wpilib.annotation.NoDiscard;
|
||||
|
||||
@NoDiscard
|
||||
class Example {
|
||||
@NoDiscard
|
||||
Example getExample() { return new Example(); }
|
||||
|
||||
void usage() {
|
||||
getExample();
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
Compilation compilation =
|
||||
javac()
|
||||
.withOptions(kJavaVersionOptions)
|
||||
.compile(JavaFileObjects.forSourceString("frc.robot.Example", source));
|
||||
|
||||
assertThat(compilation).failed();
|
||||
assertEquals(2, compilation.errors().size());
|
||||
var error1 = compilation.errors().get(0);
|
||||
var error2 = compilation.errors().get(1);
|
||||
assertEquals("Result of @NoDiscard method is ignored", error1.getMessage(null));
|
||||
assertEquals(
|
||||
"Result of method returning @NoDiscard type frc.robot.Example is ignored",
|
||||
error2.getMessage(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nodiscardOnInheritedClass() {
|
||||
String source =
|
||||
"""
|
||||
package frc.robot;
|
||||
|
||||
import org.wpilib.annotation.NoDiscard;
|
||||
|
||||
@NoDiscard("Objects of type `Base` must be used")
|
||||
abstract class Base { }
|
||||
|
||||
class Example extends Base {
|
||||
Example getExample() { return new Example(); }
|
||||
|
||||
void usage() {
|
||||
getExample();
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
Compilation compilation =
|
||||
javac()
|
||||
.withOptions(kJavaVersionOptions)
|
||||
.compile(JavaFileObjects.forSourceString("frc.robot.Example", source));
|
||||
|
||||
assertThat(compilation).failed();
|
||||
assertEquals(1, compilation.errors().size());
|
||||
var error = compilation.errors().get(0);
|
||||
assertEquals("Objects of type `Base` must be used", error.getMessage(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nodiscardOnSingleInterface() {
|
||||
String source =
|
||||
"""
|
||||
package frc.robot;
|
||||
|
||||
import org.wpilib.annotation.NoDiscard;
|
||||
|
||||
@NoDiscard("Objects implementing `I` must be used")
|
||||
interface I { }
|
||||
|
||||
class Example implements I {
|
||||
Example getExample() { return new Example(); }
|
||||
|
||||
void usage() {
|
||||
getExample();
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
Compilation compilation =
|
||||
javac()
|
||||
.withOptions(kJavaVersionOptions)
|
||||
.compile(JavaFileObjects.forSourceString("frc.robot.Example", source));
|
||||
|
||||
assertThat(compilation).failed();
|
||||
assertEquals(1, compilation.errors().size());
|
||||
var error = compilation.errors().get(0);
|
||||
assertEquals("Objects implementing `I` must be used", error.getMessage(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nodiscardOnMultipleInterfaces() {
|
||||
String source =
|
||||
"""
|
||||
package frc.robot;
|
||||
|
||||
import org.wpilib.annotation.NoDiscard;
|
||||
|
||||
@NoDiscard("Objects implementing `I` must be used")
|
||||
interface I { }
|
||||
|
||||
@NoDiscard("Objects implementing `I2` must be used")
|
||||
interface I2 { }
|
||||
|
||||
class Example implements I, I2 {
|
||||
Example getExample() { return new Example(); }
|
||||
|
||||
void usage() {
|
||||
getExample();
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
Compilation compilation =
|
||||
javac()
|
||||
.withOptions(kJavaVersionOptions)
|
||||
.compile(JavaFileObjects.forSourceString("frc.robot.Example", source));
|
||||
|
||||
assertThat(compilation).failed();
|
||||
assertEquals(2, compilation.errors().size());
|
||||
var error1 = compilation.errors().get(0);
|
||||
var error2 = compilation.errors().get(1);
|
||||
assertEquals("Objects implementing `I` must be used", error1.getMessage(null));
|
||||
assertEquals("Objects implementing `I2` must be used", error2.getMessage(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nodiscardCustomMessage() {
|
||||
String source =
|
||||
"""
|
||||
package frc.robot;
|
||||
|
||||
import org.wpilib.annotation.NoDiscard;
|
||||
|
||||
class Example {
|
||||
@NoDiscard("Custom message")
|
||||
int getI() { return 0; }
|
||||
|
||||
void usage() {
|
||||
getI();
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
Compilation compilation =
|
||||
javac()
|
||||
.withOptions(kJavaVersionOptions)
|
||||
.compile(JavaFileObjects.forSourceString("frc.robot.Example", source));
|
||||
|
||||
assertThat(compilation).failed();
|
||||
assertEquals(1, compilation.errors().size());
|
||||
var error = compilation.errors().get(0);
|
||||
assertEquals("Custom message", error.getMessage(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nodiscardMessageEmptyString() {
|
||||
String source =
|
||||
"""
|
||||
package frc.robot;
|
||||
|
||||
import org.wpilib.annotation.NoDiscard;
|
||||
|
||||
class Example {
|
||||
@NoDiscard("")
|
||||
int getI() { return 0; }
|
||||
|
||||
void usage() {
|
||||
getI();
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
Compilation compilation =
|
||||
javac()
|
||||
.withOptions(kJavaVersionOptions)
|
||||
.compile(JavaFileObjects.forSourceString("frc.robot.Example", source));
|
||||
|
||||
assertThat(compilation).failed();
|
||||
assertEquals(1, compilation.errors().size());
|
||||
var error = compilation.errors().get(0);
|
||||
assertEquals("Result of @NoDiscard method is ignored", error.getMessage(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nodiscardOnVoidMethod() {
|
||||
String source =
|
||||
"""
|
||||
package frc.robot;
|
||||
|
||||
import org.wpilib.annotation.NoDiscard;
|
||||
|
||||
class Example {
|
||||
@NoDiscard
|
||||
void voidMethod() { }
|
||||
|
||||
void usage() {
|
||||
voidMethod();
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
Compilation compilation =
|
||||
javac()
|
||||
.withOptions(kJavaVersionOptions)
|
||||
.compile(JavaFileObjects.forSourceString("frc.robot.Example", source));
|
||||
|
||||
assertThat(compilation).succeededWithoutWarnings();
|
||||
}
|
||||
|
||||
@Test
|
||||
void suppressWarningsOnNoDiscardMethod() {
|
||||
String source =
|
||||
"""
|
||||
package frc.robot;
|
||||
|
||||
import org.wpilib.annotation.NoDiscard;
|
||||
|
||||
class Example {
|
||||
@NoDiscard
|
||||
Object get() { return null; }
|
||||
|
||||
@SuppressWarnings("NoDiscard")
|
||||
void usage() {
|
||||
get();
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
Compilation compilation =
|
||||
javac()
|
||||
.withOptions(kJavaVersionOptions)
|
||||
.compile(JavaFileObjects.forSourceString("frc.robot.Example", source));
|
||||
|
||||
assertThat(compilation).succeededWithoutWarnings();
|
||||
}
|
||||
|
||||
@Test
|
||||
void suppressWarningsAllOnNoDiscardMethod() {
|
||||
String source =
|
||||
"""
|
||||
package frc.robot;
|
||||
|
||||
import org.wpilib.annotation.NoDiscard;
|
||||
|
||||
class Example {
|
||||
@NoDiscard
|
||||
Object get() { return null; }
|
||||
|
||||
@SuppressWarnings("all")
|
||||
void usage() {
|
||||
get();
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
Compilation compilation =
|
||||
javac()
|
||||
.withOptions(kJavaVersionOptions)
|
||||
.compile(JavaFileObjects.forSourceString("frc.robot.Example", source));
|
||||
|
||||
assertThat(compilation).succeededWithoutWarnings();
|
||||
}
|
||||
|
||||
@Test
|
||||
void suppressWarningsOnNoDiscardClass() {
|
||||
String source =
|
||||
"""
|
||||
package frc.robot;
|
||||
|
||||
import org.wpilib.annotation.NoDiscard;
|
||||
|
||||
@SuppressWarnings("NoDiscard")
|
||||
class Example {
|
||||
@NoDiscard
|
||||
Object get() { return null; }
|
||||
|
||||
void usage() {
|
||||
get();
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
Compilation compilation =
|
||||
javac()
|
||||
.withOptions(kJavaVersionOptions)
|
||||
.compile(JavaFileObjects.forSourceString("frc.robot.Example", source));
|
||||
|
||||
assertThat(compilation).succeededWithoutWarnings();
|
||||
}
|
||||
|
||||
@Test
|
||||
void suppressWarningsAllOnNoDiscardClass() {
|
||||
String source =
|
||||
"""
|
||||
package frc.robot;
|
||||
|
||||
import org.wpilib.annotation.NoDiscard;
|
||||
|
||||
@SuppressWarnings("all")
|
||||
class Example {
|
||||
@NoDiscard
|
||||
Object get() { return null; }
|
||||
|
||||
void usage() {
|
||||
get();
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
Compilation compilation =
|
||||
javac()
|
||||
.withOptions(kJavaVersionOptions)
|
||||
.compile(JavaFileObjects.forSourceString("frc.robot.Example", source));
|
||||
|
||||
assertThat(compilation).succeededWithoutWarnings();
|
||||
}
|
||||
|
||||
@Test
|
||||
void commandsv2CommandFactoryResultIsAssigned() {
|
||||
String source =
|
||||
"""
|
||||
package frc.robot;
|
||||
|
||||
import edu.wpi.first.wpilibj2.command.Command;
|
||||
import edu.wpi.first.wpilibj2.command.Commands;
|
||||
import org.wpilib.annotation.NoDiscard;
|
||||
|
||||
class Example {
|
||||
Command getCommand() {
|
||||
return Commands.print("");
|
||||
}
|
||||
|
||||
void usage() {
|
||||
Command theCommand = getCommand();
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
Compilation compilation =
|
||||
javac()
|
||||
.withOptions(kJavaVersionOptions)
|
||||
.compile(JavaFileObjects.forSourceString("frc.robot.Example", source));
|
||||
|
||||
assertThat(compilation).succeededWithoutWarnings();
|
||||
}
|
||||
|
||||
@Test
|
||||
void commandsv2CommandFactoryResultIsPassed() {
|
||||
String source =
|
||||
"""
|
||||
package frc.robot;
|
||||
|
||||
import edu.wpi.first.wpilibj2.command.Command;
|
||||
import edu.wpi.first.wpilibj2.command.Commands;
|
||||
import org.wpilib.annotation.NoDiscard;
|
||||
|
||||
class Example {
|
||||
Command getCommand() {
|
||||
return Commands.print("");
|
||||
}
|
||||
|
||||
void usage() {
|
||||
System.out.println(getCommand());
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
Compilation compilation =
|
||||
javac()
|
||||
.withOptions(kJavaVersionOptions)
|
||||
.compile(JavaFileObjects.forSourceString("frc.robot.Example", source));
|
||||
|
||||
assertThat(compilation).succeededWithoutWarnings();
|
||||
}
|
||||
|
||||
@Test
|
||||
void commandsv2CommandFactoryResultIsChainedAndUsed() {
|
||||
String source =
|
||||
"""
|
||||
package frc.robot;
|
||||
|
||||
import edu.wpi.first.wpilibj2.command.Command;
|
||||
import edu.wpi.first.wpilibj2.command.Commands;
|
||||
import org.wpilib.annotation.NoDiscard;
|
||||
|
||||
class Example {
|
||||
Command getCommand() {
|
||||
return Commands.print("");
|
||||
}
|
||||
|
||||
void usage() {
|
||||
Command theCommand = getCommand().withName("The name");
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
Compilation compilation =
|
||||
javac()
|
||||
.withOptions(kJavaVersionOptions)
|
||||
.compile(JavaFileObjects.forSourceString("frc.robot.Example", source));
|
||||
|
||||
assertThat(compilation).succeededWithoutWarnings();
|
||||
}
|
||||
|
||||
@Test
|
||||
void commandsv2CommandFactoryResultNotUsed() {
|
||||
String source =
|
||||
"""
|
||||
package frc.robot;
|
||||
|
||||
import edu.wpi.first.wpilibj2.command.Command;
|
||||
import edu.wpi.first.wpilibj2.command.Commands;
|
||||
import org.wpilib.annotation.NoDiscard;
|
||||
|
||||
class Example {
|
||||
Command getCommand() {
|
||||
return Commands.print("");
|
||||
}
|
||||
|
||||
void usage() {
|
||||
getCommand();
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
Compilation compilation =
|
||||
javac()
|
||||
.withOptions(kJavaVersionOptions)
|
||||
.compile(JavaFileObjects.forSourceString("frc.robot.Example", source));
|
||||
|
||||
assertThat(compilation).failed();
|
||||
assertEquals(1, compilation.errors().size());
|
||||
var error = compilation.errors().get(0);
|
||||
assertEquals(
|
||||
"Commands must be used! Did you mean to bind it to a trigger?", error.getMessage(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void commandsv2CommandFactoryResultIsChainedAndNotUsed() {
|
||||
String source =
|
||||
"""
|
||||
package frc.robot;
|
||||
|
||||
import edu.wpi.first.wpilibj2.command.Command;
|
||||
import edu.wpi.first.wpilibj2.command.Commands;
|
||||
import org.wpilib.annotation.NoDiscard;
|
||||
|
||||
class Example {
|
||||
Command getCommand() {
|
||||
return Commands.print("");
|
||||
}
|
||||
|
||||
void usage() {
|
||||
getCommand().withName("The name");
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
Compilation compilation =
|
||||
javac()
|
||||
.withOptions(kJavaVersionOptions)
|
||||
.compile(JavaFileObjects.forSourceString("frc.robot.Example", source));
|
||||
|
||||
assertThat(compilation).failed();
|
||||
assertEquals(1, compilation.errors().size());
|
||||
var error = compilation.errors().get(0);
|
||||
assertEquals(
|
||||
"Commands must be used! Did you mean to bind it to a trigger?", error.getMessage(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void commandsv2NewCommandInstanceNotUsed() {
|
||||
String source =
|
||||
"""
|
||||
package frc.robot;
|
||||
|
||||
import edu.wpi.first.wpilibj2.command.Command;
|
||||
import edu.wpi.first.wpilibj2.command.Commands;
|
||||
import edu.wpi.first.wpilibj2.command.WaitCommand;
|
||||
import org.wpilib.annotation.NoDiscard;
|
||||
|
||||
class Example {
|
||||
void usage() {
|
||||
new WaitCommand(1);
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
Compilation compilation =
|
||||
javac()
|
||||
.withOptions(kJavaVersionOptions)
|
||||
.compile(JavaFileObjects.forSourceString("frc.robot.Example", source));
|
||||
|
||||
assertThat(compilation).failed();
|
||||
assertEquals(1, compilation.errors().size());
|
||||
var error = compilation.errors().get(0);
|
||||
assertEquals(
|
||||
"Commands must be used! Did you mean to bind it to a trigger?", error.getMessage(null));
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,8 @@ include 'wpilibc'
|
||||
include 'wpilibcExamples'
|
||||
include 'wpilibjExamples'
|
||||
include 'wpilibj'
|
||||
include 'javacPlugin'
|
||||
include 'wpiannotations'
|
||||
include 'wpiunits'
|
||||
include 'fieldImages'
|
||||
include 'glass'
|
||||
|
||||
@@ -38,12 +38,14 @@ nativeUtils.platformConfigs.each {
|
||||
}
|
||||
}
|
||||
|
||||
// Compress debug info on Linux
|
||||
nativeUtils.platformConfigs.each {
|
||||
if (it.name.contains('linux')) {
|
||||
// Compress debug info on Linux
|
||||
it.cppCompiler.debugArgs.add("-gz=zlib")
|
||||
// Make warning in OpenCV 4.10 from GCC 15 not an error
|
||||
it.cppCompiler.args.add("-Wno-error=overloaded-virtual")
|
||||
// Make warning from Google Benchmark not an error
|
||||
it.cppCompiler.args.add("-Wno-error=restrict")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -149,7 +149,11 @@ model {
|
||||
// By default, a development executable will be generated. This is to help the case of
|
||||
// testing specific functionality of the library.
|
||||
"${nativeName}Dev"(NativeExecutableSpec) {
|
||||
targetBuildTypes 'debug'
|
||||
if (project.hasProperty('ciDebugOnly') || project.hasProperty('debugJNI')) {
|
||||
targetBuildTypes 'debug'
|
||||
} else {
|
||||
targetBuildTypes 'release'
|
||||
}
|
||||
sources {
|
||||
cpp {
|
||||
source {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Tyler Veness <calcmogul@gmail.com>
|
||||
Date: Fri, 8 Sep 2023 19:21:41 -0700
|
||||
Subject: [PATCH 1/4] Remove version from namespace
|
||||
Subject: [PATCH 1/5] Remove version from namespace
|
||||
|
||||
---
|
||||
include/nlohmann/detail/abi_macros.hpp | 45 ++------------------------
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Tyler Veness <calcmogul@gmail.com>
|
||||
Date: Thu, 7 Sep 2023 22:02:27 -0700
|
||||
Subject: [PATCH 2/4] Make serializer public
|
||||
Subject: [PATCH 2/5] Make serializer public
|
||||
|
||||
---
|
||||
include/nlohmann/detail/output/serializer.hpp | 4 +++-
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Tyler Veness <calcmogul@gmail.com>
|
||||
Date: Fri, 8 Sep 2023 21:42:01 -0700
|
||||
Subject: [PATCH 3/4] Make dump_escaped() take std::string_view
|
||||
Subject: [PATCH 3/5] Make dump_escaped() take std::string_view
|
||||
|
||||
---
|
||||
include/nlohmann/detail/output/serializer.hpp | 2 +-
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: PJ Reiniger <pj.reiniger@gmail.com>
|
||||
Date: Wed, 20 Sep 2023 02:23:10 -0400
|
||||
Subject: [PATCH 4/4] Add llvm stream support
|
||||
Subject: [PATCH 4/5] Add llvm stream support
|
||||
|
||||
---
|
||||
.../detail/output/output_adapters.hpp | 26 +++++++++++++++++++
|
||||
|
||||
93
upstream_utils/json_patches/0005-Fix-Doxygen-warnings.patch
Normal file
93
upstream_utils/json_patches/0005-Fix-Doxygen-warnings.patch
Normal file
@@ -0,0 +1,93 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Gold856 <117957790+Gold856@users.noreply.github.com>
|
||||
Date: Tue, 27 May 2025 23:39:02 -0400
|
||||
Subject: [PATCH 5/5] Fix Doxygen warnings
|
||||
|
||||
---
|
||||
include/nlohmann/json.hpp | 31 ++++++-------------------------
|
||||
1 file changed, 6 insertions(+), 25 deletions(-)
|
||||
|
||||
diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp
|
||||
index a89e2151e589663ba487a462c3d15cd247ff06cf..a5b4f8b4a118c1f5763ec6ba596a8a2d3d5791eb 100644
|
||||
--- a/include/nlohmann/json.hpp
|
||||
+++ b/include/nlohmann/json.hpp
|
||||
@@ -161,7 +161,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
using serializer = ::nlohmann::detail::serializer<basic_json>;
|
||||
|
||||
using value_t = detail::value_t;
|
||||
- /// JSON Pointer, see @ref nlohmann::json_pointer
|
||||
+ /// JSON Pointer, see @ref json_pointer
|
||||
using json_pointer = ::nlohmann::json_pointer<StringType>;
|
||||
template<typename T, typename SFINAE>
|
||||
using json_serializer = JSONSerializer<T, SFINAE>;
|
||||
@@ -173,7 +173,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
|
||||
|
||||
using input_format_t = detail::input_format_t;
|
||||
- /// SAX interface type, see @ref nlohmann::json_sax
|
||||
+ /// SAX interface type, see nlohmann::json_sax
|
||||
using json_sax_t = json_sax<basic_json>;
|
||||
|
||||
////////////////
|
||||
@@ -1606,13 +1606,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
|
||||
@throw what @ref json_serializer<ValueType> `from_json()` method throws
|
||||
|
||||
- @liveexample{The example below shows several conversions from JSON values
|
||||
- to other types. There a few things to note: (1) Floating-point numbers can
|
||||
- be converted to integers\, (2) A JSON array can be converted to a standard
|
||||
- `std::vector<short>`\, (3) A JSON object can be converted to C++
|
||||
- associative containers such as `std::unordered_map<std::string\,
|
||||
- json>`.,get__ValueType_const}
|
||||
-
|
||||
@since version 2.1.0
|
||||
*/
|
||||
template < typename ValueType,
|
||||
@@ -1678,7 +1671,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
|
||||
@return a copy of *this, converted into @a BasicJsonType
|
||||
|
||||
- @complexity Depending on the implementation of the called `from_json()`
|
||||
+ Complexity: Depending on the implementation of the called `from_json()`
|
||||
method.
|
||||
|
||||
@since version 3.2.0
|
||||
@@ -1702,7 +1695,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
|
||||
@return a copy of *this
|
||||
|
||||
- @complexity Constant.
|
||||
+ Complexity: Constant.
|
||||
|
||||
@since version 2.1.0
|
||||
*/
|
||||
@@ -1786,12 +1779,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
@return pointer to the internally stored JSON value if the requested
|
||||
pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
|
||||
|
||||
- @complexity Constant.
|
||||
-
|
||||
- @liveexample{The example below shows how pointers to internal values of a
|
||||
- JSON value can be requested. Note that no type conversions are made and a
|
||||
- `nullptr` is returned if the value and the requested pointer type does not
|
||||
- match.,get__PointerType}
|
||||
+ Complexity: Constant.
|
||||
|
||||
@sa see @ref get_ptr() for explicit pointer-member access
|
||||
|
||||
@@ -1883,14 +1871,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
to the JSON value type (e.g., the JSON value is of type boolean, but a
|
||||
string is requested); see example below
|
||||
|
||||
- @complexity Linear in the size of the JSON value.
|
||||
-
|
||||
- @liveexample{The example below shows several conversions from JSON values
|
||||
- to other types. There a few things to note: (1) Floating-point numbers can
|
||||
- be converted to integers\, (2) A JSON array can be converted to a standard
|
||||
- `std::vector<short>`\, (3) A JSON object can be converted to C++
|
||||
- associative containers such as `std::unordered_map<std::string\,
|
||||
- json>`.,operator__ValueType}
|
||||
+ Complexity: Linear in the size of the JSON value.
|
||||
|
||||
@since version 1.0.0
|
||||
*/
|
||||
8
wpiannotations/BUILD.bazel
Normal file
8
wpiannotations/BUILD.bazel
Normal file
@@ -0,0 +1,8 @@
|
||||
load("@rules_java//java:defs.bzl", "java_library")
|
||||
|
||||
java_library(
|
||||
name = "wpiannotations",
|
||||
srcs = glob(["src/main/java/**/*.java"]),
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [],
|
||||
)
|
||||
37
wpiannotations/CMakeLists.txt
Normal file
37
wpiannotations/CMakeLists.txt
Normal file
@@ -0,0 +1,37 @@
|
||||
project(wpiannotations)
|
||||
|
||||
# Java bindings
|
||||
if(WITH_JAVA)
|
||||
include(UseJava)
|
||||
|
||||
file(GLOB_RECURSE JAVA_SOURCES src/main/java/*.java)
|
||||
|
||||
add_jar(
|
||||
wpiannotations_jar
|
||||
${JAVA_SOURCES}
|
||||
OUTPUT_NAME wpiannotations
|
||||
OUTPUT_DIR ${WPILIB_BINARY_DIR}/${java_lib_dest}
|
||||
)
|
||||
set_property(TARGET wpiannotations_jar PROPERTY FOLDER "java")
|
||||
|
||||
install_jar(wpiannotations_jar DESTINATION ${java_lib_dest})
|
||||
install_jar_exports(
|
||||
TARGETS wpiannotations_jar
|
||||
FILE wpiannotations.cmake
|
||||
DESTINATION share/wpiannotations
|
||||
)
|
||||
install(FILES wpiannotations-config.cmake DESTINATION share/wpiannotations)
|
||||
endif()
|
||||
|
||||
if(WITH_JAVA_SOURCE)
|
||||
include(UseJava)
|
||||
include(CreateSourceJar)
|
||||
add_source_jar(
|
||||
wpiannotations_src_jar
|
||||
BASE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/src/main/java
|
||||
OUTPUT_NAME wpiannotations-sources
|
||||
)
|
||||
set_property(TARGET wpiannotations_src_jar PROPERTY FOLDER "java")
|
||||
|
||||
install_jar(wpiannotations_src_jar DESTINATION ${java_lib_dest})
|
||||
endif()
|
||||
11
wpiannotations/build.gradle
Normal file
11
wpiannotations/build.gradle
Normal file
@@ -0,0 +1,11 @@
|
||||
ext {
|
||||
useJava = true
|
||||
useCpp = false
|
||||
baseId = 'annotations'
|
||||
groupId = 'org.wpilib'
|
||||
|
||||
nativeName = ''
|
||||
devMain = ''
|
||||
}
|
||||
|
||||
apply from: "${rootDir}/shared/java/javacommon.gradle"
|
||||
@@ -0,0 +1,27 @@
|
||||
// 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.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marks a method as returning a value that must be used. The WPILib compiler plugin will check for
|
||||
* uses of methods with this annotation and report a compiler error if the value is unused. Marking
|
||||
* a class or interface as {@code @NoDiscard} will act as if any method that returns that type or
|
||||
* any subclass or implementor of that type has been marked with {@code @NoDiscard}.
|
||||
*/
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.CLASS) // needs to be stored in the class for use by libraries
|
||||
public @interface NoDiscard {
|
||||
/**
|
||||
* An error message to display if the return value is not used.
|
||||
*
|
||||
* @return The error message.
|
||||
*/
|
||||
String value() default "";
|
||||
}
|
||||
2
wpiannotations/wpiannotations-config.cmake
Normal file
2
wpiannotations/wpiannotations-config.cmake
Normal file
@@ -0,0 +1,2 @@
|
||||
get_filename_component(SELF_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
|
||||
include(${SELF_DIR}/wpiannotations.cmake)
|
||||
@@ -106,11 +106,14 @@ wpilib_java_library(
|
||||
srcs = glob(["src/main/java/**/*.java"]) + [":generated_java"],
|
||||
maven_artifact_name = "wpilibNewCommands-java",
|
||||
maven_group_id = "edu.wpi.first.wpilibNewCommands",
|
||||
exported_plugins = ["//javacPlugin:plugin"],
|
||||
plugins = ["//javacPlugin:plugin"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//cscore:cscore-java",
|
||||
"//hal:hal-java",
|
||||
"//ntcore:ntcore-java",
|
||||
"//wpiannotations",
|
||||
"//wpilibj:wpilibj-java",
|
||||
"//wpimath:wpimath-java",
|
||||
"//wpinet:wpinet-java",
|
||||
@@ -165,8 +168,10 @@ java_binary(
|
||||
srcs = ["src/dev/java/edu/wpi/first/wpilibj2/commands/DevMain.java"],
|
||||
main_class = "edu.wpi.first.wpilibj2.commands.DevMain",
|
||||
deps = [
|
||||
":wpilibNewCommands-java",
|
||||
"//hal:hal-java",
|
||||
"//ntcore:ntcore-java",
|
||||
"//wpiannotations",
|
||||
"//wpimath:wpimath-java",
|
||||
"//wpiutil:wpiutil-java",
|
||||
],
|
||||
|
||||
@@ -23,6 +23,7 @@ if(WITH_JAVA)
|
||||
wpiutil_jar
|
||||
wpilibj_jar
|
||||
datalog_jar
|
||||
wpiannotations_jar
|
||||
OUTPUT_NAME wpilibNewCommands
|
||||
OUTPUT_DIR ${WPILIB_BINARY_DIR}/${java_lib_dest}
|
||||
)
|
||||
|
||||
@@ -21,8 +21,10 @@ dependencies {
|
||||
implementation project(':hal')
|
||||
implementation project(':wpimath')
|
||||
implementation project(':wpilibj')
|
||||
implementation project(':wpiannotations')
|
||||
api project(':datalog')
|
||||
testImplementation 'org.mockito:mockito-core:4.1.0'
|
||||
annotationProcessor project(':javacPlugin')
|
||||
}
|
||||
|
||||
sourceSets.main.java.srcDir "${projectDir}/src/generated/main/java"
|
||||
|
||||
@@ -16,6 +16,7 @@ import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import org.wpilib.annotation.NoDiscard;
|
||||
|
||||
/**
|
||||
* A state machine representing a complete action to be performed by the robot. Commands are run by
|
||||
@@ -27,6 +28,7 @@ import java.util.function.BooleanSupplier;
|
||||
*
|
||||
* <p>This class is provided by the NewCommands VendorDep
|
||||
*/
|
||||
@NoDiscard("Commands must be used! Did you mean to bind it to a trigger?")
|
||||
public abstract class Command implements Sendable {
|
||||
/** Requirements set. */
|
||||
private final Set<Subsystem> m_requirements = new HashSet<>();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// 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.
|
||||
|
||||
#include "../../include/frc/controller/BangBangController.h"
|
||||
#include "frc/controller/BangBangController.h"
|
||||
|
||||
#include <wpi/sendable/SendableBuilder.h>
|
||||
|
||||
|
||||
@@ -1992,6 +1992,7 @@ namespace units
|
||||
* @param[in] rhs unit to copy.
|
||||
*/
|
||||
template<class UnitsRhs, typename Ty, template<typename> class NlsRhs>
|
||||
requires traits::is_unit_v<UnitsRhs> && traits::is_unit_v<Units> && traits::is_convertible_unit_v<UnitsRhs, Units>
|
||||
constexpr unit_t(const unit_t<UnitsRhs, Ty, NlsRhs>& rhs) noexcept :
|
||||
nls(units::convert<UnitsRhs, Units, T>(rhs.m_value), std::true_type() /*store linear value*/)
|
||||
{
|
||||
|
||||
@@ -3513,3 +3513,18 @@ TEST_F(CaseStudies, pythagoreanTheorum) {
|
||||
pow<2>(RightTriangle::b::value()) ==
|
||||
pow<2>(RightTriangle::c::value()));
|
||||
}
|
||||
|
||||
TEST(Units, overloadResolution) {
|
||||
// Slight hack to get nested functions
|
||||
struct Scope {
|
||||
static bool f(units::meter_t) {
|
||||
return true;
|
||||
};
|
||||
|
||||
static bool f(units::second_t) {
|
||||
return false;
|
||||
};
|
||||
};
|
||||
// Make sure this properly selects the meter overload
|
||||
EXPECT_TRUE(Scope::f(1_mm));
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ class FsEvent final : public HandleImpl<FsEvent, uv_fs_event_t> {
|
||||
* Start watching the specified path for changes.
|
||||
*
|
||||
* @param path Path to watch for changes
|
||||
* @param events Bitmask of event flags. Only UV_FS_EVENT_RECURSIVE is
|
||||
* @param flags Bitmask of event flags. Only UV_FS_EVENT_RECURSIVE is
|
||||
* supported (and only on OSX and Windows).
|
||||
*/
|
||||
void Start(std::string_view path, unsigned int flags = 0);
|
||||
|
||||
@@ -87,7 +87,6 @@ void GetNameInfo(Loop& loop,
|
||||
* @param callback Callback function to call when resolution completes
|
||||
* @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
|
||||
* @param flags Optional flags to modify the behavior of `getnameinfo`.
|
||||
* @return Connection object for the callback
|
||||
*/
|
||||
inline void GetNameInfo(const std::shared_ptr<Loop>& loop,
|
||||
std::function<void(const char*, const char*)> callback,
|
||||
|
||||
@@ -90,7 +90,6 @@ class Stream : public Handle {
|
||||
* complete. Errors will be reported to the stream error handler.
|
||||
*
|
||||
* @param callback Callback function to call when shutdown completes
|
||||
* @return Connection object for the callback
|
||||
*/
|
||||
void Shutdown(std::function<void()> callback = nullptr);
|
||||
|
||||
|
||||
@@ -146,7 +146,6 @@ class Udp final : public HandleImpl<Udp, uv_udp_t> {
|
||||
*
|
||||
* @param ip The address to which to bind.
|
||||
* @param port The port to which to bind.
|
||||
* @param flags Optional additional flags.
|
||||
*/
|
||||
void Connect6(std::string_view ip, unsigned int port);
|
||||
|
||||
|
||||
@@ -649,17 +649,10 @@ public interface Measure<U extends Unit> extends Comparable<Measure<U>> {
|
||||
return unit().ofBaseUnits(baseUnitResult);
|
||||
}
|
||||
|
||||
if (unit() instanceof DimensionlessUnit) {
|
||||
// Numerator is a dimensionless
|
||||
if (divisor.unit() instanceof PerUnit<?, ?> ratio) {
|
||||
// Dividing by a ratio, return its reciprocal scaled by this
|
||||
return ratio.reciprocal().ofBaseUnits(baseUnitResult);
|
||||
}
|
||||
if (divisor.unit() instanceof PerUnit<?, ?> ratio) {
|
||||
// Dividing by a Per<Time, U>, return its reciprocal velocity scaled by this
|
||||
// Note: Per<Time, U>.reciprocal() is coded to return a Velocity<U>
|
||||
return ratio.reciprocal().ofBaseUnits(baseUnitResult);
|
||||
}
|
||||
// Numerator is a dimensionless
|
||||
if (unit() instanceof DimensionlessUnit && divisor.unit() instanceof PerUnit<?, ?> ratio) {
|
||||
// Dividing by a ratio, return its reciprocal scaled by this
|
||||
return ratio.reciprocal().ofBaseUnits(baseUnitResult);
|
||||
}
|
||||
|
||||
if (divisor.unit() instanceof PerUnit<?, ?> ratio
|
||||
|
||||
@@ -10,6 +10,7 @@ import edu.wpi.first.util.WPISerializable;
|
||||
* Marker interface to indicate a class is serializable using Struct serialization.
|
||||
*
|
||||
* <p>While this cannot be enforced by the interface, any class implementing this interface should
|
||||
* provide a public final static `struct` member variable.
|
||||
* provide a public final static `struct` member variable, or a static final `getStruct()` method if
|
||||
* the class is generic.
|
||||
*/
|
||||
public interface StructSerializable extends WPISerializable {}
|
||||
|
||||
@@ -33,7 +33,7 @@ class circular_buffer {
|
||||
|
||||
class iterator {
|
||||
public:
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using value_type = T;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = T*;
|
||||
@@ -51,6 +51,15 @@ class circular_buffer {
|
||||
++(*this);
|
||||
return retval;
|
||||
}
|
||||
constexpr iterator& operator--() {
|
||||
--m_index;
|
||||
return *this;
|
||||
}
|
||||
constexpr iterator operator--(int) {
|
||||
iterator retval = *this;
|
||||
--(*this);
|
||||
return retval;
|
||||
}
|
||||
constexpr bool operator==(const iterator&) const = default;
|
||||
constexpr reference operator*() { return (*m_buffer)[m_index]; }
|
||||
|
||||
@@ -61,7 +70,7 @@ class circular_buffer {
|
||||
|
||||
class const_iterator {
|
||||
public:
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using value_type = T;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = T*;
|
||||
@@ -79,6 +88,15 @@ class circular_buffer {
|
||||
++(*this);
|
||||
return retval;
|
||||
}
|
||||
constexpr const_iterator& operator--() {
|
||||
--m_index;
|
||||
return *this;
|
||||
}
|
||||
constexpr const_iterator operator--(int) {
|
||||
const_iterator retval = *this;
|
||||
--(*this);
|
||||
return retval;
|
||||
}
|
||||
constexpr bool operator==(const const_iterator&) const = default;
|
||||
constexpr const_reference operator*() const { return (*m_buffer)[m_index]; }
|
||||
|
||||
@@ -87,21 +105,83 @@ class circular_buffer {
|
||||
size_t m_index;
|
||||
};
|
||||
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
|
||||
/**
|
||||
* Returns begin iterator.
|
||||
*/
|
||||
constexpr iterator begin() { return iterator(this, 0); }
|
||||
|
||||
/**
|
||||
* Returns end iterator.
|
||||
*/
|
||||
constexpr iterator end() {
|
||||
return iterator(this, ::wpi::circular_buffer<T>::size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns const begin iterator.
|
||||
*/
|
||||
constexpr const_iterator begin() const { return const_iterator(this, 0); }
|
||||
|
||||
/**
|
||||
* Returns const end iterator.
|
||||
*/
|
||||
constexpr const_iterator end() const {
|
||||
return const_iterator(this, ::wpi::circular_buffer<T>::size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns const begin iterator.
|
||||
*/
|
||||
constexpr const_iterator cbegin() const { return const_iterator(this, 0); }
|
||||
|
||||
/**
|
||||
* Returns const end iterator.
|
||||
*/
|
||||
constexpr const_iterator cend() const {
|
||||
return const_iterator(this, ::wpi::circular_buffer<T>::size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns reverse begin iterator.
|
||||
*/
|
||||
constexpr reverse_iterator rbegin() { return reverse_iterator(end()); }
|
||||
|
||||
/**
|
||||
* Returns reverse end iterator.
|
||||
*/
|
||||
constexpr reverse_iterator rend() { return reverse_iterator(begin()); }
|
||||
|
||||
/**
|
||||
* Returns const reverse begin iterator.
|
||||
*/
|
||||
constexpr const_reverse_iterator rbegin() const {
|
||||
return const_reverse_iterator(end());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns const reverse end iterator.
|
||||
*/
|
||||
constexpr const_reverse_iterator rend() const {
|
||||
return const_reverse_iterator(begin());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns const reverse begin iterator.
|
||||
*/
|
||||
constexpr const_reverse_iterator crbegin() const {
|
||||
return const_reverse_iterator(cend());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns const reverse end iterator.
|
||||
*/
|
||||
constexpr const_reverse_iterator crend() const {
|
||||
return const_reverse_iterator(cbegin());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns number of elements in buffer
|
||||
*/
|
||||
|
||||
@@ -24,7 +24,7 @@ class static_circular_buffer {
|
||||
|
||||
class iterator {
|
||||
public:
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using value_type = T;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = T*;
|
||||
@@ -42,6 +42,15 @@ class static_circular_buffer {
|
||||
++(*this);
|
||||
return retval;
|
||||
}
|
||||
constexpr iterator& operator--() {
|
||||
--m_index;
|
||||
return *this;
|
||||
}
|
||||
constexpr iterator operator--(int) {
|
||||
iterator retval = *this;
|
||||
--(*this);
|
||||
return retval;
|
||||
}
|
||||
constexpr bool operator==(const iterator&) const = default;
|
||||
constexpr reference operator*() { return (*m_buffer)[m_index]; }
|
||||
|
||||
@@ -52,7 +61,7 @@ class static_circular_buffer {
|
||||
|
||||
class const_iterator {
|
||||
public:
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using value_type = T;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = T*;
|
||||
@@ -70,6 +79,15 @@ class static_circular_buffer {
|
||||
++(*this);
|
||||
return retval;
|
||||
}
|
||||
constexpr const_iterator& operator--() {
|
||||
--m_index;
|
||||
return *this;
|
||||
}
|
||||
constexpr const_iterator operator--(int) {
|
||||
const_iterator retval = *this;
|
||||
--(*this);
|
||||
return retval;
|
||||
}
|
||||
constexpr bool operator==(const const_iterator&) const = default;
|
||||
constexpr const_reference operator*() const { return (*m_buffer)[m_index]; }
|
||||
|
||||
@@ -78,6 +96,9 @@ class static_circular_buffer {
|
||||
size_t m_index;
|
||||
};
|
||||
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
|
||||
/**
|
||||
* Returns begin iterator.
|
||||
*/
|
||||
@@ -91,29 +112,67 @@ class static_circular_buffer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns begin iterator.
|
||||
* Returns const begin iterator.
|
||||
*/
|
||||
constexpr const_iterator begin() const { return const_iterator(this, 0); }
|
||||
|
||||
/**
|
||||
* Returns end iterator.
|
||||
* Returns const end iterator.
|
||||
*/
|
||||
constexpr const_iterator end() const {
|
||||
return const_iterator(this, ::wpi::static_circular_buffer<T, N>::size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns begin iterator.
|
||||
* Returns const begin iterator.
|
||||
*/
|
||||
constexpr const_iterator cbegin() const { return const_iterator(this, 0); }
|
||||
|
||||
/**
|
||||
* Returns end iterator.
|
||||
* Returns const end iterator.
|
||||
*/
|
||||
constexpr const_iterator cend() const {
|
||||
return const_iterator(this, ::wpi::static_circular_buffer<T, N>::size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns reverse begin iterator.
|
||||
*/
|
||||
constexpr reverse_iterator rbegin() { return reverse_iterator(end()); }
|
||||
|
||||
/**
|
||||
* Returns reverse end iterator.
|
||||
*/
|
||||
constexpr reverse_iterator rend() { return reverse_iterator(begin()); }
|
||||
|
||||
/**
|
||||
* Returns const reverse begin iterator.
|
||||
*/
|
||||
constexpr const_reverse_iterator rbegin() const {
|
||||
return const_reverse_iterator(end());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns const reverse end iterator.
|
||||
*/
|
||||
constexpr const_reverse_iterator rend() const {
|
||||
return const_reverse_iterator(begin());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns const reverse begin iterator.
|
||||
*/
|
||||
constexpr const_reverse_iterator crbegin() const {
|
||||
return const_reverse_iterator(cend());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns const reverse end iterator.
|
||||
*/
|
||||
constexpr const_reverse_iterator crend() const {
|
||||
return const_reverse_iterator(cbegin());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns number of elements in buffer
|
||||
*/
|
||||
|
||||
@@ -161,7 +161,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
using serializer = ::wpi::detail::serializer<basic_json>;
|
||||
|
||||
using value_t = detail::value_t;
|
||||
/// JSON Pointer, see @ref wpi::json_pointer
|
||||
/// JSON Pointer, see @ref json_pointer
|
||||
using json_pointer = ::wpi::json_pointer<StringType>;
|
||||
template<typename T, typename SFINAE>
|
||||
using json_serializer = JSONSerializer<T, SFINAE>;
|
||||
@@ -173,7 +173,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
|
||||
|
||||
using input_format_t = detail::input_format_t;
|
||||
/// SAX interface type, see @ref wpi::json_sax
|
||||
/// SAX interface type, see wpi::json_sax
|
||||
using json_sax_t = json_sax<basic_json>;
|
||||
|
||||
////////////////
|
||||
@@ -1606,13 +1606,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
|
||||
@throw what @ref json_serializer<ValueType> `from_json()` method throws
|
||||
|
||||
@liveexample{The example below shows several conversions from JSON values
|
||||
to other types. There a few things to note: (1) Floating-point numbers can
|
||||
be converted to integers\, (2) A JSON array can be converted to a standard
|
||||
`std::vector<short>`\, (3) A JSON object can be converted to C++
|
||||
associative containers such as `std::unordered_map<std::string\,
|
||||
json>`.,get__ValueType_const}
|
||||
|
||||
@since version 2.1.0
|
||||
*/
|
||||
template < typename ValueType,
|
||||
@@ -1678,7 +1671,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
|
||||
@return a copy of *this, converted into @a BasicJsonType
|
||||
|
||||
@complexity Depending on the implementation of the called `from_json()`
|
||||
Complexity: Depending on the implementation of the called `from_json()`
|
||||
method.
|
||||
|
||||
@since version 3.2.0
|
||||
@@ -1702,7 +1695,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
|
||||
@return a copy of *this
|
||||
|
||||
@complexity Constant.
|
||||
Complexity: Constant.
|
||||
|
||||
@since version 2.1.0
|
||||
*/
|
||||
@@ -1786,12 +1779,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
@return pointer to the internally stored JSON value if the requested
|
||||
pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
|
||||
|
||||
@complexity Constant.
|
||||
|
||||
@liveexample{The example below shows how pointers to internal values of a
|
||||
JSON value can be requested. Note that no type conversions are made and a
|
||||
`nullptr` is returned if the value and the requested pointer type does not
|
||||
match.,get__PointerType}
|
||||
Complexity: Constant.
|
||||
|
||||
@sa see @ref get_ptr() for explicit pointer-member access
|
||||
|
||||
@@ -1883,14 +1871,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
to the JSON value type (e.g., the JSON value is of type boolean, but a
|
||||
string is requested); see example below
|
||||
|
||||
@complexity Linear in the size of the JSON value.
|
||||
|
||||
@liveexample{The example below shows several conversions from JSON values
|
||||
to other types. There a few things to note: (1) Floating-point numbers can
|
||||
be converted to integers\, (2) A JSON array can be converted to a standard
|
||||
`std::vector<short>`\, (3) A JSON object can be converted to C++
|
||||
associative containers such as `std::unordered_map<std::string\,
|
||||
json>`.,operator__ValueType}
|
||||
Complexity: Linear in the size of the JSON value.
|
||||
|
||||
@since version 1.0.0
|
||||
*/
|
||||
|
||||
@@ -251,4 +251,18 @@ TEST(CircularBufferTest, Iterator) {
|
||||
EXPECT_EQ(values[i], elem);
|
||||
++i;
|
||||
}
|
||||
|
||||
// reverse_iterator
|
||||
i = 2;
|
||||
for (auto it = queue.rbegin(); it != queue.rend(); ++it) {
|
||||
EXPECT_EQ(values[i], *it);
|
||||
--i;
|
||||
}
|
||||
|
||||
// const_reverse_iterator
|
||||
i = 2;
|
||||
for (auto it = queue.crbegin(); it != queue.crend(); ++it) {
|
||||
EXPECT_EQ(values[i], *it);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,4 +145,18 @@ TEST(StaticCircularBufferTest, Iterator) {
|
||||
EXPECT_EQ(values[i], elem);
|
||||
++i;
|
||||
}
|
||||
|
||||
// reverse_iterator
|
||||
i = 2;
|
||||
for (auto it = queue.rbegin(); it != queue.rend(); ++it) {
|
||||
EXPECT_EQ(values[i], *it);
|
||||
--i;
|
||||
}
|
||||
|
||||
// const_reverse_iterator
|
||||
i = 2;
|
||||
for (auto it = queue.crbegin(); it != queue.crend(); ++it) {
|
||||
EXPECT_EQ(values[i], *it);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user