Skip to content

Commit 54e3eae

Browse files
committed
Merge patch series "syn support"
This patch series introduces support for `syn` (and its dependencies): Syn is a parsing library for parsing a stream of Rust tokens into a syntax tree of Rust source code. Currently this library is geared toward use in Rust procedural macros, but contains some APIs that may be useful more generally. It is the most downloaded Rust crate (according to crates.io), and it is also used by the Rust compiler itself. Having such support allows to greatly simplify writing complex macros such as `pin-init`. We will use it in the `macros` crate too. Benno has already prepared the `pin-init` version based on this, and on top of that, we will be able to simplify the `macros` crate too. I think Jesung is working on updating the `TryFrom` and `Into` upcoming derive macros to use `syn` too. The series starts with a few preparation commits (two fixes were already merged in mainline that were discovered by this series), then each crate is added. Finally, support for using the new crates from our `macros` crate is introduced. This has been a long time coming, e.g. even before Rust for Linux was merged into the Linux kernel, Gary and Benno have wanted to use `syn`. The first iterations of this, from 2022 and 2023 (with `serde` too, another popular crate), are at: Rust-for-Linux/linux#910 Rust-for-Linux/linux#1007 After those, we considered picking these from the distributions where possible. However, after discussing it, it is not really worth the complexity: vendoring makes things less complex and is less fragile. In particular, we avoid having to support and test several versions, we avoid having to introduce Cargo just to properly fetch the right versions from the registry, we can easily customize the crates if needed (e.g. dropping the `unicode_idents` dependency like it is done in this series) and we simplify the configuration of the build for users for which the "default" paths/registries would not have worked. Moreover, nowadays, the ~57k lines introduced are not that much compared to years ago (it dwarfed the actual Rust kernel code). Moreover, back then it wasn't clear the Rust experiment would be a success, so it would have been a bit pointless/risky to add many lines for nothing. Our macro needs were also smaller in the early days. So, finally, in Kangrejos 2025 we discussed going with the original, simpler approach. Thus here it is the result. There should not be many updates needed for these, and even if there are, they should not be too big, e.g. +7k -3k lines across the 3 crates in the last year. Note that `syn` does not have all the features enabled, since we do not need them so far, but they can easily be enabled just adding them to the list. Link: https://patch.msgid.link/[email protected] Signed-off-by: Miguel Ojeda <[email protected]>
2 parents 7a0eae4 + 52ba807 commit 54e3eae

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+57906
-17
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
*.o.*
4242
*.patch
4343
*.pyc
44+
*.rlib
4445
*.rmeta
4546
*.rpm
4647
*.rsi

Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1826,10 +1826,17 @@ rusttest: prepare
18261826
$(Q)$(MAKE) $(build)=rust $@
18271827

18281828
# Formatting targets
1829+
#
1830+
# Generated files as well as vendored crates are skipped.
18291831
PHONY += rustfmt rustfmtcheck
18301832

18311833
rustfmt:
18321834
$(Q)find $(srctree) $(RCS_FIND_IGNORE) \
1835+
\( \
1836+
-path $(srctree)/rust/proc-macro2 \
1837+
-o -path $(srctree)/rust/quote \
1838+
-o -path $(srctree)/rust/syn \
1839+
\) -prune -o \
18331840
-type f -a -name '*.rs' -a ! -name '*generated*' -print \
18341841
| xargs $(RUSTFMT) $(rustfmt_flags)
18351842

rust/Makefile

Lines changed: 132 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ endif
2727

2828
obj-$(CONFIG_RUST) += exports.o
2929

30+
always-$(CONFIG_RUST) += libproc_macro2.rlib libquote.rlib libsyn.rlib
31+
3032
always-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated.rs
3133
always-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated_kunit.c
3234

@@ -60,11 +62,61 @@ rustdoc_test_quiet=--test-args -q
6062
rustdoc_test_kernel_quiet=>/dev/null
6163
endif
6264

63-
core-cfgs = \
64-
--cfg no_fp_fmt_parse
65+
cfgs-to-flags = $(patsubst %,--cfg='%',$1)
66+
67+
core-cfgs := \
68+
no_fp_fmt_parse
6569

6670
core-edition := $(if $(call rustc-min-version,108700),2024,2021)
6771

