Skip to content

Commit 1f929d2

Browse files
authored
Ban TODO comments, require FIXME comments (#2511)
This allows us to leave TODO comments for things that should be resolved before a PR is merged, and have stronger guarantees that these will actually be resolved. Previously, we've manually created GitHub PR review comments for TODO comments, which has let some TODOs through by accident, and is generally a waste of developer time. gherrit-pr-id: I4b767eb773d725fce0abefbefbe74c24a4b56d90
1 parent 1fea6a1 commit 1f929d2

File tree

25 files changed

+162
-121
lines changed

25 files changed

+162
-121
lines changed

.github/workflows/ci.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,14 @@ jobs:
774774
- name: Run dependency check
775775
run: ./ci/check_job_dependencies.sh
776776

777+
check-todo:
778+
runs-on: ubuntu-latest
779+
name: Check for todo comments
780+
steps:
781+
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
782+
- name: Run todo check
783+
run: ./ci/check_todo.sh
784+
777785
run-git-hooks:
778786
runs-on: ubuntu-latest
779787
name: Run Git hooks
@@ -800,7 +808,7 @@ jobs:
800808
# https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/troubleshooting-required-status-checks#handling-skipped-but-required-checks
801809
if: failure()
802810
runs-on: ubuntu-latest
803-
needs: [build_test, kani, check_be_aarch64, check_avr_artmega, check_fmt, check_readme, check_versions, generate_cache, check-all-toolchains-tested, check-job-dependencies, run-git-hooks]
811+
needs: [build_test, kani, check_be_aarch64, check_avr_artmega, check_fmt, check_readme, check_versions, generate_cache, check-all-toolchains-tested, check-job-dependencies, check-todo, run-git-hooks]
804812
steps:
805813
- name: Mark the job as failed
806814
run: exit 1

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ zerocopy-derive = { version = "=0.8.25-alpha.3", path = "zerocopy-derive" }
9393
[dev-dependencies]
9494
# More recent versions of `either` have an MSRV higher than ours.
9595
either = "=1.13.0"
96-
# TODO(#381) Remove this dependency once we have our own layout gadgets.
96+
# FIXME(#381) Remove this dependency once we have our own layout gadgets.
9797
elain = "0.3.0"
9898
itertools = "0.11"
9999
rand = { version = "0.8.5", default-features = false, features = ["small_rng"] }

build.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ fn main() {
7676
for version_cfg in &version_cfgs {
7777
println!("cargo:rustc-check-cfg=cfg({})", version_cfg.cfg_name);
7878
}
79-
// TODO(https://github.com/rust-lang/rust/issues/124816): Remove these
79+
// FIXME(https://github.com/rust-lang/rust/issues/124816): Remove these
8080
// once they're no longer needed.
8181
println!("cargo:rustc-check-cfg=cfg(doc_cfg)");
8282
println!("cargo:rustc-check-cfg=cfg(kani)");

ci/check_todo.sh

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Copyright 2025 The Fuchsia Authors
4+
#
5+
# Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
6+
# <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
7+
# license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
8+
# This file may not be copied, modified, or distributed except according to
9+
# those terms.
10+
11+
set -euo pipefail
12+
13+
# This allows us to leave XODO comments in this file and have them still be
14+
# picked up by this script without having the script itself trigger false
15+
# positives. The alternative would be to exclude this script entirely, which
16+
# would mean that we couldn't use XODO comments in this script.
17+
KEYWORD=$(echo XODO | sed -e 's/X/T/')
18+
19+
# -H: Print filename (default for multiple files/recursive)
20+
# -n: Print line number
21+
# -w: Match whole words
22+
output=$(rg -H -n -w "$KEYWORD" || true)
23+
24+
if [ -n "$output" ]; then
25+
echo "Found $KEYWORD markers in the codebase." >&2
26+
echo "$KEYWORD is used for tasks that should be done before merging a PR; if you want to leave a message in the codebase, use FIXME." >&2
27+
echo "" >&2
28+
echo "$output" >&2
29+
exit 1
30+
fi

githooks/pre-push

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ echo "Running pre-push git hook: $0"
1818
./ci/check_all_toolchains_tested.sh >/dev/null & TOOLCHAINS_PID=$!
1919
./ci/check_job_dependencies.sh >/dev/null & JOB_DEPS_PID=$!
2020
./ci/check_readme.sh >/dev/null & README_PID=$!
21+
./ci/check_todo.sh >/dev/null & XODO_PID=$!
2122
./ci/check_versions.sh >/dev/null & VERSIONS_PID=$!
2223

2324
# `wait <pid>` exits with the same status code as the job it's waiting for.
@@ -30,6 +31,7 @@ wait $FMT_PID
3031
wait $TOOLCHAINS_PID
3132
wait $JOB_DEPS_PID
3233
wait $README_PID
34+
wait $XODO_PID
3335
wait $VERSIONS_PID
3436

3537
# Ensure that this script calls all scripts in `ci/*`. This isn't a foolproof

src/byte_slice.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -194,15 +194,15 @@ pub unsafe trait IntoByteSliceMut<'a>: IntoByteSlice<'a> + ByteSliceMut {
194194
fn into_byte_slice_mut(self) -> &'a mut [u8];
195195
}
196196

197-
// TODO(#429): Add a "SAFETY" comment and remove this `allow`.
197+
// FIXME(#429): Add a "SAFETY" comment and remove this `allow`.
198198
#[allow(clippy::undocumented_unsafe_blocks)]
199199
unsafe impl ByteSlice for &[u8] {}
200200

201-
// TODO(#429): Add a "SAFETY" comment and remove this `allow`.
201+
// FIXME(#429): Add a "SAFETY" comment and remove this `allow`.
202202
#[allow(clippy::undocumented_unsafe_blocks)]
203203
unsafe impl CopyableByteSlice for &[u8] {}
204204

205-
// TODO(#429): Add a "SAFETY" comment and remove this `allow`.
205+
// FIXME(#429): Add a "SAFETY" comment and remove this `allow`.
206206
#[allow(clippy::undocumented_unsafe_blocks)]
207207
unsafe impl CloneableByteSlice for &[u8] {}
208208

@@ -229,7 +229,7 @@ unsafe impl<'a> IntoByteSlice<'a> for &'a [u8] {
229229
}
230230
}
231231

