rust: rust_is_available: warn for bindgen < 0.72.1 && libclang >= 22

Starting with LLVM 22, `clang_getTypeDeclaration()` may return a forward
declaration instead of the type definition. This made `bindgen` generate
opaque types [1][2], which in turn made us fail with e.g.

    error[E0609]: no field `__bindgen_anon_1` on type `bindings::kernel_param`
      --> rust/kernel/module_param.rs:78:46
       |
    78 |         let container = unsafe { &*((*param).__bindgen_anon_1.arg.cast::<SetOnce<T>>()) };
       |                                              ^^^^^^^^^^^^^^^^ unknown field
       |
       = note: available field is: `_address`

This was fixed in `bindgen` 0.72.1 [3].

In order to clarify what is going on and avoid confusion [4][5], add
a warning to `rust_is_available.sh` about it when the versions match,
similar to past warnings like the one removed in:

  commit ae64324ad5 ("rust: rust_is_available: remove warning for `bindgen` < 0.69.5 && libclang >= 19.1")

In addition, even if the versions match, check if the issue appears to
not reproduce with the given binaries, to avoid a warning in such a case.

Finally, include tests.

[ Nathan, in parallel, updated the instructions of the LLVM+Rust
  kernel.org toolchains [6] so that `--version` is not passed to
  `cargo` for `bindgen`, and thus the latest `bindgen` is installed
  by default, which should help to avoid some of these situations.

  Thanks!

    - Miguel ]

Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs).
Link: https://github.com/rust-lang/rust-bindgen/issues/3264 [1]
Link: https://github.com/Rust-for-Linux/linux/issues/353 [2] # "Missing fields in nested class with LLVM 22."
Link: https://github.com/rust-lang/rust-bindgen/pull/3278 [3]
Reported-by: Burak Emir <burak.emir@gmail.com>
Link: https://github.com/Rust-for-Linux/linux/issues/1247 [4]
Link: https://lore.kernel.org/rust-for-linux/CABwQupNfMAJOGqRM9ke6tj4f53dCCsBDKU7Vp+zf8mwk7bqt8Q@mail.gmail.com/ [5]
Link: https://mirrors.edge.kernel.org/pub/tools/llvm/rust/ [6]
Tested-by: Burak Emir <burak.emir@gmail.com>
Link: https://patch.msgid.link/20260719120514.159914-1-ojeda@kernel.org
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
This commit is contained in:
Miguel Ojeda
2026-07-19 14:05:14 +02:00
parent fe39a233ea
commit dc01dfb37b
3 changed files with 48 additions and 1 deletions

View File

@@ -208,6 +208,20 @@ if [ "$bindgen_libclang_cversion" -lt "$bindgen_libclang_min_cversion" ]; then
exit 1
fi
if [ "$bindgen_libclang_cversion" -ge 2200000 ] &&
[ "$rust_bindings_generator_cversion" -lt 7201 ]; then
# Distributions may have patched the issue.
if ! "$BINDGEN" $(dirname $0)/rust_is_available_bindgen_libclang_22.h | grep -q 'pub foo'; then
echo >&2 "***"
echo >&2 "*** Rust bindings generator '$BINDGEN' < 0.72.1 together with libclang >= 22"
echo >&2 "*** may not work due to a bug (https://github.com/rust-lang/rust-bindgen/pull/3278)."
echo >&2 "*** Your bindgen version: $rust_bindings_generator_version"
echo >&2 "*** Your libclang version: $bindgen_libclang_version"
echo >&2 "***"
warning=1
fi
fi
# If the C compiler is Clang, then we can also check whether its version
# matches the `libclang` version used by the Rust bindings generator.
#

View File

@@ -0,0 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0 */
struct S;
struct S {
int foo;
};

View File

@@ -54,16 +54,23 @@ else:
""")
@classmethod
def generate_bindgen(cls, version_stdout, libclang_stderr):
def generate_bindgen(cls, version_stdout, libclang_stderr, libclang_22_patched=False):
if libclang_stderr is None:
libclang_case = f"raise SystemExit({cls.bindgen_default_bindgen_libclang_failure_exit_code})"
else:
libclang_case = f"print({repr(libclang_stderr)}, file=sys.stderr)"
if libclang_22_patched:
libclang_22_case = "print('pub foo: ::std::os::raw::c_int,')"
else:
libclang_22_case = "pass"
return cls.generate_executable(f"""#!/usr/bin/env python3
import sys
if "rust_is_available_bindgen_libclang.h" in " ".join(sys.argv):
{libclang_case}
elif "rust_is_available_bindgen_libclang_22.h" in " ".join(sys.argv):
{libclang_22_case}
else:
print({repr(version_stdout)})
""")
@@ -260,6 +267,27 @@ else:
result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })
self.assertIn(f"libclang (used by the Rust bindings generator '{bindgen}') is too old.", result.stderr)
def test_bindgen_bad_libclang_22(self):
for (bindgen_version, libclang_version, expected_not_patched) in (
("0.71.1", "21.1.0", self.Expected.SUCCESS),
("0.71.1", "22.0.0", self.Expected.SUCCESS_WITH_WARNINGS),
("0.71.1", "22.1.0", self.Expected.SUCCESS_WITH_WARNINGS),
("0.72.0", "22.0.0", self.Expected.SUCCESS_WITH_WARNINGS),
("0.72.1", "22.0.0", self.Expected.SUCCESS),
):
with self.subTest(bindgen_version=bindgen_version, libclang_version=libclang_version):
cc = self.generate_clang(f"clang version {libclang_version}")
libclang_stderr = f"scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version {libclang_version} [-W#pragma-messages], err: false"
bindgen = self.generate_bindgen(f"bindgen {bindgen_version}", libclang_stderr)
result = self.run_script(expected_not_patched, { "BINDGEN": bindgen, "CC": cc })
if expected_not_patched == self.Expected.SUCCESS_WITH_WARNINGS:
self.assertIn(f"Rust bindings generator '{bindgen}' < 0.72.1 together with libclang >= 22", result.stderr)
bindgen = self.generate_bindgen(f"bindgen {bindgen_version}", libclang_stderr, libclang_22_patched=True)
result = self.run_script(self.Expected.SUCCESS, { "BINDGEN": bindgen, "CC": cc })
def test_clang_matches_bindgen_libclang_different_bindgen(self):
bindgen = self.generate_bindgen_libclang("scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version 999.0.0 [-W#pragma-messages], err: false")
result = self.run_script(self.Expected.SUCCESS_WITH_WARNINGS, { "BINDGEN": bindgen })