72+
core-skip_flags := \
73+
--edition=2021 \
74+
-Wunreachable_pub \
75+
-Wrustdoc::unescaped_backticks
76+
77+
core-flags := \
78+
--edition=$(core-edition) \
79+
$(call cfgs-to-flags,$(core-cfgs))
80+
81+
proc_macro2-cfgs := \
82+
feature="proc-macro" \
83+
wrap_proc_macro \
84+
$(if $(call rustc-min-version,108800),proc_macro_span_file proc_macro_span_location)
85+
86+
# Stable since Rust 1.79.0: `feature(proc_macro_byte_character,proc_macro_c_str_literals)`.
87+
proc_macro2-flags := \
88+
--cap-lints=allow \
89+
-Zcrate-attr='feature(proc_macro_byte_character,proc_macro_c_str_literals)' \
90+
$(call cfgs-to-flags,$(proc_macro2-cfgs))
91+
92+
quote-cfgs := \
93+
feature="proc-macro"
94+
95+
quote-skip_flags := \
96+
--edition=2021
97+
98+
quote-flags := \
99+
--edition=2018 \
100+
--cap-lints=allow \
101+
--extern proc_macro2 \
102+
$(call cfgs-to-flags,$(quote-cfgs))
103+
104+
# `extra-traits`, `fold` and `visit` may be enabled if needed.
105+
syn-cfgs := \
106+
feature="clone-impls" \
107+
feature="derive" \
108+
feature="full" \
109+
feature="parsing" \
110+
feature="printing" \
111+
feature="proc-macro" \
112+
feature="visit-mut"
113+
114+
syn-flags := \
115+
--cap-lints=allow \
116+
--extern proc_macro2 \
117+
--extern quote \
118+
$(call cfgs-to-flags,$(syn-cfgs))
119+
68120
# `rustdoc` did not save the target modifiers, thus workaround for
69121
# the time being (https://github.com/rust-lang/rust/issues/144521).
70122
rustdoc_modifiers_workaround := $(if $(call rustc-min-version,108800),-Cunsafe-allow-abi-mismatch=fixed-x18)
@@ -114,16 +166,33 @@ rustdoc: rustdoc-core rustdoc-macros rustdoc-compiler_builtins \
114166
$(Q)for f in $(rustdoc_output)/static.files/rustdoc-*.css; do \
115167
echo ".logo-container > img { object-fit: contain; }" >> $$f; done
116168

169+
rustdoc-proc_macro2: private rustdoc_host = yes
170+
rustdoc-proc_macro2: private rustc_target_flags = $(proc_macro2-flags)
171+
rustdoc-proc_macro2: $(src)/proc-macro2/lib.rs rustdoc-clean FORCE
172+
+$(call if_changed,rustdoc)
173+
174+
rustdoc-quote: private rustdoc_host = yes
175+
rustdoc-quote: private rustc_target_flags = $(quote-flags)
176+
rustdoc-quote: private skip_flags = $(quote-skip_flags)
177+
rustdoc-quote: $(src)/quote/lib.rs rustdoc-clean rustdoc-proc_macro2 FORCE
178+
+$(call if_changed,rustdoc)
179+
180+
rustdoc-syn: private rustdoc_host = yes
181+
rustdoc-syn: private rustc_target_flags = $(syn-flags)
182+
rustdoc-syn: $(src)/syn/lib.rs rustdoc-clean rustdoc-quote FORCE
183+
+$(call if_changed,rustdoc)
184+
117185
rustdoc-macros: private rustdoc_host = yes
118186
rustdoc-macros: private rustc_target_flags = --crate-type proc-macro \
119-
--extern proc_macro
120-
rustdoc-macros: $(src)/macros/lib.rs rustdoc-clean FORCE
187+
--extern proc_macro --extern proc_macro2 --extern quote --extern syn
188+
rustdoc-macros: $(src)/macros/lib.rs rustdoc-clean rustdoc-proc_macro2 \
189+
rustdoc-quote rustdoc-syn FORCE
121190
+$(call if_changed,rustdoc)
122191

123192
# Starting with Rust 1.82.0, skipping `-Wrustdoc::unescaped_backticks` should
124193
# not be needed -- see https://github.com/rust-lang/rust/pull/128307.
125-
rustdoc-core: private skip_flags = --edition=2021 -Wrustdoc::unescaped_backticks
126-
rustdoc-core: private rustc_target_flags = --edition=$(core-edition) $(core-cfgs)
194+
rustdoc-core: private skip_flags = $(core-skip_flags)
195+
rustdoc-core: private rustc_target_flags = $(core-flags)
127196
rustdoc-core: $(RUST_LIB_SRC)/core/src/lib.rs rustdoc-clean FORCE
128197
+$(call if_changed,rustdoc)
129198

