Skip to contentSkip to conversation

`syn` support

PATCH Superseded Miguel Ojeda 8 participants 61 messages Nov 10, 2025 – Nov 24, 2025

Subsystems (5)
Kbuild Arm STM32 Rust Rust sync +1 more subsystems

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore

Miguel Ojeda

author maintainer
#0 lore raw

cover `syn` support

This patch series introduces support for syn (and its dependencies). Having such support allows to greatly simplify writing complex macros such as pin-init.

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.

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.

This has been a long time coming -- the first iterations of this, from 2022 and 2023 (with serde too), are at:

    https://github.com/Rust-for-Linux/linux/pull/910
    https://github.com/Rust-for-Linux/linux/pull/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.

I will be updating the webpage as well with the result of this:

https://rust-for-linux.com/third-party-crates

Please give it a test -- thanks!

Miguel Ojeda (18):
rust: condvar: avoid pub in example
rust: kbuild: introduce core-flags and core-skip_flags
rust: kbuild: simplify --cfg handling
rust: kbuild: add host library support
rust: proc-macro2: import crate
rust: proc-macro2: add SPDX License Identifiers
rust: proc-macro2: remove unicode_ident dependency
rust: proc-macro2: add README.md
rust: proc-macro2: enable support in kbuild
rust: quote: import crate
rust: quote: add SPDX License Identifiers
rust: quote: add README.md
rust: quote: enable support in kbuild
rust: syn: import crate
rust: syn: add SPDX License Identifiers
rust: syn: remove unicode-ident dependency
rust: syn: add README.md
rust: syn: enable support in kbuild

 .gitignore                                    |    1 +
 Makefile                                      |    5 +
 rust/Makefile                                 |  134 +-
 rust/kernel/sync/condvar.rs                   |    2 +-
 rust/proc-macro2/README.md                    |   13 +
 rust/proc-macro2/detection.rs                 |   77 +
 rust/proc-macro2/extra.rs                     |  153 +
 rust/proc-macro2/fallback.rs                  | 1258 +++++
 rust/proc-macro2/lib.rs                       | 1351 ++++++
 rust/proc-macro2/location.rs                  |   31 +
 rust/proc-macro2/marker.rs                    |   19 +
 rust/proc-macro2/parse.rs                     |  997 ++++
 rust/proc-macro2/probe.rs                     |   12 +
 rust/proc-macro2/probe/proc_macro_span.rs     |   53 +
 .../proc-macro2/probe/proc_macro_span_file.rs |   16 +
 .../probe/proc_macro_span_location.rs         |   23 +
 rust/proc-macro2/rcvec.rs                     |  148 +
 rust/proc-macro2/wrapper.rs                   |  986 ++++
 rust/quote/README.md                          |   12 +
 rust/quote/ext.rs                             |  112 +
 rust/quote/format.rs                          |  170 +
 rust/quote/ident_fragment.rs                  |   90 +
 rust/quote/lib.rs                             | 1456 ++++++
 rust/quote/runtime.rs                         |  494 ++
 rust/quote/spanned.rs                         |   52 +
 rust/quote/to_tokens.rs                       |  273 ++
 rust/syn/README.md                            |   13 +
 rust/syn/attr.rs                              |  838 ++++
 rust/syn/bigint.rs                            |   68 +
 rust/syn/buffer.rs                            |  436 ++
 rust/syn/classify.rs                          |  313 ++
 rust/syn/custom_keyword.rs                    |  262 ++
 rust/syn/custom_punctuation.rs                |  306 ++
 rust/syn/data.rs                              |  426 ++
 rust/syn/derive.rs                            |  261 ++
 rust/syn/discouraged.rs                       |  227 +
 rust/syn/drops.rs                             |   60 +
 rust/syn/error.rs                             |  469 ++
 rust/syn/export.rs                            |   75 +
 rust/syn/expr.rs                              | 4175 +++++++++++++++++
 rust/syn/ext.rs                               |  138 +
 rust/syn/file.rs                              |  127 +
 rust/syn/fixup.rs                             |  775 +++
 rust/syn/gen/clone.rs                         | 2269 +++++++++
 rust/syn/gen/debug.rs                         | 3240 +++++++++++++
 rust/syn/gen/eq.rs                            | 2308 +++++++++
 rust/syn/gen/fold.rs                          | 3904 +++++++++++++++
 rust/syn/gen/hash.rs                          | 2878 ++++++++++++
 rust/syn/gen/visit.rs                         | 3943 ++++++++++++++++
 rust/syn/gen/visit_mut.rs                     | 3761 +++++++++++++++
 rust/syn/generics.rs                          | 1479 ++++++
 rust/syn/group.rs                             |  293 ++
 rust/syn/ident.rs                             |  110 +
 rust/syn/item.rs                              | 3492 ++++++++++++++
 rust/syn/lib.rs                               | 1013 ++++
 rust/syn/lifetime.rs                          |  158 +
 rust/syn/lit.rs                               | 1862 ++++++++
 rust/syn/lookahead.rs                         |  334 ++
 rust/syn/mac.rs                               |  227 +
 rust/syn/macros.rs                            |  184 +
 rust/syn/meta.rs                              |  429 ++
 rust/syn/op.rs                                |  221 +
 rust/syn/parse.rs                             | 1421 ++++++
 rust/syn/parse_macro_input.rs                 |  130 +
 rust/syn/parse_quote.rs                       |  242 +
 rust/syn/pat.rs                               |  957 ++++
 rust/syn/path.rs                              |  968 ++++
 rust/syn/precedence.rs                        |  212 +
 rust/syn/print.rs                             |   18 +
 rust/syn/punctuated.rs                        | 1157 +++++
 rust/syn/restriction.rs                       |  180 +
 rust/syn/scan_expr.rs                         |  267 ++
 rust/syn/sealed.rs                            |    6 +
 rust/syn/span.rs                              |   65 +
 rust/syn/spanned.rs                           |  120 +
 rust/syn/stmt.rs                              |  486 ++
 rust/syn/thread.rs                            |   62 +
 rust/syn/token.rs                             | 1098 +++++
 rust/syn/tt.rs                                |  109 +
 rust/syn/ty.rs                                | 1273 +++++
 rust/syn/verbatim.rs                          |   35 +
 rust/syn/whitespace.rs                        |   67 +
 scripts/generate_rust_analyzer.py             |   23 +-
 83 files changed, 57895 insertions(+), 13 deletions(-)
 create mode 100644 rust/proc-macro2/README.md
 create mode 100644 rust/proc-macro2/detection.rs
 create mode 100644 rust/proc-macro2/extra.rs
 create mode 100644 rust/proc-macro2/fallback.rs
 create mode 100644 rust/proc-macro2/lib.rs
 create mode 100644 rust/proc-macro2/location.rs
 create mode 100644 rust/proc-macro2/marker.rs
 create mode 100644 rust/proc-macro2/parse.rs
 create mode 100644 rust/proc-macro2/probe.rs
 create mode 100644 rust/proc-macro2/probe/proc_macro_span.rs
 create mode 100644 rust/proc-macro2/probe/proc_macro_span_file.rs
 create mode 100644 rust/proc-macro2/probe/proc_macro_span_location.rs
 create mode 100644 rust/proc-macro2/rcvec.rs
 create mode 100644 rust/proc-macro2/wrapper.rs
 create mode 100644 rust/quote/README.md
 create mode 100644 rust/quote/ext.rs
 create mode 100644 rust/quote/format.rs
 create mode 100644 rust/quote/ident_fragment.rs
 create mode 100644 rust/quote/lib.rs
 create mode 100644 rust/quote/runtime.rs
 create mode 100644 rust/quote/spanned.rs
 create mode 100644 rust/quote/to_tokens.rs
 create mode 100644 rust/syn/README.md
 create mode 100644 rust/syn/attr.rs
 create mode 100644 rust/syn/bigint.rs
 create mode 100644 rust/syn/buffer.rs
 create mode 100644 rust/syn/classify.rs
 create mode 100644 rust/syn/custom_keyword.rs
 create mode 100644 rust/syn/custom_punctuation.rs
 create mode 100644 rust/syn/data.rs
 create mode 100644 rust/syn/derive.rs
 create mode 100644 rust/syn/discouraged.rs
 create mode 100644 rust/syn/drops.rs
 create mode 100644 rust/syn/error.rs
 create mode 100644 rust/syn/export.rs
 create mode 100644 rust/syn/expr.rs
 create mode 100644 rust/syn/ext.rs
 create mode 100644 rust/syn/file.rs
 create mode 100644 rust/syn/fixup.rs
 create mode 100644 rust/syn/gen/clone.rs
 create mode 100644 rust/syn/gen/debug.rs
 create mode 100644 rust/syn/gen/eq.rs
 create mode 100644 rust/syn/gen/fold.rs
 create mode 100644 rust/syn/gen/hash.rs
 create mode 100644 rust/syn/gen/visit.rs
 create mode 100644 rust/syn/gen/visit_mut.rs
 create mode 100644 rust/syn/generics.rs
 create mode 100644 rust/syn/group.rs
 create mode 100644 rust/syn/ident.rs
 create mode 100644 rust/syn/item.rs
 create mode 100644 rust/syn/lib.rs
 create mode 100644 rust/syn/lifetime.rs
 create mode 100644 rust/syn/lit.rs
 create mode 100644 rust/syn/lookahead.rs
 create mode 100644 rust/syn/mac.rs
 create mode 100644 rust/syn/macros.rs
 create mode 100644 rust/syn/meta.rs
 create mode 100644 rust/syn/op.rs
 create mode 100644 rust/syn/parse.rs
 create mode 100644 rust/syn/parse_macro_input.rs
 create mode 100644 rust/syn/parse_quote.rs
 create mode 100644 rust/syn/pat.rs
 create mode 100644 rust/syn/path.rs
 create mode 100644 rust/syn/precedence.rs
 create mode 100644 rust/syn/print.rs
 create mode 100644 rust/syn/punctuated.rs
 create mode 100644 rust/syn/restriction.rs
 create mode 100644 rust/syn/scan_expr.rs
 create mode 100644 rust/syn/sealed.rs
 create mode 100644 rust/syn/span.rs
 create mode 100644 rust/syn/spanned.rs
 create mode 100644 rust/syn/stmt.rs
 create mode 100644 rust/syn/thread.rs
 create mode 100644 rust/syn/token.rs
 create mode 100644 rust/syn/tt.rs
 create mode 100644 rust/syn/ty.rs
 create mode 100644 rust/syn/verbatim.rs
 create mode 100644 rust/syn/whitespace.rs

--
2.51.2

author maintainer
#1 lore raw

PATCH 1/18 rust: condvar: avoid `pub` in example

