Summary
no_std_fs_operations ignores in-source lint-level attributes entirely. Neither #[allow(no_std_fs_operations)] nor #[expect(no_std_fs_operations)] (gated via cfg_attr(dylint_lib = "whitaker_suite", …) per ADR-002) suppresses the diagnostic. With expect, the outcome is contradictory: the original diagnostic and an unfulfilled_lint_expectations error are emitted for the same site.
Environment
- whitaker-installer / suite v0.2.5, pinned toolchain
nightly-2025-09-18
cargo-dylint 5.0.0, invoked via the whitaker wrapper: RUSTFLAGS="-D warnings" whitaker --all -- --all-targets --all-features
- Observed while adopting the suite in
leynos/netsuke (branch adopt-whitaker)
Reproduction
In any linted crate:
/// Sync a temporary file via its open handle.
#[cfg_attr(
dylint_lib = "whitaker_suite",
expect(
no_std_fs_operations,
reason = "NamedTempFile lives in the ambient system temp directory by design"
)
)]
fn sync_temp_file(tmp: &tempfile::NamedTempFile) -> std::io::Result<()> {
tmp.as_file().sync_all()
}
Result:
error: std::fs operation `std::fs::File::sync_all` bypasses the capability-based filesystem policy.
--> src/runner/process/file_io.rs:47:5
error: this lint expectation is unfulfilled
--> src/runner/process/file_io.rs:41:9
|
41 | no_std_fs_operations,
The pair of errors proves the cfg matched, the attribute was active, and rustc registered the expectation against the correctly-resolved lint name — yet the diagnostic was still emitted at its default Deny level, and the expectation was never fulfilled. Swapping expect for allow removes the unfulfilled-expectation error but the primary diagnostic still fires. A module-level inner attribute (#![cfg_attr(…)]) behaves identically. The same behaviour was observed on function-scoped attributes.
Expected
Standard rustc lint-level semantics: an enclosing allow/expect attribute suppresses the diagnostic (and fulfils the expectation), as ADR-002 ("Attribute macro for conditional Dylint expect") assumes when it prescribes the cfg_attr(dylint_lib = "…", expect(…)) pattern.
Suspected area
emit_diagnostic in crates/no_std_fs_operations/src/diagnostics.rs uses cx.span_lint(NO_STD_FS_OPERATIONS, span, …), which looks up the lint level at the visitor's current lint node rather than the HIR node that owns the offending expression/item. Other lints in the suite may share the pattern; no_expect_outside_tests and no_unwrap_or_else_panic were not exercised with attributes during the netsuke adoption, so their behaviour is unverified. Emitting via cx.tcx.node_span_lint / span_lint_hir (or the clippy_utils helpers that take a HirId) should restore attribute handling. A UI fixture asserting that allow-annotated code produces no diagnostic would prevent regression — the current UI suites only cover positive findings and excluded_crates.
Impact
Crate-level excluded_crates in dylint.toml is currently the only working escape hatch. Single-crate consumers with legitimately-ambient code (e.g. a which-style PATH resolver) cannot scope a suppression tighter than the whole crate; netsuke worked around it by extracting an ambient_fs boundary crate. This blocks the estate-wide rollout pattern for other single-crate repositories, and it makes ADR-002's proposed dylint_expect macro moot until the underlying level lookup is fixed.
Summary
no_std_fs_operationsignores in-source lint-level attributes entirely. Neither#[allow(no_std_fs_operations)]nor#[expect(no_std_fs_operations)](gated viacfg_attr(dylint_lib = "whitaker_suite", …)per ADR-002) suppresses the diagnostic. Withexpect, the outcome is contradictory: the original diagnostic and anunfulfilled_lint_expectationserror are emitted for the same site.Environment
nightly-2025-09-18cargo-dylint5.0.0, invoked via thewhitakerwrapper:RUSTFLAGS="-D warnings" whitaker --all -- --all-targets --all-featuresleynos/netsuke(branchadopt-whitaker)Reproduction
In any linted crate:
Result:
The pair of errors proves the
cfgmatched, the attribute was active, and rustc registered the expectation against the correctly-resolved lint name — yet the diagnostic was still emitted at its defaultDenylevel, and the expectation was never fulfilled. Swappingexpectforallowremoves the unfulfilled-expectation error but the primary diagnostic still fires. A module-level inner attribute (#![cfg_attr(…)]) behaves identically. The same behaviour was observed on function-scoped attributes.Expected
Standard rustc lint-level semantics: an enclosing
allow/expectattribute suppresses the diagnostic (and fulfils the expectation), as ADR-002 ("Attribute macro for conditional Dylintexpect") assumes when it prescribes thecfg_attr(dylint_lib = "…", expect(…))pattern.Suspected area
emit_diagnosticincrates/no_std_fs_operations/src/diagnostics.rsusescx.span_lint(NO_STD_FS_OPERATIONS, span, …), which looks up the lint level at the visitor's current lint node rather than the HIR node that owns the offending expression/item. Other lints in the suite may share the pattern;no_expect_outside_testsandno_unwrap_or_else_panicwere not exercised with attributes during the netsuke adoption, so their behaviour is unverified. Emitting viacx.tcx.node_span_lint/span_lint_hir(or theclippy_utilshelpers that take aHirId) should restore attribute handling. A UI fixture asserting thatallow-annotated code produces no diagnostic would prevent regression — the current UI suites only cover positive findings andexcluded_crates.Impact
Crate-level
excluded_cratesindylint.tomlis currently the only working escape hatch. Single-crate consumers with legitimately-ambient code (e.g. awhich-style PATH resolver) cannot scope a suppression tighter than the whole crate; netsuke worked around it by extracting anambient_fsboundary crate. This blocks the estate-wide rollout pattern for other single-crate repositories, and it makes ADR-002's proposeddylint_expectmacro moot until the underlying level lookup is fixed.