Skip to content

Commit 145f07a

Browse files
committed
initial commit
0 parents  commit 145f07a

12 files changed

Lines changed: 2504 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
env:
9+
NIGHTLY: nightly-2026-03-17
10+
11+
jobs:
12+
build-and-test:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v4
16+
17+
- name: Install nightly toolchain
18+
run: rustup toolchain install ${{ env.NIGHTLY }} --component rustc-dev llvm-tools-preview
19+
20+
- name: Install dylint tooling
21+
run: cargo install cargo-dylint@5.0.0 dylint-link@5.0.0
22+
23+
- uses: actions/cache@v4
24+
with:
25+
path: |
26+
~/.cargo/registry
27+
instrument-fields/target
28+
key: lint-ci-${{ env.NIGHTLY }}-${{ hashFiles('instrument-fields/Cargo.lock') }}
29+
restore-keys: lint-ci-${{ env.NIGHTLY }}-
30+
31+
- name: Build
32+
working-directory: instrument-fields
33+
run: cargo build
34+
35+
- name: Test
36+
working-directory: instrument-fields
37+
run: cargo test

.github/workflows/dylint.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: dylint
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
packages:
7+
description: >
8+
Space-separated list of packages to check.
9+
If empty, checks the entire workspace.
10+
required: false
11+
type: string
12+
default: ""
13+
14+
env:
15+
NIGHTLY: nightly-2026-03-17
16+
17+
jobs:
18+
dylint:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- uses: actions/checkout@v4
22+
23+
- name: Install nightly toolchain
24+
run: rustup toolchain install ${{ env.NIGHTLY }} --component rustc-dev llvm-tools-preview
25+
26+
- name: Install dylint tooling
27+
uses: taiki-e/cache-cargo-install-action@v2
28+
with:
29+
tool: cargo-dylint@5.0.0,dylint-link@5.0.0
30+
31+
- uses: actions/cache@v4
32+
with:
33+
path: target/dylint
34+
key: dylint-${{ env.NIGHTLY }}-${{ hashFiles('Cargo.lock') }}
35+
restore-keys: dylint-${{ env.NIGHTLY }}-
36+
37+
- name: Run dylint
38+
env:
39+
RUSTFLAGS: "-D bare_instrument_fields"
40+
run: |
41+
args="--all --no-deps"
42+
if [ -n "${{ inputs.packages }}" ]; then
43+
for pkg in ${{ inputs.packages }}; do
44+
args="$args -p $pkg"
45+
done
46+
else
47+
args="$args --workspace"
48+
fi
49+
50+
cargo +${{ env.NIGHTLY }} dylint $args

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
target/
2+
.DS_Store
3+
.claude/*.local.*
4+
CLAUDE.local.md

CLAUDE.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# init4 custom lints
2+
3+
Custom [dylint](https://github.com/trailofbits/dylint) lints for init4 Rust projects.
4+
5+
## Structure
6+
7+
Each lint is a standalone crate under its own directory (e.g. `instrument-fields/`).
8+
Each crate has its own `Cargo.toml`, `rust-toolchain.toml`, and `[workspace]` -
9+
they are not members of a shared workspace.
10+
11+
## Toolchain
12+
13+
All lint crates are pinned to `nightly-2026-03-17` for compatibility with
14+
`dylint_linting` 5.0.0. The `.cargo/config.toml` in each crate sets the
15+
`dylint-link` linker. Both `cargo-dylint` (5.0.0) and `dylint-link` (5.0.0)
16+
must be installed to build.
17+
18+
## CI
19+
20+
- `.github/workflows/ci.yml` - builds and tests the lint crates themselves.
21+
- `.github/workflows/dylint.yml` - reusable workflow that consumer repos call.
22+
It installs the toolchain, builds the lints, and runs them against the caller's
23+
workspace. Lint warnings are denied (fail the build).
24+
25+
## Adding a new lint
26+
27+
1. Create a new directory at the repo root (e.g. `my-lint/`).
28+
2. Copy the structure from `instrument-fields/` - `Cargo.toml`, `.cargo/config.toml`,
29+
`rust-toolchain.toml`, `src/lib.rs`, and `ui/` test directory.
30+
3. Use `declare_pre_expansion_lint!` for lints that inspect proc-macro attributes
31+
(like `#[instrument]`), or `declare_early_lint!` / `declare_late_lint!` otherwise.
32+
4. Add a UI test under `ui/` and run `cargo test` to generate the `.stderr` file
33+
(copy it from the path shown in the test failure output).
34+
5. Add the new crate to `.github/workflows/ci.yml`.
35+
6. Add the new lint name to the `RUSTFLAGS` deny list in `.github/workflows/dylint.yml`.
36+
7. Update `CLAUDE.md` and `README.md` to document the new lint.
37+
38+
## Modifying a lint
39+
40+
When changing lint behaviour, diagnostic messages, or adding new cases:
41+
42+
1. Update `src/lib.rs` with the change.
43+
2. Update or add UI test cases in `ui/bare_fields.rs` (or equivalent).
44+
3. Rebuild (`cargo build`) and re-run tests (`cargo test`).
45+
4. The test will fail and print the path to the actual stderr - copy it over the
46+
existing `.stderr` file, then re-run `cargo test` to confirm it passes.
47+
5. Update `CLAUDE.md` and `README.md` if the change affects documented behaviour.

README.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# init4 custom lints
2+
3+
Custom [dylint](https://github.com/trailofbits/dylint) lints for init4 Rust projects.
4+
5+
## Lints
6+
7+
### `bare_instrument_fields`
8+
9+
Detects bare identifiers in `#[instrument(fields(x))]` that silently create
10+
`tracing::field::Empty` span fields instead of capturing the variable value.
11+
12+
Three cases are flagged:
13+
14+
- **Bare param with `skip_all`** - `fields(x)` creates an Empty field instead of
15+
capturing the parameter. Suggests replacing `skip_all` with a concrete `skip(...)`.
16+
- **Bare param without `skip_all`** - `fields(x)` shadows the auto-captured
17+
parameter with an Empty value. Suggests removing from `fields()`.
18+
- **Bare non-param, never recorded** - `fields(x)` where `x` is not a parameter
19+
and no `.record("x", ...)` call exists in the body. The field is permanently Empty.
20+
21+
Fields that are assigned (`x = expr`), format-hinted (`%x`, `?x`), or recorded
22+
via `.record("x", ...)` in the function body are not flagged.
23+
24+
## Integrating into a repo
25+
26+
### 1. Add workspace metadata
27+
28+
In the consumer repo's `Cargo.toml`:
29+
30+
```toml
31+
[workspace.metadata.dylint]
32+
libraries = [
33+
{ git = "https://github.com/init4tech/lints", pattern = "instrument-fields" }
34+
]
35+
```
36+
37+
### 2. Add the CI workflow
38+
39+
In `.github/workflows/dylint.yml`:
40+
41+
```yaml
42+
name: dylint
43+
44+
on:
45+
push:
46+
branches: [main]
47+
pull_request:
48+
49+
jobs:
50+
dylint:
51+
uses: init4tech/lints/.github/workflows/dylint.yml@main
52+
```
53+
54+
To check specific packages only:
55+
56+
```yaml
57+
jobs:
58+
dylint:
59+
uses: init4tech/lints/.github/workflows/dylint.yml@main
60+
with:
61+
packages: "my-crate other-crate"
62+
```
63+
64+
### Running locally
65+
66+
```sh
67+
rustup toolchain install nightly-2026-03-17 --component rustc-dev llvm-tools-preview
68+
cargo install cargo-dylint@5.0.0 dylint-link@5.0.0
69+
70+
# If using workspace metadata:
71+
cargo +nightly-2026-03-17 dylint --all --workspace
72+
73+
# Or with a local checkout of the lints repo:
74+
DYLINT_LIBRARY_PATH=<PATH TO REPO>/instrument-fields/target/debug \
75+
cargo +nightly-2026-03-17 dylint --all --no-deps --workspace
76+
```
77+
78+
## Development
79+
80+
```sh
81+
cd instrument-fields
82+
cargo build # builds the lint .so
83+
cargo test # runs UI tests
84+
```
85+
86+
Pinned to `nightly-2026-03-17` for compatibility with `dylint_linting` 5.0.0.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[target.'cfg(all())']
2+
rustflags = ["-C", "linker=dylint-link"]

0 commit comments

Comments
 (0)