The future move of pin-init to syn uncovers the following unreachable public item in an example:

    error: unreachable `pub` item
         --> rust/doctests_kernel_generated.rs:14683:1
          |
    14683 | pub struct Example {
          | ---^^^^^^^^^^^^^^^
          | |
          | help: consider restricting its visibility: `pub(crate)`
          |
          = help: or consider exporting it for use by other crates
          = note: `-D unreachable-pub` implied by `-D warnings`
          = help: to override `-D warnings` add `#[allow(unreachable_pub)]`

There is no real downside of keeping the example as-is until the syn-based pin-init is introduced, so there is no need to treat it as a fix nor to backport it. However, we still need to change it before introducing the new pin-init.

Thus do so.

---
 rust/kernel/sync/condvar.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
rust/kernel/sync/condvar.rs +1 -1
index aa5b9a7a726d..1e66e9f548a3 100644
@@ -46,7 +46,7 @@ macro_rules! new_condvar {
 /// use kernel::sync::{new_condvar, new_mutex, CondVar, Mutex};
 ///
 /// #[pin_data]
-/// pub struct Example {
+/// struct Example {
 ///     #[pin]
 ///     value: Mutex<u32>,
 ///
Signature · 1 line
2.51.2
maintainer Reviewed-by on v1 1/18
#2 lore raw

Re: [PATCH 01/18] rust: condvar: avoid `pub` in example

Quoted 15 lines · Miguel Ojeda

The future move of pin-init to syn uncovers the following unreachable public item in an example:

    error: unreachable `pub` item
         --> rust/doctests_kernel_generated.rs:14683:1
          |
    14683 | pub struct Example {
          | ---^^^^^^^^^^^^^^^
          | |
          | help: consider restricting its visibility: `pub(crate)`
          |
          = help: or consider exporting it for use by other crates
          = note: `-D unreachable-pub` implied by `-D warnings`
          = help: to override `-D warnings` add `#[allow(unreachable_pub)]`
Quoted 8 lines

There is no real downside of keeping the example as-is until the syn-based pin-init is introduced, so there is no need to treat it as a fix nor to backport it. However, we still need to change it before introducing the new pin-init.

Thus do so.

The unreachable_pub warning normally does not trigger for documentation tests since using pub in documentation tests is not wrong. So this sounds like a bug in our doc-test setup.

Regardless, I don't mind changing it here for now.

#3 lore raw

Re: [PATCH 01/18] rust: condvar: avoid `pub` in example

Quoted 4 lines · Alice Ryhl

The unreachable_pub warning normally does not trigger for documentation tests since using pub in documentation tests is not wrong. So this sounds like a bug in our doc-test setup.

Yeah, we can allow the warning for doctests, that is easy.

In any case, this was needed for the future pin-init, not the current one, so I should have probably removed it (we had a bigger branch with the new pin-init).

Cheers,
Miguel

author maintainer
#4 lore raw

PATCH 2/18 rust: kbuild: introduce `core-flags` and `core-skip_flags`

In reply to Miguel Ojeda

In the next commits we are introducing *-{cfgs,skip_flags,flags} variables for other crates.

Thus do so here for core, which simplifies a bit the Makefile (including the next commit) and makes it more consistent.

This means we stop passing -Wrustdoc::unescaped_backticks to rustc and -Wunreachable_pub to rustdoc, i.e. we skip more, which is fine since it shouldn't have an effect.

---
 rust/Makefile | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)
rust/Makefile +14 -5
index 3e545c1a0ff4..a001aae0da9d 100644
@@ -60,11 +60,20 @@ rustdoc_test_quiet=--test-args -q
 rustdoc_test_kernel_quiet=>/dev/null
 endif
 
-core-cfgs = \
+core-cfgs := \
     --cfg no_fp_fmt_parse
 
 core-edition := $(if $(call rustc-min-version,108700),2024,2021)
 
+core-skip_flags := \
+    --edition=2021 \
+    -Wunreachable_pub \
+    -Wrustdoc::unescaped_backticks
+
+core-flags := \
+    --edition=$(core-edition) \
+    $(core-cfgs)
+
 # `rustdoc` did not save the target modifiers, thus workaround for
 # the time being (https://github.com/rust-lang/rust/issues/144521).
 rustdoc_modifiers_workaround := $(if $(call rustc-min-version,108800),-Cunsafe-allow-abi-mismatch=fixed-x18)
@@ -125,8 +134,8 @@ rustdoc-macros: $(src)/macros/lib.rs rustdoc-clean FORCE
 
 # Starting with Rust 1.82.0, skipping `-Wrustdoc::unescaped_backticks` should
 # not be needed -- see https://github.com/rust-lang/rust/pull/128307.
-rustdoc-core: private skip_flags = --edition=2021 -Wrustdoc::unescaped_backticks
-rustdoc-core: private rustc_target_flags = --edition=$(core-edition) $(core-cfgs)
+rustdoc-core: private skip_flags = $(core-skip_flags)
+rustdoc-core: private rustc_target_flags = $(core-flags)
 rustdoc-core: $(RUST_LIB_SRC)/core/src/lib.rs rustdoc-clean FORCE
 	+$(call if_changed,rustdoc)
 
@@ -508,9 +517,9 @@ $(obj)/helpers/helpers.o: $(src)/helpers/helpers.c $(recordmcount_source) FORCE
 $(obj)/exports.o: private skip_gendwarfksyms = 1
 
 $(obj)/core.o: private skip_clippy = 1
-$(obj)/core.o: private skip_flags = --edition=2021 -Wunreachable_pub
+$(obj)/core.o: private skip_flags = $(core-skip_flags)
 $(obj)/core.o: private rustc_objcopy = $(foreach sym,$(redirect-intrinsics),--redefine-sym $(sym)=__rust$(sym))
-$(obj)/core.o: private rustc_target_flags = --edition=$(core-edition) $(core-cfgs)
+$(obj)/core.o: private rustc_target_flags = $(core-flags)
 $(obj)/core.o: $(RUST_LIB_SRC)/core/src/lib.rs \
     $(wildcard $(objtree)/include/config/RUSTC_VERSION_TEXT) FORCE
 	+$(call if_changed_rule,rustc_library)
Signature · 1 line
2.51.2
#5 lore raw

Re: [PATCH 02/18] rust: kbuild: introduce `core-flags` and `core-skip_flags`

Quoted 3 lines · Miguel Ojeda

Thus do so here for core, which simplifies a bit the Makefile (including the next commit) and makes it more consistent.

We may want to eventually use --cap-lints=allow here too, but the skip flags here were already feeling a bit more than needed for the series.

Cheers,
Miguel

maintainer
#6 lore raw

Re: [PATCH 02/18] rust: kbuild: introduce `core-flags` and `core-skip_flags`

Quoted 8 lines · Miguel Ojeda
Quoted 3 lines · Miguel Ojeda

Thus do so here for core, which simplifies a bit the Makefile (including the next commit) and makes it more consistent.

We may want to eventually use --cap-lints=allow here too, but the skip flags here were already feeling a bit more than needed for the series.

Using cap-lints allow for core sgtm.

Alice

maintainer Reviewed-by on v1 2/18
#7 lore raw

Re: [PATCH 02/18] rust: kbuild: introduce `core-flags` and `core-skip_flags`

In reply to Miguel Ojeda
Quoted 11 lines · Miguel Ojeda

In the next commits we are introducing *-{cfgs,skip_flags,flags} variables for other crates.

Thus do so here for core, which simplifies a bit the Makefile (including the next commit) and makes it more consistent.

This means we stop passing -Wrustdoc::unescaped_backticks to rustc and -Wunreachable_pub to rustdoc, i.e. we skip more, which is fine since it shouldn't have an effect.

#8 lore raw

Re: [PATCH 02/18] rust: kbuild: introduce `core-flags` and `core-skip_flags`

In reply to Miguel Ojeda
Quoted 20 lines · Miguel Ojeda

In the next commits we are introducing *-{cfgs,skip_flags,flags} variables for other crates.

Thus do so here for core, which simplifies a bit the Makefile (including the next commit) and makes it more consistent.

This means we stop passing -Wrustdoc::unescaped_backticks to rustc and -Wunreachable_pub to rustdoc, i.e. we skip more, which is fine since it shouldn't have an effect.

---
 rust/Makefile | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)
rust/Makefile +0 -0
index 3e545c1a0ff4..a001aae0da9d 100644

rust/Makefile rustdoc_test_quiet=--test-args -q · line 63

Quoted 5 lines
rust/Makefile +1 -1
@@ -60,11 +60,20 @@ rustdoc_test_quiet=--test-args -q
 rustdoc_test_kernel_quiet=>/dev/null
 endif
 
-core-cfgs = \
+core-cfgs := \

I agree that switching to ":=" for this kind of Makefile variable is a good move. However, it should be mentioned in the commit log so that it doesn't look like it was accidentally introduced.

thanks,

Signature · 1 line
John Hubbard
Quoted 39 lines
rust/Makefile +13 -4
     --cfg no_fp_fmt_parse
 
 core-edition := $(if $(call rustc-min-version,108700),2024,2021)
 
+core-skip_flags := \
+    --edition=2021 \
+    -Wunreachable_pub \
+    -Wrustdoc::unescaped_backticks
+
+core-flags := \
+    --edition=$(core-edition) \
+    $(core-cfgs)
+
 # `rustdoc` did not save the target modifiers, thus workaround for
 # the time being (https://github.com/rust-lang/rust/issues/144521).
 rustdoc_modifiers_workaround := $(if $(call rustc-min-version,108800),-Cunsafe-allow-abi-mismatch=fixed-x18)
@@ -125,8 +134,8 @@ rustdoc-macros: $(src)/macros/lib.rs rustdoc-clean FORCE
 
 # Starting with Rust 1.82.0, skipping `-Wrustdoc::unescaped_backticks` should
 # not be needed -- see https://github.com/rust-lang/rust/pull/128307.
-rustdoc-core: private skip_flags = --edition=2021 -Wrustdoc::unescaped_backticks
-rustdoc-core: private rustc_target_flags = --edition=$(core-edition) $(core-cfgs)
+rustdoc-core: private skip_flags = $(core-skip_flags)
+rustdoc-core: private rustc_target_flags = $(core-flags)
 rustdoc-core: $(RUST_LIB_SRC)/core/src/lib.rs rustdoc-clean FORCE
 	+$(call if_changed,rustdoc)
 
@@ -508,9 +517,9 @@ $(obj)/helpers/helpers.o: $(src)/helpers/helpers.c $(recordmcount_source) FORCE
 $(obj)/exports.o: private skip_gendwarfksyms = 1
 
 $(obj)/core.o: private skip_clippy = 1
-$(obj)/core.o: private skip_flags = --edition=2021 -Wunreachable_pub
+$(obj)/core.o: private skip_flags = $(core-skip_flags)
 $(obj)/core.o: private rustc_objcopy = $(foreach sym,$(redirect-intrinsics),--redefine-sym $(sym)=__rust$(sym))
-$(obj)/core.o: private rustc_target_flags = --edition=$(core-edition) $(core-cfgs)
+$(obj)/core.o: private rustc_target_flags = $(core-flags)
 $(obj)/core.o: $(RUST_LIB_SRC)/core/src/lib.rs \
     $(wildcard $(objtree)/include/config/RUSTC_VERSION_TEXT) FORCE
 	+$(call if_changed_rule,rustc_library)
#9 lore raw

Re: [PATCH 02/18] rust: kbuild: introduce `core-flags` and `core-skip_flags`

Quoted 4 lines · John Hubbard

I agree that switching to ":=" for this kind of Makefile variable is a good move. However, it should be mentioned in the commit log so that it doesn't look like it was accidentally introduced.

It was meant to make it consistent with the upcoming additions since this commit is essentially about that, i.e. it was not intended as a drive-by cleanup (which I usually oppose), if that makes sense.

I will clarify in the log.

Thanks for taking a look!

Cheers,
Miguel

author maintainer
#10 lore raw

PATCH 3/18 rust: kbuild: simplify `--cfg` handling

In reply to Miguel Ojeda

We need to handle cfgs in both rustc and rust-analyzer, and in future commits some of those contain double quotes, which complicates things further.

Thus, instead of removing the --cfg part in the rust-analyzer generation script, have the *-cfgs variables contain just the actual cfg, and use that to generate the actual flags in *-flags.

---
 rust/Makefile                     | 6 ++++--
 scripts/generate_rust_analyzer.py | 2 +-
 2 files changed, 5 insertions(+), 3 deletions(-)
rust/Makefile +4 -2
index a001aae0da9d..7efed88d032c 100644
@@ -60,8 +60,10 @@ rustdoc_test_quiet=--test-args -q
 rustdoc_test_kernel_quiet=>/dev/null
 endif
 
+cfgs-to-flags = $(patsubst %,--cfg='%',$1)
+
 core-cfgs := \
-    --cfg no_fp_fmt_parse
+    no_fp_fmt_parse
 
 core-edition := $(if $(call rustc-min-version,108700),2024,2021)
 
@@ -72,7 +74,7 @@ core-skip_flags := \
 
 core-flags := \
     --edition=$(core-edition) \
-    $(core-cfgs)
+    $(call cfgs-to-flags,$(core-cfgs))
 
 # `rustdoc` did not save the target modifiers, thus workaround for
 # the time being (https://github.com/rust-lang/rust/issues/144521).
scripts/generate_rust_analyzer.py +1 -1
index fc27f0cca752..dedca470adc1 100755
@@ -15,7 +15,7 @@ def args_crates_cfgs(cfgs):
     crates_cfgs = {}
     for cfg in cfgs:
         crate, vals = cfg.split("=", 1)
-        crates_cfgs[crate] = vals.replace("--cfg", "").split()
+        crates_cfgs[crate] = vals.split()
 
     return crates_cfgs
 
Signature · 1 line
2.51.2
maintainer Reviewed-by on v1 3/18
#11 lore raw

Re: [PATCH 03/18] rust: kbuild: simplify `--cfg` handling

Quoted 9 lines · Miguel Ojeda

We need to handle cfgs in both rustc and rust-analyzer, and in future commits some of those contain double quotes, which complicates things further.

Thus, instead of removing the --cfg part in the rust-analyzer generation script, have the *-cfgs variables contain just the actual cfg, and use that to generate the actual flags in *-flags.

For anyone else reviewing this, here is the relevant part of the makefile that calls generate_rust_analyzer:

rust-analyzer:

	$(Q)MAKEFLAGS= $(srctree)/scripts/generate_rust_analyzer.py \
		--cfgs='core=$(core-cfgs)' $(core-edition) \
		$(realpath $(srctree)) $(realpath $(objtree)) \
		$(rustc_sysroot) $(RUST_LIB_SRC) $(if $(KBUILD_EXTMOD),$(srcroot)) \
		> rust-project.json
author maintainer
#12 lore raw

PATCH 4/18 rust: kbuild: add host library support

In reply to Miguel Ojeda

Add the a host library rule that produces .rlib files to be used by the macros crate.

---
 .gitignore    |  1 +
 rust/Makefile | 10 ++++++++++
 2 files changed, 11 insertions(+)
.gitignore +1 -0
index 86a1ba0d9035..3a7241c941f5 100644
@@ -41,6 +41,7 @@
 *.o.*
 *.patch
 *.pyc
+*.rlib
 *.rmeta
 *.rpm
 *.rsi
rust/Makefile +10 -0
index 7efed88d032c..9eea6563ef35 100644
@@ -430,6 +430,16 @@ $(obj)/exports_bindings_generated.h: $(obj)/bindings.o FORCE
 $(obj)/exports_kernel_generated.h: $(obj)/kernel.o FORCE
 	$(call if_changed,exports)
 
+quiet_cmd_rustc_hostlibrary = $(RUSTC_OR_CLIPPY_QUIET) H $@
+      cmd_rustc_hostlibrary = \
+	$(if $(skip_clippy),$(RUSTC),$(RUSTC_OR_CLIPPY)) \
+		$(filter-out $(skip_flags),$(rust_common_flags) $(rustc_target_flags)) \
+		--emit=dep-info,link --crate-type rlib -O \
+		--out-dir $(objtree)/$(obj) -L$(objtree)/$(obj) \
+		--crate-name $(patsubst lib%.rlib,%,$(notdir $@)) $<; \
+	mv $(objtree)/$(obj)/$(patsubst lib%.rlib,%,$(notdir $@)).d $(depfile); \
+	sed -i '/^\#/d' $(depfile)
+
 quiet_cmd_rustc_procmacro = $(RUSTC_OR_CLIPPY_QUIET) P $@
       cmd_rustc_procmacro = \
 	$(RUSTC_OR_CLIPPY) $(rust_common_flags) $(rustc_target_flags) \
Signature · 1 line
2.51.2
maintainer
#13 lore raw

Re: [PATCH 04/18] rust: kbuild: add host library support

Quoted 4 lines · Miguel Ojeda

Add the a host library rule that produces .rlib files to be used by the macros crate.

We currently have two kinds of build rules:

* cmd_host-rust used for host binaries such as generate_rust_target
* cmd_rustc_procmacro used for proc macros

This is a library for the second case, not the first case, so it may make more sense to name this rule procmacrolibrary?

Note that HOSTRUSTFLAGS is passed to only the first category, but this naming makes it sound like it should be passed to this new build rule, but I don't think it should be since it is not passed to proc macros.

Alice

#14 lore raw

Re: [PATCH 04/18] rust: kbuild: add host library support

Quoted 3 lines · Alice Ryhl

This is a library for the second case, not the first case, so it may make more sense to name this rule procmacrolibrary?

Making it more concrete sounds good to me. We can always rename later if we find some other use.

Cheers,
Miguel

author maintainer
#15 lore raw

PATCH 5/18 rust: proc-macro2: import crate

In reply to Miguel Ojeda

This is a subset of the Rust proc-macro2 crate, version 1.0.101 (released 2025-08-16), licensed under "Apache-2.0 OR MIT", from:

    https://github.com/dtolnay/proc-macro2/raw/1.0.101/src

The files are copied as-is, with no modifications whatsoever (not even adding the SPDX identifiers).

For copyright details, please see:

    https://github.com/dtolnay/proc-macro2/blob/1.0.101/README.md#license
    https://github.com/dtolnay/proc-macro2/blob/1.0.101/LICENSE-APACHE
    https://github.com/dtolnay/proc-macro2/blob/1.0.101/LICENSE-MIT

The next two patches modify these files as needed for use within the kernel. This patch split allows reviewers to double-check the import and to clearly see the differences introduced.

The following script may be used to verify the contents:

    for path in $(cd rust/proc-macro2/ && find . -type f -name '*.rs'); do
        curl --silent --show-error --location \
            https://github.com/dtolnay/proc-macro2/raw/1.0.101/src/$path \
            | diff --unified rust/proc-macro2/$path - && echo $path: OK
    done
---
 rust/proc-macro2/detection.rs                 |   75 +
 rust/proc-macro2/extra.rs                     |  151 ++
 rust/proc-macro2/fallback.rs                  | 1256 +++++++++++++++
 rust/proc-macro2/lib.rs                       | 1349 +++++++++++++++++
 rust/proc-macro2/location.rs                  |   29 +
 rust/proc-macro2/marker.rs                    |   17 +
 rust/proc-macro2/parse.rs                     |  995 ++++++++++++
 rust/proc-macro2/probe.rs                     |   10 +
 rust/proc-macro2/probe/proc_macro_span.rs     |   51 +
 .../proc-macro2/probe/proc_macro_span_file.rs |   14 +
 .../probe/proc_macro_span_location.rs         |   21 +
 rust/proc-macro2/rcvec.rs                     |  146 ++
 rust/proc-macro2/wrapper.rs                   |  984 ++++++++++++
 13 files changed, 5098 insertions(+)
 create mode 100644 rust/proc-macro2/detection.rs
 create mode 100644 rust/proc-macro2/extra.rs
 create mode 100644 rust/proc-macro2/fallback.rs
 create mode 100644 rust/proc-macro2/lib.rs
 create mode 100644 rust/proc-macro2/location.rs
 create mode 100644 rust/proc-macro2/marker.rs
 create mode 100644 rust/proc-macro2/parse.rs
 create mode 100644 rust/proc-macro2/probe.rs
 create mode 100644 rust/proc-macro2/probe/proc_macro_span.rs
 create mode 100644 rust/proc-macro2/probe/proc_macro_span_file.rs
 create mode 100644 rust/proc-macro2/probe/proc_macro_span_location.rs
 create mode 100644 rust/proc-macro2/rcvec.rs
 create mode 100644 rust/proc-macro2/wrapper.rs
rust/proc-macro2/detection.rs new file +75 -0
new file mode 100644
index 000000000000..beba7b237395
@@ -0,0 +1,75 @@
+use core::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::Once;
+
+static WORKS: AtomicUsize = AtomicUsize::new(0);
+static INIT: Once = Once::new();
+
+pub(crate) fn inside_proc_macro() -> bool {
+    match WORKS.load(Ordering::Relaxed) {
+        1 => return false,
+        2 => return true,
+        _ => {}
+    }
+
+    INIT.call_once(initialize);
+    inside_proc_macro()
+}
+
+pub(crate) fn force_fallback() {
+    WORKS.store(1, Ordering::Relaxed);
+}
+
+pub(crate) fn unforce_fallback() {
+    initialize();
+}
+
+#[cfg(not(no_is_available))]
+fn initialize() {
+    let available = proc_macro::is_available();
+    WORKS.store(available as usize + 1, Ordering::Relaxed);
+}
+
+// Swap in a null panic hook to avoid printing "thread panicked" to stderr,
+// then use catch_unwind to determine whether the compiler's proc_macro is
+// working. When proc-macro2 is used from outside of a procedural macro all
+// of the proc_macro crate's APIs currently panic.
+//
+// The Once is to prevent the possibility of this ordering:
+//
+//     thread 1 calls take_hook, gets the user's original hook
+//     thread 1 calls set_hook with the null hook
+//     thread 2 calls take_hook, thinks null hook is the original hook
+//     thread 2 calls set_hook with the null hook
+//     thread 1 calls set_hook with the actual original hook
+//     thread 2 calls set_hook with what it thinks is the original hook
+//
+// in which the user's hook has been lost.
+//
+// There is still a race condition where a panic in a different thread can
+// happen during the interval that the user's original panic hook is
+// unregistered such that their hook is incorrectly not called. This is
+// sufficiently unlikely and less bad than printing panic messages to stderr
+// on correct use of this crate. Maybe there is a libstd feature request
+// here. For now, if a user needs to guarantee that this failure mode does
+// not occur, they need to call e.g. `proc_macro2::Span::call_site()` from
+// the main thread before launching any other threads.
+#[cfg(no_is_available)]
+fn initialize() {
+    use std::panic::{self, PanicInfo};
+
+    type PanicHook = dyn Fn(&PanicInfo) + Sync + Send + 'static;
+
+    let null_hook: Box<PanicHook> = Box::new(|_panic_info| { /* ignore */ });
+    let sanity_check = &*null_hook as *const PanicHook;
+    let original_hook = panic::take_hook();
+    panic::set_hook(null_hook);
+
+    let works = panic::catch_unwind(proc_macro::Span::call_site).is_ok();
+    WORKS.store(works as usize + 1, Ordering::Relaxed);
+
+    let hopefully_null_hook = panic::take_hook();
+    panic::set_hook(original_hook);
+    if sanity_check != &*hopefully_null_hook {
+        panic!("observed race condition in proc_macro2::inside_proc_macro");
+    }
+}
rust/proc-macro2/extra.rs new file +151 -0

151 lines not on this page · raw message

rust/proc-macro2/fallback.rs new file +1256 -0

1,256 lines not on this page · raw message

rust/proc-macro2/lib.rs new file +1349 -0

1,349 lines not on this page · raw message

rust/proc-macro2/location.rs new file +29 -0
new file mode 100644
index 000000000000..7190e2d05255
@@ -0,0 +1,29 @@
+use core::cmp::Ordering;
+
+/// A line-column pair representing the start or end of a `Span`.
+///
+/// This type is semver exempt and not exposed by default.
+#[cfg_attr(docsrs, doc(cfg(feature = "span-locations")))]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub struct LineColumn {
+    /// The 1-indexed line in the source file on which the span starts or ends
+    /// (inclusive).
+    pub line: usize,
+    /// The 0-indexed column (in UTF-8 characters) in the source file on which
+    /// the span starts or ends (inclusive).
+    pub column: usize,
+}
+
+impl Ord for LineColumn {
+    fn cmp(&self, other: &Self) -> Ordering {
+        self.line
+            .cmp(&other.line)
+            .then(self.column.cmp(&other.column))
+    }
+}
+
+impl PartialOrd for LineColumn {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
rust/proc-macro2/marker.rs new file +17 -0
new file mode 100644
index 000000000000..23b94ce6fa85
@@ -0,0 +1,17 @@
+use alloc::rc::Rc;
+use core::marker::PhantomData;
+use core::panic::{RefUnwindSafe, UnwindSafe};
+
+// Zero sized marker with the correct set of autotrait impls we want all proc
+// macro types to have.
+#[derive(Copy, Clone)]
+#[cfg_attr(
+    all(procmacro2_semver_exempt, any(not(wrap_proc_macro), super_unstable)),
+    derive(PartialEq, Eq)
+)]
+pub(crate) struct ProcMacroAutoTraits(PhantomData<Rc<()>>);
+
+pub(crate) const MARKER: ProcMacroAutoTraits = ProcMacroAutoTraits(PhantomData);
+
+impl UnwindSafe for ProcMacroAutoTraits {}
+impl RefUnwindSafe for ProcMacroAutoTraits {}
rust/proc-macro2/parse.rs new file +995 -0

123 lines not on this page · raw message

rust/proc-macro2/probe.rs new file +10 -0
new file mode 100644
index 000000000000..b67f52036218

Lines of this file are not shown. View the raw message.

rust/proc-macro2/probe/proc_macro_span.rs new file +51 -0

51 lines not on this page · raw message

rust/proc-macro2/probe/proc_macro_span_file.rs new file +14 -0
new file mode 100644
index 000000000000..8b76bdf5007b

Lines of this file are not shown. View the raw message.

rust/proc-macro2/probe/proc_macro_span_location.rs new file +21 -0
new file mode 100644
index 000000000000..79da34af54af

Lines of this file are not shown. View the raw message.

rust/proc-macro2/rcvec.rs new file +146 -0

146 lines not on this page · raw message

rust/proc-macro2/wrapper.rs new file +984 -0

984 lines not on this page · raw message

The rest of this diff is not shown. View the raw message.

Signature · 1 line
2.51.2

Part of the diff is not shown here. View the raw message.

author maintainer
#16 lore raw

PATCH 6/18 rust: proc-macro2: add SPDX License Identifiers

In reply to Miguel Ojeda

Originally, when the Rust upstream alloc standard library crate was vendored in commit 057b8d257107 ("rust: adapt alloc crate to the kernel"), the SPDX License Identifiers were added to every file so that the license on those was clear.

Thus do the same for the proc-macro2 crate.

This makes scripts/spdxcheck.py pass.

---
 rust/proc-macro2/detection.rs                      | 2 ++
 rust/proc-macro2/extra.rs                          | 2 ++
 rust/proc-macro2/fallback.rs                       | 2 ++
 rust/proc-macro2/lib.rs                            | 2 ++
 rust/proc-macro2/location.rs                       | 2 ++
 rust/proc-macro2/marker.rs                         | 2 ++
 rust/proc-macro2/parse.rs                          | 2 ++
 rust/proc-macro2/probe.rs                          | 2 ++
 rust/proc-macro2/probe/proc_macro_span.rs          | 2 ++
 rust/proc-macro2/probe/proc_macro_span_file.rs     | 2 ++
 rust/proc-macro2/probe/proc_macro_span_location.rs | 2 ++
 rust/proc-macro2/rcvec.rs                          | 2 ++
 rust/proc-macro2/wrapper.rs                        | 2 ++
 13 files changed, 26 insertions(+)
rust/proc-macro2/detection.rs +2 -0
index beba7b237395..3de448cb2dde 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use core::sync::atomic::{AtomicUsize, Ordering};
 use std::sync::Once;
 
rust/proc-macro2/extra.rs +2 -0
index 522a90e136be..55feb5ec7526 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 //! Items which do not have a correspondence to any API in the proc_macro crate,
 //! but are necessary to include in proc-macro2.
 
rust/proc-macro2/fallback.rs +2 -0
index 1560105cfd25..9e005d67f7f5 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 #[cfg(wrap_proc_macro)]
 use crate::imp;
 #[cfg(span_locations)]
rust/proc-macro2/lib.rs +2 -0
index 2984af335adc..7b78d065d51c 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 //! [![github]](https://github.com/dtolnay/proc-macro2)&ensp;[![crates-io]](https://crates.io/crates/proc-macro2)&ensp;[![docs-rs]](crate)
 //!
 //! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
rust/proc-macro2/location.rs +2 -0
index 7190e2d05255..3a11871e2943 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use core::cmp::Ordering;
 
 /// A line-column pair representing the start or end of a `Span`.
rust/proc-macro2/marker.rs +2 -0
index 23b94ce6fa85..6f322424808c 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use alloc::rc::Rc;
 use core::marker::PhantomData;
 use core::panic::{RefUnwindSafe, UnwindSafe};