@@ -161,8 +230,8 @@ rustdoc-clean: FORCE
161230
quiet_cmd_rustc_test_library = $(RUSTC_OR_CLIPPY_QUIET) TL $<
162231
cmd_rustc_test_library = \
163232
OBJTREE=$(abspath $(objtree)) \
164-
$(RUSTC_OR_CLIPPY) $(rust_common_flags) \
165-
@$(objtree)/include/generated/rustc_cfg $(rustc_target_flags) \
233+
$(RUSTC_OR_CLIPPY) $(filter-out $(skip_flags),$(rust_common_flags) $(rustc_target_flags)) \
234+
@$(objtree)/include/generated/rustc_cfg \
166235
--crate-type $(if $(rustc_test_library_proc),proc-macro,rlib) \
167236
--out-dir $(objtree)/$(obj)/test --cfg testlib \
168237
-L$(objtree)/$(obj)/test \
@@ -174,9 +243,24 @@ rusttestlib-build_error: $(src)/build_error.rs FORCE
174243
rusttestlib-ffi: $(src)/ffi.rs FORCE
175244
+$(call if_changed,rustc_test_library)
176245

177-
rusttestlib-macros: private rustc_target_flags = --extern proc_macro
246+
rusttestlib-proc_macro2: private rustc_target_flags = $(proc_macro2-flags)
247+
rusttestlib-proc_macro2: $(src)/proc-macro2/lib.rs FORCE
248+
+$(call if_changed,rustc_test_library)
249+
250+
rusttestlib-quote: private skip_flags = $(quote-skip_flags)
251+
rusttestlib-quote: private rustc_target_flags = $(quote-flags)
252+
rusttestlib-quote: $(src)/quote/lib.rs rusttestlib-proc_macro2 FORCE
253+
+$(call if_changed,rustc_test_library)
254+
255+
rusttestlib-syn: private rustc_target_flags = $(syn-flags)
256+
rusttestlib-syn: $(src)/syn/lib.rs rusttestlib-quote FORCE
257+
+$(call if_changed,rustc_test_library)
258+
259+
rusttestlib-macros: private rustc_target_flags = --extern proc_macro \
260+
--extern proc_macro2 --extern quote --extern syn
178261
rusttestlib-macros: private rustc_test_library_proc = yes
179-
rusttestlib-macros: $(src)/macros/lib.rs FORCE
262+
rusttestlib-macros: $(src)/macros/lib.rs \
263+
rusttestlib-proc_macro2 rusttestlib-quote rusttestlib-syn FORCE
180264
+$(call if_changed,rustc_test_library)
181265

182266
rusttestlib-pin_init_internal: private rustc_target_flags = --cfg kernel \
@@ -257,7 +341,8 @@ quiet_cmd_rustc_test = $(RUSTC_OR_CLIPPY_QUIET) T $<
257341
rusttest: rusttest-macros
258342

259343
rusttest-macros: private rustc_target_flags = --extern proc_macro \
260-
--extern macros --extern kernel --extern pin_init
344+
--extern macros --extern kernel --extern pin_init \
345+
--extern proc_macro2 --extern quote --extern syn
261346
rusttest-macros: private rustdoc_test_target_flags = --crate-type proc-macro
262347
rusttest-macros: $(src)/macros/lib.rs \
263348
rusttestlib-macros rusttestlib-kernel rusttestlib-pin_init FORCE
@@ -410,18 +495,47 @@ $(obj)/exports_bindings_generated.h: $(obj)/bindings.o FORCE
410495
$(obj)/exports_kernel_generated.h: $(obj)/kernel.o FORCE
411496
$(call if_changed,exports)
412497

