fix(xcheck): mirror [workspace.lints] too — and the two things that hid behind it
windows-host / package (push) Failing after 5m0s
windows-host / winget-source (push) Skipped
ci / rust-arm64 (push) Failing after 22s
ci / web (push) Successful in 2m28s
ci / docs-site (push) Successful in 2m55s
ci / rust (push) Failing after 6m19s
decky / build-publish (push) Successful in 20s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 9s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 9s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 9s
ci / bench (push) Successful in 6m20s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 10s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 12s
android / android (push) Successful in 12m2s
deb / build-publish-client-arm64 (push) Successful in 8m59s
arch / build-publish (push) Successful in 14m57s
deb / build-publish (push) Successful in 12m3s
deb / build-publish-host (push) Successful in 13m33s
apple / swift (push) Canceled after 0s
apple / screenshots (push) Canceled after 0s
docker / build-push-arm64cross (push) Canceled after 0s
docker / deploy-docs (push) Canceled after 0s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Canceled after 14m27s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Canceled after 14m22s
windows-host / package (push) Failing after 5m0s
windows-host / winget-source (push) Skipped
ci / rust-arm64 (push) Failing after 22s
ci / web (push) Successful in 2m28s
ci / docs-site (push) Successful in 2m55s
ci / rust (push) Failing after 6m19s
decky / build-publish (push) Successful in 20s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 9s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 9s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 9s
ci / bench (push) Successful in 6m20s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 10s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 12s
android / android (push) Successful in 12m2s
deb / build-publish-client-arm64 (push) Successful in 8m59s
arch / build-publish (push) Successful in 14m57s
deb / build-publish (push) Successful in 12m3s
deb / build-publish-host (push) Successful in 13m33s
apple / swift (push) Canceled after 0s
apple / screenshots (push) Canceled after 0s
docker / build-push-arm64cross (push) Canceled after 0s
docker / deploy-docs (push) Canceled after 0s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Canceled after 14m27s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Canceled after 14m22s
`scripts/xcheck.sh` stopped working the moment the workspace adopted its unsafe discipline
(`52191071`). That commit added `[lints] workspace = true` to every crate manifest, and a member
inheriting a lint table the workspace ROOT does not define does not merely lose the lint — cargo
refuses to parse the manifest at all:
error inheriting `lints` from workspace root manifest's `workspace.lints`
`workspace.lints` was not defined
The generated root mirrored `[workspace.package]` but nothing else. It now mirrors every
`[workspace.lints.*]` table generically, so a future table (clippy, rustdoc, …) is picked up
without touching the script again.
That mattered more than a broken helper, because xcheck is the ONLY thing that lints these crates
for Windows — the Windows CI job clippies `-p punktfunk-host -p punktfunk-tray`, which does not
lint a dependency. So while it was down, two real failures sat on main unseen:
* **pf-frame `dxgi.rs`, 8 × `clippy::undocumented_unsafe_blocks`.** `307ca88b`/`0a710fdf` split
the D3DKMT/DXGI calls into one `unsafe` block per FFI call under a single shared SAFETY comment,
but the lint wants a proof per block — a comment two lines up with an intervening statement does
not count. Each site now carries its own. Not "as above", which this sweep already found to be a
proof that rots when the block it points at moves.
* **pf-win-display `input_desktop.rs`, one E0133.** `GetUserObjectInformationW` is an unsafe
operation in an `unsafe fn` body, which `unsafe_op_in_unsafe_fn` (now warn workspace-wide, and
`-D warnings` here) requires to sit in its own block. The SAFETY proof was already written; only
the block was missing.
The `&&` chain in `elevate_process_gpu_priority` keeps two separate blocks rather than one around
the chain — wrapping it would destroy the short-circuit and always issue the relative-priority
call. Same trap this sweep hit in `wait_mode_settled`.
Verified: both xcheck targets clean again.
This commit is contained in:
@@ -134,6 +134,9 @@ pub unsafe fn make_device(adapter: &IDXGIAdapter1) -> Result<(ID3D11Device, ID3D
|
|||||||
// SAFETY: `dxgi_dev` is a live interface just obtained by a checked `cast`; both calls take
|
// SAFETY: `dxgi_dev` is a live interface just obtained by a checked `cast`; both calls take
|
||||||
// a scalar and only report failure through their return value.
|
// a scalar and only report failure through their return value.
|
||||||
if unsafe { dxgi_dev.SetGPUThreadPriority(0x4000_001E) }.is_err()
|
if unsafe { dxgi_dev.SetGPUThreadPriority(0x4000_001E) }.is_err()
|
||||||
|
// SAFETY: same live interface, same scalar-in/HRESULT-out contract as the call above.
|
||||||
|
// Deliberately its own block rather than one around the whole chain, which would
|
||||||
|
// destroy the short-circuit and always issue the relative-priority call too.
|
||||||
&& unsafe { dxgi_dev.SetGPUThreadPriority(7) }.is_err()
|
&& unsafe { dxgi_dev.SetGPUThreadPriority(7) }.is_err()
|
||||||
{
|
{
|
||||||
tracing::warn!("SetGPUThreadPriority failed (run as admin/SYSTEM for GPU priority)");
|
tracing::warn!("SetGPUThreadPriority failed (run as admin/SYSTEM for GPU priority)");
|
||||||
@@ -256,12 +259,17 @@ unsafe fn d3dkmt_set_scheduling_priority_class(
|
|||||||
// process keeps for its lifetime (gdi32 is never unloaded here), and `GetProcAddress` is passed
|
// process keeps for its lifetime (gdi32 is never unloaded here), and `GetProcAddress` is passed
|
||||||
// that live handle. Both results are checked by `?` before use.
|
// that live handle. Both results are checked by `?` before use.
|
||||||
let gdi32 = unsafe { LoadLibraryA(s!("gdi32.dll")) }.ok()?;
|
let gdi32 = unsafe { LoadLibraryA(s!("gdi32.dll")) }.ok()?;
|
||||||
|
// SAFETY: `gdi32` is the live module handle the checked `LoadLibraryA` just returned, and the
|
||||||
|
// export name is a static NUL-terminated literal; the result is checked by `?` before use.
|
||||||
let p = unsafe { GetProcAddress(gdi32, s!("D3DKMTSetProcessSchedulingPriorityClass")) }?;
|
let p = unsafe { GetProcAddress(gdi32, s!("D3DKMTSetProcessSchedulingPriorityClass")) }?;
|
||||||
type SetPrio = unsafe extern "system" fn(HANDLE, i32) -> i32;
|
type SetPrio = unsafe extern "system" fn(HANDLE, i32) -> i32;
|
||||||
// SAFETY: `p` is the non-null export just resolved, and `SetPrio` is its documented signature
|
// SAFETY: `p` is the non-null export just resolved, and `SetPrio` is its documented signature
|
||||||
// (`NTSTATUS D3DKMTSetProcessSchedulingPriorityClass(HANDLE, D3DKMT_SCHEDULINGPRIORITYCLASS)`,
|
// (`NTSTATUS D3DKMTSetProcessSchedulingPriorityClass(HANDLE, D3DKMT_SCHEDULINGPRIORITYCLASS)`,
|
||||||
// both arguments 4/8-byte scalars). `process` is a valid handle by this fn's own contract.
|
// both arguments 4/8-byte scalars). `process` is a valid handle by this fn's own contract.
|
||||||
let f: SetPrio = unsafe { std::mem::transmute(p) };
|
let f: SetPrio = unsafe { std::mem::transmute(p) };
|
||||||
|
// SAFETY: `f` is that export transmuted to its documented signature directly above; `process`
|
||||||
|
// is a valid handle by this fn's own contract and `prio` is a plain scalar. The call returns an
|
||||||
|
// NTSTATUS and retains nothing.
|
||||||
Some(unsafe { f(process, prio) })
|
Some(unsafe { f(process, prio) })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,8 +373,12 @@ fn hags_enabled(luid: LUID) -> Option<bool> {
|
|||||||
// SAFETY: static NUL-terminated literals; gdi32 stays loaded for the process lifetime, and each
|
// SAFETY: static NUL-terminated literals; gdi32 stays loaded for the process lifetime, and each
|
||||||
// result is checked by `?`/`.ok()?` before the next call uses it.
|
// result is checked by `?`/`.ok()?` before the next call uses it.
|
||||||
let gdi32 = unsafe { LoadLibraryA(s!("gdi32.dll")) }.ok()?;
|
let gdi32 = unsafe { LoadLibraryA(s!("gdi32.dll")) }.ok()?;
|
||||||
|
// SAFETY: `gdi32` is the live handle from the `.ok()?`-checked load above; static literal name;
|
||||||
|
// `?` checks the result.
|
||||||
let open = unsafe { GetProcAddress(gdi32, s!("D3DKMTOpenAdapterFromLuid")) }?;
|
let open = unsafe { GetProcAddress(gdi32, s!("D3DKMTOpenAdapterFromLuid")) }?;
|
||||||
|
// SAFETY: same live `gdi32` handle and static-literal name; `?` checks the result.
|
||||||
let query = unsafe { GetProcAddress(gdi32, s!("D3DKMTQueryAdapterInfo")) }?;
|
let query = unsafe { GetProcAddress(gdi32, s!("D3DKMTQueryAdapterInfo")) }?;
|
||||||
|
// SAFETY: same live `gdi32` handle and static-literal name; `?` checks the result.
|
||||||
let close = unsafe { GetProcAddress(gdi32, s!("D3DKMTCloseAdapter")) }?;
|
let close = unsafe { GetProcAddress(gdi32, s!("D3DKMTCloseAdapter")) }?;
|
||||||
type OpenFn = unsafe extern "system" fn(*mut OpenFromLuid) -> i32;
|
type OpenFn = unsafe extern "system" fn(*mut OpenFromLuid) -> i32;
|
||||||
type QueryFn = unsafe extern "system" fn(*mut QueryInfo) -> i32;
|
type QueryFn = unsafe extern "system" fn(*mut QueryInfo) -> i32;
|
||||||
@@ -375,7 +387,11 @@ fn hags_enabled(luid: LUID) -> Option<bool> {
|
|||||||
// export's documented signature — one `*mut` to the matching `repr(C)` struct declared here,
|
// export's documented signature — one `*mut` to the matching `repr(C)` struct declared here,
|
||||||
// returning NTSTATUS.
|
// returning NTSTATUS.
|
||||||
let open: OpenFn = unsafe { std::mem::transmute(open) };
|
let open: OpenFn = unsafe { std::mem::transmute(open) };
|
||||||
|
// SAFETY: `query` is the non-null export resolved above and `QueryFn` mirrors its documented
|
||||||
|
// signature — one `*mut` to the `repr(C)` struct declared here, returning NTSTATUS.
|
||||||
let query: QueryFn = unsafe { std::mem::transmute(query) };
|
let query: QueryFn = unsafe { std::mem::transmute(query) };
|
||||||
|
// SAFETY: `close` is the non-null export resolved above and `CloseFn` mirrors its documented
|
||||||
|
// signature — one `*mut` to the `repr(C)` struct declared here, returning NTSTATUS.
|
||||||
let close: CloseFn = unsafe { std::mem::transmute(close) };
|
let close: CloseFn = unsafe { std::mem::transmute(close) };
|
||||||
|
|
||||||
let mut oa = OpenFromLuid { luid, h_adapter: 0 };
|
let mut oa = OpenFromLuid { luid, h_adapter: 0 };
|
||||||
|
|||||||
@@ -133,13 +133,15 @@ unsafe fn desktop_name(h: HDESK) -> Option<String> {
|
|||||||
let mut needed = 0u32;
|
let mut needed = 0u32;
|
||||||
// SAFETY: `h` is live per this fn's contract; `name`/`needed` are live out-params and the call
|
// SAFETY: `h` is live per this fn's contract; `name`/`needed` are live out-params and the call
|
||||||
// writes at most `nlength` bytes, exactly the size passed.
|
// writes at most `nlength` bytes, exactly the size passed.
|
||||||
GetUserObjectInformationW(
|
unsafe {
|
||||||
HANDLE(h.0),
|
GetUserObjectInformationW(
|
||||||
UOI_NAME,
|
HANDLE(h.0),
|
||||||
Some(name.as_mut_ptr().cast()),
|
UOI_NAME,
|
||||||
(name.len() * 2) as u32,
|
Some(name.as_mut_ptr().cast()),
|
||||||
Some(&mut needed),
|
(name.len() * 2) as u32,
|
||||||
)
|
Some(&mut needed),
|
||||||
|
)
|
||||||
|
}
|
||||||
.ok()?;
|
.ok()?;
|
||||||
let len = name.iter().position(|&c| c == 0).unwrap_or(name.len());
|
let len = name.iter().position(|&c| c == 0).unwrap_or(name.len());
|
||||||
Some(String::from_utf16_lossy(&name[..len]))
|
Some(String::from_utf16_lossy(&name[..len]))
|
||||||
|
|||||||
@@ -111,6 +111,14 @@ mkdir -p "$OUT"
|
|||||||
echo
|
echo
|
||||||
echo '[workspace.package]'
|
echo '[workspace.package]'
|
||||||
sed -n '/^\[workspace\.package\]/,/^$/p' "$REPO/Cargo.toml" | sed '1d;/^$/d'
|
sed -n '/^\[workspace\.package\]/,/^$/p' "$REPO/Cargo.toml" | sed '1d;/^$/d'
|
||||||
|
# …and the real [workspace.lints.*] tables. Every crate manifest now carries
|
||||||
|
# `[lints] workspace = true` (the workspace-wide unsafe discipline), and a member inheriting a
|
||||||
|
# lint table the generated ROOT does not define does not merely lose the lint — cargo refuses to
|
||||||
|
# parse the manifest at all ("error inheriting `lints` from workspace root manifest's
|
||||||
|
# `workspace.lints`"), which broke this script outright the day that landed. Mirrored generically
|
||||||
|
# so a new table (clippy, rustdoc, …) is picked up without touching this script again.
|
||||||
|
echo
|
||||||
|
awk '/^\[/ { in_lints = ($0 ~ /^\[workspace\.lints/) } in_lints' "$REPO/Cargo.toml"
|
||||||
} > "$OUT/Cargo.toml"
|
} > "$OUT/Cargo.toml"
|
||||||
|
|
||||||
mkdir -p "$OUT/punktfunk-core/src"
|
mkdir -p "$OUT/punktfunk-core/src"
|
||||||
|
|||||||
Reference in New Issue
Block a user