rust/proc-macro2/parse.rs +2 -0
index b8be403f842f..a005dea1fe88 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use crate::fallback::{
     self, is_ident_continue, is_ident_start, Group, Ident, LexError, Literal, Span, TokenStream,
     TokenStreamBuilder,
rust/proc-macro2/probe.rs +2 -0
index b67f52036218..d68aa8cfd85e 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 #![allow(dead_code)]
 
 #[cfg(proc_macro_span)]
rust/proc-macro2/probe/proc_macro_span.rs +2 -0
index 2d7d44e07708..892a7eb3e5a0 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 // This code exercises the surface area that we expect of Span's unstable API.
 // If the current toolchain is able to compile it, then proc-macro2 is able to
 // offer these APIs too.
rust/proc-macro2/probe/proc_macro_span_file.rs +2 -0
index 8b76bdf5007b..f2dbc4056ebe 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 // The subset of Span's API stabilized in Rust 1.88.
 
 extern crate proc_macro;
rust/proc-macro2/probe/proc_macro_span_location.rs +2 -0
index 79da34af54af..ae19c93394fa 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 // The subset of Span's API stabilized in Rust 1.88.
 
 extern crate proc_macro;
rust/proc-macro2/rcvec.rs +2 -0
index 23edc77d597f..e224ebe277ef 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use alloc::rc::Rc;
 use alloc::vec;
 use core::mem;
rust/proc-macro2/wrapper.rs +2 -0
index 2e3eb5b4d04e..6792e9834111 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use crate::detection::inside_proc_macro;
 use crate::fallback::{self, FromStr2 as _};
 #[cfg(span_locations)]
Signature · 1 line
2.51.2
author maintainer
#17 lore raw

PATCH 7/18 rust: proc-macro2: remove `unicode_ident` dependency

In reply to Miguel Ojeda

The proc-macro2 crate depends on the unicode-ident crate to determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31.

However, we only need ASCII identifiers in the kernel, thus we can simplify the check and remove completely that dependency.

---
 rust/proc-macro2/fallback.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
rust/proc-macro2/fallback.rs +2 -2
index 9e005d67f7f5..9b43c97df97a 100644
@@ -818,11 +818,11 @@ pub(crate) fn set_span(&mut self, span: Span) {
 }
 
 pub(crate) fn is_ident_start(c: char) -> bool {
-    c == '_' || unicode_ident::is_xid_start(c)
+    c == '_' || c.is_ascii_alphabetic()
 }
 
 pub(crate) fn is_ident_continue(c: char) -> bool {
-    unicode_ident::is_xid_continue(c)
+    c == '_' || c.is_ascii_alphanumeric()
 }
 
 #[track_caller]
Signature · 1 line
2.51.2
maintainer Reviewed-by on v1 7/18
#18 lore raw

Re: [PATCH 07/18] rust: proc-macro2: remove `unicode_ident` dependency

Quoted 8 lines · Miguel Ojeda

The proc-macro2 crate depends on the unicode-ident crate to determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31.

However, we only need ASCII identifiers in the kernel, thus we can simplify the check and remove completely that dependency.

Verified that these match exactly for ascii characters.

author maintainer
#19 lore raw

PATCH 8/18 rust: proc-macro2: add `README.md`

In reply to Miguel Ojeda

Originally, when the Rust upstream alloc standard library crate was vendored in commit 057b8d257107 ("rust: adapt alloc crate to the kernel"), a README.md file was added to explain the provenance and licensing of the source files.

Thus do the same for the proc-macro2 crate.

---
 rust/proc-macro2/README.md | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 rust/proc-macro2/README.md
rust/proc-macro2/README.md new file +13 -0
new file mode 100644
index 000000000000..af044fee4f59
@@ -0,0 +1,13 @@
+# `proc-macro2`
+
+These source files come from the Rust `proc-macro2` crate, version
+1.0.101 (released 2025-08-16), hosted in the
+<https://github.com/dtolnay/proc-macro2> repository, licensed under
+"Apache-2.0 OR MIT" and only modified to add the SPDX license
+identifiers and to remove the `unicode-ident` dependency.
+
+For copyright details, please see:
+
+    https://github.com/dtolnay/proc-macro2/blob/1.0.101/README.md#license
+    https://github.com/dtolnay/proc-macro2/blob/1.0.101/LICENSE-APACHE
+    https://github.com/dtolnay/proc-macro2/blob/1.0.101/LICENSE-MIT
Signature · 1 line
2.51.2
maintainer Reviewed-by on v1 8/18
#20 lore raw

Re: [PATCH 08/18] rust: proc-macro2: add `README.md`

Quoted 22 lines · Miguel Ojeda

Originally, when the Rust upstream alloc standard library crate was vendored in commit 057b8d257107 ("rust: adapt alloc crate to the kernel"), a README.md file was added to explain the provenance and licensing of the source files.

Thus do the same for the proc-macro2 crate.

---
 rust/proc-macro2/README.md | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 rust/proc-macro2/README.md
rust/proc-macro2/README.md new file +3 -0
new file mode 100644
index 000000000000..af044fee4f59
@@ -0,0 +1,13 @@
+# `proc-macro2`
+
+These source files come from the Rust `proc-macro2` crate, version

rust/proc-macro2/README.md line 13

Quoted 10 lines
rust/proc-macro2/README.md new file +10 -0
+1.0.101 (released 2025-08-16), hosted in the
+<https://github.com/dtolnay/proc-macro2> repository, licensed under
+"Apache-2.0 OR MIT" and only modified to add the SPDX license
+identifiers and to remove the `unicode-ident` dependency.
+
+For copyright details, please see:
+
+    https://github.com/dtolnay/proc-macro2/blob/1.0.101/README.md#license
+    https://github.com/dtolnay/proc-macro2/blob/1.0.101/LICENSE-APACHE
+    https://github.com/dtolnay/proc-macro2/blob/1.0.101/LICENSE-MIT
author maintainer
#21 lore raw

PATCH 9/18 rust: proc-macro2: enable support in kbuild

In reply to Miguel Ojeda

With all the new files in place and ready from the new crate, enable the support for it in the build system.

proc_macro_byte_character and proc_macro_c_str_literals were stabilized in Rust 1.79.0 [1] and were implemented earlier than our minimum Rust version (1.78) [2][3]. Thus just enable them instead of using the cfg that proc-macro2 uses to emulate them in older compilers.

---
 Makefile                          |  3 +++
 rust/Makefile                     | 32 +++++++++++++++++++++++++++++--
 scripts/generate_rust_analyzer.py |  7 +++++++
 3 files changed, 40 insertions(+), 2 deletions(-)
Makefile +3 -0
index fb4389aa5d5f..6ff887523eee 100644
@@ -1830,6 +1830,9 @@ PHONY += rustfmt rustfmtcheck
 
 rustfmt:
 	$(Q)find $(srctree) $(RCS_FIND_IGNORE) \
+		\( \
+			-path $(srctree)/rust/proc-macro2 \
+		\) -prune -o \
 		-type f -a -name '*.rs' -a ! -name '*generated*' -print \
 		| xargs $(RUSTFMT) $(rustfmt_flags)
 
rust/Makefile +30 -2
index 9eea6563ef35..a614a23023cb 100644
@@ -27,6 +27,8 @@ endif
 
 obj-$(CONFIG_RUST) += exports.o
 
+always-$(CONFIG_RUST) += libproc_macro2.rlib
+
 always-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated.rs
 always-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated_kunit.c
 
@@ -76,6 +78,17 @@ core-flags := \
     --edition=$(core-edition) \
     $(call cfgs-to-flags,$(core-cfgs))
 
+proc_macro2-cfgs := \
+    feature="proc-macro" \
+    wrap_proc_macro \
+    $(if $(call rustc-min-version,108800),proc_macro_span_file proc_macro_span_location)
+
+# Stable since Rust 1.79.0: `feature(proc_macro_byte_character,proc_macro_c_str_literals)`.
+proc_macro2-flags := \
+    --cap-lints=allow \
+    -Zcrate-attr='feature(proc_macro_byte_character,proc_macro_c_str_literals)' \
+    $(call cfgs-to-flags,$(proc_macro2-cfgs))
+
 # `rustdoc` did not save the target modifiers, thus workaround for
 # the time being (https://github.com/rust-lang/rust/issues/144521).
 rustdoc_modifiers_workaround := $(if $(call rustc-min-version,108800),-Cunsafe-allow-abi-mismatch=fixed-x18)
@@ -128,10 +141,15 @@ rustdoc: rustdoc-core rustdoc-macros rustdoc-compiler_builtins \
 	$(Q)for f in $(rustdoc_output)/static.files/rustdoc-*.css; do \
 		echo ".logo-container > img { object-fit: contain; }" >> $$f; done
 
+rustdoc-proc_macro2: private rustdoc_host = yes
+rustdoc-proc_macro2: private rustc_target_flags = $(proc_macro2-flags)
+rustdoc-proc_macro2: $(src)/proc-macro2/lib.rs rustdoc-clean FORCE
+	+$(call if_changed,rustdoc)
+
 rustdoc-macros: private rustdoc_host = yes
 rustdoc-macros: private rustc_target_flags = --crate-type proc-macro \
     --extern proc_macro
-rustdoc-macros: $(src)/macros/lib.rs rustdoc-clean FORCE
+rustdoc-macros: $(src)/macros/lib.rs rustdoc-clean rustdoc-proc_macro2 FORCE
 	+$(call if_changed,rustdoc)
 
 # Starting with Rust 1.82.0, skipping `-Wrustdoc::unescaped_backticks` should
@@ -194,6 +212,10 @@ rusttestlib-build_error: $(src)/build_error.rs FORCE
 rusttestlib-ffi: $(src)/ffi.rs FORCE
 	+$(call if_changed,rustc_test_library)
 
+rusttestlib-proc_macro2: private rustc_target_flags = $(proc_macro2-flags)
+rusttestlib-proc_macro2: $(src)/proc-macro2/lib.rs FORCE
+	+$(call if_changed,rustc_test_library)
+
 rusttestlib-macros: private rustc_target_flags = --extern proc_macro
 rusttestlib-macros: private rustc_test_library_proc = yes
 rusttestlib-macros: $(src)/macros/lib.rs FORCE
@@ -440,6 +462,11 @@ quiet_cmd_rustc_hostlibrary = $(RUSTC_OR_CLIPPY_QUIET) H $@
 	mv $(objtree)/$(obj)/$(patsubst lib%.rlib,%,$(notdir $@)).d $(depfile); \
 	sed -i '/^\#/d' $(depfile)
 
+$(obj)/libproc_macro2.rlib: private skip_clippy = 1
+$(obj)/libproc_macro2.rlib: private rustc_target_flags = $(proc_macro2-flags)
+$(obj)/libproc_macro2.rlib: $(src)/proc-macro2/lib.rs FORCE
+	+$(call if_changed_dep,rustc_hostlibrary)
+
 quiet_cmd_rustc_procmacro = $(RUSTC_OR_CLIPPY_QUIET) P $@
       cmd_rustc_procmacro = \
 	$(RUSTC_OR_CLIPPY) $(rust_common_flags) $(rustc_target_flags) \
@@ -451,7 +478,7 @@ quiet_cmd_rustc_procmacro = $(RUSTC_OR_CLIPPY_QUIET) P $@
 		@$(objtree)/include/generated/rustc_cfg $<
 
 # Procedural macros can only be used with the `rustc` that compiled it.
-$(obj)/$(libmacros_name): $(src)/macros/lib.rs FORCE
+$(obj)/$(libmacros_name): $(src)/macros/lib.rs $(obj)/libproc_macro2.rlib FORCE
 	+$(call if_changed_dep,rustc_procmacro)
 
 $(obj)/$(libpin_init_internal_name): private rustc_target_flags = --cfg kernel
@@ -474,6 +501,7 @@ quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L
 rust-analyzer:
 	$(Q)MAKEFLAGS= $(srctree)/scripts/generate_rust_analyzer.py \
 		--cfgs='core=$(core-cfgs)' $(core-edition) \
+		--cfgs='proc_macro2=$(proc_macro2-cfgs)' \
 		$(realpath $(srctree)) $(realpath $(objtree)) \
 		$(rustc_sysroot) $(RUST_LIB_SRC) $(if $(KBUILD_EXTMOD),$(srcroot)) \
 		> rust-project.json
scripts/generate_rust_analyzer.py +7 -0
index dedca470adc1..00c6b7cc94b7 100755
@@ -86,6 +86,13 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs, core_edit
         [],
     )
 
+    append_crate(
+        "proc_macro2",
+        srctree / "rust" / "proc-macro2" / "lib.rs",
+        ["core", "alloc", "std", "proc_macro"],
+        cfg=crates_cfgs["proc_macro2"],
+    )
+
     append_crate(
         "macros",
         srctree / "rust" / "macros" / "lib.rs",
Signature · 1 line
2.51.2
maintainer
#22 lore raw

Re: [PATCH 09/18] rust: proc-macro2: enable support in kbuild

Quoted 50 lines · Miguel Ojeda

With all the new files in place and ready from the new crate, enable the support for it in the build system.

proc_macro_byte_character and proc_macro_c_str_literals were stabilized in Rust 1.79.0 [1] and were implemented earlier than our minimum Rust version (1.78) [2][3]. Thus just enable them instead of using the cfg that proc-macro2 uses to emulate them in older compilers.

---
 Makefile                          |  3 +++
 rust/Makefile                     | 32 +++++++++++++++++++++++++++++--
 scripts/generate_rust_analyzer.py |  7 +++++++
 3 files changed, 40 insertions(+), 2 deletions(-)
Makefile +3 -0
index fb4389aa5d5f..6ff887523eee 100644
@@ -1830,6 +1830,9 @@ PHONY += rustfmt rustfmtcheck
 
 rustfmt:
 	$(Q)find $(srctree) $(RCS_FIND_IGNORE) \
+		\( \
+			-path $(srctree)/rust/proc-macro2 \
+		\) -prune -o \
 		-type f -a -name '*.rs' -a ! -name '*generated*' -print \
 		| xargs $(RUSTFMT) $(rustfmt_flags)
 
rust/Makefile +3 -0
index 9eea6563ef35..a614a23023cb 100644
@@ -27,6 +27,8 @@ endif
 
 obj-$(CONFIG_RUST) += exports.o
 
+always-$(CONFIG_RUST) += libproc_macro2.rlib
+
 always-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated.rs
 always-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated_kunit.c
 
@@ -76,6 +78,17 @@ core-flags := \
     --edition=$(core-edition) \
     $(call cfgs-to-flags,$(core-cfgs))
 
+proc_macro2-cfgs := \

rust/Makefile core-flags := \ · line 91

Quoted 10 lines
rust/Makefile +10 -0
+    feature="proc-macro" \
+    wrap_proc_macro \
+    $(if $(call rustc-min-version,108800),proc_macro_span_file proc_macro_span_location)
+
+# Stable since Rust 1.79.0: `feature(proc_macro_byte_character,proc_macro_c_str_literals)`.
+proc_macro2-flags := \
+    --cap-lints=allow \
+    -Zcrate-attr='feature(proc_macro_byte_character,proc_macro_c_str_literals)' \
+    $(call cfgs-to-flags,$(proc_macro2-cfgs))
+

I don't understand this. We enable the features even on 1.79, but we only pass the proc_macro_span_file, proc_macro_span_location cfgs on 1.88 and above?

We add the appropriate feature(_) invocations on older compilers, so should we not support those things on those compilers?

Alice

maintainer
#23 lore raw

Re: [PATCH 09/18] rust: proc-macro2: enable support in kbuild

Quoted 66 lines · On Mon, 10 Nov 2025 11:38:04 +0000 Alice Ryhl <[email protected]> wrote:
Quoted 50 lines · Miguel Ojeda

With all the new files in place and ready from the new crate, enable the support for it in the build system.

proc_macro_byte_character and proc_macro_c_str_literals were stabilized in Rust 1.79.0 [1] and were implemented earlier than our minimum Rust version (1.78) [2][3]. Thus just enable them instead of using the cfg that proc-macro2 uses to emulate them in older compilers.

---
 Makefile                          |  3 +++
 rust/Makefile                     | 32 +++++++++++++++++++++++++++++--
 scripts/generate_rust_analyzer.py |  7 +++++++
 3 files changed, 40 insertions(+), 2 deletions(-)
Makefile +3 -0
index fb4389aa5d5f..6ff887523eee 100644
@@ -1830,6 +1830,9 @@ PHONY += rustfmt rustfmtcheck
 
 rustfmt:
 	$(Q)find $(srctree) $(RCS_FIND_IGNORE) \
+		\( \
+			-path $(srctree)/rust/proc-macro2 \
+		\) -prune -o \
 		-type f -a -name '*.rs' -a ! -name '*generated*' -print \
 		| xargs $(RUSTFMT) $(rustfmt_flags)
 
rust/Makefile +3 -0
index 9eea6563ef35..a614a23023cb 100644
@@ -27,6 +27,8 @@ endif
 
 obj-$(CONFIG_RUST) += exports.o
 
+always-$(CONFIG_RUST) += libproc_macro2.rlib
+
 always-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated.rs
 always-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated_kunit.c
 
@@ -76,6 +78,17 @@ core-flags := \
     --edition=$(core-edition) \
     $(call cfgs-to-flags,$(core-cfgs))
 
+proc_macro2-cfgs := \

rust/Makefile core-flags := \ · line 91

Quoted 10 lines
rust/Makefile +10 -0
+    feature="proc-macro" \
+    wrap_proc_macro \
+    $(if $(call rustc-min-version,108800),proc_macro_span_file proc_macro_span_location)
+
+# Stable since Rust 1.79.0: `feature(proc_macro_byte_character,proc_macro_c_str_literals)`.
+proc_macro2-flags := \
+    --cap-lints=allow \
+    -Zcrate-attr='feature(proc_macro_byte_character,proc_macro_c_str_literals)' \
+    $(call cfgs-to-flags,$(proc_macro2-cfgs))
+  

I don't understand this. We enable the features even on 1.79, but we only pass the proc_macro_span_file, proc_macro_span_location cfgs on 1.88 and above?

Quoted 4 lines

We add the appropriate feature(_) invocations on older compilers, so should we not support those things on those compilers?

Alice

For 1.79- it switches to any polyfill for byte_character and c_str_literals using cfg no_literal_byte_character and no_literal_c_string. These feature gates are stablised already so I think turning on features on makes sense for this scenario.

For 1.88+, Span::file, Span::line etc are stable so proc-macro2 can stop using polyfills. These APIs are not gated under specific feature gates before stablising however. They were gated under the generic proc_macro_span feature gate and only renamed to proc_macro_span_location during stabilisation. So in this case there's no already-stable feature flags to turn on. (proc_macro_span feature would make these APIs usable in 1.79, but that's a still-unstable feature flag in all versions of rustc).

So I think the implementation here is sensible. I believe Miguel's patch is also pretty much replicating the logic in proc-macro2's build.rs.

Best,
Gary

#24 lore raw

Re: [PATCH 09/18] rust: proc-macro2: enable support in kbuild

Quoted 4 lines · Gary Guo

So I think the implementation here is sensible. I believe Miguel's patch is also pretty much replicating the logic in proc-macro2's build.rs.

Exactly, I was essentially following what upstream does for stable compilers.