498+
quiet_cmd_rustc_procmacrolibrary = $(RUSTC_OR_CLIPPY_QUIET) PL $@
499+
cmd_rustc_procmacrolibrary = \
500+
$(if $(skip_clippy),$(RUSTC),$(RUSTC_OR_CLIPPY)) \
501+
$(filter-out $(skip_flags),$(rust_common_flags) $(rustc_target_flags)) \
502+
--emit=dep-info,link --crate-type rlib -O \
503+
--out-dir $(objtree)/$(obj) -L$(objtree)/$(obj) \
504+
--crate-name $(patsubst lib%.rlib,%,$(notdir $@)) $<; \
505+
mv $(objtree)/$(obj)/$(patsubst lib%.rlib,%,$(notdir $@)).d $(depfile); \
506+
sed -i '/^\#/d' $(depfile)
507+
508+
$(obj)/libproc_macro2.rlib: private skip_clippy = 1
509+
$(obj)/libproc_macro2.rlib: private rustc_target_flags = $(proc_macro2-flags)
510+
$(obj)/libproc_macro2.rlib: $(src)/proc-macro2/lib.rs FORCE
511+
+$(call if_changed_dep,rustc_procmacrolibrary)
512+
513+
$(obj)/libquote.rlib: private skip_clippy = 1
514+
$(obj)/libquote.rlib: private skip_flags = $(quote-skip_flags)
515+
$(obj)/libquote.rlib: private rustc_target_flags = $(quote-flags)
516+
$(obj)/libquote.rlib: $(src)/quote/lib.rs $(obj)/libproc_macro2.rlib FORCE
517+
+$(call if_changed_dep,rustc_procmacrolibrary)
518+
519+
$(obj)/libsyn.rlib: private skip_clippy = 1
520+
$(obj)/libsyn.rlib: private rustc_target_flags = $(syn-flags)
521+
$(obj)/libsyn.rlib: $(src)/syn/lib.rs $(obj)/libquote.rlib FORCE
522+
+$(call if_changed_dep,rustc_procmacrolibrary)
523+
413524
quiet_cmd_rustc_procmacro = $(RUSTC_OR_CLIPPY_QUIET) P $@
414525
cmd_rustc_procmacro = \
415526
$(RUSTC_OR_CLIPPY) $(rust_common_flags) $(rustc_target_flags) \
416527
-Clinker-flavor=gcc -Clinker=$(HOSTCC) \
417528
-Clink-args='$(call escsq,$(KBUILD_PROCMACROLDFLAGS))' \
418529
--emit=dep-info=$(depfile) --emit=link=$@ --extern proc_macro \
419-
--crate-type proc-macro \
530+
--crate-type proc-macro -L$(objtree)/$(obj) \
420531
--crate-name $(patsubst lib%.$(libmacros_extension),%,$(notdir $@)) \
421532
@$(objtree)/include/generated/rustc_cfg $<
422533

423534
# Procedural macros can only be used with the `rustc` that compiled it.
424-
$(obj)/$(libmacros_name): $(src)/macros/lib.rs FORCE
535+
$(obj)/$(libmacros_name): private rustc_target_flags = \
536+
--extern proc_macro2 --extern quote --extern syn
537+
$(obj)/$(libmacros_name): $(src)/macros/lib.rs $(obj)/libproc_macro2.rlib \
538+
$(obj)/libquote.rlib $(obj)/libsyn.rlib FORCE
425539
+$(call if_changed_dep,rustc_procmacro)
426540

427541
$(obj)/$(libpin_init_internal_name): private rustc_target_flags = --cfg kernel
@@ -444,6 +558,9 @@ quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L
444558
rust-analyzer:
445559
$(Q)MAKEFLAGS= $(srctree)/scripts/generate_rust_analyzer.py \
446560
--cfgs='core=$(core-cfgs)' $(core-edition) \
561+
--cfgs='proc_macro2=$(proc_macro2-cfgs)' \
562+
--cfgs='quote=$(quote-cfgs)' \
563+
--cfgs='syn=$(syn-cfgs)' \
447564
$(realpath $(srctree)) $(realpath $(objtree)) \
448565
$(rustc_sysroot) $(RUST_LIB_SRC) $(if $(KBUILD_EXTMOD),$(srcroot)) \
449566
> rust-project.json
@@ -499,9 +616,9 @@ $(obj)/helpers/helpers.o: $(src)/helpers/helpers.c $(recordmcount_source) FORCE
499616
$(obj)/exports.o: private skip_gendwarfksyms = 1
500617

501618
$(obj)/core.o: private skip_clippy = 1
502-
$(obj)/core.o: private skip_flags = --edition=2021 -Wunreachable_pub
619+
$(obj)/core.o: private skip_flags = $(core-skip_flags)
503620
$(obj)/core.o: private rustc_objcopy = $(foreach sym,$(redirect-intrinsics),--redefine-sym $(sym)=__rust$(sym))
504-
$(obj)/core.o: private rustc_target_flags = --edition=$(core-edition) $(core-cfgs)
621+
$(obj)/core.o: private rustc_target_flags = $(core-flags)
505622
$(obj)/core.o: $(RUST_LIB_SRC)/core/src/lib.rs \
506623
$(wildcard $(objtree)/include/config/RUSTC_VERSION_TEXT) FORCE
507624
+$(call if_changed_rule,rustc_library)