232-
// TODO(#429): Add a "SAFETY" comment and remove this `allow`.
232+
// FIXME(#429): Add a "SAFETY" comment and remove this `allow`.
233233
#[allow(clippy::undocumented_unsafe_blocks)]
234234
unsafe impl ByteSlice for &mut [u8] {}
235235

@@ -254,7 +254,7 @@ unsafe impl SplitByteSlice for &mut [u8] {
254254
// SAFETY: By contract on caller, `mid` is not greater than
255255
// `self.len()`.
256256
//
257-
// TODO(#67): Remove this allow. See NumExt for more details.
257+
// FIXME(#67): Remove this allow. See NumExt for more details.
258258
#[allow(unstable_name_collisions)]
259259
let r_len = unsafe { self.len().unchecked_sub(mid) };
260260

@@ -314,7 +314,7 @@ unsafe impl<'a> IntoByteSliceMut<'a> for &'a mut [u8] {
314314
}
315315
}
316316

317-
// TODO(#429): Add a "SAFETY" comment and remove this `allow`.
317+
// FIXME(#429): Add a "SAFETY" comment and remove this `allow`.
318318
#[allow(clippy::undocumented_unsafe_blocks)]
319319
unsafe impl ByteSlice for cell::Ref<'_, [u8]> {}
320320

@@ -334,7 +334,7 @@ unsafe impl SplitByteSlice for cell::Ref<'_, [u8]> {
334334
}
335335
}
336336

337-
// TODO(#429): Add a "SAFETY" comment and remove this `allow`.
337+
// FIXME(#429): Add a "SAFETY" comment and remove this `allow`.
338338
#[allow(clippy::undocumented_unsafe_blocks)]
339339
unsafe impl ByteSlice for cell::RefMut<'_, [u8]> {}
340340

src/byteorder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -868,7 +868,7 @@ define_type!(
868868
[]
869869
);
870870