(More generally, even if a feature may work, if upstream doesn't usually test "older nightlies", then I wonder if we should enable such combinations/setups anyway, unless we need them for a particular reason).

Cheers,
Miguel

maintainer
#25 lore raw

Re: [PATCH 09/18] rust: proc-macro2: enable support in kbuild

Quoted 6 lines · Miguel Ojeda
Quoted 4 lines · Gary Guo

So I think the implementation here is sensible. I believe Miguel's patch is also pretty much replicating the logic in proc-macro2's build.rs.

Quoted 6 lines

Exactly, I was essentially following what upstream does for stable compilers.

(More generally, even if a feature may work, if upstream doesn't usually test "older nightlies", then I wonder if we should enable such combinations/setups anyway, unless we need them for a particular reason).

If these features change the public API exposed by proc-macro2, then it seems better to always disable it to prevent situations where you accidentally wrote code that does not work on our minimum rustc version.

(Of course, this is a polyfill and no API is changed, then that's a different situation and then this LGTM.)

Alice

#26 lore raw

Re: [PATCH 09/18] rust: proc-macro2: enable support in kbuild

In reply to Miguel Ojeda
Quoted 23 lines · Miguel Ojeda

With all the new files in place and ready from the new crate, enable the support for it in the build system.

proc_macro_byte_character and proc_macro_c_str_literals were stabilized in Rust 1.79.0 [1] and were implemented earlier than our minimum Rust version (1.78) [2][3]. Thus just enable them instead of using the cfg that proc-macro2 uses to emulate them in older compilers.

---
 Makefile                          |  3 +++
 rust/Makefile                     | 32 +++++++++++++++++++++++++++++--
 scripts/generate_rust_analyzer.py |  7 +++++++
 3 files changed, 40 insertions(+), 2 deletions(-)
Makefile +0 -0
index fb4389aa5d5f..6ff887523eee 100644

Makefile PHONY += rustfmt rustfmtcheck · line 1835

Quoted 6 lines
Makefile +3 -0
@@ -1830,6 +1830,9 @@ PHONY += rustfmt rustfmtcheck
 
 rustfmt:
 	$(Q)find $(srctree) $(RCS_FIND_IGNORE) \
+		\( \
+			-path $(srctree)/rust/proc-macro2 \
+		\) -prune -o \

Looks good. But for future code and commit log readers, should we leave a small comment, to explain that this was vendored in, and therefore excluded from "make rustfmt"?

An alternative approach might be to create a "vendored" subdir, to make it clear which parts of the Rust for Linux code are special in that way.

thanks,

Signature · 1 line
John Hubbard
Quoted 109 lines
Makefile +0 -0
 		-type f -a -name '*.rs' -a ! -name '*generated*' -print \
 		| xargs $(RUSTFMT) $(rustfmt_flags)
 
rust/Makefile +30 -2
index 9eea6563ef35..a614a23023cb 100644
@@ -27,6 +27,8 @@ endif
 
 obj-$(CONFIG_RUST) += exports.o
 
+always-$(CONFIG_RUST) += libproc_macro2.rlib
+
 always-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated.rs
 always-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated_kunit.c
 
@@ -76,6 +78,17 @@ core-flags := \
     --edition=$(core-edition) \
     $(call cfgs-to-flags,$(core-cfgs))
 
+proc_macro2-cfgs := \
+    feature="proc-macro" \
+    wrap_proc_macro \
+    $(if $(call rustc-min-version,108800),proc_macro_span_file proc_macro_span_location)
+
+# Stable since Rust 1.79.0: `feature(proc_macro_byte_character,proc_macro_c_str_literals)`.
+proc_macro2-flags := \
+    --cap-lints=allow \
+    -Zcrate-attr='feature(proc_macro_byte_character,proc_macro_c_str_literals)' \
+    $(call cfgs-to-flags,$(proc_macro2-cfgs))
+
 # `rustdoc` did not save the target modifiers, thus workaround for
 # the time being (https://github.com/rust-lang/rust/issues/144521).
 rustdoc_modifiers_workaround := $(if $(call rustc-min-version,108800),-Cunsafe-allow-abi-mismatch=fixed-x18)
@@ -128,10 +141,15 @@ rustdoc: rustdoc-core rustdoc-macros rustdoc-compiler_builtins \
 	$(Q)for f in $(rustdoc_output)/static.files/rustdoc-*.css; do \
 		echo ".logo-container > img { object-fit: contain; }" >> $$f; done
 
+rustdoc-proc_macro2: private rustdoc_host = yes
+rustdoc-proc_macro2: private rustc_target_flags = $(proc_macro2-flags)
+rustdoc-proc_macro2: $(src)/proc-macro2/lib.rs rustdoc-clean FORCE
+	+$(call if_changed,rustdoc)
+
 rustdoc-macros: private rustdoc_host = yes
 rustdoc-macros: private rustc_target_flags = --crate-type proc-macro \
     --extern proc_macro
-rustdoc-macros: $(src)/macros/lib.rs rustdoc-clean FORCE
+rustdoc-macros: $(src)/macros/lib.rs rustdoc-clean rustdoc-proc_macro2 FORCE
 	+$(call if_changed,rustdoc)
 
 # Starting with Rust 1.82.0, skipping `-Wrustdoc::unescaped_backticks` should
@@ -194,6 +212,10 @@ rusttestlib-build_error: $(src)/build_error.rs FORCE
 rusttestlib-ffi: $(src)/ffi.rs FORCE
 	+$(call if_changed,rustc_test_library)
 
+rusttestlib-proc_macro2: private rustc_target_flags = $(proc_macro2-flags)
+rusttestlib-proc_macro2: $(src)/proc-macro2/lib.rs FORCE
+	+$(call if_changed,rustc_test_library)
+
 rusttestlib-macros: private rustc_target_flags = --extern proc_macro
 rusttestlib-macros: private rustc_test_library_proc = yes
 rusttestlib-macros: $(src)/macros/lib.rs FORCE
@@ -440,6 +462,11 @@ quiet_cmd_rustc_hostlibrary = $(RUSTC_OR_CLIPPY_QUIET) H $@
 	mv $(objtree)/$(obj)/$(patsubst lib%.rlib,%,$(notdir $@)).d $(depfile); \
 	sed -i '/^\#/d' $(depfile)
 
+$(obj)/libproc_macro2.rlib: private skip_clippy = 1
+$(obj)/libproc_macro2.rlib: private rustc_target_flags = $(proc_macro2-flags)
+$(obj)/libproc_macro2.rlib: $(src)/proc-macro2/lib.rs FORCE
+	+$(call if_changed_dep,rustc_hostlibrary)
+
 quiet_cmd_rustc_procmacro = $(RUSTC_OR_CLIPPY_QUIET) P $@
       cmd_rustc_procmacro = \
 	$(RUSTC_OR_CLIPPY) $(rust_common_flags) $(rustc_target_flags) \
@@ -451,7 +478,7 @@ quiet_cmd_rustc_procmacro = $(RUSTC_OR_CLIPPY_QUIET) P $@
 		@$(objtree)/include/generated/rustc_cfg $<
 
 # Procedural macros can only be used with the `rustc` that compiled it.
-$(obj)/$(libmacros_name): $(src)/macros/lib.rs FORCE
+$(obj)/$(libmacros_name): $(src)/macros/lib.rs $(obj)/libproc_macro2.rlib FORCE
 	+$(call if_changed_dep,rustc_procmacro)
 
 $(obj)/$(libpin_init_internal_name): private rustc_target_flags = --cfg kernel
@@ -474,6 +501,7 @@ quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L
 rust-analyzer:
 	$(Q)MAKEFLAGS= $(srctree)/scripts/generate_rust_analyzer.py \
 		--cfgs='core=$(core-cfgs)' $(core-edition) \
+		--cfgs='proc_macro2=$(proc_macro2-cfgs)' \
 		$(realpath $(srctree)) $(realpath $(objtree)) \
 		$(rustc_sysroot) $(RUST_LIB_SRC) $(if $(KBUILD_EXTMOD),$(srcroot)) \
 		> rust-project.json
scripts/generate_rust_analyzer.py +7 -0
index dedca470adc1..00c6b7cc94b7 100755
@@ -86,6 +86,13 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs, core_edit
         [],
     )
 
+    append_crate(
+        "proc_macro2",
+        srctree / "rust" / "proc-macro2" / "lib.rs",
+        ["core", "alloc", "std", "proc_macro"],
+        cfg=crates_cfgs["proc_macro2"],
+    )
+
     append_crate(
         "macros",
         srctree / "rust" / "macros" / "lib.rs",
#27 lore raw

Re: [PATCH 09/18] rust: proc-macro2: enable support in kbuild

Quoted 8 lines · John Hubbard

Looks good. But for future code and commit log readers, should we leave a small comment, to explain that this was vendored in, and therefore excluded from "make rustfmt"?

An alternative approach might be to create a "vendored" subdir, to make it clear which parts of the Rust for Linux code are special in that way.

Sure, that can help, I will add it.

The subdir makes sense, although it makes it into a different level than the other crates and I don't see it done with a folder in other cases either. Hmm... I will think about it.

Thanks for taking a look!

Cheers,
Miguel

#28 lore raw

Re: [PATCH 09/18] rust: proc-macro2: enable support in kbuild

Quoted 11 lines · Miguel Ojeda
Quoted 8 lines · John Hubbard

Looks good. But for future code and commit log readers, should we leave a small comment, to explain that this was vendored in, and therefore excluded from "make rustfmt"?

An alternative approach might be to create a "vendored" subdir, to make it clear which parts of the Rust for Linux code are special in that way.

Sure, that can help, I will add it.

A minor thing, but, it looks like maybe this was forgotten or overlooked in v2?

Quoted 9 lines

The subdir makes sense, although it makes it into a different level than the other crates and I don't see it done with a folder in other cases either. Hmm... I will think about it.

Thanks for taking a look!

Cheers,
Miguel

thanks,

Signature · 1 line
John Hubbard
#29 lore raw

Re: [PATCH 09/18] rust: proc-macro2: enable support in kbuild

Quoted 3 lines · John Hubbard

A minor thing, but, it looks like maybe this was forgotten or overlooked in v2?

No, it is there.

Cheers,
Miguel

#30 lore raw

Re: [PATCH 09/18] rust: proc-macro2: enable support in kbuild

Quoted 7 lines · Miguel Ojeda
Quoted 3 lines · John Hubbard

A minor thing, but, it looks like maybe this was forgotten or overlooked in v2?

No, it is there.

ah I see it now, it's a couple lines earlier than I expected, but looks good!

thanks,

Signature · 1 line
John Hubbard
author maintainer
#31 lore raw

PATCH 10/18 rust: quote: import crate

In reply to Miguel Ojeda

This is a subset of the Rust quote crate, version 1.0.40 (released 2025-03-12), licensed under "Apache-2.0 OR MIT", from:

    https://github.com/dtolnay/quote/raw/1.0.40/src

The files are copied as-is, with no modifications whatsoever (not even adding the SPDX identifiers).

For copyright details, please see:

    https://github.com/dtolnay/quote/blob/1.0.40/README.md#license
    https://github.com/dtolnay/quote/blob/1.0.40/LICENSE-APACHE
    https://github.com/dtolnay/quote/blob/1.0.40/LICENSE-MIT

The next patch modifies these files as needed for use within the kernel. This patch split allows reviewers to double-check the import and to clearly see the differences introduced.

The following script may be used to verify the contents:

    for path in $(cd rust/quote/ && find . -type f -name '*.rs'); do
        curl --silent --show-error --location \
            https://github.com/dtolnay/quote/raw/1.0.40/src/$path \
            | diff --unified rust/quote/$path - && echo $path: OK
    done
---
 rust/quote/ext.rs            |  110 +++
 rust/quote/format.rs         |  168 ++++
 rust/quote/ident_fragment.rs |   88 ++
 rust/quote/lib.rs            | 1454 ++++++++++++++++++++++++++++++++++
 rust/quote/runtime.rs        |  492 ++++++++++++
 rust/quote/spanned.rs        |   50 ++
 rust/quote/to_tokens.rs      |  271 +++++++
 7 files changed, 2633 insertions(+)
 create mode 100644 rust/quote/ext.rs
 create mode 100644 rust/quote/format.rs
 create mode 100644 rust/quote/ident_fragment.rs
 create mode 100644 rust/quote/lib.rs
 create mode 100644 rust/quote/runtime.rs
 create mode 100644 rust/quote/spanned.rs
 create mode 100644 rust/quote/to_tokens.rs
rust/quote/ext.rs new file +110 -0
new file mode 100644
index 000000000000..92c2315b182d
@@ -0,0 +1,110 @@
+use super::ToTokens;
+use core::iter;
+use proc_macro2::{TokenStream, TokenTree};
+
+/// TokenStream extension trait with methods for appending tokens.
+///
+/// This trait is sealed and cannot be implemented outside of the `quote` crate.
+pub trait TokenStreamExt: private::Sealed {
+    /// For use by `ToTokens` implementations.
+    ///
+    /// Appends the token specified to this list of tokens.
+    fn append<U>(&mut self, token: U)
+    where
+        U: Into<TokenTree>;
+
+    /// For use by `ToTokens` implementations.
+    ///
+    /// ```
+    /// # use quote::{quote, TokenStreamExt, ToTokens};
+    /// # use proc_macro2::TokenStream;
+    /// #
+    /// struct X;
+    ///
+    /// impl ToTokens for X {
+    ///     fn to_tokens(&self, tokens: &mut TokenStream) {
+    ///         tokens.append_all(&[true, false]);
+    ///     }
+    /// }
+    ///
+    /// let tokens = quote!(#X);
+    /// assert_eq!(tokens.to_string(), "true false");
+    /// ```
+    fn append_all<I>(&mut self, iter: I)
+    where
+        I: IntoIterator,
+        I::Item: ToTokens;
+
+    /// For use by `ToTokens` implementations.
+    ///
+    /// Appends all of the items in the iterator `I`, separated by the tokens
+    /// `U`.
+    fn append_separated<I, U>(&mut self, iter: I, op: U)
+    where
+        I: IntoIterator,
+        I::Item: ToTokens,
+        U: ToTokens;
+
+    /// For use by `ToTokens` implementations.
+    ///
+    /// Appends all tokens in the iterator `I`, appending `U` after each
+    /// element, including after the last element of the iterator.
+    fn append_terminated<I, U>(&mut self, iter: I, term: U)
+    where
+        I: IntoIterator,
+        I::Item: ToTokens,
+        U: ToTokens;
+}
+
+impl TokenStreamExt for TokenStream {
+    fn append<U>(&mut self, token: U)
+    where
+        U: Into<TokenTree>,
+    {
+        self.extend(iter::once(token.into()));
+    }
+
+    fn append_all<I>(&mut self, iter: I)
+    where
+        I: IntoIterator,
+        I::Item: ToTokens,
+    {
+        for token in iter {
+            token.to_tokens(self);
+        }
+    }
+
+    fn append_separated<I, U>(&mut self, iter: I, op: U)
+    where
+        I: IntoIterator,
+        I::Item: ToTokens,
+        U: ToTokens,
+    {
+        for (i, token) in iter.into_iter().enumerate() {
+            if i > 0 {
+                op.to_tokens(self);
+            }
+            token.to_tokens(self);
+        }
+    }
+
+    fn append_terminated<I, U>(&mut self, iter: I, term: U)
+    where
+        I: IntoIterator,
+        I::Item: ToTokens,
+        U: ToTokens,
+    {
+        for token in iter {
+            token.to_tokens(self);
+            term.to_tokens(self);
+        }
+    }
+}
+
+mod private {
+    use proc_macro2::TokenStream;
+
+    pub trait Sealed {}
+
+    impl Sealed for TokenStream {}
+}
rust/quote/format.rs new file +168 -0

168 lines not on this page · raw message

rust/quote/ident_fragment.rs new file +88 -0

88 lines not on this page · raw message

rust/quote/lib.rs new file +1454 -0

1,454 lines not on this page · raw message

rust/quote/runtime.rs new file +492 -0

492 lines not on this page · raw message

rust/quote/spanned.rs new file +50 -0

50 lines not on this page · raw message

rust/quote/to_tokens.rs new file +271 -0

271 lines not on this page · raw message

Signature · 1 line
2.51.2
author maintainer
#32 lore raw

PATCH 11/18 rust: quote: add SPDX License Identifiers

In reply to Miguel Ojeda

Originally, when the Rust upstream alloc standard library crate was vendored in commit 057b8d257107 ("rust: adapt alloc crate to the kernel"), the SPDX License Identifiers were added to every file so that the license on those was clear.

Thus do the same for the quote crate.

This makes scripts/spdxcheck.py pass.

---
 rust/quote/ext.rs            | 2 ++
 rust/quote/format.rs         | 2 ++
 rust/quote/ident_fragment.rs | 2 ++
 rust/quote/lib.rs            | 2 ++
 rust/quote/runtime.rs        | 2 ++
 rust/quote/spanned.rs        | 2 ++
 rust/quote/to_tokens.rs      | 2 ++
 7 files changed, 14 insertions(+)
rust/quote/ext.rs +2 -0
index 92c2315b182d..977d2f0c5919 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use super::ToTokens;
 use core::iter;
 use proc_macro2::{TokenStream, TokenTree};
rust/quote/format.rs +2 -0
index ec0bbf38ba37..6e3d55b6e427 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 /// Formatting macro for constructing `Ident`s.
 ///
 /// <br>
rust/quote/ident_fragment.rs +2 -0
index 6c2a9a87acb4..d98106f17666 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use alloc::borrow::Cow;
 use core::fmt;
 use proc_macro2::{Ident, Span};
rust/quote/lib.rs +2 -0
index 0a12d607f279..cc1637660a75 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 //! [![github]](https://github.com/dtolnay/quote)&ensp;[![crates-io]](https://crates.io/crates/quote)&ensp;[![docs-rs]](https://docs.rs/quote)
 //!
 //! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
rust/quote/runtime.rs +2 -0
index c704ca89411f..09a94f5dd4fe 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use self::get_span::{GetSpan, GetSpanBase, GetSpanInner};
 use crate::{IdentFragment, ToTokens, TokenStreamExt};
 use core::fmt;
rust/quote/spanned.rs +2 -0
index 6eba64445d89..54ce9177f45e 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use crate::ToTokens;
 use proc_macro2::extra::DelimSpan;
 use proc_macro2::{Span, TokenStream};
rust/quote/to_tokens.rs +2 -0
index f373092b650f..1af1089e1423 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use super::TokenStreamExt;
 use alloc::borrow::Cow;
 use alloc::rc::Rc;
Signature · 1 line
2.51.2
author maintainer
#33 lore raw

PATCH 12/18 rust: quote: add `README.md`

In reply to Miguel Ojeda

Originally, when the Rust upstream alloc standard library crate was vendored in commit 057b8d257107 ("rust: adapt alloc crate to the kernel"), a README.md file was added to explain the provenance and licensing of the source files.

Thus do the same for the quote crate.

---
 rust/quote/README.md | 12 ++++++++++++
 1 file changed, 12 insertions(+)
 create mode 100644 rust/quote/README.md
rust/quote/README.md new file +12 -0
new file mode 100644
index 000000000000..186123eff1cb
@@ -0,0 +1,12 @@
+# `quote`
+
+These source files come from the Rust `quote` crate, version 1.0.40
+(released 2025-03-12), hosted in the <https://github.com/dtolnay/quote>
+repository, licensed under "Apache-2.0 OR MIT" and only modified to add
+the SPDX license identifiers.
+
+For copyright details, please see:
+
+    https://github.com/dtolnay/quote/blob/1.0.40/README.md#license
+    https://github.com/dtolnay/quote/blob/1.0.40/LICENSE-APACHE
+    https://github.com/dtolnay/quote/blob/1.0.40/LICENSE-MIT
Signature · 1 line
2.51.2
author maintainer
#34 lore raw

PATCH 13/18 rust: quote: enable support in kbuild

In reply to Miguel Ojeda

With all the new files in place and ready from the new crate, enable the support for it in the build system.

---
 Makefile                          |  1 +
 rust/Makefile                     | 42 +++++++++++++++++++++++++++----
 scripts/generate_rust_analyzer.py |  7 ++++++
 3 files changed, 45 insertions(+), 5 deletions(-)
Makefile +1 -0
index 6ff887523eee..f1b38b7fed1e 100644
@@ -1832,6 +1832,7 @@ rustfmt:
 	$(Q)find $(srctree) $(RCS_FIND_IGNORE) \
 		\( \
 			-path $(srctree)/rust/proc-macro2 \
+			-o -path $(srctree)/rust/quote \
 		\) -prune -o \
 		-type f -a -name '*.rs' -a ! -name '*generated*' -print \
 		| xargs $(RUSTFMT) $(rustfmt_flags)
rust/Makefile +37 -5

93 lines not on this page · raw message

scripts/generate_rust_analyzer.py +7 -0
index 00c6b7cc94b7..4faf153ed2ee 100755
@@ -93,6 +93,13 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs, core_edit
         cfg=crates_cfgs["proc_macro2"],
     )
 
+    append_crate(
+        "quote",
+        srctree / "rust" / "quote" / "lib.rs",
+        ["alloc", "proc_macro", "proc_macro2"],
+        cfg=crates_cfgs["quote"],
+    )
+
     append_crate(
         "macros",
         srctree / "rust" / "macros" / "lib.rs",
Signature · 1 line
2.51.2
maintainer
#35 lore raw

Re: [PATCH 13/18] rust: quote: enable support in kbuild

Quoted 10 lines · Miguel Ojeda

With all the new files in place and ready from the new crate, enable the support for it in the build system.

rust/Makefile +0 -0
index a614a23023cb..801a8cbf3bdd 100644

rust/Makefile rustdoc-clean: FORCE · line 225

Quoted 10 lines
rust/Makefile +2 -2
@@ -199,8 +218,8 @@ rustdoc-clean: FORCE
 quiet_cmd_rustc_test_library = $(RUSTC_OR_CLIPPY_QUIET) TL $<
       cmd_rustc_test_library = \
 	OBJTREE=$(abspath $(objtree)) \
-	$(RUSTC_OR_CLIPPY) $(rust_common_flags) \
-		@$(objtree)/include/generated/rustc_cfg $(rustc_target_flags) \
+	$(RUSTC_OR_CLIPPY) $(filter-out $(skip_flags),$(rust_common_flags) $(rustc_target_flags)) \
+		@$(objtree)/include/generated/rustc_cfg \
 		--crate-type $(if $(rustc_test_library_proc),proc-macro,rlib) \
 		--out-dir $(objtree)/$(obj)/test --cfg testlib \
 		-L$(objtree)/$(obj)/test \

This change seems unrelated?

Alice

maintainer
#36 lore raw

Re: [PATCH 13/18] rust: quote: enable support in kbuild

Quoted 24 lines · On Mon, 10 Nov 2025 11:44:03 +0000 Alice Ryhl <[email protected]> wrote:
Quoted 10 lines · Miguel Ojeda

With all the new files in place and ready from the new crate, enable the support for it in the build system.

rust/Makefile +0 -0
index a614a23023cb..801a8cbf3bdd 100644

rust/Makefile rustdoc-clean: FORCE · line 225

Quoted 10 lines
rust/Makefile +2 -2
@@ -199,8 +218,8 @@ rustdoc-clean: FORCE
 quiet_cmd_rustc_test_library = $(RUSTC_OR_CLIPPY_QUIET) TL $<
       cmd_rustc_test_library = \
 	OBJTREE=$(abspath $(objtree)) \
-	$(RUSTC_OR_CLIPPY) $(rust_common_flags) \
-		@$(objtree)/include/generated/rustc_cfg $(rustc_target_flags) \
+	$(RUSTC_OR_CLIPPY) $(filter-out $(skip_flags),$(rust_common_flags) $(rustc_target_flags)) \
+		@$(objtree)/include/generated/rustc_cfg \
 		--crate-type $(if $(rustc_test_library_proc),proc-macro,rlib) \
 		--out-dir $(objtree)/$(obj)/test --cfg testlib \
 		-L$(objtree)/$(obj)/test \  

This change seems unrelated?

Quoted 1 line

Alice

This is to be able to skip over edition flags for quote when building the rusttestlib?

Best,
Gary

#37 lore raw

Re: [PATCH 13/18] rust: quote: enable support in kbuild

Quoted 3 lines · Gary Guo

This is to be able to skip over edition flags for quote when building the rusttestlib?

Yes, it is needed to support skipping the flags, which isn't needed until this patch.

It can be done earlier separately since it doesn't hurt -- a simpler option is just to mention it here in the log.

Cheers,
Miguel

maintainer Reviewed-by
#38 lore raw

Re: [PATCH 13/18] rust: quote: enable support in kbuild

Quoted 10 lines · Miguel Ojeda
Quoted 3 lines · Gary Guo

This is to be able to skip over edition flags for quote when building the rusttestlib?

Yes, it is needed to support skipping the flags, which isn't needed until this patch.

It can be done earlier separately since it doesn't hurt -- a simpler option is just to mention it here in the log.

With that mentioned:

author maintainer
#39 lore raw

PATCH 14/18 rust: syn: import crate

In reply to Miguel Ojeda

This is a subset of the Rust syn crate, version 2.0.106 (released 2025-08-16), licensed under "Apache-2.0 OR MIT", from:

    https://github.com/dtolnay/syn/raw/2.0.106/src

The files are copied as-is, with no modifications whatsoever (not even adding the SPDX identifiers).

For copyright details, please see:

    https://github.com/dtolnay/syn/blob/2.0.106/README.md#license
    https://github.com/dtolnay/syn/blob/2.0.106/LICENSE-APACHE
    https://github.com/dtolnay/syn/blob/2.0.106/LICENSE-MIT

The next two patches modify these files as needed for use within the kernel. This patch split allows reviewers to double-check the import and to clearly see the differences introduced.

The following script may be used to verify the contents:

    for path in $(cd rust/syn/ && find . -type f -name '*.rs'); do
        curl --silent --show-error --location \
            https://github.com/dtolnay/syn/raw/2.0.106/src/$path \
            | diff --unified rust/syn/$path - && echo $path: OK
    done
---
 rust/syn/attr.rs               |  836 +++++++
 rust/syn/bigint.rs             |   66 +
 rust/syn/buffer.rs             |  434 ++++
 rust/syn/classify.rs           |  311 +++
 rust/syn/custom_keyword.rs     |  260 ++
 rust/syn/custom_punctuation.rs |  304 +++
 rust/syn/data.rs               |  424 ++++
 rust/syn/derive.rs             |  259 ++
 rust/syn/discouraged.rs        |  225 ++
 rust/syn/drops.rs              |   58 +
 rust/syn/error.rs              |  467 ++++
 rust/syn/export.rs             |   73 +
 rust/syn/expr.rs               | 4173 ++++++++++++++++++++++++++++++++
 rust/syn/ext.rs                |  136 ++
 rust/syn/file.rs               |  125 +
 rust/syn/fixup.rs              |  773 ++++++
 rust/syn/gen/clone.rs          | 2267 +++++++++++++++++
 rust/syn/gen/debug.rs          | 3238 +++++++++++++++++++++++++
 rust/syn/gen/eq.rs             | 2306 ++++++++++++++++++
 rust/syn/gen/fold.rs           | 3902 +++++++++++++++++++++++++++++
 rust/syn/gen/hash.rs           | 2876 ++++++++++++++++++++++
 rust/syn/gen/visit.rs          | 3941 ++++++++++++++++++++++++++++++
 rust/syn/gen/visit_mut.rs      | 3759 ++++++++++++++++++++++++++++
 rust/syn/generics.rs           | 1477 +++++++++++
 rust/syn/group.rs              |  291 +++
 rust/syn/ident.rs              |  108 +
 rust/syn/item.rs               | 3490 ++++++++++++++++++++++++++
 rust/syn/lib.rs                | 1011 ++++++++
 rust/syn/lifetime.rs           |  156 ++
 rust/syn/lit.rs                | 1860 ++++++++++++++
 rust/syn/lookahead.rs          |  332 +++
 rust/syn/mac.rs                |  225 ++
 rust/syn/macros.rs             |  182 ++
 rust/syn/meta.rs               |  427 ++++
 rust/syn/op.rs                 |  219 ++
 rust/syn/parse.rs              | 1419 +++++++++++
 rust/syn/parse_macro_input.rs  |  128 +
 rust/syn/parse_quote.rs        |  240 ++
 rust/syn/pat.rs                |  955 ++++++++
 rust/syn/path.rs               |  966 ++++++++
 rust/syn/precedence.rs         |  210 ++
 rust/syn/print.rs              |   16 +
 rust/syn/punctuated.rs         | 1155 +++++++++
 rust/syn/restriction.rs        |  178 ++
 rust/syn/scan_expr.rs          |  265 ++
 rust/syn/sealed.rs             |    4 +
 rust/syn/span.rs               |   63 +
 rust/syn/spanned.rs            |  118 +
 rust/syn/stmt.rs               |  484 ++++
 rust/syn/thread.rs             |   60 +
 rust/syn/token.rs              | 1096 +++++++++
 rust/syn/tt.rs                 |  107 +
 rust/syn/ty.rs                 | 1271 ++++++++++
 rust/syn/verbatim.rs           |   33 +
 rust/syn/whitespace.rs         |   65 +
 55 files changed, 49824 insertions(+)
 create mode 100644 rust/syn/attr.rs
 create mode 100644 rust/syn/bigint.rs
 create mode 100644 rust/syn/buffer.rs
 create mode 100644 rust/syn/classify.rs
 create mode 100644 rust/syn/custom_keyword.rs
 create mode 100644 rust/syn/custom_punctuation.rs
 create mode 100644 rust/syn/data.rs
 create mode 100644 rust/syn/derive.rs
 create mode 100644 rust/syn/discouraged.rs
 create mode 100644 rust/syn/drops.rs
 create mode 100644 rust/syn/error.rs
 create mode 100644 rust/syn/export.rs
 create mode 100644 rust/syn/expr.rs
 create mode 100644 rust/syn/ext.rs
 create mode 100644 rust/syn/file.rs
 create mode 100644 rust/syn/fixup.rs
 create mode 100644 rust/syn/gen/clone.rs
 create mode 100644 rust/syn/gen/debug.rs
 create mode 100644 rust/syn/gen/eq.rs
 create mode 100644 rust/syn/gen/fold.rs
 create mode 100644 rust/syn/gen/hash.rs
 create mode 100644 rust/syn/gen/visit.rs
 create mode 100644 rust/syn/gen/visit_mut.rs
 create mode 100644 rust/syn/generics.rs
 create mode 100644 rust/syn/group.rs
 create mode 100644 rust/syn/ident.rs
 create mode 100644 rust/syn/item.rs
 create mode 100644 rust/syn/lib.rs
 create mode 100644 rust/syn/lifetime.rs
 create mode 100644 rust/syn/lit.rs
 create mode 100644 rust/syn/lookahead.rs
 create mode 100644 rust/syn/mac.rs
 create mode 100644 rust/syn/macros.rs
 create mode 100644 rust/syn/meta.rs
 create mode 100644 rust/syn/op.rs
 create mode 100644 rust/syn/parse.rs
 create mode 100644 rust/syn/parse_macro_input.rs
 create mode 100644 rust/syn/parse_quote.rs
 create mode 100644 rust/syn/pat.rs
 create mode 100644 rust/syn/path.rs
 create mode 100644 rust/syn/precedence.rs
 create mode 100644 rust/syn/print.rs
 create mode 100644 rust/syn/punctuated.rs
 create mode 100644 rust/syn/restriction.rs
 create mode 100644 rust/syn/scan_expr.rs
 create mode 100644 rust/syn/sealed.rs
 create mode 100644 rust/syn/span.rs
 create mode 100644 rust/syn/spanned.rs
 create mode 100644 rust/syn/stmt.rs
 create mode 100644 rust/syn/thread.rs
 create mode 100644 rust/syn/token.rs
 create mode 100644 rust/syn/tt.rs
 create mode 100644 rust/syn/ty.rs
 create mode 100644 rust/syn/verbatim.rs
 create mode 100644 rust/syn/whitespace.rs
rust/syn/attr.rs new file +836 -0
new file mode 100644
index 000000000000..2bdf96ee7fa9
@@ -0,0 +1,836 @@
+#[cfg(feature = "parsing")]
+use crate::error::Error;
+#[cfg(feature = "parsing")]
+use crate::error::Result;
+use crate::expr::Expr;
+use crate::mac::MacroDelimiter;
+#[cfg(feature = "parsing")]
+use crate::meta::{self, ParseNestedMeta};
+#[cfg(feature = "parsing")]
+use crate::parse::{Parse, ParseStream, Parser};
+use crate::path::Path;
+use crate::token;
+use proc_macro2::TokenStream;
+#[cfg(feature = "printing")]
+use std::iter;
+#[cfg(feature = "printing")]
+use std::slice;
+
+ast_struct! {
+    /// An attribute, like `#[repr(transparent)]`.
+    ///
+    /// <br>
+    ///
+    /// # Syntax
+    ///
+    /// Rust has six types of attributes.
+    ///
+    /// - Outer attributes like `#[repr(transparent)]`. These appear outside or
+    ///   in front of the item they describe.
+    ///
+    /// - Inner attributes like `#![feature(proc_macro)]`. These appear inside
+    ///   of the item they describe, usually a module.
+    ///
+    /// - Outer one-line doc comments like `/// Example`.
+    ///
+    /// - Inner one-line doc comments like `//! Please file an issue`.
+    ///
+    /// - Outer documentation blocks `/** Example */`.
+    ///
+    /// - Inner documentation blocks `/*! Please file an issue */`.
+    ///
+    /// The `style` field of type `AttrStyle` distinguishes whether an attribute
+    /// is outer or inner.
+    ///
+    /// Every attribute has a `path` that indicates the intended interpretation
+    /// of the rest of the attribute's contents. The path and the optional
+    /// additional contents are represented together in the `meta` field of the
+    /// attribute in three possible varieties:
+    ///
+    /// - Meta::Path &mdash; attributes whose information content conveys just a
+    ///   path, for example the `#[test]` attribute.
+    ///
+    /// - Meta::List &mdash; attributes that carry arbitrary tokens after the
+    ///   path, surrounded by a delimiter (parenthesis, bracket, or brace). For
+    ///   example `#[derive(Copy)]` or `#[precondition(x < 5)]`.
+    ///
+    /// - Meta::NameValue &mdash; attributes with an `=` sign after the path,
+    ///   followed by a Rust expression. For example `#[path =
+    ///   "sys/windows.rs"]`.
+    ///
+    /// All doc comments are represented in the NameValue style with a path of
+    /// "doc", as this is how they are processed by the compiler and by
+    /// `macro_rules!` macros.
+    ///
+    /// ```text
+    /// #[derive(Copy, Clone)]
+    ///   ~~~~~~Path
+    ///   ^^^^^^^^^^^^^^^^^^^Meta::List
+    ///
+    /// #[path = "sys/windows.rs"]
+    ///   ~~~~Path
+    ///   ^^^^^^^^^^^^^^^^^^^^^^^Meta::NameValue
+    ///
+    /// #[test]
+    ///   ^^^^Meta::Path
+    /// ```
+    ///
+    /// <br>
+    ///
+    /// # Parsing from tokens to Attribute
+    ///
+    /// This type does not implement the [`Parse`] trait and thus cannot be
+    /// parsed directly by [`ParseStream::parse`]. Instead use
+    /// [`ParseStream::call`] with one of the two parser functions
+    /// [`Attribute::parse_outer`] or [`Attribute::parse_inner`] depending on
+    /// which you intend to parse.
+    ///
+    /// [`Parse`]: crate::parse::Parse
+    /// [`ParseStream::parse`]: crate::parse::ParseBuffer::parse
+    /// [`ParseStream::call`]: crate::parse::ParseBuffer::call
+    ///
+    /// ```
+    /// use syn::{Attribute, Ident, Result, Token};
+    /// use syn::parse::{Parse, ParseStream};
+    ///
+    /// // Parses a unit struct with attributes.
+    /// //
+    /// //     #[path = "s.tmpl"]
+    /// //     struct S;
+    /// struct UnitStruct {
+    ///     attrs: Vec<Attribute>,
+    ///     struct_token: Token![struct],
+    ///     name: Ident,
+    ///     semi_token: Token![;],
+    /// }
+    ///
+    /// impl Parse for UnitStruct {
+    ///     fn parse(input: ParseStream) -> Result<Self> {
+    ///         Ok(UnitStruct {
+    ///             attrs: input.call(Attribute::parse_outer)?,
+    ///             struct_token: input.parse()?,
+    ///             name: input.parse()?,
+    ///             semi_token: input.parse()?,
+    ///         })
+    ///     }
+    /// }
+    /// ```
+    ///
+    /// <p><br></p>
+    ///
+    /// # Parsing from Attribute to structured arguments
+    ///
+    /// The grammar of attributes in Rust is very flexible, which makes the
+    /// syntax tree not that useful on its own. In particular, arguments of the
+    /// `Meta::List` variety of attribute are held in an arbitrary `tokens:
+    /// TokenStream`. Macros are expected to check the `path` of the attribute,
+    /// decide whether they recognize it, and then parse the remaining tokens
+    /// according to whatever grammar they wish to require for that kind of
+    /// attribute. Use [`parse_args()`] to parse those tokens into the expected
+    /// data structure.
+    ///
+    /// [`parse_args()`]: Attribute::parse_args
+    ///
+    /// <p><br></p>
+    ///
+    /// # Doc comments
+    ///
+    /// The compiler transforms doc comments, such as `/// comment` and `/*!
+    /// comment */`, into attributes before macros are expanded. Each comment is
+    /// expanded into an attribute of the form `#[doc = r"comment"]`.
+    ///
+    /// As an example, the following `mod` items are expanded identically:
+    ///
+    /// ```
+    /// # use syn::{ItemMod, parse_quote};
+    /// let doc: ItemMod = parse_quote! {
+    ///     /// Single line doc comments
+    ///     /// We write so many!
+    ///     /**
+    ///      * Multi-line comments...
+    ///      * May span many lines
+    ///      */
+    ///     mod example {
+    ///         //! Of course, they can be inner too
+    ///         /*! And fit in a single line */
+    ///     }
+    /// };
+    /// let attr: ItemMod = parse_quote! {
+    ///     #[doc = r" Single line doc comments"]
+    ///     #[doc = r" We write so many!"]
+    ///     #[doc = r"
+    ///      * Multi-line comments...
+    ///      * May span many lines
+    ///      "]
+    ///     mod example {
+    ///         #![doc = r" Of course, they can be inner too"]
+    ///         #![doc = r" And fit in a single line "]
+    ///     }
+    /// };
+    /// assert_eq!(doc, attr);
+    /// ```
+    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
+    pub struct Attribute {
+        pub pound_token: Token![#],
+        pub style: AttrStyle,
+        pub bracket_token: token::Bracket,
+        pub meta: Meta,
+    }
+}
+
+impl Attribute {
+    /// Returns the path that identifies the interpretation of this attribute.
+    ///
+    /// For example this would return the `test` in `#[test]`, the `derive` in
+    /// `#[derive(Copy)]`, and the `path` in `#[path = "sys/windows.rs"]`.
+    pub fn path(&self) -> &Path {
+        self.meta.path()
+    }
+
+    /// Parse the arguments to the attribute as a syntax tree.
+    ///
+    /// This is similar to pulling out the `TokenStream` from `Meta::List` and
+    /// doing `syn::parse2::<T>(meta_list.tokens)`, except that using
+    /// `parse_args` the error message has a more useful span when `tokens` is
+    /// empty.
+    ///
+    /// The surrounding delimiters are *not* included in the input to the
+    /// parser.
+    ///
+    /// ```text
+    /// #[my_attr(value < 5)]
+    ///           ^^^^^^^^^ what gets parsed
+    /// ```
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use syn::{parse_quote, Attribute, Expr};
+    ///
+    /// let attr: Attribute = parse_quote! {
+    ///     #[precondition(value < 5)]
+    /// };
+    ///
+    /// if attr.path().is_ident("precondition") {
+    ///     let precondition: Expr = attr.parse_args()?;
+    ///     // ...
+    /// }
+    /// # anyhow::Ok(())
+    /// ```
+    #[cfg(feature = "parsing")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
+    pub fn parse_args<T: Parse>(&self) -> Result<T> {
+        self.parse_args_with(T::parse)
+    }
+
+    /// Parse the arguments to the attribute using the given parser.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use syn::{parse_quote, Attribute};
+    ///
+    /// let attr: Attribute = parse_quote! {
+    ///     #[inception { #[brrrrrrraaaaawwwwrwrrrmrmrmmrmrmmmmm] }]
+    /// };
+    ///
+    /// let bwom = attr.parse_args_with(Attribute::parse_outer)?;
+    ///
+    /// // Attribute does not have a Parse impl, so we couldn't directly do:
+    /// // let bwom: Attribute = attr.parse_args()?;
+    /// # anyhow::Ok(())
+    /// ```
+    #[cfg(feature = "parsing")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
+    pub fn parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output> {
+        match &self.meta {
+            Meta::Path(path) => Err(crate::error::new2(
+                path.segments.first().unwrap().ident.span(),
+                path.segments.last().unwrap().ident.span(),
+                format!(
+                    "expected attribute arguments in parentheses: {}[{}(...)]",
+                    parsing::DisplayAttrStyle(&self.style),
+                    parsing::DisplayPath(path),
+                ),
+            )),
+            Meta::NameValue(meta) => Err(Error::new(
+                meta.eq_token.span,
+                format_args!(
+                    "expected parentheses: {}[{}(...)]",
+                    parsing::DisplayAttrStyle(&self.style),
+                    parsing::DisplayPath(&meta.path),
+                ),
+            )),
+            Meta::List(meta) => meta.parse_args_with(parser),
+        }
+    }
+
+    /// Parse the arguments to the attribute, expecting it to follow the
+    /// conventional structure used by most of Rust's built-in attributes.
+    ///
+    /// The [*Meta Item Attribute Syntax*][syntax] section in the Rust reference
+    /// explains the convention in more detail. Not all attributes follow this
+    /// convention, so [`parse_args()`][Self::parse_args] is available if you
+    /// need to parse arbitrarily goofy attribute syntax.
+    ///
+    /// [syntax]: https://doc.rust-lang.org/reference/attributes.html#meta-item-attribute-syntax
+    ///
+    /// # Example
+    ///
+    /// We'll parse a struct, and then parse some of Rust's `#[repr]` attribute
+    /// syntax.
+    ///
+    /// ```
+    /// use syn::{parenthesized, parse_quote, token, ItemStruct, LitInt};
+    ///
+    /// let input: ItemStruct = parse_quote! {
+    ///     #[repr(C, align(4))]
+    ///     pub struct MyStruct(u16, u32);
+    /// };
+    ///
+    /// let mut repr_c = false;
+    /// let mut repr_transparent = false;
+    /// let mut repr_align = None::<usize>;
+    /// let mut repr_packed = None::<usize>;
+    /// for attr in &input.attrs {
+    ///     if attr.path().is_ident("repr") {
+    ///         attr.parse_nested_meta(|meta| {
+    ///             // #[repr(C)]
+    ///             if meta.path.is_ident("C") {
+    ///                 repr_c = true;
+    ///                 return Ok(());
+    ///             }
+    ///
+    ///             // #[repr(transparent)]
+    ///             if meta.path.is_ident("transparent") {
+    ///                 repr_transparent = true;
+    ///                 return Ok(());
+    ///             }
+    ///
+    ///             // #[repr(align(N))]
+    ///             if meta.path.is_ident("align") {
+    ///                 let content;
+    ///                 parenthesized!(content in meta.input);
+    ///                 let lit: LitInt = content.parse()?;
+    ///                 let n: usize = lit.base10_parse()?;
+    ///                 repr_align = Some(n);
+    ///                 return Ok(());
+    ///             }
+    ///
+    ///             // #[repr(packed)] or #[repr(packed(N))], omitted N means 1
+    ///             if meta.path.is_ident("packed") {
+    ///                 if meta.input.peek(token::Paren) {
+    ///                     let content;
+    ///                     parenthesized!(content in meta.input);
+    ///                     let lit: LitInt = content.parse()?;
+    ///                     let n: usize = lit.base10_parse()?;
+    ///                     repr_packed = Some(n);
+    ///                 } else {
+    ///                     repr_packed = Some(1);
+    ///                 }
+    ///                 return Ok(());
+    ///             }
+    ///
+    ///             Err(meta.error("unrecognized repr"))
+    ///         })?;
+    ///     }
+    /// }
+    /// # anyhow::Ok(())
+    /// ```
+    ///
+    /// # Alternatives
+    ///
+    /// In some cases, for attributes which have nested layers of structured
+    /// content, the following less flexible approach might be more convenient:
+    ///
+    /// ```
+    /// # use syn::{parse_quote, ItemStruct};
+    /// #
+    /// # let input: ItemStruct = parse_quote! {
+    /// #     #[repr(C, align(4))]
+    /// #     pub struct MyStruct(u16, u32);
+    /// # };
+    /// #
+    /// use syn::punctuated::Punctuated;
+    /// use syn::{parenthesized, token, Error, LitInt, Meta, Token};
+    ///
+    /// let mut repr_c = false;
+    /// let mut repr_transparent = false;
+    /// let mut repr_align = None::<usize>;
+    /// let mut repr_packed = None::<usize>;
+    /// for attr in &input.attrs {
+    ///     if attr.path().is_ident("repr") {
+    ///         let nested = attr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
+    ///         for meta in nested {
+    ///             match meta {
+    ///                 // #[repr(C)]
+    ///                 Meta::Path(path) if path.is_ident("C") => {
+    ///                     repr_c = true;
+    ///                 }
+    ///
+    ///                 // #[repr(align(N))]
+    ///                 Meta::List(meta) if meta.path.is_ident("align") => {
+    ///                     let lit: LitInt = meta.parse_args()?;
+    ///                     let n: usize = lit.base10_parse()?;
+    ///                     repr_align = Some(n);
+    ///                 }
+    ///
+    ///                 /* ... */
+    ///
+    ///                 _ => {
+    ///                     return Err(Error::new_spanned(meta, "unrecognized repr"));
+    ///                 }
+    ///             }
+    ///         }
+    ///     }
+    /// }
+    /// # Ok(())
+    /// ```
+    #[cfg(feature = "parsing")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
+    pub fn parse_nested_meta(
+        &self,
+        logic: impl FnMut(ParseNestedMeta) -> Result<()>,
+    ) -> Result<()> {
+        self.parse_args_with(meta::parser(logic))
+    }
+
+    /// Parses zero or more outer attributes from the stream.
+    ///
+    /// # Example
+    ///
+    /// See
+    /// [*Parsing from tokens to Attribute*](#parsing-from-tokens-to-attribute).
+    #[cfg(feature = "parsing")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
+    pub fn parse_outer(input: ParseStream) -> Result<Vec<Self>> {
+        let mut attrs = Vec::new();
+        while input.peek(Token![#]) {
+            attrs.push(input.call(parsing::single_parse_outer)?);
+        }
+        Ok(attrs)
+    }
+
+    /// Parses zero or more inner attributes from the stream.
+    ///
+    /// # Example
+    ///
+    /// See
+    /// [*Parsing from tokens to Attribute*](#parsing-from-tokens-to-attribute).
+    #[cfg(feature = "parsing")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
+    pub fn parse_inner(input: ParseStream) -> Result<Vec<Self>> {
+        let mut attrs = Vec::new();
+        parsing::parse_inner(input, &mut attrs)?;
+        Ok(attrs)
+    }
+}
+
+ast_enum! {
+    /// Distinguishes between attributes that decorate an item and attributes
+    /// that are contained within an item.
+    ///
+    /// # Outer attributes
+    ///
+    /// - `#[repr(transparent)]`
+    /// - `/// # Example`
+    /// - `/** Please file an issue */`
+    ///
+    /// # Inner attributes
+    ///
+    /// - `#![feature(proc_macro)]`
+    /// - `//! # Example`
+    /// - `/*! Please file an issue */`
+    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
+    pub enum AttrStyle {
+        Outer,
+        Inner(Token![!]),
+    }
+}
+
+ast_enum! {
+    /// Content of a compile-time structured attribute.
+    ///
+    /// ## Path
+    ///
+    /// A meta path is like the `test` in `#[test]`.
+    ///
+    /// ## List
+    ///
+    /// A meta list is like the `derive(Copy)` in `#[derive(Copy)]`.
+    ///
+    /// ## NameValue
+    ///
+    /// A name-value meta is like the `path = "..."` in `#[path =
+    /// "sys/windows.rs"]`.
+    ///
+    /// # Syntax tree enum
+    ///
+    /// This type is a [syntax tree enum].
+    ///
+    /// [syntax tree enum]: crate::expr::Expr#syntax-tree-enums
+    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
+    pub enum Meta {
+        Path(Path),
+
+        /// A structured list within an attribute, like `derive(Copy, Clone)`.
+        List(MetaList),
+
+        /// A name-value pair within an attribute, like `feature = "nightly"`.
+        NameValue(MetaNameValue),
+    }
+}
+
+ast_struct! {
+    /// A structured list within an attribute, like `derive(Copy, Clone)`.
+    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
+    pub struct MetaList {
+        pub path: Path,
+        pub delimiter: MacroDelimiter,
+        pub tokens: TokenStream,
+    }
+}
+
+ast_struct! {
+    /// A name-value pair within an attribute, like `feature = "nightly"`.
+    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
+    pub struct MetaNameValue {
+        pub path: Path,
+        pub eq_token: Token![=],
+        pub value: Expr,
+    }
+}
+
+impl Meta {
+    /// Returns the path that begins this structured meta item.
+    ///
+    /// For example this would return the `test` in `#[test]`, the `derive` in
+    /// `#[derive(Copy)]`, and the `path` in `#[path = "sys/windows.rs"]`.
+    pub fn path(&self) -> &Path {
+        match self {
+            Meta::Path(path) => path,
+            Meta::List(meta) => &meta.path,
+            Meta::NameValue(meta) => &meta.path,
+        }
+    }
+
+    /// Error if this is a `Meta::List` or `Meta::NameValue`.
+    #[cfg(feature = "parsing")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
+    pub fn require_path_only(&self) -> Result<&Path> {
+        let error_span = match self {
+            Meta::Path(path) => return Ok(path),
+            Meta::List(meta) => meta.delimiter.span().open(),
+            Meta::NameValue(meta) => meta.eq_token.span,
+        };
+        Err(Error::new(error_span, "unexpected token in attribute"))
+    }
+
+    /// Error if this is a `Meta::Path` or `Meta::NameValue`.
+    #[cfg(feature = "parsing")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
+    pub fn require_list(&self) -> Result<&MetaList> {
+        match self {
+            Meta::List(meta) => Ok(meta),
+            Meta::Path(path) => Err(crate::error::new2(
+                path.segments.first().unwrap().ident.span(),
+                path.segments.last().unwrap().ident.span(),
+                format!(
+                    "expected attribute arguments in parentheses: `{}(...)`",
+                    parsing::DisplayPath(path),
+                ),
+            )),
+            Meta::NameValue(meta) => Err(Error::new(meta.eq_token.span, "expected `(`")),
+        }
+    }
+
+    /// Error if this is a `Meta::Path` or `Meta::List`.
+    #[cfg(feature = "parsing")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
+    pub fn require_name_value(&self) -> Result<&MetaNameValue> {
+        match self {
+            Meta::NameValue(meta) => Ok(meta),
+            Meta::Path(path) => Err(crate::error::new2(
+                path.segments.first().unwrap().ident.span(),
+                path.segments.last().unwrap().ident.span(),
+                format!(
+                    "expected a value for this attribute: `{} = ...`",
+                    parsing::DisplayPath(path),
+                ),
+            )),
+            Meta::List(meta) => Err(Error::new(meta.delimiter.span().open(), "expected `=`")),
+        }
+    }
+}
+
+impl MetaList {
+    /// See [`Attribute::parse_args`].
+    #[cfg(feature = "parsing")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
+    pub fn parse_args<T: Parse>(&self) -> Result<T> {
+        self.parse_args_with(T::parse)
+    }
+
+    /// See [`Attribute::parse_args_with`].
+    #[cfg(feature = "parsing")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
+    pub fn parse_args_with<F: Parser>(&self, parser: F) -> Result<F::Output> {
+        let scope = self.delimiter.span().close();
+        crate::parse::parse_scoped(parser, scope, self.tokens.clone())
+    }
+
+    /// See [`Attribute::parse_nested_meta`].
+    #[cfg(feature = "parsing")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
+    pub fn parse_nested_meta(
+        &self,
+        logic: impl FnMut(ParseNestedMeta) -> Result<()>,
+    ) -> Result<()> {
+        self.parse_args_with(meta::parser(logic))
+    }
+}
+
+#[cfg(feature = "printing")]
+pub(crate) trait FilterAttrs<'a> {
+    type Ret: Iterator<Item = &'a Attribute>;
+
+    fn outer(self) -> Self::Ret;
+    #[cfg(feature = "full")]
+    fn inner(self) -> Self::Ret;
+}
+
+#[cfg(feature = "printing")]
+impl<'a> FilterAttrs<'a> for &'a [Attribute] {
+    type Ret = iter::Filter<slice::Iter<'a, Attribute>, fn(&&Attribute) -> bool>;
+
+    fn outer(self) -> Self::Ret {
+        fn is_outer(attr: &&Attribute) -> bool {
+            match attr.style {
+                AttrStyle::Outer => true,
+                AttrStyle::Inner(_) => false,
+            }
+        }
+        self.iter().filter(is_outer)
+    }
+
+    #[cfg(feature = "full")]
+    fn inner(self) -> Self::Ret {
+        fn is_inner(attr: &&Attribute) -> bool {
+            match attr.style {
+                AttrStyle::Inner(_) => true,
+                AttrStyle::Outer => false,
+            }
+        }
+        self.iter().filter(is_inner)
+    }
+}
+
+impl From<Path> for Meta {
+    fn from(meta: Path) -> Meta {
+        Meta::Path(meta)
+    }
+}
+
+impl From<MetaList> for Meta {
+    fn from(meta: MetaList) -> Meta {
+        Meta::List(meta)
+    }
+}
+
+impl From<MetaNameValue> for Meta {
+    fn from(meta: MetaNameValue) -> Meta {
+        Meta::NameValue(meta)
+    }
+}
+
+#[cfg(feature = "parsing")]
+pub(crate) mod parsing {
+    use crate::attr::{AttrStyle, Attribute, Meta, MetaList, MetaNameValue};
+    use crate::error::Result;
+    use crate::expr::{Expr, ExprLit};
+    use crate::lit::Lit;
+    use crate::parse::discouraged::Speculative as _;
+    use crate::parse::{Parse, ParseStream};
+    use crate::path::Path;
+    use crate::{mac, token};
+    use proc_macro2::Ident;
+    use std::fmt::{self, Display};
+
+    pub(crate) fn parse_inner(input: ParseStream, attrs: &mut Vec<Attribute>) -> Result<()> {
+        while input.peek(Token![#]) && input.peek2(Token![!]) {
+            attrs.push(input.call(single_parse_inner)?);
+        }
+        Ok(())
+    }
+
+    pub(crate) fn single_parse_inner(input: ParseStream) -> Result<Attribute> {
+        let content;
+        Ok(Attribute {
+            pound_token: input.parse()?,
+            style: AttrStyle::Inner(input.parse()?),
+            bracket_token: bracketed!(content in input),
+            meta: content.parse()?,
+        })
+    }
+
+    pub(crate) fn single_parse_outer(input: ParseStream) -> Result<Attribute> {
+        let content;
+        Ok(Attribute {
+            pound_token: input.parse()?,
+            style: AttrStyle::Outer,
+            bracket_token: bracketed!(content in input),
+            meta: content.parse()?,
+        })
+    }
+
+    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
+    impl Parse for Meta {
+        fn parse(input: ParseStream) -> Result<Self> {
+            let path = parse_outermost_meta_path(input)?;
+            parse_meta_after_path(path, input)
+        }
+    }
+
+    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
+    impl Parse for MetaList {
+        fn parse(input: ParseStream) -> Result<Self> {
+            let path = parse_outermost_meta_path(input)?;
+            parse_meta_list_after_path(path, input)
+        }
+    }
+
+    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
+    impl Parse for MetaNameValue {
+        fn parse(input: ParseStream) -> Result<Self> {
+            let path = parse_outermost_meta_path(input)?;
+            parse_meta_name_value_after_path(path, input)
+        }
+    }
+
+    // Unlike meta::parse_meta_path which accepts arbitrary keywords in the path,
+    // only the `unsafe` keyword is accepted as an attribute's outermost path.
+    fn parse_outermost_meta_path(input: ParseStream) -> Result<Path> {
+        if input.peek(Token![unsafe]) {
+            let unsafe_token: Token![unsafe] = input.parse()?;
+            Ok(Path::from(Ident::new("unsafe", unsafe_token.span)))
+        } else {
+            Path::parse_mod_style(input)
+        }
+    }
+
+    pub(crate) fn parse_meta_after_path(path: Path, input: ParseStream) -> Result<Meta> {
+        if input.peek(token::Paren) || input.peek(token::Bracket) || input.peek(token::Brace) {
+            parse_meta_list_after_path(path, input).map(Meta::List)
+        } else if input.peek(Token![=]) {
+            parse_meta_name_value_after_path(path, input).map(Meta::NameValue)
+        } else {
+            Ok(Meta::Path(path))
+        }
+    }
+
+    fn parse_meta_list_after_path(path: Path, input: ParseStream) -> Result<MetaList> {
+        let (delimiter, tokens) = mac::parse_delimiter(input)?;
+        Ok(MetaList {
+            path,
+            delimiter,
+            tokens,
+        })
+    }
+
+    fn parse_meta_name_value_after_path(path: Path, input: ParseStream) -> Result<MetaNameValue> {
+        let eq_token: Token![=] = input.parse()?;
+        let ahead = input.fork();
+        let lit: Option<Lit> = ahead.parse()?;
+        let value = if let (Some(lit), true) = (lit, ahead.is_empty()) {
+            input.advance_to(&ahead);
+            Expr::Lit(ExprLit {
+                attrs: Vec::new(),
+                lit,
+            })
+        } else if input.peek(Token![#]) && input.peek2(token::Bracket) {
+            return Err(input.error("unexpected attribute inside of attribute"));
+        } else {
+            input.parse()?
+        };
+        Ok(MetaNameValue {
+            path,
+            eq_token,
+            value,
+        })
+    }
+
+    pub(super) struct DisplayAttrStyle<'a>(pub &'a AttrStyle);
+
+    impl<'a> Display for DisplayAttrStyle<'a> {
+        fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+            formatter.write_str(match self.0 {
+                AttrStyle::Outer => "#",
+                AttrStyle::Inner(_) => "#!",
+            })
+        }
+    }
+
+    pub(super) struct DisplayPath<'a>(pub &'a Path);
+
+    impl<'a> Display for DisplayPath<'a> {
+        fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+            for (i, segment) in self.0.segments.iter().enumerate() {
+                if i > 0 || self.0.leading_colon.is_some() {
+                    formatter.write_str("::")?;
+                }
+                write!(formatter, "{}", segment.ident)?;
+            }
+            Ok(())
+        }
+    }
+}
+
+#[cfg(feature = "printing")]
+mod printing {
+    use crate::attr::{AttrStyle, Attribute, Meta, MetaList, MetaNameValue};
+    use crate::path;
+    use crate::path::printing::PathStyle;
+    use proc_macro2::TokenStream;
+    use quote::ToTokens;
+
+    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
+    impl ToTokens for Attribute {
+        fn to_tokens(&self, tokens: &mut TokenStream) {
+            self.pound_token.to_tokens(tokens);
+            if let AttrStyle::Inner(b) = &self.style {
+                b.to_tokens(tokens);
+            }
+            self.bracket_token.surround(tokens, |tokens| {
+                self.meta.to_tokens(tokens);
+            });
+        }
+    }
+
+    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
+    impl ToTokens for Meta {
+        fn to_tokens(&self, tokens: &mut TokenStream) {
+            match self {
+                Meta::Path(path) => path::printing::print_path(tokens, path, PathStyle::Mod),
+                Meta::List(meta_list) => meta_list.to_tokens(tokens),
+                Meta::NameValue(meta_name_value) => meta_name_value.to_tokens(tokens),
+            }
+        }
+    }
+
+    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
+    impl ToTokens for MetaList {
+        fn to_tokens(&self, tokens: &mut TokenStream) {
+            path::printing::print_path(tokens, &self.path, PathStyle::Mod);
+            self.delimiter.surround(tokens, self.tokens.clone());
+        }
+    }
+
+    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
+    impl ToTokens for MetaNameValue {
+        fn to_tokens(&self, tokens: &mut TokenStream) {
+            path::printing::print_path(tokens, &self.path, PathStyle::Mod);
+            self.eq_token.to_tokens(tokens);
+            self.value.to_tokens(tokens);
+        }
+    }
+}
rust/syn/bigint.rs new file +66 -0

66 lines not on this page · raw message

rust/syn/buffer.rs new file +434 -0

434 lines not on this page · raw message

rust/syn/classify.rs new file +311 -0

311 lines not on this page · raw message

rust/syn/custom_keyword.rs new file +260 -0

260 lines not on this page · raw message

rust/syn/custom_punctuation.rs new file +304 -0

304 lines not on this page · raw message

rust/syn/data.rs new file +424 -0

424 lines not on this page · raw message

rust/syn/derive.rs new file +259 -0

259 lines not on this page · raw message

rust/syn/discouraged.rs new file +225 -0

106 lines not on this page · raw message

rust/syn/drops.rs new file +58 -0

58 lines not on this page · raw message

rust/syn/error.rs new file +467 -0

467 lines not on this page · raw message

rust/syn/export.rs new file +73 -0

73 lines not on this page · raw message

rust/syn/expr.rs new file +4173 -0

4,173 lines not on this page · raw message

rust/syn/ext.rs new file +136 -0

136 lines not on this page · raw message

rust/syn/file.rs new file +125 -0

125 lines not on this page · raw message

rust/syn/fixup.rs new file +773 -0

773 lines not on this page · raw message

rust/syn/gen/clone.rs new file +2267 -0

2,267 lines not on this page · raw message

rust/syn/gen/debug.rs new file +3238 -0

3,238 lines not on this page · raw message

rust/syn/gen/eq.rs new file +2306 -0

2,306 lines not on this page · raw message

rust/syn/gen/fold.rs new file +3902 -0

3,902 lines not on this page · raw message

rust/syn/gen/hash.rs new file +2876 -0

2,876 lines not on this page · raw message

rust/syn/gen/visit.rs new file +3941 -0

3,941 lines not on this page · raw message

rust/syn/gen/visit_mut.rs new file +3759 -0

3,759 lines not on this page · raw message

rust/syn/generics.rs new file +1477 -0

1,477 lines not on this page · raw message

rust/syn/group.rs new file +291 -0

291 lines not on this page · raw message

rust/syn/ident.rs new file +108 -0

108 lines not on this page · raw message

rust/syn/item.rs new file +3490 -0

3,490 lines not on this page · raw message

rust/syn/lib.rs new file +1011 -0

1,011 lines not on this page · raw message

rust/syn/lifetime.rs new file +156 -0

156 lines not on this page · raw message

rust/syn/lit.rs new file +1860 -0

1,860 lines not on this page · raw message

rust/syn/lookahead.rs new file +332 -0

332 lines not on this page · raw message

rust/syn/mac.rs new file +225 -0

225 lines not on this page · raw message

rust/syn/macros.rs new file +182 -0

182 lines not on this page · raw message

rust/syn/meta.rs new file +427 -0

427 lines not on this page · raw message

rust/syn/op.rs new file +219 -0

219 lines not on this page · raw message

rust/syn/parse.rs new file +1419 -0

1,419 lines not on this page · raw message

rust/syn/parse_macro_input.rs new file +128 -0

128 lines not on this page · raw message

rust/syn/parse_quote.rs new file +240 -0

240 lines not on this page · raw message

rust/syn/pat.rs new file +955 -0

955 lines not on this page · raw message

rust/syn/path.rs new file +966 -0

966 lines not on this page · raw message

rust/syn/precedence.rs new file +210 -0

210 lines not on this page · raw message

rust/syn/print.rs new file +16 -0
new file mode 100644
index 000000000000..07409932677b

Lines of this file are not shown. View the raw message.

rust/syn/punctuated.rs new file +1155 -0

1,155 lines not on this page · raw message

rust/syn/restriction.rs new file +178 -0

178 lines not on this page · raw message

rust/syn/scan_expr.rs new file +265 -0

265 lines not on this page · raw message

rust/syn/sealed.rs new file +4 -0
new file mode 100644
index 000000000000..dc804742d12d

Lines of this file are not shown. View the raw message.

rust/syn/span.rs new file +63 -0

63 lines not on this page · raw message

rust/syn/spanned.rs new file +118 -0

118 lines not on this page · raw message

rust/syn/stmt.rs new file +484 -0

484 lines not on this page · raw message

rust/syn/thread.rs new file +60 -0

60 lines not on this page · raw message

rust/syn/token.rs new file +1096 -0

1,096 lines not on this page · raw message

rust/syn/tt.rs new file +107 -0

107 lines not on this page · raw message

rust/syn/ty.rs new file +1271 -0

1,271 lines not on this page · raw message

rust/syn/verbatim.rs new file +33 -0
new file mode 100644
index 000000000000..54dc1cfa0d82

Lines of this file are not shown. View the raw message.

rust/syn/whitespace.rs new file +65 -0

65 lines not on this page · raw message

The rest of this diff is not shown. View the raw message.

Signature · 1 line
2.51.2

Part of the diff is not shown here. View the raw message.

#40 lore raw

Re: [PATCH 14/18] rust: syn: import crate

Quoted 3 lines · Miguel Ojeda

This is a subset of the Rust syn crate, version 2.0.106 (released 2025-08-16), licensed under "Apache-2.0 OR MIT", from:

In case someone is wondering: I didn't use the very latest since it was quite new (even if the diff was fairly small).

Same for quote.

Cheers,
Miguel

author maintainer
#41 lore raw

PATCH 15/18 rust: syn: add SPDX License Identifiers

In reply to Miguel Ojeda

Originally, when the Rust upstream alloc standard library crate was vendored in commit 057b8d257107 ("rust: adapt alloc crate to the kernel"), the SPDX License Identifiers were added to every file so that the license on those was clear.

Thus do the same for the syn crate.

This makes scripts/spdxcheck.py pass.

---
 rust/syn/attr.rs               | 2 ++
 rust/syn/bigint.rs             | 2 ++
 rust/syn/buffer.rs             | 2 ++
 rust/syn/classify.rs           | 2 ++
 rust/syn/custom_keyword.rs     | 2 ++
 rust/syn/custom_punctuation.rs | 2 ++
 rust/syn/data.rs               | 2 ++
 rust/syn/derive.rs             | 2 ++
 rust/syn/discouraged.rs        | 2 ++
 rust/syn/drops.rs              | 2 ++
 rust/syn/error.rs              | 2 ++
 rust/syn/export.rs             | 2 ++
 rust/syn/expr.rs               | 2 ++
 rust/syn/ext.rs                | 2 ++
 rust/syn/file.rs               | 2 ++
 rust/syn/fixup.rs              | 2 ++
 rust/syn/gen/clone.rs          | 2 ++
 rust/syn/gen/debug.rs          | 2 ++
 rust/syn/gen/eq.rs             | 2 ++
 rust/syn/gen/fold.rs           | 2 ++
 rust/syn/gen/hash.rs           | 2 ++
 rust/syn/gen/visit.rs          | 2 ++
 rust/syn/gen/visit_mut.rs      | 2 ++
 rust/syn/generics.rs           | 2 ++
 rust/syn/group.rs              | 2 ++
 rust/syn/ident.rs              | 2 ++
 rust/syn/item.rs               | 2 ++
 rust/syn/lib.rs                | 2 ++
 rust/syn/lifetime.rs           | 2 ++
 rust/syn/lit.rs                | 2 ++
 rust/syn/lookahead.rs          | 2 ++
 rust/syn/mac.rs                | 2 ++
 rust/syn/macros.rs             | 2 ++
 rust/syn/meta.rs               | 2 ++
 rust/syn/op.rs                 | 2 ++
 rust/syn/parse.rs              | 2 ++
 rust/syn/parse_macro_input.rs  | 2 ++
 rust/syn/parse_quote.rs        | 2 ++
 rust/syn/pat.rs                | 2 ++
 rust/syn/path.rs               | 2 ++
 rust/syn/precedence.rs         | 2 ++
 rust/syn/print.rs              | 2 ++
 rust/syn/punctuated.rs         | 2 ++
 rust/syn/restriction.rs        | 2 ++
 rust/syn/scan_expr.rs          | 2 ++
 rust/syn/sealed.rs             | 2 ++
 rust/syn/span.rs               | 2 ++
 rust/syn/spanned.rs            | 2 ++
 rust/syn/stmt.rs               | 2 ++
 rust/syn/thread.rs             | 2 ++
 rust/syn/token.rs              | 2 ++
 rust/syn/tt.rs                 | 2 ++
 rust/syn/ty.rs                 | 2 ++
 rust/syn/verbatim.rs           | 2 ++
 rust/syn/whitespace.rs         | 2 ++
 55 files changed, 110 insertions(+)
rust/syn/attr.rs +2 -0
index 2bdf96ee7fa9..020b31f9de22 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 #[cfg(feature = "parsing")]
 use crate::error::Error;
 #[cfg(feature = "parsing")]
rust/syn/bigint.rs +2 -0
index 66aaa9372540..d706f9f515e9 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use std::ops::{AddAssign, MulAssign};
 
 // For implementing base10_digits() accessor on LitInt.
rust/syn/buffer.rs +2 -0
index 7b6a504eeb7c..406f70db323c 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 //! A stably addressed token buffer supporting efficient traversal based on a
 //! cheaply copyable cursor.
 
rust/syn/classify.rs +2 -0
index 8eab19dbc37c..92a975bfe4aa 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 #[cfg(feature = "full")]
 use crate::expr::Expr;
 #[cfg(any(feature = "printing", feature = "full"))]
rust/syn/custom_keyword.rs +2 -0
index cc4f632c981a..ff2c415f1aab 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 /// Define a type that supports parsing and printing a given identifier as if it
 /// were a keyword.
 ///
rust/syn/custom_punctuation.rs +2 -0
index eef5f5458459..d37c0e9c3a1e 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 /// Define a type that supports parsing and printing a multi-character symbol
 /// as if it were a punctuation token.
 ///
rust/syn/data.rs +2 -0
index 96db2a0b7c6f..c32c1366c1db 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use crate::attr::Attribute;
 use crate::expr::{Expr, Index, Member};
 use crate::ident::Ident;
rust/syn/derive.rs +2 -0
index 3443ecfc05cb..27fd9f12c88c 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use crate::attr::Attribute;
 use crate::data::{Fields, FieldsNamed, Variant};
 use crate::generics::Generics;
rust/syn/discouraged.rs +2 -0
index c8d6bfe89a14..2acd631ece84 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 //! Extensions to the parsing API with niche applicability.
 
 use crate::buffer::Cursor;
rust/syn/drops.rs +2 -0
index c54308f02c13..2c4ae333dad8 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use std::iter;
 use std::mem::ManuallyDrop;
 use std::ops::{Deref, DerefMut};
rust/syn/error.rs +2 -0
index 63310543a3b4..6fa0faf7f4e4 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 #[cfg(feature = "parsing")]
 use crate::buffer::Cursor;
 use crate::thread::ThreadBound;
rust/syn/export.rs +2 -0
index b9ea5c747b75..05dad324ba51 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 #[doc(hidden)]
 pub use std::clone::Clone;
 #[doc(hidden)]
rust/syn/expr.rs +2 -0
index 1e49d9a6633e..deb918436604 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use crate::attr::Attribute;
 #[cfg(all(feature = "parsing", feature = "full"))]
 use crate::error::Result;
rust/syn/ext.rs +2 -0
index 5cd79e863a9d..a9be9af42787 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 //! Extension traits to provide parsing methods on foreign types.
 
 use crate::buffer::Cursor;
rust/syn/file.rs +2 -0
index 8956d82eee93..ce2d2680ca9a 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use crate::attr::Attribute;
 use crate::item::Item;
 
rust/syn/fixup.rs +2 -0
index 6d2c3092d548..79b6d7618bb7 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use crate::classify;
 use crate::expr::Expr;
 #[cfg(feature = "full")]
rust/syn/gen/clone.rs +2 -0
index be2b698422da..dfb53182e378 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 // This file is @generated by syn-internal-codegen.
 // It is not intended for manual editing.
 
rust/syn/gen/debug.rs +2 -0
index aa42e32c60ed..d594e57fcf21 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 // This file is @generated by syn-internal-codegen.
 // It is not intended for manual editing.
 
rust/syn/gen/eq.rs +2 -0
index 128e8991eecc..79e9691b8c67 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 // This file is @generated by syn-internal-codegen.
 // It is not intended for manual editing.
 
rust/syn/gen/fold.rs +2 -0
index 1f0afd31919d..0643a72f87ca 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 // This file is @generated by syn-internal-codegen.
 // It is not intended for manual editing.
 
rust/syn/gen/hash.rs +2 -0
index 04f23453a117..fae2197a5610 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 // This file is @generated by syn-internal-codegen.
 // It is not intended for manual editing.
 
rust/syn/gen/visit.rs +2 -0
index cd258fcde120..a82293c053d6 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 // This file is @generated by syn-internal-codegen.
 // It is not intended for manual editing.
 
rust/syn/gen/visit_mut.rs +2 -0
index 2bbd6895db75..08e134b43a92 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 // This file is @generated by syn-internal-codegen.
 // It is not intended for manual editing.
 
rust/syn/generics.rs +2 -0
index 0c77193da043..9f8df8a155e1 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use crate::attr::Attribute;
 use crate::expr::Expr;
 use crate::ident::Ident;
rust/syn/group.rs +2 -0
index 1534ae995dd5..57586c58fd92 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use crate::error::Result;
 use crate::parse::ParseBuffer;
 use crate::token;
rust/syn/ident.rs +2 -0
index 8a8e8a50d9b0..2c4b56505bec 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 #[cfg(feature = "parsing")]
 use crate::lookahead;
 
rust/syn/item.rs +2 -0
index 1716f5137afb..1ab10cf218a6 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use crate::attr::Attribute;
 use crate::data::{Fields, FieldsNamed, Variant};
 use crate::derive::{Data, DataEnum, DataStruct, DataUnion, DeriveInput};
rust/syn/lib.rs +2 -0
index 79dc9e7c3324..b0006fd30b08 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 //! [![github]](https://github.com/dtolnay/syn)&ensp;[![crates-io]](https://crates.io/crates/syn)&ensp;[![docs-rs]](crate)
 //!
 //! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
rust/syn/lifetime.rs +2 -0
index cc189d1ac91f..24bec7fe969d 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 #[cfg(feature = "parsing")]
 use crate::lookahead;
 use proc_macro2::{Ident, Span};
rust/syn/lit.rs +2 -0
index 45c26d56fed3..1d2d8bdccfb1 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 #[cfg(feature = "parsing")]
 use crate::lookahead;
 #[cfg(feature = "parsing")]
rust/syn/lookahead.rs +2 -0
index da13ffc47a7a..a7994cb6ae88 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use crate::buffer::Cursor;
 use crate::error::{self, Error};
 use crate::sealed::lookahead::Sealed;
rust/syn/mac.rs +2 -0
index 15107801cfee..ce4b022b4d14 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 #[cfg(feature = "parsing")]
 use crate::error::Result;
 #[cfg(feature = "parsing")]
rust/syn/macros.rs +2 -0
index 167f2cf260a7..8676f6492a9c 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 #[cfg_attr(
     not(any(feature = "full", feature = "derive")),
     allow(unknown_lints, unused_macro_rules)
rust/syn/meta.rs +2 -0
index ffeeb2629f4f..2dd138e6dd55 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 //! Facility for interpreting structured content inside of an `Attribute`.
 
 use crate::error::{Error, Result};
rust/syn/op.rs +2 -0
index 575d9faa1273..73dada3fae2d 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 ast_enum! {
     /// A binary operator: `+`, `+=`, `&`.
     #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
rust/syn/parse.rs +2 -0
index 57531005ac4e..c6f6c0eece02 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 //! Parsing interface for parsing a token stream into a syntax tree node.
 //!
 //! Parsing in Syn is built on parser functions that take in a [`ParseStream`]
rust/syn/parse_macro_input.rs +2 -0
index f0660aedd7df..2f977754d41c 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 /// Parse the input TokenStream of a macro, triggering a compile error if the
 /// tokens fail to parse.
 ///
rust/syn/parse_quote.rs +2 -0
index 2db20597c436..7cc7b9b8fd75 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 /// Quasi-quotation macro that accepts input like the [`quote!`] macro but uses
 /// type inference to figure out a return type for those tokens.
 ///
rust/syn/pat.rs +2 -0
index 81a2f183f6b9..46edd63d5ed8 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use crate::attr::Attribute;
 use crate::expr::Member;
 use crate::ident::Ident;
rust/syn/path.rs +2 -0
index d2fcb9bc5d28..f2453bea4a3c 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 #[cfg(feature = "parsing")]
 use crate::error::Result;
 use crate::expr::Expr;
rust/syn/precedence.rs +2 -0
index 1891bfc202fd..cc4dba24a433 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 #[cfg(all(feature = "printing", feature = "full"))]
 use crate::attr::{AttrStyle, Attribute};
 #[cfg(feature = "printing")]
rust/syn/print.rs +2 -0
index 07409932677b..d73262cda060 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use proc_macro2::TokenStream;
 use quote::ToTokens;
 
rust/syn/punctuated.rs +2 -0
index ebaaa94f9a26..f79152940db6 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 //! A punctuated sequence of syntax tree nodes separated by punctuation.
 //!
 //! Lots of things in Rust are punctuated sequences.
rust/syn/restriction.rs +2 -0
index 6e6758f3cd73..b844f64c3a73 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use crate::path::Path;
 use crate::token;
 
rust/syn/scan_expr.rs +2 -0
index 3d88cf06acea..a5fd1ddc1501 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use self::{Action::*, Input::*};
 use proc_macro2::{Delimiter, Ident, Spacing, TokenTree};
 use syn::parse::{ParseStream, Result};
rust/syn/sealed.rs +2 -0
index dc804742d12d..541df9623755 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 #[cfg(feature = "parsing")]
 pub(crate) mod lookahead {
     pub trait Sealed: Copy {}
rust/syn/span.rs +2 -0
index eb2779479aaa..e6d55b3ccd03 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use proc_macro2::extra::DelimSpan;
 use proc_macro2::{Delimiter, Group, Span, TokenStream};
 
rust/syn/spanned.rs +2 -0
index 17b69e9f5b28..fcc93699f742 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 //! A trait that can provide the `Span` of the complete contents of a syntax
 //! tree node.
 //!
rust/syn/stmt.rs +2 -0
index 8ea0077f63df..029171a98dca 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use crate::attr::Attribute;
 use crate::expr::Expr;
 use crate::item::Item;
rust/syn/thread.rs +2 -0
index b33d248afc60..4165a173d9e1 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use std::fmt::{self, Debug};
 use std::thread::{self, ThreadId};
 
rust/syn/token.rs +2 -0
index 752c188883cb..18345cc5831a 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 //! Tokens representing Rust punctuation, keywords, and delimiters.
 //!
 //! The type names in this module can be difficult to keep straight, so we
rust/syn/tt.rs +2 -0
index 7d5d6a1ac326..fc7adec3da90 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use proc_macro2::{Delimiter, TokenStream, TokenTree};
 use std::hash::{Hash, Hasher};
 
rust/syn/ty.rs +2 -0
index c093f5877b3c..ae660d2d2e74 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use crate::attr::Attribute;
 use crate::expr::Expr;
 use crate::generics::{BoundLifetimes, TypeParamBound};
rust/syn/verbatim.rs +2 -0
index 54dc1cfa0d82..97bc08f70afe 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 use crate::parse::ParseStream;
 use proc_macro2::{Delimiter, TokenStream};
 use std::cmp::Ordering;
rust/syn/whitespace.rs +2 -0
index a50b5069a68b..b7319bd7af18 100644
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
 pub(crate) fn skip(mut s: &str) -> &str {
     'skip: while !s.is_empty() {
         let byte = s.as_bytes()[0];
Signature · 1 line
2.51.2
author maintainer
#42 lore raw

PATCH 16/18 rust: syn: remove `unicode-ident` dependency

In reply to Miguel Ojeda

The syn crate depends on the unicode-ident crate to determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31.

However, we only need ASCII identifiers in the kernel, thus we can simplify the check and remove completely that dependency.

---
 rust/syn/ident.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
rust/syn/ident.rs +2 -2
index 2c4b56505bec..03ccebf9c022 100644
@@ -39,11 +39,11 @@ fn from(token: Token![_]) -> Ident {
 pub(crate) fn xid_ok(symbol: &str) -> bool {
     let mut chars = symbol.chars();
     let first = chars.next().unwrap();
-    if !(first == '_' || unicode_ident::is_xid_start(first)) {
+    if !(first == '_' || first.is_ascii_alphabetic()) {
         return false;
     }
     for ch in chars {
-        if !unicode_ident::is_xid_continue(ch) {
+        if !(ch == '_' || ch.is_ascii_alphanumeric()) {
             return false;
         }
     }
Signature · 1 line
2.51.2
maintainer Reviewed-by on v1 16/18
#43 lore raw

Re: [PATCH 16/18] rust: syn: remove `unicode-ident` dependency

Quoted 8 lines · Miguel Ojeda

The syn crate depends on the unicode-ident crate to determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31.

However, we only need ASCII identifiers in the kernel, thus we can simplify the check and remove completely that dependency.

author maintainer
#44 lore raw

PATCH 17/18 rust: syn: add `README.md`

In reply to Miguel Ojeda

Originally, when the Rust upstream alloc standard library crate was vendored in commit 057b8d257107 ("rust: adapt alloc crate to the kernel"), a README.md file was added to explain the provenance and licensing of the source files.

Thus do the same for the syn crate.

---
 rust/syn/README.md | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 rust/syn/README.md
rust/syn/README.md new file +13 -0
new file mode 100644
index 000000000000..d3e3981da2fe
@@ -0,0 +1,13 @@
+# `syn`
+
+These source files come from the Rust `syn` crate, version 2.0.106
+(released 2025-08-16), hosted in the <https://github.com/dtolnay/syn>
+repository, licensed under "Apache-2.0 OR MIT" and only modified to add
+the SPDX license identifiers and to remove the `unicode-ident`
+dependency.
+
+For copyright details, please see:
+
+    https://github.com/dtolnay/syn/blob/2.0.106/README.md#license
+    https://github.com/dtolnay/syn/blob/2.0.106/LICENSE-APACHE
+    https://github.com/dtolnay/syn/blob/2.0.106/LICENSE-MIT
Signature · 1 line
2.51.2
maintainer Reviewed-by on v1 17/18
#45 lore raw

Re: [PATCH 17/18] rust: syn: add `README.md`

Quoted 35 lines · Miguel Ojeda

Originally, when the Rust upstream alloc standard library crate was vendored in commit 057b8d257107 ("rust: adapt alloc crate to the kernel"), a README.md file was added to explain the provenance and licensing of the source files.

Thus do the same for the syn crate.

---
 rust/syn/README.md | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 rust/syn/README.md
rust/syn/README.md new file +13 -0
new file mode 100644
index 000000000000..d3e3981da2fe
@@ -0,0 +1,13 @@
+# `syn`
+
+These source files come from the Rust `syn` crate, version 2.0.106
+(released 2025-08-16), hosted in the <https://github.com/dtolnay/syn>
+repository, licensed under "Apache-2.0 OR MIT" and only modified to add
+the SPDX license identifiers and to remove the `unicode-ident`
+dependency.
+
+For copyright details, please see:
+
+    https://github.com/dtolnay/syn/blob/2.0.106/README.md#license
+    https://github.com/dtolnay/syn/blob/2.0.106/LICENSE-APACHE
+    https://github.com/dtolnay/syn/blob/2.0.106/LICENSE-MIT
Signature · 1 line
2.51.2
author maintainer
#46 lore raw

PATCH 18/18 rust: syn: enable support in kbuild

In reply to Miguel Ojeda

With all the new files in place and ready from the new crate, enable the support for it in the build system.

---
 Makefile                          |  1 +
 rust/Makefile                     | 39 +++++++++++++++++++++++++++----
 scripts/generate_rust_analyzer.py |  7 ++++++
 3 files changed, 43 insertions(+), 4 deletions(-)
Makefile +1 -0
index f1b38b7fed1e..5364b3ad3600 100644
@@ -1833,6 +1833,7 @@ rustfmt:
 		\( \
 			-path $(srctree)/rust/proc-macro2 \
 			-o -path $(srctree)/rust/quote \
+			-o -path $(srctree)/rust/syn \
 		\) -prune -o \
 		-type f -a -name '*.rs' -a ! -name '*generated*' -print \
 		| xargs $(RUSTFMT) $(rustfmt_flags)
rust/Makefile +35 -4
index 801a8cbf3bdd..984aec608c27 100644
@@ -27,7 +27,7 @@ endif
 
 obj-$(CONFIG_RUST) += exports.o
 
-always-$(CONFIG_RUST) += libproc_macro2.rlib libquote.rlib
+always-$(CONFIG_RUST) += libproc_macro2.rlib libquote.rlib libsyn.rlib
 
 always-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated.rs
 always-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated_kunit.c
@@ -101,6 +101,22 @@ quote-flags := \
     --extern proc_macro2 \
     $(call cfgs-to-flags,$(quote-cfgs))
 
+# `extra-traits`, `fold` and `visit` may be enabled if needed.
+syn-cfgs := \
+    feature="clone-impls" \
+    feature="derive" \
+    feature="full" \
+    feature="parsing" \
+    feature="printing" \
+    feature="proc-macro" \
+    feature="visit-mut"
+
+syn-flags := \
+    --cap-lints=allow \
+    --extern proc_macro2 \
+    --extern quote \
+    $(call cfgs-to-flags,$(syn-cfgs))
+
 # `rustdoc` did not save the target modifiers, thus workaround for
 # the time being (https://github.com/rust-lang/rust/issues/144521).
 rustdoc_modifiers_workaround := $(if $(call rustc-min-version,108800),-Cunsafe-allow-abi-mismatch=fixed-x18)
@@ -164,11 +180,16 @@ rustdoc-quote: private skip_flags = $(quote-skip_flags)
 rustdoc-quote: $(src)/quote/lib.rs rustdoc-clean rustdoc-proc_macro2 FORCE
 	+$(call if_changed,rustdoc)
 
+rustdoc-syn: private rustdoc_host = yes
+rustdoc-syn: private rustc_target_flags = $(syn-flags)
+rustdoc-syn: $(src)/syn/lib.rs rustdoc-clean rustdoc-quote FORCE
+	+$(call if_changed,rustdoc)
+
 rustdoc-macros: private rustdoc_host = yes
 rustdoc-macros: private rustc_target_flags = --crate-type proc-macro \
     --extern proc_macro
 rustdoc-macros: $(src)/macros/lib.rs rustdoc-clean rustdoc-proc_macro2 \
-    rustdoc-quote FORCE
+    rustdoc-quote rustdoc-syn FORCE
 	+$(call if_changed,rustdoc)
 
 # Starting with Rust 1.82.0, skipping `-Wrustdoc::unescaped_backticks` should
@@ -240,6 +261,10 @@ rusttestlib-quote: private rustc_target_flags = $(quote-flags)
 rusttestlib-quote: $(src)/quote/lib.rs rusttestlib-proc_macro2 FORCE
 	+$(call if_changed,rustc_test_library)
 
+rusttestlib-syn: private rustc_target_flags = $(syn-flags)
+rusttestlib-syn: $(src)/syn/lib.rs rusttestlib-quote FORCE
+	+$(call if_changed,rustc_test_library)
+
 rusttestlib-macros: private rustc_target_flags = --extern proc_macro
 rusttestlib-macros: private rustc_test_library_proc = yes
 rusttestlib-macros: $(src)/macros/lib.rs FORCE
@@ -497,19 +522,24 @@ $(obj)/libquote.rlib: private rustc_target_flags = $(quote-flags)
 $(obj)/libquote.rlib: $(src)/quote/lib.rs $(obj)/libproc_macro2.rlib FORCE
 	+$(call if_changed_dep,rustc_hostlibrary)
 
+$(obj)/libsyn.rlib: private skip_clippy = 1
+$(obj)/libsyn.rlib: private rustc_target_flags = $(syn-flags)
+$(obj)/libsyn.rlib: $(src)/syn/lib.rs $(obj)/libquote.rlib FORCE
+	+$(call if_changed_dep,rustc_hostlibrary)
+
 quiet_cmd_rustc_procmacro = $(RUSTC_OR_CLIPPY_QUIET) P $@
       cmd_rustc_procmacro = \
 	$(RUSTC_OR_CLIPPY) $(rust_common_flags) $(rustc_target_flags) \
 		-Clinker-flavor=gcc -Clinker=$(HOSTCC) \
 		-Clink-args='$(call escsq,$(KBUILD_PROCMACROLDFLAGS))' \
 		--emit=dep-info=$(depfile) --emit=link=$@ --extern proc_macro \
-		--crate-type proc-macro \
+		--crate-type proc-macro -L$(objtree)/$(obj) \
 		--crate-name $(patsubst lib%.$(libmacros_extension),%,$(notdir $@)) \
 		@$(objtree)/include/generated/rustc_cfg $<
 
 # Procedural macros can only be used with the `rustc` that compiled it.
 $(obj)/$(libmacros_name): $(src)/macros/lib.rs $(obj)/libproc_macro2.rlib \
-    $(obj)/libquote.rlib FORCE
+    $(obj)/libquote.rlib $(obj)/libsyn.rlib FORCE
 	+$(call if_changed_dep,rustc_procmacro)
 
 $(obj)/$(libpin_init_internal_name): private rustc_target_flags = --cfg kernel
@@ -534,6 +564,7 @@ rust-analyzer:
 		--cfgs='core=$(core-cfgs)' $(core-edition) \
 		--cfgs='proc_macro2=$(proc_macro2-cfgs)' \
 		--cfgs='quote=$(quote-cfgs)' \
+		--cfgs='syn=$(syn-cfgs)' \
 		$(realpath $(srctree)) $(realpath $(objtree)) \
 		$(rustc_sysroot) $(RUST_LIB_SRC) $(if $(KBUILD_EXTMOD),$(srcroot)) \
 		> rust-project.json
scripts/generate_rust_analyzer.py +7 -0
index 4faf153ed2ee..5b6f7b8d6918 100755
@@ -100,6 +100,13 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs, core_edit
         cfg=crates_cfgs["quote"],
     )
 
+    append_crate(
+        "syn",
+        srctree / "rust" / "syn" / "lib.rs",
+        ["proc_macro", "proc_macro2", "quote"],
+        cfg=crates_cfgs["syn"],
+    )
+
     append_crate(
         "macros",
         srctree / "rust" / "macros" / "lib.rs",
Signature · 1 line
2.51.2
maintainer
#47 lore raw

Re: [PATCH 18/18] rust: syn: enable support in kbuild

Quoted 7 lines · Miguel Ojeda

With all the new files in place and ready from the new crate, enable the support for it in the build system.

quiet_cmd_rustc_procmacro = $(RUSTC_OR_CLIPPY_QUIET) P $@

       cmd_rustc_procmacro = \

rust/Makefile $(obj)/libquote.rlib: private rustc_target_flags = $(quote-flags) · line 538

Quoted 8 lines
rust/Makefile +1 -1
 	$(RUSTC_OR_CLIPPY) $(rust_common_flags) $(rustc_target_flags) \
 		-Clinker-flavor=gcc -Clinker=$(HOSTCC) \
 		-Clink-args='$(call escsq,$(KBUILD_PROCMACROLDFLAGS))' \
 		--emit=dep-info=$(depfile) --emit=link=$@ --extern proc_macro \
-		--crate-type proc-macro \
+		--crate-type proc-macro -L$(objtree)/$(obj) \
 		--crate-name $(patsubst lib%.$(libmacros_extension),%,$(notdir $@)) \
 		@$(objtree)/include/generated/rustc_cfg $<

This change seems unrelated?

Alice

#48 lore raw

Re: [PATCH 18/18] rust: syn: enable support in kbuild

Quoted 2 lines · Alice Ryhl

This change seems unrelated?

It is needed to actually use them in proc macros, e.g. to build the new pin-init. We could move it to that patch series, but then it means it is harder to use this series (e.g. someone was waiting to port other macros).

I can put it in a final patch or perhaps just mention it in the log, similar to the quote one.

Cheers,
Miguel

maintainer Reviewed-by
#49 lore raw

Re: [PATCH 18/18] rust: syn: enable support in kbuild

Quoted 11 lines · Miguel Ojeda
Quoted 2 lines · Alice Ryhl

This change seems unrelated?

It is needed to actually use them in proc macros, e.g. to build the new pin-init. We could move it to that patch series, but then it means it is harder to use this series (e.g. someone was waiting to port other macros).

I can put it in a final patch or perhaps just mention it in the log, similar to the quote one.

With that mentioned in the commit message:

#50 lore raw

Re: [PATCH 18/18] rust: syn: enable support in kbuild

In reply to Miguel Ojeda
Quoted 15 lines · Miguel Ojeda

With all the new files in place and ready from the new crate, enable the support for it in the build system.

---
 Makefile                          |  1 +
 rust/Makefile                     | 39 +++++++++++++++++++++++++++----
 scripts/generate_rust_analyzer.py |  7 ++++++
 3 files changed, 43 insertions(+), 4 deletions(-)
Makefile +0 -0
index f1b38b7fed1e..5364b3ad3600 100644

Makefile rustfmt: · line 1836

Quoted 4 lines
Makefile +1 -0
@@ -1833,6 +1833,7 @@ rustfmt:
 		\( \
 			-path $(srctree)/rust/proc-macro2 \
 			-o -path $(srctree)/rust/quote \
+			-o -path $(srctree)/rust/syn \

At this point, a "vendored" subdirectory for these three items is starting to sound even better, from a self-documenting point of view, yes?

thanks,

Signature · 1 line
John Hubbard
Quoted 121 lines
Makefile +0 -0
 		\) -prune -o \
 		-type f -a -name '*.rs' -a ! -name '*generated*' -print \
 		| xargs $(RUSTFMT) $(rustfmt_flags)
rust/Makefile +35 -4
index 801a8cbf3bdd..984aec608c27 100644
@@ -27,7 +27,7 @@ endif
 
 obj-$(CONFIG_RUST) += exports.o
 
-always-$(CONFIG_RUST) += libproc_macro2.rlib libquote.rlib
+always-$(CONFIG_RUST) += libproc_macro2.rlib libquote.rlib libsyn.rlib
 
 always-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated.rs
 always-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated_kunit.c
@@ -101,6 +101,22 @@ quote-flags := \
     --extern proc_macro2 \
     $(call cfgs-to-flags,$(quote-cfgs))
 
+# `extra-traits`, `fold` and `visit` may be enabled if needed.
+syn-cfgs := \
+    feature="clone-impls" \
+    feature="derive" \
+    feature="full" \
+    feature="parsing" \
+    feature="printing" \
+    feature="proc-macro" \
+    feature="visit-mut"
+
+syn-flags := \
+    --cap-lints=allow \
+    --extern proc_macro2 \
+    --extern quote \
+    $(call cfgs-to-flags,$(syn-cfgs))
+
 # `rustdoc` did not save the target modifiers, thus workaround for
 # the time being (https://github.com/rust-lang/rust/issues/144521).
 rustdoc_modifiers_workaround := $(if $(call rustc-min-version,108800),-Cunsafe-allow-abi-mismatch=fixed-x18)
@@ -164,11 +180,16 @@ rustdoc-quote: private skip_flags = $(quote-skip_flags)
 rustdoc-quote: $(src)/quote/lib.rs rustdoc-clean rustdoc-proc_macro2 FORCE
 	+$(call if_changed,rustdoc)
 
+rustdoc-syn: private rustdoc_host = yes
+rustdoc-syn: private rustc_target_flags = $(syn-flags)
+rustdoc-syn: $(src)/syn/lib.rs rustdoc-clean rustdoc-quote FORCE
+	+$(call if_changed,rustdoc)
+
 rustdoc-macros: private rustdoc_host = yes
 rustdoc-macros: private rustc_target_flags = --crate-type proc-macro \
     --extern proc_macro
 rustdoc-macros: $(src)/macros/lib.rs rustdoc-clean rustdoc-proc_macro2 \
-    rustdoc-quote FORCE
+    rustdoc-quote rustdoc-syn FORCE
 	+$(call if_changed,rustdoc)
 
 # Starting with Rust 1.82.0, skipping `-Wrustdoc::unescaped_backticks` should
@@ -240,6 +261,10 @@ rusttestlib-quote: private rustc_target_flags = $(quote-flags)
 rusttestlib-quote: $(src)/quote/lib.rs rusttestlib-proc_macro2 FORCE
 	+$(call if_changed,rustc_test_library)
 
+rusttestlib-syn: private rustc_target_flags = $(syn-flags)
+rusttestlib-syn: $(src)/syn/lib.rs rusttestlib-quote FORCE
+	+$(call if_changed,rustc_test_library)
+
 rusttestlib-macros: private rustc_target_flags = --extern proc_macro
 rusttestlib-macros: private rustc_test_library_proc = yes
 rusttestlib-macros: $(src)/macros/lib.rs FORCE
@@ -497,19 +522,24 @@ $(obj)/libquote.rlib: private rustc_target_flags = $(quote-flags)
 $(obj)/libquote.rlib: $(src)/quote/lib.rs $(obj)/libproc_macro2.rlib FORCE
 	+$(call if_changed_dep,rustc_hostlibrary)
 
+$(obj)/libsyn.rlib: private skip_clippy = 1
+$(obj)/libsyn.rlib: private rustc_target_flags = $(syn-flags)
+$(obj)/libsyn.rlib: $(src)/syn/lib.rs $(obj)/libquote.rlib FORCE
+	+$(call if_changed_dep,rustc_hostlibrary)
+
 quiet_cmd_rustc_procmacro = $(RUSTC_OR_CLIPPY_QUIET) P $@
       cmd_rustc_procmacro = \
 	$(RUSTC_OR_CLIPPY) $(rust_common_flags) $(rustc_target_flags) \
 		-Clinker-flavor=gcc -Clinker=$(HOSTCC) \
 		-Clink-args='$(call escsq,$(KBUILD_PROCMACROLDFLAGS))' \
 		--emit=dep-info=$(depfile) --emit=link=$@ --extern proc_macro \
-		--crate-type proc-macro \
+		--crate-type proc-macro -L$(objtree)/$(obj) \
 		--crate-name $(patsubst lib%.$(libmacros_extension),%,$(notdir $@)) \
 		@$(objtree)/include/generated/rustc_cfg $<
 
 # Procedural macros can only be used with the `rustc` that compiled it.
 $(obj)/$(libmacros_name): $(src)/macros/lib.rs $(obj)/libproc_macro2.rlib \
-    $(obj)/libquote.rlib FORCE
+    $(obj)/libquote.rlib $(obj)/libsyn.rlib FORCE
 	+$(call if_changed_dep,rustc_procmacro)
 
 $(obj)/$(libpin_init_internal_name): private rustc_target_flags = --cfg kernel
@@ -534,6 +564,7 @@ rust-analyzer:
 		--cfgs='core=$(core-cfgs)' $(core-edition) \
 		--cfgs='proc_macro2=$(proc_macro2-cfgs)' \
 		--cfgs='quote=$(quote-cfgs)' \
+		--cfgs='syn=$(syn-cfgs)' \
 		$(realpath $(srctree)) $(realpath $(objtree)) \
 		$(rustc_sysroot) $(RUST_LIB_SRC) $(if $(KBUILD_EXTMOD),$(srcroot)) \
 		> rust-project.json
scripts/generate_rust_analyzer.py +7 -0
index 4faf153ed2ee..5b6f7b8d6918 100755
@@ -100,6 +100,13 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs, core_edit
         cfg=crates_cfgs["quote"],
     )
 
+    append_crate(
+        "syn",
+        srctree / "rust" / "syn" / "lib.rs",
+        ["proc_macro", "proc_macro2", "quote"],
+        cfg=crates_cfgs["syn"],
+    )
+
     append_crate(
         "macros",
         srctree / "rust" / "macros" / "lib.rs",
maintainer Reviewed-by Tested-by on the v1 series
#51 lore raw
In reply to Miguel Ojeda
Quoted 45 lines · On Mon, 10 Nov 2025 10:50:05 +0100 Miguel Ojeda <[email protected]> wrote:

This patch series introduces support for syn (and its dependencies). Having such support allows to greatly simplify writing complex macros such as pin-init.

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.

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.

This has been a long time coming -- the first iterations of this, from 2022 and 2023 (with serde too), are at:

    https://github.com/Rust-for-Linux/linux/pull/910
    https://github.com/Rust-for-Linux/linux/pull/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.

Quoted 5 lines

I will be updating the webpage as well with the result of this:

https://rust-for-linux.com/third-party-crates

Please give it a test -- thanks!

Hi Miguel,

Thank you for creating this. I have been wanting to use syn even before RfL was merged into mainline :)

I've gone through all commits and they all look good to me (I've also verified the "import crate" patches against the source). So

I can build with these patches and I've verified that libmacros can indeed reference types within syn, so also

Best,
Gary

Quoted 186 lines

Miguel Ojeda (18):
rust: condvar: avoid pub in example
rust: kbuild: introduce core-flags and core-skip_flags
rust: kbuild: simplify --cfg handling
rust: kbuild: add host library support
rust: proc-macro2: import crate
rust: proc-macro2: add SPDX License Identifiers
rust: proc-macro2: remove unicode_ident dependency
rust: proc-macro2: add README.md
rust: proc-macro2: enable support in kbuild
rust: quote: import crate
rust: quote: add SPDX License Identifiers
rust: quote: add README.md
rust: quote: enable support in kbuild
rust: syn: import crate
rust: syn: add SPDX License Identifiers
rust: syn: remove unicode-ident dependency
rust: syn: add README.md
rust: syn: enable support in kbuild

 .gitignore                                    |    1 +
 Makefile                                      |    5 +
 rust/Makefile                                 |  134 +-
 rust/kernel/sync/condvar.rs                   |    2 +-
 rust/proc-macro2/README.md                    |   13 +
 rust/proc-macro2/detection.rs                 |   77 +
 rust/proc-macro2/extra.rs                     |  153 +
 rust/proc-macro2/fallback.rs                  | 1258 +++++
 rust/proc-macro2/lib.rs                       | 1351 ++++++
 rust/proc-macro2/location.rs                  |   31 +
 rust/proc-macro2/marker.rs                    |   19 +
 rust/proc-macro2/parse.rs                     |  997 ++++
 rust/proc-macro2/probe.rs                     |   12 +
 rust/proc-macro2/probe/proc_macro_span.rs     |   53 +
 .../proc-macro2/probe/proc_macro_span_file.rs |   16 +
 .../probe/proc_macro_span_location.rs         |   23 +
 rust/proc-macro2/rcvec.rs                     |  148 +
 rust/proc-macro2/wrapper.rs                   |  986 ++++
 rust/quote/README.md                          |   12 +
 rust/quote/ext.rs                             |  112 +
 rust/quote/format.rs                          |  170 +
 rust/quote/ident_fragment.rs                  |   90 +
 rust/quote/lib.rs                             | 1456 ++++++
 rust/quote/runtime.rs                         |  494 ++
 rust/quote/spanned.rs                         |   52 +
 rust/quote/to_tokens.rs                       |  273 ++
 rust/syn/README.md                            |   13 +
 rust/syn/attr.rs                              |  838 ++++
 rust/syn/bigint.rs                            |   68 +
 rust/syn/buffer.rs                            |  436 ++
 rust/syn/classify.rs                          |  313 ++
 rust/syn/custom_keyword.rs                    |  262 ++
 rust/syn/custom_punctuation.rs                |  306 ++
 rust/syn/data.rs                              |  426 ++
 rust/syn/derive.rs                            |  261 ++
 rust/syn/discouraged.rs                       |  227 +
 rust/syn/drops.rs                             |   60 +
 rust/syn/error.rs                             |  469 ++
 rust/syn/export.rs                            |   75 +
 rust/syn/expr.rs                              | 4175 +++++++++++++++++
 rust/syn/ext.rs                               |  138 +
 rust/syn/file.rs                              |  127 +
 rust/syn/fixup.rs                             |  775 +++
 rust/syn/gen/clone.rs                         | 2269 +++++++++
 rust/syn/gen/debug.rs                         | 3240 +++++++++++++
 rust/syn/gen/eq.rs                            | 2308 +++++++++
 rust/syn/gen/fold.rs                          | 3904 +++++++++++++++
 rust/syn/gen/hash.rs                          | 2878 ++++++++++++
 rust/syn/gen/visit.rs                         | 3943 ++++++++++++++++
 rust/syn/gen/visit_mut.rs                     | 3761 +++++++++++++++
 rust/syn/generics.rs                          | 1479 ++++++
 rust/syn/group.rs                             |  293 ++
 rust/syn/ident.rs                             |  110 +
 rust/syn/item.rs                              | 3492 ++++++++++++++
 rust/syn/lib.rs                               | 1013 ++++
 rust/syn/lifetime.rs                          |  158 +
 rust/syn/lit.rs                               | 1862 ++++++++
 rust/syn/lookahead.rs                         |  334 ++
 rust/syn/mac.rs                               |  227 +
 rust/syn/macros.rs                            |  184 +
 rust/syn/meta.rs                              |  429 ++
 rust/syn/op.rs                                |  221 +
 rust/syn/parse.rs                             | 1421 ++++++
 rust/syn/parse_macro_input.rs                 |  130 +
 rust/syn/parse_quote.rs                       |  242 +
 rust/syn/pat.rs                               |  957 ++++
 rust/syn/path.rs                              |  968 ++++
 rust/syn/precedence.rs                        |  212 +
 rust/syn/print.rs                             |   18 +
 rust/syn/punctuated.rs                        | 1157 +++++
 rust/syn/restriction.rs                       |  180 +
 rust/syn/scan_expr.rs                         |  267 ++
 rust/syn/sealed.rs                            |    6 +
 rust/syn/span.rs                              |   65 +
 rust/syn/spanned.rs                           |  120 +
 rust/syn/stmt.rs                              |  486 ++
 rust/syn/thread.rs                            |   62 +
 rust/syn/token.rs                             | 1098 +++++
 rust/syn/tt.rs                                |  109 +
 rust/syn/ty.rs                                | 1273 +++++
 rust/syn/verbatim.rs                          |   35 +
 rust/syn/whitespace.rs                        |   67 +
 scripts/generate_rust_analyzer.py             |   23 +-
 83 files changed, 57895 insertions(+), 13 deletions(-)
 create mode 100644 rust/proc-macro2/README.md
 create mode 100644 rust/proc-macro2/detection.rs
 create mode 100644 rust/proc-macro2/extra.rs
 create mode 100644 rust/proc-macro2/fallback.rs
 create mode 100644 rust/proc-macro2/lib.rs
 create mode 100644 rust/proc-macro2/location.rs
 create mode 100644 rust/proc-macro2/marker.rs
 create mode 100644 rust/proc-macro2/parse.rs
 create mode 100644 rust/proc-macro2/probe.rs
 create mode 100644 rust/proc-macro2/probe/proc_macro_span.rs
 create mode 100644 rust/proc-macro2/probe/proc_macro_span_file.rs
 create mode 100644 rust/proc-macro2/probe/proc_macro_span_location.rs
 create mode 100644 rust/proc-macro2/rcvec.rs
 create mode 100644 rust/proc-macro2/wrapper.rs
 create mode 100644 rust/quote/README.md
 create mode 100644 rust/quote/ext.rs
 create mode 100644 rust/quote/format.rs
 create mode 100644 rust/quote/ident_fragment.rs
 create mode 100644 rust/quote/lib.rs
 create mode 100644 rust/quote/runtime.rs
 create mode 100644 rust/quote/spanned.rs
 create mode 100644 rust/quote/to_tokens.rs
 create mode 100644 rust/syn/README.md
 create mode 100644 rust/syn/attr.rs
 create mode 100644 rust/syn/bigint.rs
 create mode 100644 rust/syn/buffer.rs
 create mode 100644 rust/syn/classify.rs
 create mode 100644 rust/syn/custom_keyword.rs
 create mode 100644 rust/syn/custom_punctuation.rs
 create mode 100644 rust/syn/data.rs
 create mode 100644 rust/syn/derive.rs
 create mode 100644 rust/syn/discouraged.rs
 create mode 100644 rust/syn/drops.rs
 create mode 100644 rust/syn/error.rs
 create mode 100644 rust/syn/export.rs
 create mode 100644 rust/syn/expr.rs
 create mode 100644 rust/syn/ext.rs
 create mode 100644 rust/syn/file.rs
 create mode 100644 rust/syn/fixup.rs
 create mode 100644 rust/syn/gen/clone.rs
 create mode 100644 rust/syn/gen/debug.rs
 create mode 100644 rust/syn/gen/eq.rs
 create mode 100644 rust/syn/gen/fold.rs
 create mode 100644 rust/syn/gen/hash.rs
 create mode 100644 rust/syn/gen/visit.rs
 create mode 100644 rust/syn/gen/visit_mut.rs
 create mode 100644 rust/syn/generics.rs
 create mode 100644 rust/syn/group.rs
 create mode 100644 rust/syn/ident.rs
 create mode 100644 rust/syn/item.rs
 create mode 100644 rust/syn/lib.rs
 create mode 100644 rust/syn/lifetime.rs
 create mode 100644 rust/syn/lit.rs
 create mode 100644 rust/syn/lookahead.rs
 create mode 100644 rust/syn/mac.rs
 create mode 100644 rust/syn/macros.rs
 create mode 100644 rust/syn/meta.rs
 create mode 100644 rust/syn/op.rs
 create mode 100644 rust/syn/parse.rs
 create mode 100644 rust/syn/parse_macro_input.rs
 create mode 100644 rust/syn/parse_quote.rs
 create mode 100644 rust/syn/pat.rs
 create mode 100644 rust/syn/path.rs
 create mode 100644 rust/syn/precedence.rs
 create mode 100644 rust/syn/print.rs
 create mode 100644 rust/syn/punctuated.rs
 create mode 100644 rust/syn/restriction.rs
 create mode 100644 rust/syn/scan_expr.rs
 create mode 100644 rust/syn/sealed.rs
 create mode 100644 rust/syn/span.rs
 create mode 100644 rust/syn/spanned.rs
 create mode 100644 rust/syn/stmt.rs
 create mode 100644 rust/syn/thread.rs
 create mode 100644 rust/syn/token.rs
 create mode 100644 rust/syn/tt.rs
 create mode 100644 rust/syn/ty.rs
 create mode 100644 rust/syn/verbatim.rs
 create mode 100644 rust/syn/whitespace.rs

--
2.51.2

#52 lore raw
Quoted 3 lines · Gary Guo

Thank you for creating this. I have been wanting to use syn even before RfL was merged into mainline :)

Indeed, you were definitely one of the biggest proponents of syn since the beginning! I could have mentioned a bit more of the older history in the cover letter, sorry.

In a sense, this cover letter could have had a Supported-by/Wanted-by/...: Gary & Benno et al. Actually, I may just add that :) And/or perhaps something about the history in the actual commit messages or use the cover letter in a merge.

Thanks a lot for the very quick reviews and tests!

Cheers,
Miguel

reviewer
#53 lore raw
In reply to Gary Guo
Quoted 2 lines · Gary Guo

I can build with these patches and I've verified that libmacros can indeed reference types within syn, so also

Hi,

I've been trying to apply this patch on the current rust-next HEAD 494de8f67b1e ("rust: sync: replace kernel::c_str! with C-Strings") and test it, since I need this series for my future patch revision.

Unfortunately, when I make the following change:

    diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
    index 793f712dbf7c..084633b517be 100644
    --- a/rust/macros/lib.rs
    +++ b/rust/macros/lib.rs
    @@ -444,3 +444,14 @@ pub fn paste(input: TokenStream) -> TokenStream {
    pub fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream {
        kunit::kunit_tests(attr, ts)
    }
    +
    +use syn::{
    +    parse_macro_input,
    +    DeriveInput, //
    +};
    +
    +#[proc_macro_attribute]
    +pub fn dummy(input: TokenStream) -> TokenStream {
    +    let input = parse_macro_input!(input as DeriveInput);
    +    todo!()
    +}

It fails to compile:

error[E0432]: unresolved import syn
--> rust/macros/lib.rs:448:5
|
448 | use syn::{
| ^^^ use of unresolved module or unlinked crate syn
|
= help: you might be missing a crate named syn

error: aborting due to 1 previous error

It appears that the vendored crates are indeed compiled; I can see some build artifacts like rust/libsyn.rlib, libquote.rlib, etc.

For reference, here's the sequence of commands I ran to compile the kernel:

    make LLVM=1 ARCH=arm64 rustavailable
    make LLVM=1 ARCH=arm64 defconfig
    scripts/config --enable CONFIG_RUST
    scripts/config --enable CONFIG_SAMPLES
    scripts/config --enable CONFIG_SAMPLES_RUST
    scripts/config --module CONFIG_SAMPLE_RUST_MINIMAL
    scripts/config --enable CONFIG_KUNIT
    scripts/config --enable CONFIG_RUST_KERNEL_DOCTESTS
    make LLVM=1 ARCH=arm64 olddefconfig
    make LLVM=1 ARCH=arm64 CLIPPY=1 -j8

I'd appreciate any guidance on steps I might be missing. Thanks in advance!

Best Regards,
Jesung

author maintainer
#54 lore raw
Quoted 3 lines · On Fri, 21 Nov 2025 04:37:47 +0000 Jesung Yang <[email protected]> wrote:

I'd appreciate any guidance on steps I might be missing. Thanks in advance!

For using (all of) them from macros, you would need [1].

Maybe I should just put it on top of this series for convenience.

Cheers,
Miguel

rust/Makefile +9 -4
index 984aec608c27..640e51129be0 100644
@@ -187,7 +187,7 @@ rustdoc-syn: $(src)/syn/lib.rs rustdoc-clean rustdoc-quote FORCE
 
 rustdoc-macros: private rustdoc_host = yes
 rustdoc-macros: private rustc_target_flags = --crate-type proc-macro \
-    --extern proc_macro
+    --extern proc_macro --extern proc_macro2 --extern quote --extern syn
 rustdoc-macros: $(src)/macros/lib.rs rustdoc-clean rustdoc-proc_macro2 \
     rustdoc-quote rustdoc-syn FORCE
        +$(call if_changed,rustdoc)
@@ -265,9 +265,11 @@ rusttestlib-syn: private rustc_target_flags = $(syn-flags)
 rusttestlib-syn: $(src)/syn/lib.rs rusttestlib-quote FORCE
        +$(call if_changed,rustc_test_library)
 
-rusttestlib-macros: private rustc_target_flags = --extern proc_macro
+rusttestlib-macros: private rustc_target_flags = --extern proc_macro \
+    --extern proc_macro2 --extern quote --extern syn
 rusttestlib-macros: private rustc_test_library_proc = yes
-rusttestlib-macros: $(src)/macros/lib.rs FORCE
+rusttestlib-macros: $(src)/macros/lib.rs \
+    rusttestlib-proc_macro2 rusttestlib-quote rusttestlib-syn FORCE
        +$(call if_changed,rustc_test_library)
 
 rusttestlib-pin_init_internal: private rustc_target_flags = --cfg kernel \
@@ -348,7 +350,8 @@ quiet_cmd_rustc_test = $(RUSTC_OR_CLIPPY_QUIET) T  $<
 rusttest: rusttest-macros
 
 rusttest-macros: private rustc_target_flags = --extern proc_macro \
-       --extern macros --extern kernel --extern pin_init
+       --extern macros --extern kernel --extern pin_init \
+       --extern proc_macro2 --extern quote --extern syn
 rusttest-macros: private rustdoc_test_target_flags = --crate-type proc-macro
 rusttest-macros: $(src)/macros/lib.rs \
     rusttestlib-macros rusttestlib-kernel rusttestlib-pin_init FORCE
@@ -538,6 +541,8 @@ quiet_cmd_rustc_procmacro = $(RUSTC_OR_CLIPPY_QUIET) P $@
                @$(objtree)/include/generated/rustc_cfg $<
 
 # Procedural macros can only be used with the `rustc` that compiled it.
+$(obj)/$(libmacros_name): private rustc_target_flags = \
+    --extern proc_macro2 --extern quote --extern syn
 $(obj)/$(libmacros_name): $(src)/macros/lib.rs $(obj)/libproc_macro2.rlib \
     $(obj)/libquote.rlib $(obj)/libsyn.rlib FORCE
        +$(call if_changed_dep,rustc_procmacro)
reviewer
#55 lore raw
Quoted 1 line · Miguel Ojeda

For using (all of) them from macros, you would need [1].

Now everything compiles. Thanks!

By the way, I needed to make a small change to generate_rust_analyzer.py for rust-analyzer to correctly reference symbols from the vendored crates:

(quoted diff) +0 -0

b/scripts/generate_rust_analyzer.py
index 5b6f7b8d6918..147d0cc94068 100755

scripts/generate_rust_analyzer.py +0 -0
@@ -110,7 +110,7 @@ def generate_crates(srctree, objtree, sysroot_src,

external_src, cfgs, core_edit

     append_crate(
         "macros",
         srctree / "rust" / "macros" / "lib.rs",
-        ["std", "proc_macro"],
+        ["std", "proc_macro", "proc_macro2", "quote", "syn"],
         is_proc_macro=True,
     )

This might already be covered somewhere else, but I'm bringing it up just in case.

Best Regards,
Jesung

Tested-by Jesung Yang
#56 lore raw
Quoted 2 lines · Jesung Yang

Now everything compiles. Thanks!

Great and thanks for testing! Do you mind if I add a:

?

Quoted 6 lines

By the way, I needed to make a small change to generate_rust_analyzer.py for rust-analyzer to correctly reference symbols from the vendored crates:

This might already be covered somewhere else, but I'm bringing it up just in case.

Definitely, it should be included.

Cheers,
Miguel

#58 lore raw
In reply to Miguel Ojeda
Quoted 3 lines · Miguel Ojeda

This patch series introduces support for syn (and its dependencies). Having such support allows to greatly simplify writing complex macros such as pin-init.

Just in case I'm not the only ignorant person who didn't immediately know what "syn" is and why we might want 57,000 lines of it in the kernel... :)

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

https://docs.rs/syn/latest/syn/

Thanks,

jon

#59 lore raw
Quoted 4 lines · Jonathan Corbet

Just in case I'm not the only ignorant person who didn't immediately know what "syn" is and why we might want 57,000 lines of it in the kernel... :)

Thanks -- I will add something like that.

It has been such a long and recurrent topic in our discussions that I guess I was writing from inside the Rust for Linux's bubble.

Just to give a bit more context, it is the most downloaded Rust crate (according to crates.io). It (or something like it) is essentially a necessity for writing complex macros without too much pain.

Cheers,
Miguel

reviewer
#60 lore raw
Quoted 6 lines · Miguel Ojeda
Quoted 4 lines · Jonathan Corbet

Just in case I'm not the only ignorant person who didn't immediately know what "syn" is and why we might want 57,000 lines of it in the kernel... :)

Quoted 8 lines

Thanks -- I will add something like that.

It has been such a long and recurrent topic in our discussions that I guess I was writing from inside the Rust for Linux's bubble.

Just to give a bit more context, it is the most downloaded Rust crate (according to crates.io). It (or something like it) is essentially a necessity for writing complex macros without too much pain.

It also is used by the Rust compiler itself for some proc-macros of their own.

Cheers,
Benno