rust/proc-macro2/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# `proc-macro2`
2+
3+
These source files come from the Rust `proc-macro2` crate, version
4+
1.0.101 (released 2025-08-16), hosted in the
5+
<https://github.com/dtolnay/proc-macro2> repository, licensed under
6+
"Apache-2.0 OR MIT" and only modified to add the SPDX license
7+
identifiers and to remove the `unicode-ident` dependency.
8+
9+
For copyright details, please see:
10+
11+
https://github.com/dtolnay/proc-macro2/blob/1.0.101/README.md#license
12+
https://github.com/dtolnay/proc-macro2/blob/1.0.101/LICENSE-APACHE
13+
https://github.com/dtolnay/proc-macro2/blob/1.0.101/LICENSE-MIT

rust/proc-macro2/detection.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// SPDX-License-Identifier: Apache-2.0 OR MIT
2+
3+
use core::sync::atomic::{AtomicUsize, Ordering};
4+
use std::sync::Once;
5+
6+
static WORKS: AtomicUsize = AtomicUsize::new(0);
7+
static INIT: Once = Once::new();
8+
9+
pub(crate) fn inside_proc_macro() -> bool {
10+
match WORKS.load(Ordering::Relaxed) {
11+
1 => return false,
12+
2 => return true,
13+
_ => {}
14+
}
15+
16+
INIT.call_once(initialize);
17+
inside_proc_macro()
18+
}
19+
20+
pub(crate) fn force_fallback() {
21+
WORKS.store(1, Ordering::Relaxed);
22+
}
23+
24+
pub(crate) fn unforce_fallback() {
25+
initialize();
26+
}
27+
28+
#[cfg(not(no_is_available))]
29+
fn initialize() {
30+
let available = proc_macro::is_available();
31+
WORKS.store(available as usize + 1, Ordering::Relaxed);
32+
}
33+
34+
// Swap in a null panic hook to avoid printing "thread panicked" to stderr,
35+
// then use catch_unwind to determine whether the compiler's proc_macro is
36+
// working. When proc-macro2 is used from outside of a procedural macro all
37+
// of the proc_macro crate's APIs currently panic.
38+
//
39+
// The Once is to prevent the possibility of this ordering:
40+
//
41+
// thread 1 calls take_hook, gets the user's original hook
42+
// thread 1 calls set_hook with the null hook
43+
// thread 2 calls take_hook, thinks null hook is the original hook
44+
// thread 2 calls set_hook with the null hook
45+
// thread 1 calls set_hook with the actual original hook
46+
// thread 2 calls set_hook with what it thinks is the original hook
47+
//
48+
// in which the user's hook has been lost.
49+
//
50+
// There is still a race condition where a panic in a different thread can
51+
// happen during the interval that the user's original panic hook is
52+
// unregistered such that their hook is incorrectly not called. This is
53+
// sufficiently unlikely and less bad than printing panic messages to stderr
54+
// on correct use of this crate. Maybe there is a libstd feature request
55+
// here. For now, if a user needs to guarantee that this failure mode does
56+
// not occur, they need to call e.g. `proc_macro2::Span::call_site()` from
57+
// the main thread before launching any other threads.
58+
#[cfg(no_is_available)]
59+
fn initialize() {
60+
use std::panic::{self, PanicInfo};
61+
62+
type PanicHook = dyn Fn(&PanicInfo) + Sync + Send + 'static;
63+
64+
let null_hook: Box<PanicHook> = Box::new(|_panic_info| { /* ignore */ });
65+
let sanity_check = &*null_hook as *const PanicHook;
66+
let original_hook = panic::take_hook();
67+
panic::set_hook(null_hook);
68+
69+
let works = panic::catch_unwind(proc_macro::Span::call_site).is_ok();
70+
WORKS.store(works as usize + 1, Ordering::Relaxed);
71+
72+
let hopefully_null_hook = panic::take_hook();
73+
panic::set_hook(original_hook);
74+
if sanity_check != &*hopefully_null_hook {
75+
panic!("observed race condition in proc_macro2::inside_proc_macro");
76+
}
77+
}

0 commit comments

Comments
 (0)