871-
// TODO(https://github.com/rust-lang/rust/issues/72447): Use the endianness
871+
// FIXME(https://github.com/rust-lang/rust/issues/72447): Use the endianness
872872
// conversion methods directly once those are const-stable.
873873
macro_rules! define_float_conversion {
874874
($ty:ty, $bits:ident, $bytes:expr, $mod:ident) => {

src/error.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -797,9 +797,9 @@ impl<Src, Dst: ?Sized + Unaligned> From<CastError<Src, Dst>> for SizeError<Src,
797797
pub type TryCastError<Src, Dst: ?Sized + TryFromBytes> =
798798
ConvertError<AlignmentError<Src, Dst>, SizeError<Src, Dst>, ValidityError<Src, Dst>>;
799799

800-
// TODO(#1139): Remove the `TryFromBytes` here and in other downstream locations
801-
// (all the way to `ValidityError`) if we determine it's not necessary for rich
802-
// validity errors.
800+
// FIXME(#1139): Remove the `TryFromBytes` here and in other downstream
801+
// locations (all the way to `ValidityError`) if we determine it's not necessary
802+
// for rich validity errors.
803803
impl<Src, Dst: ?Sized + TryFromBytes> TryCastError<Src, Dst> {
804804
/// Produces the source underlying the failed conversion.
805805
#[inline]

src/impls.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ const _: () = unsafe {
5757
//
5858
// The size of a value is always a multiple of its alignment.
5959
//
60-
// TODO(#278): Once we've updated the trait docs to refer to `u8`s rather than
60+
// FIXME(#278): Once we've updated the trait docs to refer to `u8`s rather than
6161
// bits or bytes, update this comment, especially the reference to [1].
6262
const _: () = unsafe {
6363
unsafe_impl!(u8: Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned);
@@ -150,7 +150,7 @@ impl_size_eq!(char, Unalign<u32>);
150150
// Note that we don't `assert_unaligned!(str)` because `assert_unaligned!` uses
151151
// `align_of`, which only works for `Sized` types.
152152
//
153-
// TODO(#429):
153+
// FIXME(#429):
154154
// - Add quotes from documentation.
155155
// - Improve safety proof for `FromZeros` and `IntoBytes`; having the same
156156
// layout as `[u8]` isn't sufficient.
@@ -231,7 +231,7 @@ macro_rules! unsafe_impl_try_from_bytes_for_nonzero {
231231
// multiple states, so they cannot be 0 bytes, which means that they must be 1
232232
// byte. The only valid alignment for a 1-byte type is 1.
233233
//
234-
// TODO(#429):
234+
// FIXME(#429):
235235
// - Add quotes from documentation.
236236
// - Add safety comment for `Immutable`. How can we prove that `NonZeroXxx`
237237
// doesn't contain any `UnsafeCell`s? It's obviously true, but it's not clear
@@ -245,8 +245,8 @@ macro_rules! unsafe_impl_try_from_bytes_for_nonzero {
245245
//
246246
// [2] https://doc.rust-lang.org/1.81.0/std/num/type.NonZeroI8.html
247247
//
248-
// TODO(https://github.com/rust-lang/rust/pull/104082): Cite documentation
249-
// that layout is the same as primitive layout.
248+
// FIXME(https://github.com/rust-lang/rust/pull/104082): Cite documentation that
249+
// layout is the same as primitive layout.
250250
const _: () = unsafe {
251251
unsafe_impl!(NonZeroU8: Immutable, IntoBytes, Unaligned);
252252
unsafe_impl!(NonZeroI8: Immutable, IntoBytes, Unaligned);
@@ -288,13 +288,13 @@ const _: () = unsafe {
288288
// purpose of those types, it's virtually unthinkable that that would ever
289289
// change. The only valid alignment for a 1-byte type is 1.
290290
//
291-
// TODO(#429): Add quotes from documentation.
291+
// FIXME(#429): Add quotes from documentation.
292292
//
293293
// [1] https://doc.rust-lang.org/stable/std/num/struct.NonZeroU8.html
294294
// [2] https://doc.rust-lang.org/stable/std/num/struct.NonZeroI8.html
295295
//
296-
// TODO(https://github.com/rust-lang/rust/pull/104082): Cite documentation
297-
// for layout guarantees.
296+
// FIXME(https://github.com/rust-lang/rust/pull/104082): Cite documentation for
297+
// layout guarantees.
298298
const _: () = unsafe {
299299
unsafe_impl!(Option<NonZeroU8>: TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned);
300300
unsafe_impl!(Option<NonZeroI8>: TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned);
@@ -342,7 +342,7 @@ const _: () = unsafe {
342342
// | [`ptr::NonNull<U>`] | when `U: Sized` |
343343
// | `fn`, `extern "C" fn` | always |
344344
//
345-
// TODO(#429), TODO(https://github.com/rust-lang/rust/pull/115333): Cite the
345+
// FIXME(#429), FIXME(https://github.com/rust-lang/rust/pull/115333): Cite the
346346
// Stable docs once they're available.
347347
const _: () = unsafe {
348348
#[cfg(feature = "alloc")]
@@ -638,7 +638,7 @@ mod atomics {
638638

639639
impl_known_layout!(T => AtomicPtr<T>);
640640

641-
// TODO(#170): Implement `FromBytes` and `IntoBytes` once we implement
641+
// FIXME(#170): Implement `FromBytes` and `IntoBytes` once we implement
642642
// those traits for `*mut T`.
643643
impl_for_transmute_from!(T => TryFromBytes for AtomicPtr<T> [UnsafeCell<*mut T>]);
644644
impl_for_transmute_from!(T => FromZeros for AtomicPtr<T> [UnsafeCell<*mut T>]);
@@ -914,7 +914,7 @@ const _: () = unsafe {
914914
// `IntoBytes` for raw pointers eventually, but we are holding off until we can
915915
// figure out how to address those footguns.
916916
//
917-
// [1] TODO(https://github.com/rust-lang/rust/pull/116988): Cite the
917+
// [1] FIXME(https://github.com/rust-lang/rust/pull/116988): Cite the
918918
// documentation once this PR lands.
919919
const _: () = unsafe {
920920
unsafe_impl!(T: ?Sized => Immutable for *const T);
@@ -1474,7 +1474,7 @@ mod tests {
14741474
}
14751475

14761476
<$ty as TryFromBytesTestable>::with_passing_test_cases(|mut val| {
1477-
// TODO(#494): These tests only get exercised for types
1477+
// FIXME(#494): These tests only get exercised for types
14781478
// which are `IntoBytes`. Once we implement #494, we should
14791479
// be able to support non-`IntoBytes` types by zeroing
14801480
// padding.
@@ -1492,7 +1492,7 @@ mod tests {
14921492

14931493
let c = Ptr::from_ref(&*val);
14941494
let c = c.forget_aligned();
1495-
// SAFETY: TODO(#899): This is unsound. `$ty` is not
1495+
// SAFETY: FIXME(#899): This is unsound. `$ty` is not
14961496
// necessarily `IntoBytes`, but that's the corner we've
14971497
// backed ourselves into by using `Ptr::from_ref`.
14981498
let c = unsafe { c.assume_initialized() };
@@ -1503,7 +1503,7 @@ mod tests {
15031503

15041504
let c = Ptr::from_mut(&mut *val);
15051505
let c = c.forget_aligned();
1506-
// SAFETY: TODO(#899): This is unsound. `$ty` is not
1506+
// SAFETY: FIXME(#899): This is unsound. `$ty` is not
15071507
// necessarily `IntoBytes`, but that's the corner we've
15081508
// backed ourselves into by using `Ptr::from_ref`.
15091509
let c = unsafe { c.assume_initialized() };

src/layout.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,7 @@ impl DstLayout {
484484
// would have failed anyway for runtime reasons (such as a too-small
485485
// memory region).
486486
//
487-
// TODO(#67): Once our MSRV is 1.65, use let-else:
487+
// FIXME(#67): Once our MSRV is 1.65, use let-else:
488488
// https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html#let-else-statements
489489
let size_info = match self.size_info.try_to_nonzero_elem_size() {
490490
Some(size_info) => size_info,
@@ -544,7 +544,7 @@ impl DstLayout {
544544
// Calculate the maximum number of bytes that could be consumed
545545
// by the trailing slice.
546546
//
547-
// TODO(#67): Once our MSRV is 1.65, use let-else:
547+
// FIXME(#67): Once our MSRV is 1.65, use let-else:
548548
// https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html#let-else-statements
549549
let max_slice_and_padding_bytes = match max_total_bytes.checked_sub(offset) {
550550
Some(max) => max,
@@ -608,7 +608,7 @@ impl DstLayout {
608608
}
609609
}
610610

611-
// TODO(#67): For some reason, on our MSRV toolchain, this `allow` isn't
611+
// FIXME(#67): For some reason, on our MSRV toolchain, this `allow` isn't
612612
// enforced despite having `#![allow(unknown_lints)]` at the crate root, but
613613
// putting it here works. Once our MSRV is high enough that this bug has been
614614
// fixed, remove this `allow`.

0 commit comments

Comments
 (0)