From nobody Tue Dec 2 00:46:10 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E5857313522; Mon, 24 Nov 2025 15:19:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997545; cv=none; b=scxd3i4VHir2jJK/5fhlIeyaCSb4uqXZ4Beo++ZcagpPVU5ZkeWWNtjU86EdIP0CQGG0XwS3utdS/Eu6Bt2aR7mdSA+8P0KVlp66x+iPklulD6bIZtMFaTMOKwfGicdtO8Ru4SEnEkMxJZEE42YCmM4Df/anez3HF37FNzw/NMg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997545; c=relaxed/simple; bh=6Y31Ep13sBVEy0VoLZvLjFfOWYJl9hv0DSol9lc30Go=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=hWhT2NQoGYv3W/uT3YL5l3bBcZhr7sON8yc7ELHWjHimh6L6qmjgix1e2WOUAEPk2V8TpjjAtve37fUFQ3x111qcZTpdDDNU3O4z5xhz1ZCuZCKYmxn7lC84tdkBph8SEpxRH8rxBTOgjl4/4tbabiqZ+Trg17IirW9pYkuBSFw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=vKWEzcrg; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="vKWEzcrg" Received: by smtp.kernel.org (Postfix) with ESMTPSA id B5239C116C6; Mon, 24 Nov 2025 15:19:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763997544; bh=6Y31Ep13sBVEy0VoLZvLjFfOWYJl9hv0DSol9lc30Go=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=vKWEzcrg2SwqKC2t07VmZacO+CFm39z7vRUTOR8DuST7F6E/9zhu/kRsRky8igt8M DrP0JMXXViKQRE8iLXbzUb18V0NKR7RfT1+HRSsD+RkmYYigtLww80bBpx+cr1W/j9 GlT8k9ipyHY6XCZwnOIfB/UnQrC3DXRtTO5Hr4qvx6mIq7CGAlbYAU2CLQhA8h/bcV 20UoHqf73UB1CV1QHzfn6rhUqJILq6j1G9flVyS5jHtJq3FNn3y3/LZn4tlCQYg937 SfmFPzdcb3W2ZUcrIIMgkhGUopKQa461aEDzgsZPt2/0HJKsyd7bgPwbPoSLcw7BZ+ BNZn4NOVNrDCw== From: Miguel Ojeda To: Miguel Ojeda , Alex Gaynor , Nathan Chancellor , Nicolas Schier Cc: Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , rust-for-linux@vger.kernel.org, linux-kbuild@vger.kernel.org, linux-kernel@vger.kernel.org, patches@lists.linux.dev, Jesung Yang Subject: [PATCH v2 01/20] rust: kbuild: introduce `core-flags` and `core-skip_flags` Date: Mon, 24 Nov 2025 16:18:13 +0100 Message-ID: <20251124151837.2184382-2-ojeda@kernel.org> In-Reply-To: <20251124151837.2184382-1-ojeda@kernel.org> References: <20251124151837.2184382-1-ojeda@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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. In addition, use `:=3D` for `core-cfgs` to make it consistent with the upcoming additions. Reviewed-by: Alice Ryhl Reviewed-by: Gary Guo Tested-by: Gary Guo Tested-by: Jesung Yang Signed-off-by: Miguel Ojeda --- rust/Makefile | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/rust/Makefile b/rust/Makefile index 23c7ae905bd2..ce1853a09d3d 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -60,11 +60,20 @@ rustdoc_test_quiet=3D--test-args -q rustdoc_test_kernel_quiet=3D>/dev/null endif =20 -core-cfgs =3D \ +core-cfgs :=3D \ --cfg no_fp_fmt_parse =20 core-edition :=3D $(if $(call rustc-min-version,108700),2024,2021) =20 +core-skip_flags :=3D \ + --edition=3D2021 \ + -Wunreachable_pub \ + -Wrustdoc::unescaped_backticks + +core-flags :=3D \ + --edition=3D$(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 :=3D $(if $(call rustc-min-version,108800),-C= unsafe-allow-abi-mismatch=3Dfixed-x18) @@ -122,8 +131,8 @@ rustdoc-macros: $(src)/macros/lib.rs rustdoc-clean FORCE =20 # Starting with Rust 1.82.0, skipping `-Wrustdoc::unescaped_backticks` sho= uld # not be needed -- see https://github.com/rust-lang/rust/pull/128307. -rustdoc-core: private skip_flags =3D --edition=3D2021 -Wrustdoc::unescaped= _backticks -rustdoc-core: private rustc_target_flags =3D --edition=3D$(core-edition) $= (core-cfgs) +rustdoc-core: private skip_flags =3D $(core-skip_flags) +rustdoc-core: private rustc_target_flags =3D $(core-flags) rustdoc-core: $(RUST_LIB_SRC)/core/src/lib.rs rustdoc-clean FORCE +$(call if_changed,rustdoc) =20 @@ -499,9 +508,9 @@ $(obj)/helpers/helpers.o: $(src)/helpers/helpers.c $(re= cordmcount_source) FORCE $(obj)/exports.o: private skip_gendwarfksyms =3D 1 =20 $(obj)/core.o: private skip_clippy =3D 1 -$(obj)/core.o: private skip_flags =3D --edition=3D2021 -Wunreachable_pub +$(obj)/core.o: private skip_flags =3D $(core-skip_flags) $(obj)/core.o: private rustc_objcopy =3D $(foreach sym,$(redirect-intrinsi= cs),--redefine-sym $(sym)=3D__rust$(sym)) -$(obj)/core.o: private rustc_target_flags =3D --edition=3D$(core-edition) = $(core-cfgs) +$(obj)/core.o: private rustc_target_flags =3D $(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) --=20 2.52.0 From nobody Tue Dec 2 00:46:10 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1D10A31355A; Mon, 24 Nov 2025 15:19:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997549; cv=none; b=dEyOprfmCQhDoA65IDHlI8RuXlo2UzMg03PoLoqvKuqqX8ACpS7TFslklvDf+0Oo1M3EBrbYoNNbTnyAwj7dYT5kU6tz3a4CG5/S5vEXycqW8rUkovYaFsn/SyewCG8BqMAmZkpaLMyOGsw6vkqtKqy56u6fLwySXkJbA+4E/fs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997549; c=relaxed/simple; bh=ftNkFtBoYt7uNDdSMESD1jcmUrVtwSeg8Bd6lydF1ds=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=K1z23rRLN3KnM20h+Fl+RKsOTpaupaUSePDKHdKnll/QHSHw+7qu7jyhGaz6TCKgQIA8tIB1NLTW6lnhdIq43Ap5o8tan19lKvUy9XK66bU4MompAgCGMevwDnxzsF72Fv5SgPP8zcJI+M7bZaLHB8xyv/nhU8Zek7hveEMBwWE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=APY0OstT; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="APY0OstT" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E7330C4CEF1; Mon, 24 Nov 2025 15:19:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763997548; bh=ftNkFtBoYt7uNDdSMESD1jcmUrVtwSeg8Bd6lydF1ds=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=APY0OstTrkQon/azznA2KlNMW/ctRvIvIf1vJmirfS44ypiwyPGKH653mBMkLi1td YS84hnR6zdShcUxZXRuCu7vFwrwc7pIYx8Cs/r/QCWV9sA2u5s9oTSua/R5bi1Z8hD itLt5GUaturVVE8plBxWFSETC+Uikr7M20YUXb0OyMKS5Q2NgelsT33AMnwXmGCiEV dDNM697RuQ/MBjCny/OsBE8I4w+nEAhBh/B5nxel3W3UnCUircaa8FkkqOZBuq4jlX ftkdVfERhIQttHsLf0XHNkbpSduQCIv/jH5dEV3L2bJHvCIk6gXtWkzEZY5jfgn2v9 QoWaf5kwW7YLQ== From: Miguel Ojeda To: Miguel Ojeda , Alex Gaynor , Nathan Chancellor , Nicolas Schier Cc: Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , rust-for-linux@vger.kernel.org, linux-kbuild@vger.kernel.org, linux-kernel@vger.kernel.org, patches@lists.linux.dev, Jesung Yang Subject: [PATCH v2 02/20] rust: kbuild: simplify `--cfg` handling Date: Mon, 24 Nov 2025 16:18:14 +0100 Message-ID: <20251124151837.2184382-3-ojeda@kernel.org> In-Reply-To: <20251124151837.2184382-1-ojeda@kernel.org> References: <20251124151837.2184382-1-ojeda@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" We need to handle `cfg`s 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`. Reviewed-by: Alice Ryhl Reviewed-by: Gary Guo Tested-by: Gary Guo Tested-by: Jesung Yang Signed-off-by: Miguel Ojeda --- rust/Makefile | 6 ++++-- scripts/generate_rust_analyzer.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/rust/Makefile b/rust/Makefile index ce1853a09d3d..9967f3457d44 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -60,8 +60,10 @@ rustdoc_test_quiet=3D--test-args -q rustdoc_test_kernel_quiet=3D>/dev/null endif =20 +cfgs-to-flags =3D $(patsubst %,--cfg=3D'%',$1) + core-cfgs :=3D \ - --cfg no_fp_fmt_parse + no_fp_fmt_parse =20 core-edition :=3D $(if $(call rustc-min-version,108700),2024,2021) =20 @@ -72,7 +74,7 @@ core-skip_flags :=3D \ =20 core-flags :=3D \ --edition=3D$(core-edition) \ - $(core-cfgs) + $(call cfgs-to-flags,$(core-cfgs)) =20 # `rustdoc` did not save the target modifiers, thus workaround for # the time being (https://github.com/rust-lang/rust/issues/144521). diff --git a/scripts/generate_rust_analyzer.py b/scripts/generate_rust_anal= yzer.py index fc27f0cca752..dedca470adc1 100755 --- a/scripts/generate_rust_analyzer.py +++ b/scripts/generate_rust_analyzer.py @@ -15,7 +15,7 @@ def args_crates_cfgs(cfgs): crates_cfgs =3D {} for cfg in cfgs: crate, vals =3D cfg.split("=3D", 1) - crates_cfgs[crate] =3D vals.replace("--cfg", "").split() + crates_cfgs[crate] =3D vals.split() =20 return crates_cfgs =20 --=20 2.52.0 From nobody Tue Dec 2 00:46:10 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5915231355A; Mon, 24 Nov 2025 15:19:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997554; cv=none; b=pPQ8Jj97As9zVFTf+iaNY/B5+S3HvLSoHqFUu/ZYDGtTLvXMe2Ma1YbKs/OVBqI8IymF5i6pFNU66NLX3tq83p65mXxe1Zj2CL0sb8Nu8oKpsUG7i6MKSGHjWJaYF2HFRzNql3EUOoa+vEr6vblPufQNKNDXLXH61T51Z+a+jR8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997554; c=relaxed/simple; bh=2rKN1/YUZ7uLwEL6BZnfvfhRWHuJSYRLW4VPV/aKGc8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=kCWi1CfniKYkhGWABbaCEMzOg10M/1HAF66CePoX421wN49Mho0Cm5n/uoFpMaV7txSSs/iRgpHCj+LHaA8ZvXiPTrRTZUPJXdblmD6U7a/S1pWer5tUU2FCCkUlb2qSOosp8CeHtq706V8vBMOP9DS4p8iQCjmdl6fgm54d6Gk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=EN8mRtu0; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="EN8mRtu0" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 21400C116C6; Mon, 24 Nov 2025 15:19:08 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763997552; bh=2rKN1/YUZ7uLwEL6BZnfvfhRWHuJSYRLW4VPV/aKGc8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=EN8mRtu0ItyWhuV7lVKp+N8BjQtjfGAWr2JcN2TSqODxPS4FvE6TpOhtB9W276j5V TWLEBZqc4kRiciyL3gGLXipwiHCwZJDZ5YZ0d1ZvRWN1DEnr/srOW2KT3Isif4wBb9 E7+4+6GtKocqGINJYOy4thb3a+C/sZR6RUjCdvqLQQMAeRaRaMlyYM4dnothJ040Z6 yaKaAUYniA70rfzog14HTeZmVsSvYf8Hg8ekrn1LnjjbJ4N9optv5Dpyrr+o3nlaj8 ULU23ypnODG9oBubWqwBZmqsul44lYL3kayxJcwNTsFB3A6NoxBYaJg/2OBCDXhZsw CwVYDavfPcb4A== From: Miguel Ojeda To: Miguel Ojeda , Alex Gaynor , Nathan Chancellor , Nicolas Schier Cc: Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , rust-for-linux@vger.kernel.org, linux-kbuild@vger.kernel.org, linux-kernel@vger.kernel.org, patches@lists.linux.dev, Jesung Yang Subject: [PATCH v2 03/20] rust: kbuild: add proc macro library support Date: Mon, 24 Nov 2025 16:18:15 +0100 Message-ID: <20251124151837.2184382-4-ojeda@kernel.org> In-Reply-To: <20251124151837.2184382-1-ojeda@kernel.org> References: <20251124151837.2184382-1-ojeda@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add the proc macro library rule that produces `.rlib` files to be used by proc macros such as the `macros` crate. Reviewed-by: Gary Guo Tested-by: Gary Guo Tested-by: Jesung Yang Signed-off-by: Miguel Ojeda --- .gitignore | 1 + rust/Makefile | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/.gitignore b/.gitignore index 86a1ba0d9035..3a7241c941f5 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ *.o.* *.patch *.pyc +*.rlib *.rmeta *.rpm *.rsi diff --git a/rust/Makefile b/rust/Makefile index 9967f3457d44..45ef84513b6c 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -421,6 +421,16 @@ $(obj)/exports_bindings_generated.h: $(obj)/bindings.o= FORCE $(obj)/exports_kernel_generated.h: $(obj)/kernel.o FORCE $(call if_changed,exports) =20 +quiet_cmd_rustc_procmacrolibrary =3D $(RUSTC_OR_CLIPPY_QUIET) PL $@ + cmd_rustc_procmacrolibrary =3D \ + $(if $(skip_clippy),$(RUSTC),$(RUSTC_OR_CLIPPY)) \ + $(filter-out $(skip_flags),$(rust_common_flags) $(rustc_target_flags)) \ + --emit=3Ddep-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 =3D $(RUSTC_OR_CLIPPY_QUIET) P $@ cmd_rustc_procmacro =3D \ $(RUSTC_OR_CLIPPY) $(rust_common_flags) $(rustc_target_flags) \ --=20 2.52.0 From nobody Tue Dec 2 00:46:10 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 71BC6314A80; Mon, 24 Nov 2025 15:19:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997557; cv=none; b=uYoLdsB1k61Lc37ShiVwsuDqKkSJxXG3y8qPP8AK1VSSuCa4/Gnh5BNl62DbGPSkQNPhA4nTO392sri7EfEzP5xrHleD91eGqAxIDYG3boxTRHGQX+lN2AxLrltKyYNEBEzFQjx1TPU+vbofmlcfaddAZ+aofPtFIEx4qwbVflE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997557; c=relaxed/simple; bh=BYq28y2dSLEjQ5ZXYLjJVlE82VaBFdRbCujBSLfgKb0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=aWx+rQ9lhDD9AUD8cElwjYw3QpdpDOoyLVF8nCCtqpkAzNkV41go+k1G4FLO2IYXF9QrvW4Ksu1FE+wj4tO/T87S5lmyUggqq0ssH/5vUCPJaY13IGiuZH2xt5k08V9CDQGeiOOalRgAXOK3BbaFaJ6rHeZ0jT8DMoYLu4fcZJU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=nCncgcZo; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="nCncgcZo" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 54A82C116D0; Mon, 24 Nov 2025 15:19:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763997557; bh=BYq28y2dSLEjQ5ZXYLjJVlE82VaBFdRbCujBSLfgKb0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=nCncgcZovWdjf7jPcvgBsiEYMQXZwHpn7KP7SEh1sGDSvmKbeUAX9tzd1EpbN3r5v DnF7phm6zt+jiN5y+zyPyxI7CYTOpHbwCLF3NG1vLhIm+K3ZZPOp0ttmDLmMcID8Ex 28ifw6viYURIQfS+4GKPyODveRmVoqIP+mDhVK5WvQohyjSddg3QrkkHf8EHTvJJ3h YviCrY306H+C+8isMOHcA41j+a4cH/3CyjgIavt4kt8OYRB+Visgrt2NzZ5gBmDau3 NNtI97f6fAm6GK02u1+7Mg54whKiMvWMOoaIPUtBpxCViE7htDUi3HrbrArRO1pmMN yVRvilZQAWeHw== From: Miguel Ojeda To: Miguel Ojeda , Alex Gaynor , Nathan Chancellor , Nicolas Schier Cc: Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , rust-for-linux@vger.kernel.org, linux-kbuild@vger.kernel.org, linux-kernel@vger.kernel.org, patches@lists.linux.dev, Jesung Yang Subject: [PATCH v2 04/20] rust: kbuild: support skipping flags in `rustc_test_library` Date: Mon, 24 Nov 2025 16:18:16 +0100 Message-ID: <20251124151837.2184382-5-ojeda@kernel.org> In-Reply-To: <20251124151837.2184382-1-ojeda@kernel.org> References: <20251124151837.2184382-1-ojeda@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Crates like `quote` (added later) will need the ability to skip flags in the `rustc_test_library` command. Thus add the support for it. Reviewed-by: Alice Ryhl Reviewed-by: Gary Guo Tested-by: Gary Guo Tested-by: Jesung Yang Signed-off-by: Miguel Ojeda --- rust/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/Makefile b/rust/Makefile index 45ef84513b6c..6fdab341fc48 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -172,8 +172,8 @@ rustdoc-clean: FORCE quiet_cmd_rustc_test_library =3D $(RUSTC_OR_CLIPPY_QUIET) TL $< cmd_rustc_test_library =3D \ OBJTREE=3D$(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) $(rust= c_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 \ --=20 2.52.0 From nobody Tue Dec 2 00:46:10 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 51BAE313E06; Mon, 24 Nov 2025 15:19:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997561; cv=none; b=tJkKuyUC/JH4NoXVrNRg98y9WHfe7bvck6KqAivosA3r/rUTesLqPXyXFOxmz3mlAYSMpXVFiFW8Sk11gdW6j0S7dPh6aLLHCd493O3Gcq6z2SagYystf968b7PPjY+hJztMObuFyMfuTOklbcXk8P6XLAKM5TgVIjA9vJXL86c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997561; c=relaxed/simple; bh=xvgRLIv5dNjSzULhi3eB0gHy3Q8Jnreth6xUv0VM3ZU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=QVm0VP1FEC4zoa0OsDf5DjTAnH2KoUTr9PaQYTfb2kEOBo9d8rydBrpveHvCzfzYsmBJdZduZ5Vv0sE8oi1NkenRI6T1vNOLuHe5fSw1M1EgxEsa3YpMjfpWPeQI7jeSjgyAEaF9gvLOXEXm8hCSYXNcDCims1cJRWX67jo86Ss= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Rn+1KhbR; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Rn+1KhbR" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 77491C4CEF1; Mon, 24 Nov 2025 15:19:17 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763997561; bh=xvgRLIv5dNjSzULhi3eB0gHy3Q8Jnreth6xUv0VM3ZU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Rn+1KhbRY3v/FKQBVbCDvk6aM89E9yb1zidCQCKRvtI3TW3NjO8WAf3rCB7to/SN0 p37PYLh/jEahxDApbsZtFd9yLjCjS/dX7W86SQbYTIYx/hPRqZd4eFe1wjUwQfApiV Uo7tkLx4FnzINYSuQXuBJz080NK0tukrejiZC3+BWDW1I9fRTd6F0S8iqDVly9iyN0 JDczr2xJYYf8OrXyFEfDGiU+Rgkcq1iihZzWvDXeZ1zSwOgAjVeXy9HRTfGw0QT167 tFqWeO4tsyLcxj1riPun57NghOezPTsAfQl4Av8abCRc4tXzHmg6SXxB9geE7EVPmT mmgzefoF+2+Jw== From: Miguel Ojeda To: Miguel Ojeda , Alex Gaynor , Nathan Chancellor , Nicolas Schier Cc: Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , rust-for-linux@vger.kernel.org, linux-kbuild@vger.kernel.org, linux-kernel@vger.kernel.org, patches@lists.linux.dev, Jesung Yang Subject: [PATCH v2 05/20] rust: kbuild: support using libraries in `rustc_procmacro` Date: Mon, 24 Nov 2025 16:18:17 +0100 Message-ID: <20251124151837.2184382-6-ojeda@kernel.org> In-Reply-To: <20251124151837.2184382-1-ojeda@kernel.org> References: <20251124151837.2184382-1-ojeda@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Proc macros such as `macros` and `pin-init` will need the ability to use libraries such as `syn` (added later) in the `rustc_procmacro` command. Thus add the support for it. Reviewed-by: Alice Ryhl Reviewed-by: Gary Guo Tested-by: Gary Guo Tested-by: Jesung Yang Signed-off-by: Miguel Ojeda --- rust/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/Makefile b/rust/Makefile index 6fdab341fc48..0288ca8d270c 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -437,7 +437,7 @@ quiet_cmd_rustc_procmacro =3D $(RUSTC_OR_CLIPPY_QUIET) = P $@ -Clinker-flavor=3Dgcc -Clinker=3D$(HOSTCC) \ -Clink-args=3D'$(call escsq,$(KBUILD_PROCMACROLDFLAGS))' \ --emit=3Ddep-info=3D$(depfile) --emit=3Dlink=3D$@ --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 $< =20 --=20 2.52.0 From nobody Tue Dec 2 00:46:11 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4A10F314D1A; Mon, 24 Nov 2025 15:19:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997566; cv=none; b=YsWsitNgOtnSaMX0PSiMiGKbJvXa/JWvz02vVS3bMgngaHIn0Hzbyh6lVXrGYBEbpzw3cXf6dAebxkn4CGUjX9smdsWhpQBWh3P4JBAkc04ahLSfwPk+aRTMcfiXbJ+FCiRXhBEiTAeUgV3fCqzDZSd+ArdJ1HNlkuMoja7okN0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997566; c=relaxed/simple; bh=/55FuWUTmaU2nJynd4JhaRBaiA6IgejrAOGDmO6T4Mk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=iIz0B2LmAkj+eTQCRiJSSZpBKtAowGDdCE0+Gdc23vtUbhwHTtkECPNKd278Gje/m2hCQYG2QoiIsvCdjIlFAeTo1Q4CmK205NoLLUMAtPy1WA7zTpFn1/Qnz5j4W46vT11Jv0fX6qdxuqeo/sJ3lPY6Whs4eKc8lTmct4noW2o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Qb/a/qMf; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Qb/a/qMf" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A935CC19423; Mon, 24 Nov 2025 15:19:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763997565; bh=/55FuWUTmaU2nJynd4JhaRBaiA6IgejrAOGDmO6T4Mk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Qb/a/qMfI5AzQsXIlqK4qGQAeKX0mCeqmAO1LYdBBL0Cuvenqf8BeI2MNI+q6tp40 h75vRLyZidptFuHfJQGIKnz4u9LE9c0NyrHRwQHKT3FP24Mgj1zUD0QiV0bhrfI52x U2AVexkhnSyubRQLw0od+CyKjoBDXos7Q8Eimjo2fKoMEMkqTPi/1HfSC55kQkiA/a KRRO/x3ar/A2NagE6nRtqEpwbj98wULc5UgUljreMWJ1A2/wJIkoor1aHYkTYBfaoV emnicbk6SestlrnVql6HE6nl222Zm4nX1u/IGUmr7VHO05aeVfBCjQcfAstAiJHTHd Q/f1W+IBWbH0g== From: Miguel Ojeda To: Miguel Ojeda , Alex Gaynor , Nathan Chancellor , Nicolas Schier Cc: Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , rust-for-linux@vger.kernel.org, linux-kbuild@vger.kernel.org, linux-kernel@vger.kernel.org, patches@lists.linux.dev, Jesung Yang Subject: [PATCH v2 06/20] rust: proc-macro2: import crate Date: Mon, 24 Nov 2025 16:18:18 +0100 Message-ID: <20251124151837.2184382-7-ojeda@kernel.org> In-Reply-To: <20251124151837.2184382-1-ojeda@kernel.org> References: <20251124151837.2184382-1-ojeda@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable 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 Reviewed-by: Gary Guo Tested-by: Gary Guo Tested-by: Jesung Yang Signed-off-by: Miguel Ojeda --- 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 diff --git a/rust/proc-macro2/detection.rs b/rust/proc-macro2/detection.rs new file mode 100644 index 000000000000..beba7b237395 --- /dev/null +++ b/rust/proc-macro2/detection.rs @@ -0,0 +1,75 @@ +use core::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Once; + +static WORKS: AtomicUsize =3D AtomicUsize::new(0); +static INIT: Once =3D Once::new(); + +pub(crate) fn inside_proc_macro() -> bool { + match WORKS.load(Ordering::Relaxed) { + 1 =3D> return false, + 2 =3D> return true, + _ =3D> {} + } + + 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 =3D 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 stde= rr +// 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 =3D dyn Fn(&PanicInfo) + Sync + Send + 'static; + + let null_hook: Box =3D Box::new(|_panic_info| { /* ignore *= / }); + let sanity_check =3D &*null_hook as *const PanicHook; + let original_hook =3D panic::take_hook(); + panic::set_hook(null_hook); + + let works =3D panic::catch_unwind(proc_macro::Span::call_site).is_ok(); + WORKS.store(works as usize + 1, Ordering::Relaxed); + + let hopefully_null_hook =3D panic::take_hook(); + panic::set_hook(original_hook); + if sanity_check !=3D &*hopefully_null_hook { + panic!("observed race condition in proc_macro2::inside_proc_macro"= ); + } +} diff --git a/rust/proc-macro2/extra.rs b/rust/proc-macro2/extra.rs new file mode 100644 index 000000000000..522a90e136be --- /dev/null +++ b/rust/proc-macro2/extra.rs @@ -0,0 +1,151 @@ +//! Items which do not have a correspondence to any API in the proc_macro = crate, +//! but are necessary to include in proc-macro2. + +use crate::fallback; +use crate::imp; +use crate::marker::{ProcMacroAutoTraits, MARKER}; +use crate::Span; +use core::fmt::{self, Debug}; + +/// Invalidate any `proc_macro2::Span` that exist on the current thread. +/// +/// The implementation of `Span` uses thread-local data structures and this +/// function clears them. Calling any method on a `Span` on the current th= read +/// created prior to the invalidation will return incorrect values or cras= h. +/// +/// This function is useful for programs that process more than 232 +/// bytes of Rust source code on the same thread. Just like rustc, proc-ma= cro2 +/// uses 32-bit source locations, and these wrap around when the total sou= rce +/// code processed by the same thread exceeds 232 bytes (4 +/// gigabytes). After a wraparound, `Span` methods such as `source_text()`= can +/// return wrong data. +/// +/// # Example +/// +/// As of late 2023, there is 200 GB of Rust code published on crates.io. +/// Looking at just the newest version of every crate, it is 16 GB of code= . So a +/// workload that involves parsing it all would overflow a 32-bit source +/// location unless spans are being invalidated. +/// +/// ``` +/// use flate2::read::GzDecoder; +/// use std::ffi::OsStr; +/// use std::io::{BufReader, Read}; +/// use std::str::FromStr; +/// use tar::Archive; +/// +/// rayon::scope(|s| { +/// for krate in every_version_of_every_crate() { +/// s.spawn(move |_| { +/// proc_macro2::extra::invalidate_current_thread_spans(); +/// +/// let reader =3D BufReader::new(krate); +/// let tar =3D GzDecoder::new(reader); +/// let mut archive =3D Archive::new(tar); +/// for entry in archive.entries().unwrap() { +/// let mut entry =3D entry.unwrap(); +/// let path =3D entry.path().unwrap(); +/// if path.extension() !=3D Some(OsStr::new("rs")) { +/// continue; +/// } +/// let mut content =3D String::new(); +/// entry.read_to_string(&mut content).unwrap(); +/// match proc_macro2::TokenStream::from_str(&content) { +/// Ok(tokens) =3D> {/* ... */}, +/// Err(_) =3D> continue, +/// } +/// } +/// }); +/// } +/// }); +/// # +/// # fn every_version_of_every_crate() -> Vec { +/// # Vec::new() +/// # } +/// ``` +/// +/// # Panics +/// +/// This function is not applicable to and will panic if called from a +/// procedural macro. +#[cfg(span_locations)] +#[cfg_attr(docsrs, doc(cfg(feature =3D "span-locations")))] +pub fn invalidate_current_thread_spans() { + crate::imp::invalidate_current_thread_spans(); +} + +/// An object that holds a [`Group`]'s `span_open()` and `span_close()` to= gether +/// in a more compact representation than holding those 2 spans individual= ly. +/// +/// [`Group`]: crate::Group +#[derive(Copy, Clone)] +pub struct DelimSpan { + inner: DelimSpanEnum, + _marker: ProcMacroAutoTraits, +} + +#[derive(Copy, Clone)] +enum DelimSpanEnum { + #[cfg(wrap_proc_macro)] + Compiler { + join: proc_macro::Span, + open: proc_macro::Span, + close: proc_macro::Span, + }, + Fallback(fallback::Span), +} + +impl DelimSpan { + pub(crate) fn new(group: &imp::Group) -> Self { + #[cfg(wrap_proc_macro)] + let inner =3D match group { + imp::Group::Compiler(group) =3D> DelimSpanEnum::Compiler { + join: group.span(), + open: group.span_open(), + close: group.span_close(), + }, + imp::Group::Fallback(group) =3D> DelimSpanEnum::Fallback(group= .span()), + }; + + #[cfg(not(wrap_proc_macro))] + let inner =3D DelimSpanEnum::Fallback(group.span()); + + DelimSpan { + inner, + _marker: MARKER, + } + } + + /// Returns a span covering the entire delimited group. + pub fn join(&self) -> Span { + match &self.inner { + #[cfg(wrap_proc_macro)] + DelimSpanEnum::Compiler { join, .. } =3D> Span::_new(imp::Span= ::Compiler(*join)), + DelimSpanEnum::Fallback(span) =3D> Span::_new_fallback(*span), + } + } + + /// Returns a span for the opening punctuation of the group only. + pub fn open(&self) -> Span { + match &self.inner { + #[cfg(wrap_proc_macro)] + DelimSpanEnum::Compiler { open, .. } =3D> Span::_new(imp::Span= ::Compiler(*open)), + DelimSpanEnum::Fallback(span) =3D> Span::_new_fallback(span.fi= rst_byte()), + } + } + + /// Returns a span for the closing punctuation of the group only. + pub fn close(&self) -> Span { + match &self.inner { + #[cfg(wrap_proc_macro)] + DelimSpanEnum::Compiler { close, .. } =3D> Span::_new(imp::Spa= n::Compiler(*close)), + DelimSpanEnum::Fallback(span) =3D> Span::_new_fallback(span.la= st_byte()), + } + } +} + +impl Debug for DelimSpan { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(&self.join(), f) + } +} diff --git a/rust/proc-macro2/fallback.rs b/rust/proc-macro2/fallback.rs new file mode 100644 index 000000000000..1560105cfd25 --- /dev/null +++ b/rust/proc-macro2/fallback.rs @@ -0,0 +1,1256 @@ +#[cfg(wrap_proc_macro)] +use crate::imp; +#[cfg(span_locations)] +use crate::location::LineColumn; +use crate::parse::{self, Cursor}; +use crate::rcvec::{RcVec, RcVecBuilder, RcVecIntoIter, RcVecMut}; +use crate::{Delimiter, Spacing, TokenTree}; +#[cfg(all(span_locations, not(fuzzing)))] +use alloc::collections::BTreeMap; +#[cfg(all(span_locations, not(fuzzing)))] +use core::cell::RefCell; +#[cfg(span_locations)] +use core::cmp; +#[cfg(all(span_locations, not(fuzzing)))] +use core::cmp::Ordering; +use core::fmt::{self, Debug, Display, Write}; +use core::mem::ManuallyDrop; +#[cfg(span_locations)] +use core::ops::Range; +use core::ops::RangeBounds; +use core::ptr; +use core::str; +#[cfg(feature =3D "proc-macro")] +use core::str::FromStr; +use std::ffi::CStr; +#[cfg(wrap_proc_macro)] +use std::panic; +#[cfg(span_locations)] +use std::path::PathBuf; + +/// Force use of proc-macro2's fallback implementation of the API for now,= even +/// if the compiler's implementation is available. +pub fn force() { + #[cfg(wrap_proc_macro)] + crate::detection::force_fallback(); +} + +/// Resume using the compiler's implementation of the proc macro API if it= is +/// available. +pub fn unforce() { + #[cfg(wrap_proc_macro)] + crate::detection::unforce_fallback(); +} + +#[derive(Clone)] +pub(crate) struct TokenStream { + inner: RcVec, +} + +#[derive(Debug)] +pub(crate) struct LexError { + pub(crate) span: Span, +} + +impl LexError { + pub(crate) fn span(&self) -> Span { + self.span + } + + pub(crate) fn call_site() -> Self { + LexError { + span: Span::call_site(), + } + } +} + +impl TokenStream { + pub(crate) fn new() -> Self { + TokenStream { + inner: RcVecBuilder::new().build(), + } + } + + pub(crate) fn from_str_checked(src: &str) -> Result { + // Create a dummy file & add it to the source map + let mut cursor =3D get_cursor(src); + + // Strip a byte order mark if present + const BYTE_ORDER_MARK: &str =3D "\u{feff}"; + if cursor.starts_with(BYTE_ORDER_MARK) { + cursor =3D cursor.advance(BYTE_ORDER_MARK.len()); + } + + parse::token_stream(cursor) + } + + #[cfg(feature =3D "proc-macro")] + pub(crate) fn from_str_unchecked(src: &str) -> Self { + Self::from_str_checked(src).unwrap() + } + + pub(crate) fn is_empty(&self) -> bool { + self.inner.len() =3D=3D 0 + } + + fn take_inner(self) -> RcVecBuilder { + let nodrop =3D ManuallyDrop::new(self); + unsafe { ptr::read(&nodrop.inner) }.make_owned() + } +} + +fn push_token_from_proc_macro(mut vec: RcVecMut, token: TokenTr= ee) { + // https://github.com/dtolnay/proc-macro2/issues/235 + match token { + TokenTree::Literal(crate::Literal { + #[cfg(wrap_proc_macro)] + inner: crate::imp::Literal::Fallback(literal), + #[cfg(not(wrap_proc_macro))] + inner: literal, + .. + }) if literal.repr.starts_with('-') =3D> { + push_negative_literal(vec, literal); + } + _ =3D> vec.push(token), + } + + #[cold] + fn push_negative_literal(mut vec: RcVecMut, mut literal: Li= teral) { + literal.repr.remove(0); + let mut punct =3D crate::Punct::new('-', Spacing::Alone); + punct.set_span(crate::Span::_new_fallback(literal.span)); + vec.push(TokenTree::Punct(punct)); + vec.push(TokenTree::Literal(crate::Literal::_new_fallback(literal)= )); + } +} + +// Nonrecursive to prevent stack overflow. +impl Drop for TokenStream { + fn drop(&mut self) { + let mut stack =3D Vec::new(); + let mut current =3D match self.inner.get_mut() { + Some(inner) =3D> inner.take().into_iter(), + None =3D> return, + }; + loop { + while let Some(token) =3D current.next() { + let group =3D match token { + TokenTree::Group(group) =3D> group.inner, + _ =3D> continue, + }; + #[cfg(wrap_proc_macro)] + let group =3D match group { + crate::imp::Group::Fallback(group) =3D> group, + crate::imp::Group::Compiler(_) =3D> continue, + }; + let mut group =3D group; + if let Some(inner) =3D group.stream.inner.get_mut() { + stack.push(current); + current =3D inner.take().into_iter(); + } + } + match stack.pop() { + Some(next) =3D> current =3D next, + None =3D> return, + } + } + } +} + +pub(crate) struct TokenStreamBuilder { + inner: RcVecBuilder, +} + +impl TokenStreamBuilder { + pub(crate) fn new() -> Self { + TokenStreamBuilder { + inner: RcVecBuilder::new(), + } + } + + pub(crate) fn with_capacity(cap: usize) -> Self { + TokenStreamBuilder { + inner: RcVecBuilder::with_capacity(cap), + } + } + + pub(crate) fn push_token_from_parser(&mut self, tt: TokenTree) { + self.inner.push(tt); + } + + pub(crate) fn build(self) -> TokenStream { + TokenStream { + inner: self.inner.build(), + } + } +} + +#[cfg(span_locations)] +fn get_cursor(src: &str) -> Cursor { + #[cfg(fuzzing)] + return Cursor { rest: src, off: 1 }; + + // Create a dummy file & add it to the source map + #[cfg(not(fuzzing))] + SOURCE_MAP.with(|sm| { + let mut sm =3D sm.borrow_mut(); + let span =3D sm.add_file(src); + Cursor { + rest: src, + off: span.lo, + } + }) +} + +#[cfg(not(span_locations))] +fn get_cursor(src: &str) -> Cursor { + Cursor { rest: src } +} + +impl Display for LexError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("cannot parse string into token stream") + } +} + +impl Display for TokenStream { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut joint =3D false; + for (i, tt) in self.inner.iter().enumerate() { + if i !=3D 0 && !joint { + write!(f, " ")?; + } + joint =3D false; + match tt { + TokenTree::Group(tt) =3D> Display::fmt(tt, f), + TokenTree::Ident(tt) =3D> Display::fmt(tt, f), + TokenTree::Punct(tt) =3D> { + joint =3D tt.spacing() =3D=3D Spacing::Joint; + Display::fmt(tt, f) + } + TokenTree::Literal(tt) =3D> Display::fmt(tt, f), + }?; + } + + Ok(()) + } +} + +impl Debug for TokenStream { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("TokenStream ")?; + f.debug_list().entries(self.clone()).finish() + } +} + +#[cfg(feature =3D "proc-macro")] +impl From for TokenStream { + fn from(inner: proc_macro::TokenStream) -> Self { + TokenStream::from_str_unchecked(&inner.to_string()) + } +} + +#[cfg(feature =3D "proc-macro")] +impl From for proc_macro::TokenStream { + fn from(inner: TokenStream) -> Self { + proc_macro::TokenStream::from_str_unchecked(&inner.to_string()) + } +} + +impl From for TokenStream { + fn from(tree: TokenTree) -> Self { + let mut stream =3D RcVecBuilder::new(); + push_token_from_proc_macro(stream.as_mut(), tree); + TokenStream { + inner: stream.build(), + } + } +} + +impl FromIterator for TokenStream { + fn from_iter>(tokens: I) -> Self { + let mut stream =3D TokenStream::new(); + stream.extend(tokens); + stream + } +} + +impl FromIterator for TokenStream { + fn from_iter>(streams: I) -> Sel= f { + let mut v =3D RcVecBuilder::new(); + + for stream in streams { + v.extend(stream.take_inner()); + } + + TokenStream { inner: v.build() } + } +} + +impl Extend for TokenStream { + fn extend>(&mut self, tokens: I) { + let mut vec =3D self.inner.make_mut(); + tokens + .into_iter() + .for_each(|token| push_token_from_proc_macro(vec.as_mut(), tok= en)); + } +} + +impl Extend for TokenStream { + fn extend>(&mut self, streams: I= ) { + self.inner.make_mut().extend(streams.into_iter().flatten()); + } +} + +pub(crate) type TokenTreeIter =3D RcVecIntoIter; + +impl IntoIterator for TokenStream { + type Item =3D TokenTree; + type IntoIter =3D TokenTreeIter; + + fn into_iter(self) -> TokenTreeIter { + self.take_inner().into_iter() + } +} + +#[cfg(all(span_locations, not(fuzzing)))] +thread_local! { + static SOURCE_MAP: RefCell =3D RefCell::new(SourceMap { + // Start with a single dummy file which all call_site() and def_si= te() + // spans reference. + files: vec![FileInfo { + source_text: String::new(), + span: Span { lo: 0, hi: 0 }, + lines: vec![0], + char_index_to_byte_offset: BTreeMap::new(), + }], + }); +} + +#[cfg(span_locations)] +pub(crate) fn invalidate_current_thread_spans() { + #[cfg(not(fuzzing))] + SOURCE_MAP.with(|sm| sm.borrow_mut().files.truncate(1)); +} + +#[cfg(all(span_locations, not(fuzzing)))] +struct FileInfo { + source_text: String, + span: Span, + lines: Vec, + char_index_to_byte_offset: BTreeMap, +} + +#[cfg(all(span_locations, not(fuzzing)))] +impl FileInfo { + fn offset_line_column(&self, offset: usize) -> LineColumn { + assert!(self.span_within(Span { + lo: offset as u32, + hi: offset as u32, + })); + let offset =3D offset - self.span.lo as usize; + match self.lines.binary_search(&offset) { + Ok(found) =3D> LineColumn { + line: found + 1, + column: 0, + }, + Err(idx) =3D> LineColumn { + line: idx, + column: offset - self.lines[idx - 1], + }, + } + } + + fn span_within(&self, span: Span) -> bool { + span.lo >=3D self.span.lo && span.hi <=3D self.span.hi + } + + fn byte_range(&mut self, span: Span) -> Range { + let lo_char =3D (span.lo - self.span.lo) as usize; + + // Look up offset of the largest already-computed char index that = is + // less than or equal to the current requested one. We resume coun= ting + // chars from that point. + let (&last_char_index, &last_byte_offset) =3D self + .char_index_to_byte_offset + .range(..=3Dlo_char) + .next_back() + .unwrap_or((&0, &0)); + + let lo_byte =3D if last_char_index =3D=3D lo_char { + last_byte_offset + } else { + let total_byte_offset =3D match self.source_text[last_byte_off= set..] + .char_indices() + .nth(lo_char - last_char_index) + { + Some((additional_offset, _ch)) =3D> last_byte_offset + add= itional_offset, + None =3D> self.source_text.len(), + }; + self.char_index_to_byte_offset + .insert(lo_char, total_byte_offset); + total_byte_offset + }; + + let trunc_lo =3D &self.source_text[lo_byte..]; + let char_len =3D (span.hi - span.lo) as usize; + lo_byte..match trunc_lo.char_indices().nth(char_len) { + Some((offset, _ch)) =3D> lo_byte + offset, + None =3D> self.source_text.len(), + } + } + + fn source_text(&mut self, span: Span) -> String { + let byte_range =3D self.byte_range(span); + self.source_text[byte_range].to_owned() + } +} + +/// Computes the offsets of each line in the given source string +/// and the total number of characters +#[cfg(all(span_locations, not(fuzzing)))] +fn lines_offsets(s: &str) -> (usize, Vec) { + let mut lines =3D vec![0]; + let mut total =3D 0; + + for ch in s.chars() { + total +=3D 1; + if ch =3D=3D '\n' { + lines.push(total); + } + } + + (total, lines) +} + +#[cfg(all(span_locations, not(fuzzing)))] +struct SourceMap { + files: Vec, +} + +#[cfg(all(span_locations, not(fuzzing)))] +impl SourceMap { + fn next_start_pos(&self) -> u32 { + // Add 1 so there's always space between files. + // + // We'll always have at least 1 file, as we initialize our files l= ist + // with a dummy file. + self.files.last().unwrap().span.hi + 1 + } + + fn add_file(&mut self, src: &str) -> Span { + let (len, lines) =3D lines_offsets(src); + let lo =3D self.next_start_pos(); + let span =3D Span { + lo, + hi: lo + (len as u32), + }; + + self.files.push(FileInfo { + source_text: src.to_owned(), + span, + lines, + // Populated lazily by source_text(). + char_index_to_byte_offset: BTreeMap::new(), + }); + + span + } + + fn find(&self, span: Span) -> usize { + match self.files.binary_search_by(|file| { + if file.span.hi < span.lo { + Ordering::Less + } else if file.span.lo > span.hi { + Ordering::Greater + } else { + assert!(file.span_within(span)); + Ordering::Equal + } + }) { + Ok(i) =3D> i, + Err(_) =3D> unreachable!("Invalid span with no related FileInf= o!"), + } + } + + fn filepath(&self, span: Span) -> String { + let i =3D self.find(span); + if i =3D=3D 0 { + "".to_owned() + } else { + format!("", i) + } + } + + fn fileinfo(&self, span: Span) -> &FileInfo { + let i =3D self.find(span); + &self.files[i] + } + + fn fileinfo_mut(&mut self, span: Span) -> &mut FileInfo { + let i =3D self.find(span); + &mut self.files[i] + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub(crate) struct Span { + #[cfg(span_locations)] + pub(crate) lo: u32, + #[cfg(span_locations)] + pub(crate) hi: u32, +} + +impl Span { + #[cfg(not(span_locations))] + pub(crate) fn call_site() -> Self { + Span {} + } + + #[cfg(span_locations)] + pub(crate) fn call_site() -> Self { + Span { lo: 0, hi: 0 } + } + + pub(crate) fn mixed_site() -> Self { + Span::call_site() + } + + #[cfg(procmacro2_semver_exempt)] + pub(crate) fn def_site() -> Self { + Span::call_site() + } + + pub(crate) fn resolved_at(&self, _other: Span) -> Span { + // Stable spans consist only of line/column information, so + // `resolved_at` and `located_at` only select which span the + // caller wants line/column information from. + *self + } + + pub(crate) fn located_at(&self, other: Span) -> Span { + other + } + + #[cfg(span_locations)] + pub(crate) fn byte_range(&self) -> Range { + #[cfg(fuzzing)] + return 0..0; + + #[cfg(not(fuzzing))] + { + if self.is_call_site() { + 0..0 + } else { + SOURCE_MAP.with(|sm| sm.borrow_mut().fileinfo_mut(*self).b= yte_range(*self)) + } + } + } + + #[cfg(span_locations)] + pub(crate) fn start(&self) -> LineColumn { + #[cfg(fuzzing)] + return LineColumn { line: 0, column: 0 }; + + #[cfg(not(fuzzing))] + SOURCE_MAP.with(|sm| { + let sm =3D sm.borrow(); + let fi =3D sm.fileinfo(*self); + fi.offset_line_column(self.lo as usize) + }) + } + + #[cfg(span_locations)] + pub(crate) fn end(&self) -> LineColumn { + #[cfg(fuzzing)] + return LineColumn { line: 0, column: 0 }; + + #[cfg(not(fuzzing))] + SOURCE_MAP.with(|sm| { + let sm =3D sm.borrow(); + let fi =3D sm.fileinfo(*self); + fi.offset_line_column(self.hi as usize) + }) + } + + #[cfg(span_locations)] + pub(crate) fn file(&self) -> String { + #[cfg(fuzzing)] + return "".to_owned(); + + #[cfg(not(fuzzing))] + SOURCE_MAP.with(|sm| { + let sm =3D sm.borrow(); + sm.filepath(*self) + }) + } + + #[cfg(span_locations)] + pub(crate) fn local_file(&self) -> Option { + None + } + + #[cfg(not(span_locations))] + pub(crate) fn join(&self, _other: Span) -> Option { + Some(Span {}) + } + + #[cfg(span_locations)] + pub(crate) fn join(&self, other: Span) -> Option { + #[cfg(fuzzing)] + return { + let _ =3D other; + None + }; + + #[cfg(not(fuzzing))] + SOURCE_MAP.with(|sm| { + let sm =3D sm.borrow(); + // If `other` is not within the same FileInfo as us, return No= ne. + if !sm.fileinfo(*self).span_within(other) { + return None; + } + Some(Span { + lo: cmp::min(self.lo, other.lo), + hi: cmp::max(self.hi, other.hi), + }) + }) + } + + #[cfg(not(span_locations))] + pub(crate) fn source_text(&self) -> Option { + None + } + + #[cfg(span_locations)] + pub(crate) fn source_text(&self) -> Option { + #[cfg(fuzzing)] + return None; + + #[cfg(not(fuzzing))] + { + if self.is_call_site() { + None + } else { + Some(SOURCE_MAP.with(|sm| sm.borrow_mut().fileinfo_mut(*se= lf).source_text(*self))) + } + } + } + + #[cfg(not(span_locations))] + pub(crate) fn first_byte(self) -> Self { + self + } + + #[cfg(span_locations)] + pub(crate) fn first_byte(self) -> Self { + Span { + lo: self.lo, + hi: cmp::min(self.lo.saturating_add(1), self.hi), + } + } + + #[cfg(not(span_locations))] + pub(crate) fn last_byte(self) -> Self { + self + } + + #[cfg(span_locations)] + pub(crate) fn last_byte(self) -> Self { + Span { + lo: cmp::max(self.hi.saturating_sub(1), self.lo), + hi: self.hi, + } + } + + #[cfg(span_locations)] + fn is_call_site(&self) -> bool { + self.lo =3D=3D 0 && self.hi =3D=3D 0 + } +} + +impl Debug for Span { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + #[cfg(span_locations)] + return write!(f, "bytes({}..{})", self.lo, self.hi); + + #[cfg(not(span_locations))] + write!(f, "Span") + } +} + +pub(crate) fn debug_span_field_if_nontrivial(debug: &mut fmt::DebugStruct,= span: Span) { + #[cfg(span_locations)] + { + if span.is_call_site() { + return; + } + } + + if cfg!(span_locations) { + debug.field("span", &span); + } +} + +#[derive(Clone)] +pub(crate) struct Group { + delimiter: Delimiter, + stream: TokenStream, + span: Span, +} + +impl Group { + pub(crate) fn new(delimiter: Delimiter, stream: TokenStream) -> Self { + Group { + delimiter, + stream, + span: Span::call_site(), + } + } + + pub(crate) fn delimiter(&self) -> Delimiter { + self.delimiter + } + + pub(crate) fn stream(&self) -> TokenStream { + self.stream.clone() + } + + pub(crate) fn span(&self) -> Span { + self.span + } + + pub(crate) fn span_open(&self) -> Span { + self.span.first_byte() + } + + pub(crate) fn span_close(&self) -> Span { + self.span.last_byte() + } + + pub(crate) fn set_span(&mut self, span: Span) { + self.span =3D span; + } +} + +impl Display for Group { + // We attempt to match libproc_macro's formatting. + // Empty parens: () + // Nonempty parens: (...) + // Empty brackets: [] + // Nonempty brackets: [...] + // Empty braces: { } + // Nonempty braces: { ... } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let (open, close) =3D match self.delimiter { + Delimiter::Parenthesis =3D> ("(", ")"), + Delimiter::Brace =3D> ("{ ", "}"), + Delimiter::Bracket =3D> ("[", "]"), + Delimiter::None =3D> ("", ""), + }; + + f.write_str(open)?; + Display::fmt(&self.stream, f)?; + if self.delimiter =3D=3D Delimiter::Brace && !self.stream.inner.is= _empty() { + f.write_str(" ")?; + } + f.write_str(close)?; + + Ok(()) + } +} + +impl Debug for Group { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let mut debug =3D fmt.debug_struct("Group"); + debug.field("delimiter", &self.delimiter); + debug.field("stream", &self.stream); + debug_span_field_if_nontrivial(&mut debug, self.span); + debug.finish() + } +} + +#[derive(Clone)] +pub(crate) struct Ident { + sym: Box, + span: Span, + raw: bool, +} + +impl Ident { + #[track_caller] + pub(crate) fn new_checked(string: &str, span: Span) -> Self { + validate_ident(string); + Ident::new_unchecked(string, span) + } + + pub(crate) fn new_unchecked(string: &str, span: Span) -> Self { + Ident { + sym: Box::from(string), + span, + raw: false, + } + } + + #[track_caller] + pub(crate) fn new_raw_checked(string: &str, span: Span) -> Self { + validate_ident_raw(string); + Ident::new_raw_unchecked(string, span) + } + + pub(crate) fn new_raw_unchecked(string: &str, span: Span) -> Self { + Ident { + sym: Box::from(string), + span, + raw: true, + } + } + + pub(crate) fn span(&self) -> Span { + self.span + } + + pub(crate) fn set_span(&mut self, span: Span) { + self.span =3D span; + } +} + +pub(crate) fn is_ident_start(c: char) -> bool { + c =3D=3D '_' || unicode_ident::is_xid_start(c) +} + +pub(crate) fn is_ident_continue(c: char) -> bool { + unicode_ident::is_xid_continue(c) +} + +#[track_caller] +fn validate_ident(string: &str) { + if string.is_empty() { + panic!("Ident is not allowed to be empty; use Option"); + } + + if string.bytes().all(|digit| b'0' <=3D digit && digit <=3D b'9') { + panic!("Ident cannot be a number; use Literal instead"); + } + + fn ident_ok(string: &str) -> bool { + let mut chars =3D string.chars(); + let first =3D chars.next().unwrap(); + if !is_ident_start(first) { + return false; + } + for ch in chars { + if !is_ident_continue(ch) { + return false; + } + } + true + } + + if !ident_ok(string) { + panic!("{:?} is not a valid Ident", string); + } +} + +#[track_caller] +fn validate_ident_raw(string: &str) { + validate_ident(string); + + match string { + "_" | "super" | "self" | "Self" | "crate" =3D> { + panic!("`r#{}` cannot be a raw identifier", string); + } + _ =3D> {} + } +} + +impl PartialEq for Ident { + fn eq(&self, other: &Ident) -> bool { + self.sym =3D=3D other.sym && self.raw =3D=3D other.raw + } +} + +impl PartialEq for Ident +where + T: ?Sized + AsRef, +{ + fn eq(&self, other: &T) -> bool { + let other =3D other.as_ref(); + if self.raw { + other.starts_with("r#") && *self.sym =3D=3D other[2..] + } else { + *self.sym =3D=3D *other + } + } +} + +impl Display for Ident { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.raw { + f.write_str("r#")?; + } + Display::fmt(&self.sym, f) + } +} + +#[allow(clippy::missing_fields_in_debug)] +impl Debug for Ident { + // Ident(proc_macro), Ident(r#union) + #[cfg(not(span_locations))] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut debug =3D f.debug_tuple("Ident"); + debug.field(&format_args!("{}", self)); + debug.finish() + } + + // Ident { + // sym: proc_macro, + // span: bytes(128..138) + // } + #[cfg(span_locations)] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut debug =3D f.debug_struct("Ident"); + debug.field("sym", &format_args!("{}", self)); + debug_span_field_if_nontrivial(&mut debug, self.span); + debug.finish() + } +} + +#[derive(Clone)] +pub(crate) struct Literal { + pub(crate) repr: String, + span: Span, +} + +macro_rules! suffixed_numbers { + ($($name:ident =3D> $kind:ident,)*) =3D> ($( + pub(crate) fn $name(n: $kind) -> Literal { + Literal::_new(format!(concat!("{}", stringify!($kind)), n)) + } + )*) +} + +macro_rules! unsuffixed_numbers { + ($($name:ident =3D> $kind:ident,)*) =3D> ($( + pub(crate) fn $name(n: $kind) -> Literal { + Literal::_new(n.to_string()) + } + )*) +} + +impl Literal { + pub(crate) fn _new(repr: String) -> Self { + Literal { + repr, + span: Span::call_site(), + } + } + + pub(crate) fn from_str_checked(repr: &str) -> Result { + let mut cursor =3D get_cursor(repr); + #[cfg(span_locations)] + let lo =3D cursor.off; + + let negative =3D cursor.starts_with_char('-'); + if negative { + cursor =3D cursor.advance(1); + if !cursor.starts_with_fn(|ch| ch.is_ascii_digit()) { + return Err(LexError::call_site()); + } + } + + if let Ok((rest, mut literal)) =3D parse::literal(cursor) { + if rest.is_empty() { + if negative { + literal.repr.insert(0, '-'); + } + literal.span =3D Span { + #[cfg(span_locations)] + lo, + #[cfg(span_locations)] + hi: rest.off, + }; + return Ok(literal); + } + } + Err(LexError::call_site()) + } + + pub(crate) unsafe fn from_str_unchecked(repr: &str) -> Self { + Literal::_new(repr.to_owned()) + } + + suffixed_numbers! { + u8_suffixed =3D> u8, + u16_suffixed =3D> u16, + u32_suffixed =3D> u32, + u64_suffixed =3D> u64, + u128_suffixed =3D> u128, + usize_suffixed =3D> usize, + i8_suffixed =3D> i8, + i16_suffixed =3D> i16, + i32_suffixed =3D> i32, + i64_suffixed =3D> i64, + i128_suffixed =3D> i128, + isize_suffixed =3D> isize, + + f32_suffixed =3D> f32, + f64_suffixed =3D> f64, + } + + unsuffixed_numbers! { + u8_unsuffixed =3D> u8, + u16_unsuffixed =3D> u16, + u32_unsuffixed =3D> u32, + u64_unsuffixed =3D> u64, + u128_unsuffixed =3D> u128, + usize_unsuffixed =3D> usize, + i8_unsuffixed =3D> i8, + i16_unsuffixed =3D> i16, + i32_unsuffixed =3D> i32, + i64_unsuffixed =3D> i64, + i128_unsuffixed =3D> i128, + isize_unsuffixed =3D> isize, + } + + pub(crate) fn f32_unsuffixed(f: f32) -> Literal { + let mut s =3D f.to_string(); + if !s.contains('.') { + s.push_str(".0"); + } + Literal::_new(s) + } + + pub(crate) fn f64_unsuffixed(f: f64) -> Literal { + let mut s =3D f.to_string(); + if !s.contains('.') { + s.push_str(".0"); + } + Literal::_new(s) + } + + pub(crate) fn string(string: &str) -> Literal { + let mut repr =3D String::with_capacity(string.len() + 2); + repr.push('"'); + escape_utf8(string, &mut repr); + repr.push('"'); + Literal::_new(repr) + } + + pub(crate) fn character(ch: char) -> Literal { + let mut repr =3D String::new(); + repr.push('\''); + if ch =3D=3D '"' { + // escape_debug turns this into '\"' which is unnecessary. + repr.push(ch); + } else { + repr.extend(ch.escape_debug()); + } + repr.push('\''); + Literal::_new(repr) + } + + pub(crate) fn byte_character(byte: u8) -> Literal { + let mut repr =3D "b'".to_string(); + #[allow(clippy::match_overlapping_arm)] + match byte { + b'\0' =3D> repr.push_str(r"\0"), + b'\t' =3D> repr.push_str(r"\t"), + b'\n' =3D> repr.push_str(r"\n"), + b'\r' =3D> repr.push_str(r"\r"), + b'\'' =3D> repr.push_str(r"\'"), + b'\\' =3D> repr.push_str(r"\\"), + b'\x20'..=3Db'\x7E' =3D> repr.push(byte as char), + _ =3D> { + let _ =3D write!(repr, r"\x{:02X}", byte); + } + } + repr.push('\''); + Literal::_new(repr) + } + + pub(crate) fn byte_string(bytes: &[u8]) -> Literal { + let mut repr =3D "b\"".to_string(); + let mut bytes =3D bytes.iter(); + while let Some(&b) =3D bytes.next() { + #[allow(clippy::match_overlapping_arm)] + match b { + b'\0' =3D> repr.push_str(match bytes.as_slice().first() { + // circumvent clippy::octal_escapes lint + Some(b'0'..=3Db'7') =3D> r"\x00", + _ =3D> r"\0", + }), + b'\t' =3D> repr.push_str(r"\t"), + b'\n' =3D> repr.push_str(r"\n"), + b'\r' =3D> repr.push_str(r"\r"), + b'"' =3D> repr.push_str("\\\""), + b'\\' =3D> repr.push_str(r"\\"), + b'\x20'..=3Db'\x7E' =3D> repr.push(b as char), + _ =3D> { + let _ =3D write!(repr, r"\x{:02X}", b); + } + } + } + repr.push('"'); + Literal::_new(repr) + } + + pub(crate) fn c_string(string: &CStr) -> Literal { + let mut repr =3D "c\"".to_string(); + let mut bytes =3D string.to_bytes(); + while !bytes.is_empty() { + let (valid, invalid) =3D match str::from_utf8(bytes) { + Ok(all_valid) =3D> { + bytes =3D b""; + (all_valid, bytes) + } + Err(utf8_error) =3D> { + let (valid, rest) =3D bytes.split_at(utf8_error.valid_= up_to()); + let valid =3D str::from_utf8(valid).unwrap(); + let invalid =3D utf8_error + .error_len() + .map_or(rest, |error_len| &rest[..error_len]); + bytes =3D &bytes[valid.len() + invalid.len()..]; + (valid, invalid) + } + }; + escape_utf8(valid, &mut repr); + for &byte in invalid { + let _ =3D write!(repr, r"\x{:02X}", byte); + } + } + repr.push('"'); + Literal::_new(repr) + } + + pub(crate) fn span(&self) -> Span { + self.span + } + + pub(crate) fn set_span(&mut self, span: Span) { + self.span =3D span; + } + + pub(crate) fn subspan>(&self, range: R) -> Optio= n { + #[cfg(not(span_locations))] + { + let _ =3D range; + None + } + + #[cfg(span_locations)] + { + use core::ops::Bound; + + let lo =3D match range.start_bound() { + Bound::Included(start) =3D> { + let start =3D u32::try_from(*start).ok()?; + self.span.lo.checked_add(start)? + } + Bound::Excluded(start) =3D> { + let start =3D u32::try_from(*start).ok()?; + self.span.lo.checked_add(start)?.checked_add(1)? + } + Bound::Unbounded =3D> self.span.lo, + }; + let hi =3D match range.end_bound() { + Bound::Included(end) =3D> { + let end =3D u32::try_from(*end).ok()?; + self.span.lo.checked_add(end)?.checked_add(1)? + } + Bound::Excluded(end) =3D> { + let end =3D u32::try_from(*end).ok()?; + self.span.lo.checked_add(end)? + } + Bound::Unbounded =3D> self.span.hi, + }; + if lo <=3D hi && hi <=3D self.span.hi { + Some(Span { lo, hi }) + } else { + None + } + } + } +} + +impl Display for Literal { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.repr, f) + } +} + +impl Debug for Literal { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let mut debug =3D fmt.debug_struct("Literal"); + debug.field("lit", &format_args!("{}", self.repr)); + debug_span_field_if_nontrivial(&mut debug, self.span); + debug.finish() + } +} + +fn escape_utf8(string: &str, repr: &mut String) { + let mut chars =3D string.chars(); + while let Some(ch) =3D chars.next() { + if ch =3D=3D '\0' { + repr.push_str( + if chars + .as_str() + .starts_with(|next| '0' <=3D next && next <=3D '7') + { + // circumvent clippy::octal_escapes lint + r"\x00" + } else { + r"\0" + }, + ); + } else if ch =3D=3D '\'' { + // escape_debug turns this into "\'" which is unnecessary. + repr.push(ch); + } else { + repr.extend(ch.escape_debug()); + } + } +} + +#[cfg(feature =3D "proc-macro")] +pub(crate) trait FromStr2: FromStr { + #[cfg(wrap_proc_macro)] + fn valid(src: &str) -> bool; + + #[cfg(wrap_proc_macro)] + fn from_str_checked(src: &str) -> Result { + // Validate using fallback parser, because rustc is incapable of + // returning a recoverable Err for certain invalid token streams, = and + // will instead permanently poison the compilation. + if !Self::valid(src) { + return Err(imp::LexError::CompilerPanic); + } + + // Catch panic to work around https://github.com/rust-lang/rust/is= sues/58736. + match panic::catch_unwind(|| Self::from_str(src)) { + Ok(Ok(ok)) =3D> Ok(ok), + Ok(Err(lex)) =3D> Err(imp::LexError::Compiler(lex)), + Err(_panic) =3D> Err(imp::LexError::CompilerPanic), + } + } + + fn from_str_unchecked(src: &str) -> Self { + Self::from_str(src).unwrap() + } +} + +#[cfg(feature =3D "proc-macro")] +impl FromStr2 for proc_macro::TokenStream { + #[cfg(wrap_proc_macro)] + fn valid(src: &str) -> bool { + TokenStream::from_str_checked(src).is_ok() + } +} + +#[cfg(feature =3D "proc-macro")] +impl FromStr2 for proc_macro::Literal { + #[cfg(wrap_proc_macro)] + fn valid(src: &str) -> bool { + Literal::from_str_checked(src).is_ok() + } +} diff --git a/rust/proc-macro2/lib.rs b/rust/proc-macro2/lib.rs new file mode 100644 index 000000000000..2984af335adc --- /dev/null +++ b/rust/proc-macro2/lib.rs @@ -0,0 +1,1349 @@ +//! [![github]](https://github.com/dtolnay/proc-macro2) [![crates-io]= ](https://crates.io/crates/proc-macro2) [![docs-rs]](crate) +//! +//! [github]: https://img.shields.io/badge/github-8da0cb?style=3Dfor-the-b= adge&labelColor=3D555555&logo=3Dgithub +//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=3Dfor= -the-badge&labelColor=3D555555&logo=3Drust +//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=3Dfor-the= -badge&labelColor=3D555555&logo=3Ddocs.rs +//! +//!
+//! +//! A wrapper around the procedural macro API of the compiler's [`proc_mac= ro`] +//! crate. This library serves two purposes: +//! +//! - **Bring proc-macro-like functionality to other contexts like build.r= s and +//! main.rs.** Types from `proc_macro` are entirely specific to procedur= al +//! macros and cannot ever exist in code outside of a procedural macro. +//! Meanwhile `proc_macro2` types may exist anywhere including non-macro= code. +//! By developing foundational libraries like [syn] and [quote] against +//! `proc_macro2` rather than `proc_macro`, the procedural macro ecosyst= em +//! becomes easily applicable to many other use cases and we avoid +//! reimplementing non-macro equivalents of those libraries. +//! +//! - **Make procedural macros unit testable.** As a consequence of being +//! specific to procedural macros, nothing that uses `proc_macro` can be +//! executed from a unit test. In order for helper libraries or componen= ts of +//! a macro to be testable in isolation, they must be implemented using +//! `proc_macro2`. +//! +//! [syn]: https://github.com/dtolnay/syn +//! [quote]: https://github.com/dtolnay/quote +//! +//! # Usage +//! +//! The skeleton of a typical procedural macro typically looks like this: +//! +//! ``` +//! extern crate proc_macro; +//! +//! # const IGNORE: &str =3D stringify! { +//! #[proc_macro_derive(MyDerive)] +//! # }; +//! # #[cfg(wrap_proc_macro)] +//! pub fn my_derive(input: proc_macro::TokenStream) -> proc_macro::TokenS= tream { +//! let input =3D proc_macro2::TokenStream::from(input); +//! +//! let output: proc_macro2::TokenStream =3D { +//! /* transform input */ +//! # input +//! }; +//! +//! proc_macro::TokenStream::from(output) +//! } +//! ``` +//! +//! If parsing with [Syn], you'll use [`parse_macro_input!`] instead to +//! propagate parse errors correctly back to the compiler when parsing fai= ls. +//! +//! [`parse_macro_input!`]: https://docs.rs/syn/2.0/syn/macro.parse_macro_= input.html +//! +//! # Unstable features +//! +//! The default feature set of proc-macro2 tracks the most recent stable +//! compiler API. Functionality in `proc_macro` that is not yet stable is = not +//! exposed by proc-macro2 by default. +//! +//! To opt into the additional APIs available in the most recent nightly +//! compiler, the `procmacro2_semver_exempt` config flag must be passed to +//! rustc. We will polyfill those nightly-only APIs back to Rust 1.56.0. As +//! these are unstable APIs that track the nightly compiler, minor version= s of +//! proc-macro2 may make breaking changes to them at any time. +//! +//! ```sh +//! RUSTFLAGS=3D'--cfg procmacro2_semver_exempt' cargo build +//! ``` +//! +//! Note that this must not only be done for your crate, but for any crate= that +//! depends on your crate. This infectious nature is intentional, as it se= rves +//! as a reminder that you are outside of the normal semver guarantees. +//! +//! Semver exempt methods are marked as such in the proc-macro2 documentat= ion. +//! +//! # Thread-Safety +//! +//! Most types in this crate are `!Sync` because the underlying compiler +//! types make use of thread-local memory, meaning they cannot be accessed= from +//! a different thread. + +// Proc-macro2 types in rustdoc of other crates get linked to here. +#![doc(html_root_url =3D "https://docs.rs/proc-macro2/1.0.101")] +#![cfg_attr(any(proc_macro_span, super_unstable), feature(proc_macro_span)= )] +#![cfg_attr(super_unstable, feature(proc_macro_def_site))] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![deny(unsafe_op_in_unsafe_fn)] +#![allow( + clippy::cast_lossless, + clippy::cast_possible_truncation, + clippy::checked_conversions, + clippy::doc_markdown, + clippy::elidable_lifetime_names, + clippy::incompatible_msrv, + clippy::items_after_statements, + clippy::iter_without_into_iter, + clippy::let_underscore_untyped, + clippy::manual_assert, + clippy::manual_range_contains, + clippy::missing_panics_doc, + clippy::missing_safety_doc, + clippy::must_use_candidate, + clippy::needless_doctest_main, + clippy::needless_lifetimes, + clippy::new_without_default, + clippy::return_self_not_must_use, + clippy::shadow_unrelated, + clippy::trivially_copy_pass_by_ref, + clippy::unnecessary_wraps, + clippy::unused_self, + clippy::used_underscore_binding, + clippy::vec_init_then_push +)] +#![allow(unknown_lints, mismatched_lifetime_syntaxes)] + +#[cfg(all(procmacro2_semver_exempt, wrap_proc_macro, not(super_unstable)))] +compile_error! {"\ + Something is not right. If you've tried to turn on \ + procmacro2_semver_exempt, you need to ensure that it \ + is turned on for the compilation of the proc-macro2 \ + build script as well. +"} + +#[cfg(all( + procmacro2_nightly_testing, + feature =3D "proc-macro", + not(proc_macro_span) +))] +compile_error! {"\ + Build script probe failed to compile. +"} + +extern crate alloc; + +#[cfg(feature =3D "proc-macro")] +extern crate proc_macro; + +mod marker; +mod parse; +mod probe; +mod rcvec; + +#[cfg(wrap_proc_macro)] +mod detection; + +// Public for proc_macro2::fallback::force() and unforce(), but those are = quite +// a niche use case so we omit it from rustdoc. +#[doc(hidden)] +pub mod fallback; + +pub mod extra; + +#[cfg(not(wrap_proc_macro))] +use crate::fallback as imp; +#[path =3D "wrapper.rs"] +#[cfg(wrap_proc_macro)] +mod imp; + +#[cfg(span_locations)] +mod location; + +use crate::extra::DelimSpan; +use crate::marker::{ProcMacroAutoTraits, MARKER}; +use core::cmp::Ordering; +use core::fmt::{self, Debug, Display}; +use core::hash::{Hash, Hasher}; +#[cfg(span_locations)] +use core::ops::Range; +use core::ops::RangeBounds; +use core::str::FromStr; +use std::error::Error; +use std::ffi::CStr; +#[cfg(span_locations)] +use std::path::PathBuf; + +#[cfg(span_locations)] +#[cfg_attr(docsrs, doc(cfg(feature =3D "span-locations")))] +pub use crate::location::LineColumn; + +/// An abstract stream of tokens, or more concretely a sequence of token t= rees. +/// +/// This type provides interfaces for iterating over token trees and for +/// collecting token trees into one stream. +/// +/// Token stream is both the input and output of `#[proc_macro]`, +/// `#[proc_macro_attribute]` and `#[proc_macro_derive]` definitions. +#[derive(Clone)] +pub struct TokenStream { + inner: imp::TokenStream, + _marker: ProcMacroAutoTraits, +} + +/// Error returned from `TokenStream::from_str`. +pub struct LexError { + inner: imp::LexError, + _marker: ProcMacroAutoTraits, +} + +impl TokenStream { + fn _new(inner: imp::TokenStream) -> Self { + TokenStream { + inner, + _marker: MARKER, + } + } + + fn _new_fallback(inner: fallback::TokenStream) -> Self { + TokenStream { + inner: imp::TokenStream::from(inner), + _marker: MARKER, + } + } + + /// Returns an empty `TokenStream` containing no token trees. + pub fn new() -> Self { + TokenStream::_new(imp::TokenStream::new()) + } + + /// Checks if this `TokenStream` is empty. + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } +} + +/// `TokenStream::default()` returns an empty stream, +/// i.e. this is equivalent with `TokenStream::new()`. +impl Default for TokenStream { + fn default() -> Self { + TokenStream::new() + } +} + +/// Attempts to break the string into tokens and parse those tokens into a= token +/// stream. +/// +/// May fail for a number of reasons, for example, if the string contains +/// unbalanced delimiters or characters not existing in the language. +/// +/// NOTE: Some errors may cause panics instead of returning `LexError`. We +/// reserve the right to change these errors into `LexError`s later. +impl FromStr for TokenStream { + type Err =3D LexError; + + fn from_str(src: &str) -> Result { + match imp::TokenStream::from_str_checked(src) { + Ok(tokens) =3D> Ok(TokenStream::_new(tokens)), + Err(lex) =3D> Err(LexError { + inner: lex, + _marker: MARKER, + }), + } + } +} + +#[cfg(feature =3D "proc-macro")] +#[cfg_attr(docsrs, doc(cfg(feature =3D "proc-macro")))] +impl From for TokenStream { + fn from(inner: proc_macro::TokenStream) -> Self { + TokenStream::_new(imp::TokenStream::from(inner)) + } +} + +#[cfg(feature =3D "proc-macro")] +#[cfg_attr(docsrs, doc(cfg(feature =3D "proc-macro")))] +impl From for proc_macro::TokenStream { + fn from(inner: TokenStream) -> Self { + proc_macro::TokenStream::from(inner.inner) + } +} + +impl From for TokenStream { + fn from(token: TokenTree) -> Self { + TokenStream::_new(imp::TokenStream::from(token)) + } +} + +impl Extend for TokenStream { + fn extend>(&mut self, streams: I) { + self.inner.extend(streams); + } +} + +impl Extend for TokenStream { + fn extend>(&mut self, streams: I= ) { + self.inner + .extend(streams.into_iter().map(|stream| stream.inner)); + } +} + +/// Collects a number of token trees into a single stream. +impl FromIterator for TokenStream { + fn from_iter>(streams: I) -> Self { + TokenStream::_new(streams.into_iter().collect()) + } +} +impl FromIterator for TokenStream { + fn from_iter>(streams: I) -> Sel= f { + TokenStream::_new(streams.into_iter().map(|i| i.inner).collect()) + } +} + +/// Prints the token stream as a string that is supposed to be losslessly +/// convertible back into the same token stream (modulo spans), except for +/// possibly `TokenTree::Group`s with `Delimiter::None` delimiters and neg= ative +/// numeric literals. +impl Display for TokenStream { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.inner, f) + } +} + +/// Prints token in a form convenient for debugging. +impl Debug for TokenStream { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(&self.inner, f) + } +} + +impl LexError { + pub fn span(&self) -> Span { + Span::_new(self.inner.span()) + } +} + +impl Debug for LexError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(&self.inner, f) + } +} + +impl Display for LexError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.inner, f) + } +} + +impl Error for LexError {} + +/// A region of source code, along with macro expansion information. +#[derive(Copy, Clone)] +pub struct Span { + inner: imp::Span, + _marker: ProcMacroAutoTraits, +} + +impl Span { + fn _new(inner: imp::Span) -> Self { + Span { + inner, + _marker: MARKER, + } + } + + fn _new_fallback(inner: fallback::Span) -> Self { + Span { + inner: imp::Span::from(inner), + _marker: MARKER, + } + } + + /// The span of the invocation of the current procedural macro. + /// + /// Identifiers created with this span will be resolved as if they were + /// written directly at the macro call location (call-site hygiene) and + /// other code at the macro call site will be able to refer to them as= well. + pub fn call_site() -> Self { + Span::_new(imp::Span::call_site()) + } + + /// The span located at the invocation of the procedural macro, but wi= th + /// local variables, labels, and `$crate` resolved at the definition s= ite + /// of the macro. This is the same hygiene behavior as `macro_rules`. + pub fn mixed_site() -> Self { + Span::_new(imp::Span::mixed_site()) + } + + /// A span that resolves at the macro definition site. + /// + /// This method is semver exempt and not exposed by default. + #[cfg(procmacro2_semver_exempt)] + #[cfg_attr(docsrs, doc(cfg(procmacro2_semver_exempt)))] + pub fn def_site() -> Self { + Span::_new(imp::Span::def_site()) + } + + /// Creates a new span with the same line/column information as `self`= but + /// that resolves symbols as though it were at `other`. + pub fn resolved_at(&self, other: Span) -> Span { + Span::_new(self.inner.resolved_at(other.inner)) + } + + /// Creates a new span with the same name resolution behavior as `self= ` but + /// with the line/column information of `other`. + pub fn located_at(&self, other: Span) -> Span { + Span::_new(self.inner.located_at(other.inner)) + } + + /// Convert `proc_macro2::Span` to `proc_macro::Span`. + /// + /// This method is available when building with a nightly compiler, or= when + /// building with rustc 1.29+ *without* semver exempt features. + /// + /// # Panics + /// + /// Panics if called from outside of a procedural macro. Unlike + /// `proc_macro2::Span`, the `proc_macro::Span` type can only exist wi= thin + /// the context of a procedural macro invocation. + #[cfg(wrap_proc_macro)] + pub fn unwrap(self) -> proc_macro::Span { + self.inner.unwrap() + } + + // Soft deprecated. Please use Span::unwrap. + #[cfg(wrap_proc_macro)] + #[doc(hidden)] + pub fn unstable(self) -> proc_macro::Span { + self.unwrap() + } + + /// Returns the span's byte position range in the source file. + /// + /// This method requires the `"span-locations"` feature to be enabled. + /// + /// When executing in a procedural macro context, the returned range i= s only + /// accurate if compiled with a nightly toolchain. The stable toolchai= n does + /// not have this information available. When executing outside of a + /// procedural macro, such as main.rs or build.rs, the byte range is a= lways + /// accurate regardless of toolchain. + #[cfg(span_locations)] + #[cfg_attr(docsrs, doc(cfg(feature =3D "span-locations")))] + pub fn byte_range(&self) -> Range { + self.inner.byte_range() + } + + /// Get the starting line/column in the source file for this span. + /// + /// This method requires the `"span-locations"` feature to be enabled. + /// + /// When executing in a procedural macro context, the returned line/co= lumn + /// are only meaningful if compiled with a nightly toolchain. The stab= le + /// toolchain does not have this information available. When executing + /// outside of a procedural macro, such as main.rs or build.rs, the + /// line/column are always meaningful regardless of toolchain. + #[cfg(span_locations)] + #[cfg_attr(docsrs, doc(cfg(feature =3D "span-locations")))] + pub fn start(&self) -> LineColumn { + self.inner.start() + } + + /// Get the ending line/column in the source file for this span. + /// + /// This method requires the `"span-locations"` feature to be enabled. + /// + /// When executing in a procedural macro context, the returned line/co= lumn + /// are only meaningful if compiled with a nightly toolchain. The stab= le + /// toolchain does not have this information available. When executing + /// outside of a procedural macro, such as main.rs or build.rs, the + /// line/column are always meaningful regardless of toolchain. + #[cfg(span_locations)] + #[cfg_attr(docsrs, doc(cfg(feature =3D "span-locations")))] + pub fn end(&self) -> LineColumn { + self.inner.end() + } + + /// The path to the source file in which this span occurs, for display + /// purposes. + /// + /// This might not correspond to a valid file system path. It might be + /// remapped, or might be an artificial path such as `""`. + #[cfg(span_locations)] + #[cfg_attr(docsrs, doc(cfg(feature =3D "span-locations")))] + pub fn file(&self) -> String { + self.inner.file() + } + + /// The path to the source file in which this span occurs on disk. + /// + /// This is the actual path on disk. It is unaffected by path remappin= g. + /// + /// This path should not be embedded in the output of the macro; prefer + /// `file()` instead. + #[cfg(span_locations)] + #[cfg_attr(docsrs, doc(cfg(feature =3D "span-locations")))] + pub fn local_file(&self) -> Option { + self.inner.local_file() + } + + /// Create a new span encompassing `self` and `other`. + /// + /// Returns `None` if `self` and `other` are from different files. + /// + /// Warning: the underlying [`proc_macro::Span::join`] method is + /// nightly-only. When called from within a procedural macro not using= a + /// nightly compiler, this method will always return `None`. + pub fn join(&self, other: Span) -> Option { + self.inner.join(other.inner).map(Span::_new) + } + + /// Compares two spans to see if they're equal. + /// + /// This method is semver exempt and not exposed by default. + #[cfg(procmacro2_semver_exempt)] + #[cfg_attr(docsrs, doc(cfg(procmacro2_semver_exempt)))] + pub fn eq(&self, other: &Span) -> bool { + self.inner.eq(&other.inner) + } + + /// Returns the source text behind a span. This preserves the original + /// source code, including spaces and comments. It only returns a resu= lt if + /// the span corresponds to real source code. + /// + /// Note: The observable result of a macro should only rely on the tok= ens + /// and not on this source text. The result of this function is a best + /// effort to be used for diagnostics only. + pub fn source_text(&self) -> Option { + self.inner.source_text() + } +} + +/// Prints a span in a form convenient for debugging. +impl Debug for Span { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(&self.inner, f) + } +} + +/// A single token or a delimited sequence of token trees (e.g. `[1, (), .= .]`). +#[derive(Clone)] +pub enum TokenTree { + /// A token stream surrounded by bracket delimiters. + Group(Group), + /// An identifier. + Ident(Ident), + /// A single punctuation character (`+`, `,`, `$`, etc.). + Punct(Punct), + /// A literal character (`'a'`), string (`"hello"`), number (`2.3`), e= tc. + Literal(Literal), +} + +impl TokenTree { + /// Returns the span of this tree, delegating to the `span` method of + /// the contained token or a delimited stream. + pub fn span(&self) -> Span { + match self { + TokenTree::Group(t) =3D> t.span(), + TokenTree::Ident(t) =3D> t.span(), + TokenTree::Punct(t) =3D> t.span(), + TokenTree::Literal(t) =3D> t.span(), + } + } + + /// Configures the span for *only this token*. + /// + /// Note that if this token is a `Group` then this method will not con= figure + /// the span of each of the internal tokens, this will simply delegate= to + /// the `set_span` method of each variant. + pub fn set_span(&mut self, span: Span) { + match self { + TokenTree::Group(t) =3D> t.set_span(span), + TokenTree::Ident(t) =3D> t.set_span(span), + TokenTree::Punct(t) =3D> t.set_span(span), + TokenTree::Literal(t) =3D> t.set_span(span), + } + } +} + +impl From for TokenTree { + fn from(g: Group) -> Self { + TokenTree::Group(g) + } +} + +impl From for TokenTree { + fn from(g: Ident) -> Self { + TokenTree::Ident(g) + } +} + +impl From for TokenTree { + fn from(g: Punct) -> Self { + TokenTree::Punct(g) + } +} + +impl From for TokenTree { + fn from(g: Literal) -> Self { + TokenTree::Literal(g) + } +} + +/// Prints the token tree as a string that is supposed to be losslessly +/// convertible back into the same token tree (modulo spans), except for +/// possibly `TokenTree::Group`s with `Delimiter::None` delimiters and neg= ative +/// numeric literals. +impl Display for TokenTree { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + TokenTree::Group(t) =3D> Display::fmt(t, f), + TokenTree::Ident(t) =3D> Display::fmt(t, f), + TokenTree::Punct(t) =3D> Display::fmt(t, f), + TokenTree::Literal(t) =3D> Display::fmt(t, f), + } + } +} + +/// Prints token tree in a form convenient for debugging. +impl Debug for TokenTree { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // Each of these has the name in the struct type in the derived de= bug, + // so don't bother with an extra layer of indirection + match self { + TokenTree::Group(t) =3D> Debug::fmt(t, f), + TokenTree::Ident(t) =3D> { + let mut debug =3D f.debug_struct("Ident"); + debug.field("sym", &format_args!("{}", t)); + imp::debug_span_field_if_nontrivial(&mut debug, t.span().i= nner); + debug.finish() + } + TokenTree::Punct(t) =3D> Debug::fmt(t, f), + TokenTree::Literal(t) =3D> Debug::fmt(t, f), + } + } +} + +/// A delimited token stream. +/// +/// A `Group` internally contains a `TokenStream` which is surrounded by +/// `Delimiter`s. +#[derive(Clone)] +pub struct Group { + inner: imp::Group, +} + +/// Describes how a sequence of token trees is delimited. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Delimiter { + /// `( ... )` + Parenthesis, + /// `{ ... }` + Brace, + /// `[ ... ]` + Bracket, + /// `=E2=88=85 ... =E2=88=85` + /// + /// An invisible delimiter, that may, for example, appear around tokens + /// coming from a "macro variable" `$var`. It is important to preserve + /// operator priorities in cases like `$var * 3` where `$var` is `1 + = 2`. + /// Invisible delimiters may not survive roundtrip of a token stream t= hrough + /// a string. + /// + ///
+ /// + /// Note: rustc currently can ignore the grouping of tokens delimited = by `None` in the output + /// of a proc_macro. Only `None`-delimited groups created by a macro_r= ules macro in the input + /// of a proc_macro macro are preserved, and only in very specific cir= cumstances. + /// Any `None`-delimited groups (re)created by a proc_macro will there= fore not preserve + /// operator priorities as indicated above. The other `Delimiter` vari= ants should be used + /// instead in this context. This is a rustc bug. For details, see + /// [rust-lang/rust#67062](https://github.com/rust-lang/rust/issues/67= 062). + /// + ///
+ None, +} + +impl Group { + fn _new(inner: imp::Group) -> Self { + Group { inner } + } + + fn _new_fallback(inner: fallback::Group) -> Self { + Group { + inner: imp::Group::from(inner), + } + } + + /// Creates a new `Group` with the given delimiter and token stream. + /// + /// This constructor will set the span for this group to + /// `Span::call_site()`. To change the span you can use the `set_span` + /// method below. + pub fn new(delimiter: Delimiter, stream: TokenStream) -> Self { + Group { + inner: imp::Group::new(delimiter, stream.inner), + } + } + + /// Returns the punctuation used as the delimiter for this group: a se= t of + /// parentheses, square brackets, or curly braces. + pub fn delimiter(&self) -> Delimiter { + self.inner.delimiter() + } + + /// Returns the `TokenStream` of tokens that are delimited in this `Gr= oup`. + /// + /// Note that the returned token stream does not include the delimiter + /// returned above. + pub fn stream(&self) -> TokenStream { + TokenStream::_new(self.inner.stream()) + } + + /// Returns the span for the delimiters of this token stream, spanning= the + /// entire `Group`. + /// + /// ```text + /// pub fn span(&self) -> Span { + /// ^^^^^^^ + /// ``` + pub fn span(&self) -> Span { + Span::_new(self.inner.span()) + } + + /// Returns the span pointing to the opening delimiter of this group. + /// + /// ```text + /// pub fn span_open(&self) -> Span { + /// ^ + /// ``` + pub fn span_open(&self) -> Span { + Span::_new(self.inner.span_open()) + } + + /// Returns the span pointing to the closing delimiter of this group. + /// + /// ```text + /// pub fn span_close(&self) -> Span { + /// ^ + /// ``` + pub fn span_close(&self) -> Span { + Span::_new(self.inner.span_close()) + } + + /// Returns an object that holds this group's `span_open()` and + /// `span_close()` together (in a more compact representation than hol= ding + /// those 2 spans individually). + pub fn delim_span(&self) -> DelimSpan { + DelimSpan::new(&self.inner) + } + + /// Configures the span for this `Group`'s delimiters, but not its int= ernal + /// tokens. + /// + /// This method will **not** set the span of all the internal tokens s= panned + /// by this group, but rather it will only set the span of the delimit= er + /// tokens at the level of the `Group`. + pub fn set_span(&mut self, span: Span) { + self.inner.set_span(span.inner); + } +} + +/// Prints the group as a string that should be losslessly convertible back +/// into the same group (modulo spans), except for possibly `TokenTree::Gr= oup`s +/// with `Delimiter::None` delimiters. +impl Display for Group { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.inner, formatter) + } +} + +impl Debug for Group { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(&self.inner, formatter) + } +} + +/// A `Punct` is a single punctuation character like `+`, `-` or `#`. +/// +/// Multicharacter operators like `+=3D` are represented as two instances = of +/// `Punct` with different forms of `Spacing` returned. +#[derive(Clone)] +pub struct Punct { + ch: char, + spacing: Spacing, + span: Span, +} + +/// Whether a `Punct` is followed immediately by another `Punct` or follow= ed by +/// another token or whitespace. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Spacing { + /// E.g. `+` is `Alone` in `+ =3D`, `+ident` or `+()`. + Alone, + /// E.g. `+` is `Joint` in `+=3D` or `'` is `Joint` in `'#`. + /// + /// Additionally, single quote `'` can join with identifiers to form + /// lifetimes `'ident`. + Joint, +} + +impl Punct { + /// Creates a new `Punct` from the given character and spacing. + /// + /// The `ch` argument must be a valid punctuation character permitted = by the + /// language, otherwise the function will panic. + /// + /// The returned `Punct` will have the default span of `Span::call_sit= e()` + /// which can be further configured with the `set_span` method below. + pub fn new(ch: char, spacing: Spacing) -> Self { + if let '!' | '#' | '$' | '%' | '&' | '\'' | '*' | '+' | ',' | '-' = | '.' | '/' | ':' | ';' + | '<' | '=3D' | '>' | '?' | '@' | '^' | '|' | '~' =3D ch + { + Punct { + ch, + spacing, + span: Span::call_site(), + } + } else { + panic!("unsupported proc macro punctuation character {:?}", ch= ); + } + } + + /// Returns the value of this punctuation character as `char`. + pub fn as_char(&self) -> char { + self.ch + } + + /// Returns the spacing of this punctuation character, indicating whet= her + /// it's immediately followed by another `Punct` in the token stream, = so + /// they can potentially be combined into a multicharacter operator + /// (`Joint`), or it's followed by some other token or whitespace (`Al= one`) + /// so the operator has certainly ended. + pub fn spacing(&self) -> Spacing { + self.spacing + } + + /// Returns the span for this punctuation character. + pub fn span(&self) -> Span { + self.span + } + + /// Configure the span for this punctuation character. + pub fn set_span(&mut self, span: Span) { + self.span =3D span; + } +} + +/// Prints the punctuation character as a string that should be losslessly +/// convertible back into the same character. +impl Display for Punct { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.ch, f) + } +} + +impl Debug for Punct { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let mut debug =3D fmt.debug_struct("Punct"); + debug.field("char", &self.ch); + debug.field("spacing", &self.spacing); + imp::debug_span_field_if_nontrivial(&mut debug, self.span.inner); + debug.finish() + } +} + +/// A word of Rust code, which may be a keyword or legal variable name. +/// +/// An identifier consists of at least one Unicode code point, the first of +/// which has the XID_Start property and the rest of which have the XID_Co= ntinue +/// property. +/// +/// - The empty string is not an identifier. Use `Option`. +/// - A lifetime is not an identifier. Use `syn::Lifetime` instead. +/// +/// An identifier constructed with `Ident::new` is permitted to be a Rust +/// keyword, though parsing one through its [`Parse`] implementation rejec= ts +/// Rust keywords. Use `input.call(Ident::parse_any)` when parsing to matc= h the +/// behaviour of `Ident::new`. +/// +/// [`Parse`]: https://docs.rs/syn/2.0/syn/parse/trait.Parse.html +/// +/// # Examples +/// +/// A new ident can be created from a string using the `Ident::new` functi= on. +/// A span must be provided explicitly which governs the name resolution +/// behavior of the resulting identifier. +/// +/// ``` +/// use proc_macro2::{Ident, Span}; +/// +/// fn main() { +/// let call_ident =3D Ident::new("calligraphy", Span::call_site()); +/// +/// println!("{}", call_ident); +/// } +/// ``` +/// +/// An ident can be interpolated into a token stream using the `quote!` ma= cro. +/// +/// ``` +/// use proc_macro2::{Ident, Span}; +/// use quote::quote; +/// +/// fn main() { +/// let ident =3D Ident::new("demo", Span::call_site()); +/// +/// // Create a variable binding whose name is this ident. +/// let expanded =3D quote! { let #ident =3D 10; }; +/// +/// // Create a variable binding with a slightly different name. +/// let temp_ident =3D Ident::new(&format!("new_{}", ident), Span::cal= l_site()); +/// let expanded =3D quote! { let #temp_ident =3D 10; }; +/// } +/// ``` +/// +/// A string representation of the ident is available through the `to_stri= ng()` +/// method. +/// +/// ``` +/// # use proc_macro2::{Ident, Span}; +/// # +/// # let ident =3D Ident::new("another_identifier", Span::call_site()); +/// # +/// // Examine the ident as a string. +/// let ident_string =3D ident.to_string(); +/// if ident_string.len() > 60 { +/// println!("Very long identifier: {}", ident_string) +/// } +/// ``` +#[derive(Clone)] +pub struct Ident { + inner: imp::Ident, + _marker: ProcMacroAutoTraits, +} + +impl Ident { + fn _new(inner: imp::Ident) -> Self { + Ident { + inner, + _marker: MARKER, + } + } + + fn _new_fallback(inner: fallback::Ident) -> Self { + Ident { + inner: imp::Ident::from(inner), + _marker: MARKER, + } + } + + /// Creates a new `Ident` with the given `string` as well as the speci= fied + /// `span`. + /// + /// The `string` argument must be a valid identifier permitted by the + /// language, otherwise the function will panic. + /// + /// Note that `span`, currently in rustc, configures the hygiene infor= mation + /// for this identifier. + /// + /// As of this time `Span::call_site()` explicitly opts-in to "call-si= te" + /// hygiene meaning that identifiers created with this span will be re= solved + /// as if they were written directly at the location of the macro call= , and + /// other code at the macro call site will be able to refer to them as= well. + /// + /// Later spans like `Span::def_site()` will allow to opt-in to + /// "definition-site" hygiene meaning that identifiers created with th= is + /// span will be resolved at the location of the macro definition and = other + /// code at the macro call site will not be able to refer to them. + /// + /// Due to the current importance of hygiene this constructor, unlike = other + /// tokens, requires a `Span` to be specified at construction. + /// + /// # Panics + /// + /// Panics if the input string is neither a keyword nor a legal variab= le + /// name. If you are not sure whether the string contains an identifie= r and + /// need to handle an error case, use + /// syn::parse_str::<Ident> + /// rather than `Ident::new`. + #[track_caller] + pub fn new(string: &str, span: Span) -> Self { + Ident::_new(imp::Ident::new_checked(string, span.inner)) + } + + /// Same as `Ident::new`, but creates a raw identifier (`r#ident`). The + /// `string` argument must be a valid identifier permitted by the lang= uage + /// (including keywords, e.g. `fn`). Keywords which are usable in path + /// segments (e.g. `self`, `super`) are not supported, and will cause a + /// panic. + #[track_caller] + pub fn new_raw(string: &str, span: Span) -> Self { + Ident::_new(imp::Ident::new_raw_checked(string, span.inner)) + } + + /// Returns the span of this `Ident`. + pub fn span(&self) -> Span { + Span::_new(self.inner.span()) + } + + /// Configures the span of this `Ident`, possibly changing its hygiene + /// context. + pub fn set_span(&mut self, span: Span) { + self.inner.set_span(span.inner); + } +} + +impl PartialEq for Ident { + fn eq(&self, other: &Ident) -> bool { + self.inner =3D=3D other.inner + } +} + +impl PartialEq for Ident +where + T: ?Sized + AsRef, +{ + fn eq(&self, other: &T) -> bool { + self.inner =3D=3D other + } +} + +impl Eq for Ident {} + +impl PartialOrd for Ident { + fn partial_cmp(&self, other: &Ident) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Ident { + fn cmp(&self, other: &Ident) -> Ordering { + self.to_string().cmp(&other.to_string()) + } +} + +impl Hash for Ident { + fn hash(&self, hasher: &mut H) { + self.to_string().hash(hasher); + } +} + +/// Prints the identifier as a string that should be losslessly convertibl= e back +/// into the same identifier. +impl Display for Ident { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.inner, f) + } +} + +impl Debug for Ident { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(&self.inner, f) + } +} + +/// A literal string (`"hello"`), byte string (`b"hello"`), character (`'a= '`), +/// byte character (`b'a'`), an integer or floating point number with or w= ithout +/// a suffix (`1`, `1u8`, `2.3`, `2.3f32`). +/// +/// Boolean literals like `true` and `false` do not belong here, they are +/// `Ident`s. +#[derive(Clone)] +pub struct Literal { + inner: imp::Literal, + _marker: ProcMacroAutoTraits, +} + +macro_rules! suffixed_int_literals { + ($($name:ident =3D> $kind:ident,)*) =3D> ($( + /// Creates a new suffixed integer literal with the specified valu= e. + /// + /// This function will create an integer like `1u32` where the int= eger + /// value specified is the first part of the token and the integra= l is + /// also suffixed at the end. Literals created from negative numbe= rs may + /// not survive roundtrips through `TokenStream` or strings and ma= y be + /// broken into two tokens (`-` and positive literal). + /// + /// Literals created through this method have the `Span::call_site= ()` + /// span by default, which can be configured with the `set_span` m= ethod + /// below. + pub fn $name(n: $kind) -> Literal { + Literal::_new(imp::Literal::$name(n)) + } + )*) +} + +macro_rules! unsuffixed_int_literals { + ($($name:ident =3D> $kind:ident,)*) =3D> ($( + /// Creates a new unsuffixed integer literal with the specified va= lue. + /// + /// This function will create an integer like `1` where the integer + /// value specified is the first part of the token. No suffix is + /// specified on this token, meaning that invocations like + /// `Literal::i8_unsuffixed(1)` are equivalent to + /// `Literal::u32_unsuffixed(1)`. Literals created from negative n= umbers + /// may not survive roundtrips through `TokenStream` or strings an= d may + /// be broken into two tokens (`-` and positive literal). + /// + /// Literals created through this method have the `Span::call_site= ()` + /// span by default, which can be configured with the `set_span` m= ethod + /// below. + pub fn $name(n: $kind) -> Literal { + Literal::_new(imp::Literal::$name(n)) + } + )*) +} + +impl Literal { + fn _new(inner: imp::Literal) -> Self { + Literal { + inner, + _marker: MARKER, + } + } + + fn _new_fallback(inner: fallback::Literal) -> Self { + Literal { + inner: imp::Literal::from(inner), + _marker: MARKER, + } + } + + suffixed_int_literals! { + u8_suffixed =3D> u8, + u16_suffixed =3D> u16, + u32_suffixed =3D> u32, + u64_suffixed =3D> u64, + u128_suffixed =3D> u128, + usize_suffixed =3D> usize, + i8_suffixed =3D> i8, + i16_suffixed =3D> i16, + i32_suffixed =3D> i32, + i64_suffixed =3D> i64, + i128_suffixed =3D> i128, + isize_suffixed =3D> isize, + } + + unsuffixed_int_literals! { + u8_unsuffixed =3D> u8, + u16_unsuffixed =3D> u16, + u32_unsuffixed =3D> u32, + u64_unsuffixed =3D> u64, + u128_unsuffixed =3D> u128, + usize_unsuffixed =3D> usize, + i8_unsuffixed =3D> i8, + i16_unsuffixed =3D> i16, + i32_unsuffixed =3D> i32, + i64_unsuffixed =3D> i64, + i128_unsuffixed =3D> i128, + isize_unsuffixed =3D> isize, + } + + /// Creates a new unsuffixed floating-point literal. + /// + /// This constructor is similar to those like `Literal::i8_unsuffixed`= where + /// the float's value is emitted directly into the token but no suffix= is + /// used, so it may be inferred to be a `f64` later in the compiler. + /// Literals created from negative numbers may not survive round-trips + /// through `TokenStream` or strings and may be broken into two tokens= (`-` + /// and positive literal). + /// + /// # Panics + /// + /// This function requires that the specified float is finite, for exa= mple + /// if it is infinity or NaN this function will panic. + pub fn f64_unsuffixed(f: f64) -> Literal { + assert!(f.is_finite()); + Literal::_new(imp::Literal::f64_unsuffixed(f)) + } + + /// Creates a new suffixed floating-point literal. + /// + /// This constructor will create a literal like `1.0f64` where the val= ue + /// specified is the preceding part of the token and `f64` is the suff= ix of + /// the token. This token will always be inferred to be an `f64` in the + /// compiler. Literals created from negative numbers may not survive + /// round-trips through `TokenStream` or strings and may be broken int= o two + /// tokens (`-` and positive literal). + /// + /// # Panics + /// + /// This function requires that the specified float is finite, for exa= mple + /// if it is infinity or NaN this function will panic. + pub fn f64_suffixed(f: f64) -> Literal { + assert!(f.is_finite()); + Literal::_new(imp::Literal::f64_suffixed(f)) + } + + /// Creates a new unsuffixed floating-point literal. + /// + /// This constructor is similar to those like `Literal::i8_unsuffixed`= where + /// the float's value is emitted directly into the token but no suffix= is + /// used, so it may be inferred to be a `f64` later in the compiler. + /// Literals created from negative numbers may not survive round-trips + /// through `TokenStream` or strings and may be broken into two tokens= (`-` + /// and positive literal). + /// + /// # Panics + /// + /// This function requires that the specified float is finite, for exa= mple + /// if it is infinity or NaN this function will panic. + pub fn f32_unsuffixed(f: f32) -> Literal { + assert!(f.is_finite()); + Literal::_new(imp::Literal::f32_unsuffixed(f)) + } + + /// Creates a new suffixed floating-point literal. + /// + /// This constructor will create a literal like `1.0f32` where the val= ue + /// specified is the preceding part of the token and `f32` is the suff= ix of + /// the token. This token will always be inferred to be an `f32` in the + /// compiler. Literals created from negative numbers may not survive + /// round-trips through `TokenStream` or strings and may be broken int= o two + /// tokens (`-` and positive literal). + /// + /// # Panics + /// + /// This function requires that the specified float is finite, for exa= mple + /// if it is infinity or NaN this function will panic. + pub fn f32_suffixed(f: f32) -> Literal { + assert!(f.is_finite()); + Literal::_new(imp::Literal::f32_suffixed(f)) + } + + /// String literal. + pub fn string(string: &str) -> Literal { + Literal::_new(imp::Literal::string(string)) + } + + /// Character literal. + pub fn character(ch: char) -> Literal { + Literal::_new(imp::Literal::character(ch)) + } + + /// Byte character literal. + pub fn byte_character(byte: u8) -> Literal { + Literal::_new(imp::Literal::byte_character(byte)) + } + + /// Byte string literal. + pub fn byte_string(bytes: &[u8]) -> Literal { + Literal::_new(imp::Literal::byte_string(bytes)) + } + + /// C string literal. + pub fn c_string(string: &CStr) -> Literal { + Literal::_new(imp::Literal::c_string(string)) + } + + /// Returns the span encompassing this literal. + pub fn span(&self) -> Span { + Span::_new(self.inner.span()) + } + + /// Configures the span associated for this literal. + pub fn set_span(&mut self, span: Span) { + self.inner.set_span(span.inner); + } + + /// Returns a `Span` that is a subset of `self.span()` containing only + /// the source bytes in range `range`. Returns `None` if the would-be + /// trimmed span is outside the bounds of `self`. + /// + /// Warning: the underlying [`proc_macro::Literal::subspan`] method is + /// nightly-only. When called from within a procedural macro not using= a + /// nightly compiler, this method will always return `None`. + pub fn subspan>(&self, range: R) -> Option= { + self.inner.subspan(range).map(Span::_new) + } + + // Intended for the `quote!` macro to use when constructing a proc-mac= ro2 + // token out of a macro_rules $:literal token, which is already known = to be + // a valid literal. This avoids reparsing/validating the literal's str= ing + // representation. This is not public API other than for quote. + #[doc(hidden)] + pub unsafe fn from_str_unchecked(repr: &str) -> Self { + Literal::_new(unsafe { imp::Literal::from_str_unchecked(repr) }) + } +} + +impl FromStr for Literal { + type Err =3D LexError; + + fn from_str(repr: &str) -> Result { + match imp::Literal::from_str_checked(repr) { + Ok(lit) =3D> Ok(Literal::_new(lit)), + Err(lex) =3D> Err(LexError { + inner: lex, + _marker: MARKER, + }), + } + } +} + +impl Debug for Literal { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(&self.inner, f) + } +} + +impl Display for Literal { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.inner, f) + } +} + +/// Public implementation details for the `TokenStream` type, such as iter= ators. +pub mod token_stream { + use crate::marker::{ProcMacroAutoTraits, MARKER}; + use crate::{imp, TokenTree}; + use core::fmt::{self, Debug}; + + pub use crate::TokenStream; + + /// An iterator over `TokenStream`'s `TokenTree`s. + /// + /// The iteration is "shallow", e.g. the iterator doesn't recurse into + /// delimited groups, and returns whole groups as token trees. + #[derive(Clone)] + pub struct IntoIter { + inner: imp::TokenTreeIter, + _marker: ProcMacroAutoTraits, + } + + impl Iterator for IntoIter { + type Item =3D TokenTree; + + fn next(&mut self) -> Option { + self.inner.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } + } + + impl Debug for IntoIter { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("TokenStream ")?; + f.debug_list().entries(self.clone()).finish() + } + } + + impl IntoIterator for TokenStream { + type Item =3D TokenTree; + type IntoIter =3D IntoIter; + + fn into_iter(self) -> IntoIter { + IntoIter { + inner: self.inner.into_iter(), + _marker: MARKER, + } + } + } +} diff --git a/rust/proc-macro2/location.rs b/rust/proc-macro2/location.rs new file mode 100644 index 000000000000..7190e2d05255 --- /dev/null +++ b/rust/proc-macro2/location.rs @@ -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 =3D "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 w= hich + /// 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 { + Some(self.cmp(other)) + } +} diff --git a/rust/proc-macro2/marker.rs b/rust/proc-macro2/marker.rs new file mode 100644 index 000000000000..23b94ce6fa85 --- /dev/null +++ b/rust/proc-macro2/marker.rs @@ -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 p= roc +// 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>); + +pub(crate) const MARKER: ProcMacroAutoTraits =3D ProcMacroAutoTraits(Phant= omData); + +impl UnwindSafe for ProcMacroAutoTraits {} +impl RefUnwindSafe for ProcMacroAutoTraits {} diff --git a/rust/proc-macro2/parse.rs b/rust/proc-macro2/parse.rs new file mode 100644 index 000000000000..b8be403f842f --- /dev/null +++ b/rust/proc-macro2/parse.rs @@ -0,0 +1,995 @@ +use crate::fallback::{ + self, is_ident_continue, is_ident_start, Group, Ident, LexError, Liter= al, Span, TokenStream, + TokenStreamBuilder, +}; +use crate::{Delimiter, Punct, Spacing, TokenTree}; +use core::char; +use core::str::{Bytes, CharIndices, Chars}; + +#[derive(Copy, Clone, Eq, PartialEq)] +pub(crate) struct Cursor<'a> { + pub(crate) rest: &'a str, + #[cfg(span_locations)] + pub(crate) off: u32, +} + +impl<'a> Cursor<'a> { + pub(crate) fn advance(&self, bytes: usize) -> Cursor<'a> { + let (_front, rest) =3D self.rest.split_at(bytes); + Cursor { + rest, + #[cfg(span_locations)] + off: self.off + _front.chars().count() as u32, + } + } + + pub(crate) fn starts_with(&self, s: &str) -> bool { + self.rest.starts_with(s) + } + + pub(crate) fn starts_with_char(&self, ch: char) -> bool { + self.rest.starts_with(ch) + } + + pub(crate) fn starts_with_fn(&self, f: Pattern) -> bool + where + Pattern: FnMut(char) -> bool, + { + self.rest.starts_with(f) + } + + pub(crate) fn is_empty(&self) -> bool { + self.rest.is_empty() + } + + fn len(&self) -> usize { + self.rest.len() + } + + fn as_bytes(&self) -> &'a [u8] { + self.rest.as_bytes() + } + + fn bytes(&self) -> Bytes<'a> { + self.rest.bytes() + } + + fn chars(&self) -> Chars<'a> { + self.rest.chars() + } + + fn char_indices(&self) -> CharIndices<'a> { + self.rest.char_indices() + } + + fn parse(&self, tag: &str) -> Result, Reject> { + if self.starts_with(tag) { + Ok(self.advance(tag.len())) + } else { + Err(Reject) + } + } +} + +pub(crate) struct Reject; +type PResult<'a, O> =3D Result<(Cursor<'a>, O), Reject>; + +fn skip_whitespace(input: Cursor) -> Cursor { + let mut s =3D input; + + while !s.is_empty() { + let byte =3D s.as_bytes()[0]; + if byte =3D=3D b'/' { + if s.starts_with("//") + && (!s.starts_with("///") || s.starts_with("////")) + && !s.starts_with("//!") + { + let (cursor, _) =3D take_until_newline_or_eof(s); + s =3D cursor; + continue; + } else if s.starts_with("/**/") { + s =3D s.advance(4); + continue; + } else if s.starts_with("/*") + && (!s.starts_with("/**") || s.starts_with("/***")) + && !s.starts_with("/*!") + { + match block_comment(s) { + Ok((rest, _)) =3D> { + s =3D rest; + continue; + } + Err(Reject) =3D> return s, + } + } + } + match byte { + b' ' | 0x09..=3D0x0d =3D> { + s =3D s.advance(1); + continue; + } + b if b.is_ascii() =3D> {} + _ =3D> { + let ch =3D s.chars().next().unwrap(); + if is_whitespace(ch) { + s =3D s.advance(ch.len_utf8()); + continue; + } + } + } + return s; + } + s +} + +fn block_comment(input: Cursor) -> PResult<&str> { + if !input.starts_with("/*") { + return Err(Reject); + } + + let mut depth =3D 0usize; + let bytes =3D input.as_bytes(); + let mut i =3D 0usize; + let upper =3D bytes.len() - 1; + + while i < upper { + if bytes[i] =3D=3D b'/' && bytes[i + 1] =3D=3D b'*' { + depth +=3D 1; + i +=3D 1; // eat '*' + } else if bytes[i] =3D=3D b'*' && bytes[i + 1] =3D=3D b'/' { + depth -=3D 1; + if depth =3D=3D 0 { + return Ok((input.advance(i + 2), &input.rest[..i + 2])); + } + i +=3D 1; // eat '/' + } + i +=3D 1; + } + + Err(Reject) +} + +fn is_whitespace(ch: char) -> bool { + // Rust treats left-to-right mark and right-to-left mark as whitespace + ch.is_whitespace() || ch =3D=3D '\u{200e}' || ch =3D=3D '\u{200f}' +} + +fn word_break(input: Cursor) -> Result { + match input.chars().next() { + Some(ch) if is_ident_continue(ch) =3D> Err(Reject), + Some(_) | None =3D> Ok(input), + } +} + +// Rustc's representation of a macro expansion error in expression positio= n or +// type position. +const ERROR: &str =3D "(/*ERROR*/)"; + +pub(crate) fn token_stream(mut input: Cursor) -> Result { + let mut trees =3D TokenStreamBuilder::new(); + let mut stack =3D Vec::new(); + + loop { + input =3D skip_whitespace(input); + + if let Ok((rest, ())) =3D doc_comment(input, &mut trees) { + input =3D rest; + continue; + } + + #[cfg(span_locations)] + let lo =3D input.off; + + let first =3D match input.bytes().next() { + Some(first) =3D> first, + None =3D> match stack.last() { + None =3D> return Ok(trees.build()), + #[cfg(span_locations)] + Some((lo, _frame)) =3D> { + return Err(LexError { + span: Span { lo: *lo, hi: *lo }, + }) + } + #[cfg(not(span_locations))] + Some(_frame) =3D> return Err(LexError { span: Span {} }), + }, + }; + + if let Some(open_delimiter) =3D match first { + b'(' if !input.starts_with(ERROR) =3D> Some(Delimiter::Parenth= esis), + b'[' =3D> Some(Delimiter::Bracket), + b'{' =3D> Some(Delimiter::Brace), + _ =3D> None, + } { + input =3D input.advance(1); + let frame =3D (open_delimiter, trees); + #[cfg(span_locations)] + let frame =3D (lo, frame); + stack.push(frame); + trees =3D TokenStreamBuilder::new(); + } else if let Some(close_delimiter) =3D match first { + b')' =3D> Some(Delimiter::Parenthesis), + b']' =3D> Some(Delimiter::Bracket), + b'}' =3D> Some(Delimiter::Brace), + _ =3D> None, + } { + let frame =3D match stack.pop() { + Some(frame) =3D> frame, + None =3D> return Err(lex_error(input)), + }; + #[cfg(span_locations)] + let (lo, frame) =3D frame; + let (open_delimiter, outer) =3D frame; + if open_delimiter !=3D close_delimiter { + return Err(lex_error(input)); + } + input =3D input.advance(1); + let mut g =3D Group::new(open_delimiter, trees.build()); + g.set_span(Span { + #[cfg(span_locations)] + lo, + #[cfg(span_locations)] + hi: input.off, + }); + trees =3D outer; + trees.push_token_from_parser(TokenTree::Group(crate::Group::_n= ew_fallback(g))); + } else { + let (rest, mut tt) =3D match leaf_token(input) { + Ok((rest, tt)) =3D> (rest, tt), + Err(Reject) =3D> return Err(lex_error(input)), + }; + tt.set_span(crate::Span::_new_fallback(Span { + #[cfg(span_locations)] + lo, + #[cfg(span_locations)] + hi: rest.off, + })); + trees.push_token_from_parser(tt); + input =3D rest; + } + } +} + +fn lex_error(cursor: Cursor) -> LexError { + #[cfg(not(span_locations))] + let _ =3D cursor; + LexError { + span: Span { + #[cfg(span_locations)] + lo: cursor.off, + #[cfg(span_locations)] + hi: cursor.off, + }, + } +} + +fn leaf_token(input: Cursor) -> PResult { + if let Ok((input, l)) =3D literal(input) { + // must be parsed before ident + Ok((input, TokenTree::Literal(crate::Literal::_new_fallback(l)))) + } else if let Ok((input, p)) =3D punct(input) { + Ok((input, TokenTree::Punct(p))) + } else if let Ok((input, i)) =3D ident(input) { + Ok((input, TokenTree::Ident(i))) + } else if input.starts_with(ERROR) { + let rest =3D input.advance(ERROR.len()); + let repr =3D crate::Literal::_new_fallback(Literal::_new(ERROR.to_= owned())); + Ok((rest, TokenTree::Literal(repr))) + } else { + Err(Reject) + } +} + +fn ident(input: Cursor) -> PResult { + if [ + "r\"", "r#\"", "r##", "b\"", "b\'", "br\"", "br#", "c\"", "cr\"", = "cr#", + ] + .iter() + .any(|prefix| input.starts_with(prefix)) + { + Err(Reject) + } else { + ident_any(input) + } +} + +fn ident_any(input: Cursor) -> PResult { + let raw =3D input.starts_with("r#"); + let rest =3D input.advance((raw as usize) << 1); + + let (rest, sym) =3D ident_not_raw(rest)?; + + if !raw { + let ident =3D + crate::Ident::_new_fallback(Ident::new_unchecked(sym, fallback= ::Span::call_site())); + return Ok((rest, ident)); + } + + match sym { + "_" | "super" | "self" | "Self" | "crate" =3D> return Err(Reject), + _ =3D> {} + } + + let ident =3D + crate::Ident::_new_fallback(Ident::new_raw_unchecked(sym, fallback= ::Span::call_site())); + Ok((rest, ident)) +} + +fn ident_not_raw(input: Cursor) -> PResult<&str> { + let mut chars =3D input.char_indices(); + + match chars.next() { + Some((_, ch)) if is_ident_start(ch) =3D> {} + _ =3D> return Err(Reject), + } + + let mut end =3D input.len(); + for (i, ch) in chars { + if !is_ident_continue(ch) { + end =3D i; + break; + } + } + + Ok((input.advance(end), &input.rest[..end])) +} + +pub(crate) fn literal(input: Cursor) -> PResult { + let rest =3D literal_nocapture(input)?; + let end =3D input.len() - rest.len(); + Ok((rest, Literal::_new(input.rest[..end].to_string()))) +} + +fn literal_nocapture(input: Cursor) -> Result { + if let Ok(ok) =3D string(input) { + Ok(ok) + } else if let Ok(ok) =3D byte_string(input) { + Ok(ok) + } else if let Ok(ok) =3D c_string(input) { + Ok(ok) + } else if let Ok(ok) =3D byte(input) { + Ok(ok) + } else if let Ok(ok) =3D character(input) { + Ok(ok) + } else if let Ok(ok) =3D float(input) { + Ok(ok) + } else if let Ok(ok) =3D int(input) { + Ok(ok) + } else { + Err(Reject) + } +} + +fn literal_suffix(input: Cursor) -> Cursor { + match ident_not_raw(input) { + Ok((input, _)) =3D> input, + Err(Reject) =3D> input, + } +} + +fn string(input: Cursor) -> Result { + if let Ok(input) =3D input.parse("\"") { + cooked_string(input) + } else if let Ok(input) =3D input.parse("r") { + raw_string(input) + } else { + Err(Reject) + } +} + +fn cooked_string(mut input: Cursor) -> Result { + let mut chars =3D input.char_indices(); + + while let Some((i, ch)) =3D chars.next() { + match ch { + '"' =3D> { + let input =3D input.advance(i + 1); + return Ok(literal_suffix(input)); + } + '\r' =3D> match chars.next() { + Some((_, '\n')) =3D> {} + _ =3D> break, + }, + '\\' =3D> match chars.next() { + Some((_, 'x')) =3D> { + backslash_x_char(&mut chars)?; + } + Some((_, 'n' | 'r' | 't' | '\\' | '\'' | '"' | '0')) =3D> = {} + Some((_, 'u')) =3D> { + backslash_u(&mut chars)?; + } + Some((newline, ch @ ('\n' | '\r'))) =3D> { + input =3D input.advance(newline + 1); + trailing_backslash(&mut input, ch as u8)?; + chars =3D input.char_indices(); + } + _ =3D> break, + }, + _ch =3D> {} + } + } + Err(Reject) +} + +fn raw_string(input: Cursor) -> Result { + let (input, delimiter) =3D delimiter_of_raw_string(input)?; + let mut bytes =3D input.bytes().enumerate(); + while let Some((i, byte)) =3D bytes.next() { + match byte { + b'"' if input.rest[i + 1..].starts_with(delimiter) =3D> { + let rest =3D input.advance(i + 1 + delimiter.len()); + return Ok(literal_suffix(rest)); + } + b'\r' =3D> match bytes.next() { + Some((_, b'\n')) =3D> {} + _ =3D> break, + }, + _ =3D> {} + } + } + Err(Reject) +} + +fn byte_string(input: Cursor) -> Result { + if let Ok(input) =3D input.parse("b\"") { + cooked_byte_string(input) + } else if let Ok(input) =3D input.parse("br") { + raw_byte_string(input) + } else { + Err(Reject) + } +} + +fn cooked_byte_string(mut input: Cursor) -> Result { + let mut bytes =3D input.bytes().enumerate(); + while let Some((offset, b)) =3D bytes.next() { + match b { + b'"' =3D> { + let input =3D input.advance(offset + 1); + return Ok(literal_suffix(input)); + } + b'\r' =3D> match bytes.next() { + Some((_, b'\n')) =3D> {} + _ =3D> break, + }, + b'\\' =3D> match bytes.next() { + Some((_, b'x')) =3D> { + backslash_x_byte(&mut bytes)?; + } + Some((_, b'n' | b'r' | b't' | b'\\' | b'0' | b'\'' | b'"')= ) =3D> {} + Some((newline, b @ (b'\n' | b'\r'))) =3D> { + input =3D input.advance(newline + 1); + trailing_backslash(&mut input, b)?; + bytes =3D input.bytes().enumerate(); + } + _ =3D> break, + }, + b if b.is_ascii() =3D> {} + _ =3D> break, + } + } + Err(Reject) +} + +fn delimiter_of_raw_string(input: Cursor) -> PResult<&str> { + for (i, byte) in input.bytes().enumerate() { + match byte { + b'"' =3D> { + if i > 255 { + // https://github.com/rust-lang/rust/pull/95251 + return Err(Reject); + } + return Ok((input.advance(i + 1), &input.rest[..i])); + } + b'#' =3D> {} + _ =3D> break, + } + } + Err(Reject) +} + +fn raw_byte_string(input: Cursor) -> Result { + let (input, delimiter) =3D delimiter_of_raw_string(input)?; + let mut bytes =3D input.bytes().enumerate(); + while let Some((i, byte)) =3D bytes.next() { + match byte { + b'"' if input.rest[i + 1..].starts_with(delimiter) =3D> { + let rest =3D input.advance(i + 1 + delimiter.len()); + return Ok(literal_suffix(rest)); + } + b'\r' =3D> match bytes.next() { + Some((_, b'\n')) =3D> {} + _ =3D> break, + }, + other =3D> { + if !other.is_ascii() { + break; + } + } + } + } + Err(Reject) +} + +fn c_string(input: Cursor) -> Result { + if let Ok(input) =3D input.parse("c\"") { + cooked_c_string(input) + } else if let Ok(input) =3D input.parse("cr") { + raw_c_string(input) + } else { + Err(Reject) + } +} + +fn raw_c_string(input: Cursor) -> Result { + let (input, delimiter) =3D delimiter_of_raw_string(input)?; + let mut bytes =3D input.bytes().enumerate(); + while let Some((i, byte)) =3D bytes.next() { + match byte { + b'"' if input.rest[i + 1..].starts_with(delimiter) =3D> { + let rest =3D input.advance(i + 1 + delimiter.len()); + return Ok(literal_suffix(rest)); + } + b'\r' =3D> match bytes.next() { + Some((_, b'\n')) =3D> {} + _ =3D> break, + }, + b'\0' =3D> break, + _ =3D> {} + } + } + Err(Reject) +} + +fn cooked_c_string(mut input: Cursor) -> Result { + let mut chars =3D input.char_indices(); + + while let Some((i, ch)) =3D chars.next() { + match ch { + '"' =3D> { + let input =3D input.advance(i + 1); + return Ok(literal_suffix(input)); + } + '\r' =3D> match chars.next() { + Some((_, '\n')) =3D> {} + _ =3D> break, + }, + '\\' =3D> match chars.next() { + Some((_, 'x')) =3D> { + backslash_x_nonzero(&mut chars)?; + } + Some((_, 'n' | 'r' | 't' | '\\' | '\'' | '"')) =3D> {} + Some((_, 'u')) =3D> { + if backslash_u(&mut chars)? =3D=3D '\0' { + break; + } + } + Some((newline, ch @ ('\n' | '\r'))) =3D> { + input =3D input.advance(newline + 1); + trailing_backslash(&mut input, ch as u8)?; + chars =3D input.char_indices(); + } + _ =3D> break, + }, + '\0' =3D> break, + _ch =3D> {} + } + } + Err(Reject) +} + +fn byte(input: Cursor) -> Result { + let input =3D input.parse("b'")?; + let mut bytes =3D input.bytes().enumerate(); + let ok =3D match bytes.next().map(|(_, b)| b) { + Some(b'\\') =3D> match bytes.next().map(|(_, b)| b) { + Some(b'x') =3D> backslash_x_byte(&mut bytes).is_ok(), + Some(b'n' | b'r' | b't' | b'\\' | b'0' | b'\'' | b'"') =3D> tr= ue, + _ =3D> false, + }, + b =3D> b.is_some(), + }; + if !ok { + return Err(Reject); + } + let (offset, _) =3D bytes.next().ok_or(Reject)?; + if !input.chars().as_str().is_char_boundary(offset) { + return Err(Reject); + } + let input =3D input.advance(offset).parse("'")?; + Ok(literal_suffix(input)) +} + +fn character(input: Cursor) -> Result { + let input =3D input.parse("'")?; + let mut chars =3D input.char_indices(); + let ok =3D match chars.next().map(|(_, ch)| ch) { + Some('\\') =3D> match chars.next().map(|(_, ch)| ch) { + Some('x') =3D> backslash_x_char(&mut chars).is_ok(), + Some('u') =3D> backslash_u(&mut chars).is_ok(), + Some('n' | 'r' | 't' | '\\' | '0' | '\'' | '"') =3D> true, + _ =3D> false, + }, + ch =3D> ch.is_some(), + }; + if !ok { + return Err(Reject); + } + let (idx, _) =3D chars.next().ok_or(Reject)?; + let input =3D input.advance(idx).parse("'")?; + Ok(literal_suffix(input)) +} + +macro_rules! next_ch { + ($chars:ident @ $pat:pat) =3D> { + match $chars.next() { + Some((_, ch)) =3D> match ch { + $pat =3D> ch, + _ =3D> return Err(Reject), + }, + None =3D> return Err(Reject), + } + }; +} + +fn backslash_x_char(chars: &mut I) -> Result<(), Reject> +where + I: Iterator, +{ + next_ch!(chars @ '0'..=3D'7'); + next_ch!(chars @ '0'..=3D'9' | 'a'..=3D'f' | 'A'..=3D'F'); + Ok(()) +} + +fn backslash_x_byte(chars: &mut I) -> Result<(), Reject> +where + I: Iterator, +{ + next_ch!(chars @ b'0'..=3Db'9' | b'a'..=3Db'f' | b'A'..=3Db'F'); + next_ch!(chars @ b'0'..=3Db'9' | b'a'..=3Db'f' | b'A'..=3Db'F'); + Ok(()) +} + +fn backslash_x_nonzero(chars: &mut I) -> Result<(), Reject> +where + I: Iterator, +{ + let first =3D next_ch!(chars @ '0'..=3D'9' | 'a'..=3D'f' | 'A'..=3D'F'= ); + let second =3D next_ch!(chars @ '0'..=3D'9' | 'a'..=3D'f' | 'A'..=3D'F= '); + if first =3D=3D '0' && second =3D=3D '0' { + Err(Reject) + } else { + Ok(()) + } +} + +fn backslash_u(chars: &mut I) -> Result +where + I: Iterator, +{ + next_ch!(chars @ '{'); + let mut value =3D 0; + let mut len =3D 0; + for (_, ch) in chars { + let digit =3D match ch { + '0'..=3D'9' =3D> ch as u8 - b'0', + 'a'..=3D'f' =3D> 10 + ch as u8 - b'a', + 'A'..=3D'F' =3D> 10 + ch as u8 - b'A', + '_' if len > 0 =3D> continue, + '}' if len > 0 =3D> return char::from_u32(value).ok_or(Reject), + _ =3D> break, + }; + if len =3D=3D 6 { + break; + } + value *=3D 0x10; + value +=3D u32::from(digit); + len +=3D 1; + } + Err(Reject) +} + +fn trailing_backslash(input: &mut Cursor, mut last: u8) -> Result<(), Reje= ct> { + let mut whitespace =3D input.bytes().enumerate(); + loop { + if last =3D=3D b'\r' && whitespace.next().map_or(true, |(_, b)| b = !=3D b'\n') { + return Err(Reject); + } + match whitespace.next() { + Some((_, b @ (b' ' | b'\t' | b'\n' | b'\r'))) =3D> { + last =3D b; + } + Some((offset, _)) =3D> { + *input =3D input.advance(offset); + return Ok(()); + } + None =3D> return Err(Reject), + } + } +} + +fn float(input: Cursor) -> Result { + let mut rest =3D float_digits(input)?; + if let Some(ch) =3D rest.chars().next() { + if is_ident_start(ch) { + rest =3D ident_not_raw(rest)?.0; + } + } + word_break(rest) +} + +fn float_digits(input: Cursor) -> Result { + let mut chars =3D input.chars().peekable(); + match chars.next() { + Some(ch) if '0' <=3D ch && ch <=3D '9' =3D> {} + _ =3D> return Err(Reject), + } + + let mut len =3D 1; + let mut has_dot =3D false; + let mut has_exp =3D false; + while let Some(&ch) =3D chars.peek() { + match ch { + '0'..=3D'9' | '_' =3D> { + chars.next(); + len +=3D 1; + } + '.' =3D> { + if has_dot { + break; + } + chars.next(); + if chars + .peek() + .map_or(false, |&ch| ch =3D=3D '.' || is_ident_start(c= h)) + { + return Err(Reject); + } + len +=3D 1; + has_dot =3D true; + } + 'e' | 'E' =3D> { + chars.next(); + len +=3D 1; + has_exp =3D true; + break; + } + _ =3D> break, + } + } + + if !(has_dot || has_exp) { + return Err(Reject); + } + + if has_exp { + let token_before_exp =3D if has_dot { + Ok(input.advance(len - 1)) + } else { + Err(Reject) + }; + let mut has_sign =3D false; + let mut has_exp_value =3D false; + while let Some(&ch) =3D chars.peek() { + match ch { + '+' | '-' =3D> { + if has_exp_value { + break; + } + if has_sign { + return token_before_exp; + } + chars.next(); + len +=3D 1; + has_sign =3D true; + } + '0'..=3D'9' =3D> { + chars.next(); + len +=3D 1; + has_exp_value =3D true; + } + '_' =3D> { + chars.next(); + len +=3D 1; + } + _ =3D> break, + } + } + if !has_exp_value { + return token_before_exp; + } + } + + Ok(input.advance(len)) +} + +fn int(input: Cursor) -> Result { + let mut rest =3D digits(input)?; + if let Some(ch) =3D rest.chars().next() { + if is_ident_start(ch) { + rest =3D ident_not_raw(rest)?.0; + } + } + word_break(rest) +} + +fn digits(mut input: Cursor) -> Result { + let base =3D if input.starts_with("0x") { + input =3D input.advance(2); + 16 + } else if input.starts_with("0o") { + input =3D input.advance(2); + 8 + } else if input.starts_with("0b") { + input =3D input.advance(2); + 2 + } else { + 10 + }; + + let mut len =3D 0; + let mut empty =3D true; + for b in input.bytes() { + match b { + b'0'..=3Db'9' =3D> { + let digit =3D (b - b'0') as u64; + if digit >=3D base { + return Err(Reject); + } + } + b'a'..=3Db'f' =3D> { + let digit =3D 10 + (b - b'a') as u64; + if digit >=3D base { + break; + } + } + b'A'..=3Db'F' =3D> { + let digit =3D 10 + (b - b'A') as u64; + if digit >=3D base { + break; + } + } + b'_' =3D> { + if empty && base =3D=3D 10 { + return Err(Reject); + } + len +=3D 1; + continue; + } + _ =3D> break, + } + len +=3D 1; + empty =3D false; + } + if empty { + Err(Reject) + } else { + Ok(input.advance(len)) + } +} + +fn punct(input: Cursor) -> PResult { + let (rest, ch) =3D punct_char(input)?; + if ch =3D=3D '\'' { + let (after_lifetime, _ident) =3D ident_any(rest)?; + if after_lifetime.starts_with_char('\'') + || (after_lifetime.starts_with_char('#') && !rest.starts_with(= "r#")) + { + Err(Reject) + } else { + Ok((rest, Punct::new('\'', Spacing::Joint))) + } + } else { + let kind =3D match punct_char(rest) { + Ok(_) =3D> Spacing::Joint, + Err(Reject) =3D> Spacing::Alone, + }; + Ok((rest, Punct::new(ch, kind))) + } +} + +fn punct_char(input: Cursor) -> PResult { + if input.starts_with("//") || input.starts_with("/*") { + // Do not accept `/` of a comment as a punct. + return Err(Reject); + } + + let mut chars =3D input.chars(); + let first =3D match chars.next() { + Some(ch) =3D> ch, + None =3D> { + return Err(Reject); + } + }; + let recognized =3D "~!@#$%^&*-=3D+|;:,<.>/?'"; + if recognized.contains(first) { + Ok((input.advance(first.len_utf8()), first)) + } else { + Err(Reject) + } +} + +fn doc_comment<'a>(input: Cursor<'a>, trees: &mut TokenStreamBuilder) -> P= Result<'a, ()> { + #[cfg(span_locations)] + let lo =3D input.off; + let (rest, (comment, inner)) =3D doc_comment_contents(input)?; + let fallback_span =3D Span { + #[cfg(span_locations)] + lo, + #[cfg(span_locations)] + hi: rest.off, + }; + let span =3D crate::Span::_new_fallback(fallback_span); + + let mut scan_for_bare_cr =3D comment; + while let Some(cr) =3D scan_for_bare_cr.find('\r') { + let rest =3D &scan_for_bare_cr[cr + 1..]; + if !rest.starts_with('\n') { + return Err(Reject); + } + scan_for_bare_cr =3D rest; + } + + let mut pound =3D Punct::new('#', Spacing::Alone); + pound.set_span(span); + trees.push_token_from_parser(TokenTree::Punct(pound)); + + if inner { + let mut bang =3D Punct::new('!', Spacing::Alone); + bang.set_span(span); + trees.push_token_from_parser(TokenTree::Punct(bang)); + } + + let doc_ident =3D crate::Ident::_new_fallback(Ident::new_unchecked("do= c", fallback_span)); + let mut equal =3D Punct::new('=3D', Spacing::Alone); + equal.set_span(span); + let mut literal =3D crate::Literal::_new_fallback(Literal::string(comm= ent)); + literal.set_span(span); + let mut bracketed =3D TokenStreamBuilder::with_capacity(3); + bracketed.push_token_from_parser(TokenTree::Ident(doc_ident)); + bracketed.push_token_from_parser(TokenTree::Punct(equal)); + bracketed.push_token_from_parser(TokenTree::Literal(literal)); + let group =3D Group::new(Delimiter::Bracket, bracketed.build()); + let mut group =3D crate::Group::_new_fallback(group); + group.set_span(span); + trees.push_token_from_parser(TokenTree::Group(group)); + + Ok((rest, ())) +} + +fn doc_comment_contents(input: Cursor) -> PResult<(&str, bool)> { + if input.starts_with("//!") { + let input =3D input.advance(3); + let (input, s) =3D take_until_newline_or_eof(input); + Ok((input, (s, true))) + } else if input.starts_with("/*!") { + let (input, s) =3D block_comment(input)?; + Ok((input, (&s[3..s.len() - 2], true))) + } else if input.starts_with("///") { + let input =3D input.advance(3); + if input.starts_with_char('/') { + return Err(Reject); + } + let (input, s) =3D take_until_newline_or_eof(input); + Ok((input, (s, false))) + } else if input.starts_with("/**") && !input.rest[3..].starts_with('*'= ) { + let (input, s) =3D block_comment(input)?; + Ok((input, (&s[3..s.len() - 2], false))) + } else { + Err(Reject) + } +} + +fn take_until_newline_or_eof(input: Cursor) -> (Cursor, &str) { + let chars =3D input.char_indices(); + + for (i, ch) in chars { + if ch =3D=3D '\n' { + return (input.advance(i), &input.rest[..i]); + } else if ch =3D=3D '\r' && input.rest[i + 1..].starts_with('\n') { + return (input.advance(i + 1), &input.rest[..i]); + } + } + + (input.advance(input.len()), input.rest) +} diff --git a/rust/proc-macro2/probe.rs b/rust/proc-macro2/probe.rs new file mode 100644 index 000000000000..b67f52036218 --- /dev/null +++ b/rust/proc-macro2/probe.rs @@ -0,0 +1,10 @@ +#![allow(dead_code)] + +#[cfg(proc_macro_span)] +pub(crate) mod proc_macro_span; + +#[cfg(proc_macro_span_file)] +pub(crate) mod proc_macro_span_file; + +#[cfg(proc_macro_span_location)] +pub(crate) mod proc_macro_span_location; diff --git a/rust/proc-macro2/probe/proc_macro_span.rs b/rust/proc-macro2/p= robe/proc_macro_span.rs new file mode 100644 index 000000000000..2d7d44e07708 --- /dev/null +++ b/rust/proc-macro2/probe/proc_macro_span.rs @@ -0,0 +1,51 @@ +// 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 abl= e to +// offer these APIs too. + +#![cfg_attr(procmacro2_build_probe, feature(proc_macro_span))] + +extern crate proc_macro; + +use core::ops::{Range, RangeBounds}; +use proc_macro::{Literal, Span}; +use std::path::PathBuf; + +pub fn byte_range(this: &Span) -> Range { + this.byte_range() +} + +pub fn start(this: &Span) -> Span { + this.start() +} + +pub fn end(this: &Span) -> Span { + this.end() +} + +pub fn line(this: &Span) -> usize { + this.line() +} + +pub fn column(this: &Span) -> usize { + this.column() +} + +pub fn file(this: &Span) -> String { + this.file() +} + +pub fn local_file(this: &Span) -> Option { + this.local_file() +} + +pub fn join(this: &Span, other: Span) -> Option { + this.join(other) +} + +pub fn subspan>(this: &Literal, range: R) -> Option<= Span> { + this.subspan(range) +} + +// Include in sccache cache key. +#[cfg(procmacro2_build_probe)] +const _: Option<&str> =3D option_env!("RUSTC_BOOTSTRAP"); diff --git a/rust/proc-macro2/probe/proc_macro_span_file.rs b/rust/proc-mac= ro2/probe/proc_macro_span_file.rs new file mode 100644 index 000000000000..8b76bdf5007b --- /dev/null +++ b/rust/proc-macro2/probe/proc_macro_span_file.rs @@ -0,0 +1,14 @@ +// The subset of Span's API stabilized in Rust 1.88. + +extern crate proc_macro; + +use proc_macro::Span; +use std::path::PathBuf; + +pub fn file(this: &Span) -> String { + this.file() +} + +pub fn local_file(this: &Span) -> Option { + this.local_file() +} diff --git a/rust/proc-macro2/probe/proc_macro_span_location.rs b/rust/proc= -macro2/probe/proc_macro_span_location.rs new file mode 100644 index 000000000000..79da34af54af --- /dev/null +++ b/rust/proc-macro2/probe/proc_macro_span_location.rs @@ -0,0 +1,21 @@ +// The subset of Span's API stabilized in Rust 1.88. + +extern crate proc_macro; + +use proc_macro::Span; + +pub fn start(this: &Span) -> Span { + this.start() +} + +pub fn end(this: &Span) -> Span { + this.end() +} + +pub fn line(this: &Span) -> usize { + this.line() +} + +pub fn column(this: &Span) -> usize { + this.column() +} diff --git a/rust/proc-macro2/rcvec.rs b/rust/proc-macro2/rcvec.rs new file mode 100644 index 000000000000..23edc77d597f --- /dev/null +++ b/rust/proc-macro2/rcvec.rs @@ -0,0 +1,146 @@ +use alloc::rc::Rc; +use alloc::vec; +use core::mem; +use core::panic::RefUnwindSafe; +use core::slice; + +pub(crate) struct RcVec { + inner: Rc>, +} + +pub(crate) struct RcVecBuilder { + inner: Vec, +} + +pub(crate) struct RcVecMut<'a, T> { + inner: &'a mut Vec, +} + +#[derive(Clone)] +pub(crate) struct RcVecIntoIter { + inner: vec::IntoIter, +} + +impl RcVec { + pub(crate) fn is_empty(&self) -> bool { + self.inner.is_empty() + } + + pub(crate) fn len(&self) -> usize { + self.inner.len() + } + + pub(crate) fn iter(&self) -> slice::Iter { + self.inner.iter() + } + + pub(crate) fn make_mut(&mut self) -> RcVecMut + where + T: Clone, + { + RcVecMut { + inner: Rc::make_mut(&mut self.inner), + } + } + + pub(crate) fn get_mut(&mut self) -> Option> { + let inner =3D Rc::get_mut(&mut self.inner)?; + Some(RcVecMut { inner }) + } + + pub(crate) fn make_owned(mut self) -> RcVecBuilder + where + T: Clone, + { + let vec =3D if let Some(owned) =3D Rc::get_mut(&mut self.inner) { + mem::take(owned) + } else { + Vec::clone(&self.inner) + }; + RcVecBuilder { inner: vec } + } +} + +impl RcVecBuilder { + pub(crate) fn new() -> Self { + RcVecBuilder { inner: Vec::new() } + } + + pub(crate) fn with_capacity(cap: usize) -> Self { + RcVecBuilder { + inner: Vec::with_capacity(cap), + } + } + + pub(crate) fn push(&mut self, element: T) { + self.inner.push(element); + } + + pub(crate) fn extend(&mut self, iter: impl IntoIterator) { + self.inner.extend(iter); + } + + pub(crate) fn as_mut(&mut self) -> RcVecMut { + RcVecMut { + inner: &mut self.inner, + } + } + + pub(crate) fn build(self) -> RcVec { + RcVec { + inner: Rc::new(self.inner), + } + } +} + +impl<'a, T> RcVecMut<'a, T> { + pub(crate) fn push(&mut self, element: T) { + self.inner.push(element); + } + + pub(crate) fn extend(&mut self, iter: impl IntoIterator) { + self.inner.extend(iter); + } + + pub(crate) fn as_mut(&mut self) -> RcVecMut { + RcVecMut { inner: self.inner } + } + + pub(crate) fn take(self) -> RcVecBuilder { + let vec =3D mem::take(self.inner); + RcVecBuilder { inner: vec } + } +} + +impl Clone for RcVec { + fn clone(&self) -> Self { + RcVec { + inner: Rc::clone(&self.inner), + } + } +} + +impl IntoIterator for RcVecBuilder { + type Item =3D T; + type IntoIter =3D RcVecIntoIter; + + fn into_iter(self) -> Self::IntoIter { + RcVecIntoIter { + inner: self.inner.into_iter(), + } + } +} + +impl Iterator for RcVecIntoIter { + type Item =3D T; + + fn next(&mut self) -> Option { + self.inner.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +impl RefUnwindSafe for RcVec where T: RefUnwindSafe {} diff --git a/rust/proc-macro2/wrapper.rs b/rust/proc-macro2/wrapper.rs new file mode 100644 index 000000000000..2e3eb5b4d04e --- /dev/null +++ b/rust/proc-macro2/wrapper.rs @@ -0,0 +1,984 @@ +use crate::detection::inside_proc_macro; +use crate::fallback::{self, FromStr2 as _}; +#[cfg(span_locations)] +use crate::location::LineColumn; +#[cfg(proc_macro_span)] +use crate::probe::proc_macro_span; +#[cfg(all(span_locations, proc_macro_span_file))] +use crate::probe::proc_macro_span_file; +#[cfg(all(span_locations, proc_macro_span_location))] +use crate::probe::proc_macro_span_location; +use crate::{Delimiter, Punct, Spacing, TokenTree}; +use core::fmt::{self, Debug, Display}; +#[cfg(span_locations)] +use core::ops::Range; +use core::ops::RangeBounds; +use std::ffi::CStr; +#[cfg(span_locations)] +use std::path::PathBuf; + +#[derive(Clone)] +pub(crate) enum TokenStream { + Compiler(DeferredTokenStream), + Fallback(fallback::TokenStream), +} + +// Work around https://github.com/rust-lang/rust/issues/65080. +// In `impl Extend for TokenStream` which is used heavily by qu= ote, +// we hold on to the appended tokens and do proc_macro::TokenStream::exten= d as +// late as possible to batch together consecutive uses of the Extend impl. +#[derive(Clone)] +pub(crate) struct DeferredTokenStream { + stream: proc_macro::TokenStream, + extra: Vec, +} + +pub(crate) enum LexError { + Compiler(proc_macro::LexError), + Fallback(fallback::LexError), + + // Rustc was supposed to return a LexError, but it panicked instead. + // https://github.com/rust-lang/rust/issues/58736 + CompilerPanic, +} + +#[cold] +fn mismatch(line: u32) -> ! { + #[cfg(procmacro2_backtrace)] + { + let backtrace =3D std::backtrace::Backtrace::force_capture(); + panic!("compiler/fallback mismatch L{}\n\n{}", line, backtrace) + } + #[cfg(not(procmacro2_backtrace))] + { + panic!("compiler/fallback mismatch L{}", line) + } +} + +impl DeferredTokenStream { + fn new(stream: proc_macro::TokenStream) -> Self { + DeferredTokenStream { + stream, + extra: Vec::new(), + } + } + + fn is_empty(&self) -> bool { + self.stream.is_empty() && self.extra.is_empty() + } + + fn evaluate_now(&mut self) { + // If-check provides a fast short circuit for the common case of `= extra` + // being empty, which saves a round trip over the proc macro bridg= e. + // Improves macro expansion time in winrt by 6% in debug mode. + if !self.extra.is_empty() { + self.stream.extend(self.extra.drain(..)); + } + } + + fn into_token_stream(mut self) -> proc_macro::TokenStream { + self.evaluate_now(); + self.stream + } +} + +impl TokenStream { + pub(crate) fn new() -> Self { + if inside_proc_macro() { + TokenStream::Compiler(DeferredTokenStream::new(proc_macro::Tok= enStream::new())) + } else { + TokenStream::Fallback(fallback::TokenStream::new()) + } + } + + pub(crate) fn from_str_checked(src: &str) -> Result { + if inside_proc_macro() { + Ok(TokenStream::Compiler(DeferredTokenStream::new( + proc_macro::TokenStream::from_str_checked(src)?, + ))) + } else { + Ok(TokenStream::Fallback( + fallback::TokenStream::from_str_checked(src)?, + )) + } + } + + pub(crate) fn is_empty(&self) -> bool { + match self { + TokenStream::Compiler(tts) =3D> tts.is_empty(), + TokenStream::Fallback(tts) =3D> tts.is_empty(), + } + } + + fn unwrap_nightly(self) -> proc_macro::TokenStream { + match self { + TokenStream::Compiler(s) =3D> s.into_token_stream(), + TokenStream::Fallback(_) =3D> mismatch(line!()), + } + } + + fn unwrap_stable(self) -> fallback::TokenStream { + match self { + TokenStream::Compiler(_) =3D> mismatch(line!()), + TokenStream::Fallback(s) =3D> s, + } + } +} + +impl Display for TokenStream { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + TokenStream::Compiler(tts) =3D> Display::fmt(&tts.clone().into= _token_stream(), f), + TokenStream::Fallback(tts) =3D> Display::fmt(tts, f), + } + } +} + +impl From for TokenStream { + fn from(inner: proc_macro::TokenStream) -> Self { + TokenStream::Compiler(DeferredTokenStream::new(inner)) + } +} + +impl From for proc_macro::TokenStream { + fn from(inner: TokenStream) -> Self { + match inner { + TokenStream::Compiler(inner) =3D> inner.into_token_stream(), + TokenStream::Fallback(inner) =3D> { + proc_macro::TokenStream::from_str_unchecked(&inner.to_stri= ng()) + } + } + } +} + +impl From for TokenStream { + fn from(inner: fallback::TokenStream) -> Self { + TokenStream::Fallback(inner) + } +} + +// Assumes inside_proc_macro(). +fn into_compiler_token(token: TokenTree) -> proc_macro::TokenTree { + match token { + TokenTree::Group(tt) =3D> proc_macro::TokenTree::Group(tt.inner.un= wrap_nightly()), + TokenTree::Punct(tt) =3D> { + let spacing =3D match tt.spacing() { + Spacing::Joint =3D> proc_macro::Spacing::Joint, + Spacing::Alone =3D> proc_macro::Spacing::Alone, + }; + let mut punct =3D proc_macro::Punct::new(tt.as_char(), spacing= ); + punct.set_span(tt.span().inner.unwrap_nightly()); + proc_macro::TokenTree::Punct(punct) + } + TokenTree::Ident(tt) =3D> proc_macro::TokenTree::Ident(tt.inner.un= wrap_nightly()), + TokenTree::Literal(tt) =3D> proc_macro::TokenTree::Literal(tt.inne= r.unwrap_nightly()), + } +} + +impl From for TokenStream { + fn from(token: TokenTree) -> Self { + if inside_proc_macro() { + TokenStream::Compiler(DeferredTokenStream::new(proc_macro::Tok= enStream::from( + into_compiler_token(token), + ))) + } else { + TokenStream::Fallback(fallback::TokenStream::from(token)) + } + } +} + +impl FromIterator for TokenStream { + fn from_iter>(trees: I) -> Self { + if inside_proc_macro() { + TokenStream::Compiler(DeferredTokenStream::new( + trees.into_iter().map(into_compiler_token).collect(), + )) + } else { + TokenStream::Fallback(trees.into_iter().collect()) + } + } +} + +impl FromIterator for TokenStream { + fn from_iter>(streams: I) -> Sel= f { + let mut streams =3D streams.into_iter(); + match streams.next() { + Some(TokenStream::Compiler(mut first)) =3D> { + first.evaluate_now(); + first.stream.extend(streams.map(|s| match s { + TokenStream::Compiler(s) =3D> s.into_token_stream(), + TokenStream::Fallback(_) =3D> mismatch(line!()), + })); + TokenStream::Compiler(first) + } + Some(TokenStream::Fallback(mut first)) =3D> { + first.extend(streams.map(|s| match s { + TokenStream::Fallback(s) =3D> s, + TokenStream::Compiler(_) =3D> mismatch(line!()), + })); + TokenStream::Fallback(first) + } + None =3D> TokenStream::new(), + } + } +} + +impl Extend for TokenStream { + fn extend>(&mut self, stream: I) { + match self { + TokenStream::Compiler(tts) =3D> { + // Here is the reason for DeferredTokenStream. + for token in stream { + tts.extra.push(into_compiler_token(token)); + } + } + TokenStream::Fallback(tts) =3D> tts.extend(stream), + } + } +} + +impl Extend for TokenStream { + fn extend>(&mut self, streams: I= ) { + match self { + TokenStream::Compiler(tts) =3D> { + tts.evaluate_now(); + tts.stream + .extend(streams.into_iter().map(TokenStream::unwrap_ni= ghtly)); + } + TokenStream::Fallback(tts) =3D> { + tts.extend(streams.into_iter().map(TokenStream::unwrap_sta= ble)); + } + } + } +} + +impl Debug for TokenStream { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + TokenStream::Compiler(tts) =3D> Debug::fmt(&tts.clone().into_t= oken_stream(), f), + TokenStream::Fallback(tts) =3D> Debug::fmt(tts, f), + } + } +} + +impl LexError { + pub(crate) fn span(&self) -> Span { + match self { + LexError::Compiler(_) | LexError::CompilerPanic =3D> Span::cal= l_site(), + LexError::Fallback(e) =3D> Span::Fallback(e.span()), + } + } +} + +impl From for LexError { + fn from(e: proc_macro::LexError) -> Self { + LexError::Compiler(e) + } +} + +impl From for LexError { + fn from(e: fallback::LexError) -> Self { + LexError::Fallback(e) + } +} + +impl Debug for LexError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + LexError::Compiler(e) =3D> Debug::fmt(e, f), + LexError::Fallback(e) =3D> Debug::fmt(e, f), + LexError::CompilerPanic =3D> { + let fallback =3D fallback::LexError::call_site(); + Debug::fmt(&fallback, f) + } + } + } +} + +impl Display for LexError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + LexError::Compiler(e) =3D> Display::fmt(e, f), + LexError::Fallback(e) =3D> Display::fmt(e, f), + LexError::CompilerPanic =3D> { + let fallback =3D fallback::LexError::call_site(); + Display::fmt(&fallback, f) + } + } + } +} + +#[derive(Clone)] +pub(crate) enum TokenTreeIter { + Compiler(proc_macro::token_stream::IntoIter), + Fallback(fallback::TokenTreeIter), +} + +impl IntoIterator for TokenStream { + type Item =3D TokenTree; + type IntoIter =3D TokenTreeIter; + + fn into_iter(self) -> TokenTreeIter { + match self { + TokenStream::Compiler(tts) =3D> { + TokenTreeIter::Compiler(tts.into_token_stream().into_iter(= )) + } + TokenStream::Fallback(tts) =3D> TokenTreeIter::Fallback(tts.in= to_iter()), + } + } +} + +impl Iterator for TokenTreeIter { + type Item =3D TokenTree; + + fn next(&mut self) -> Option { + let token =3D match self { + TokenTreeIter::Compiler(iter) =3D> iter.next()?, + TokenTreeIter::Fallback(iter) =3D> return iter.next(), + }; + Some(match token { + proc_macro::TokenTree::Group(tt) =3D> { + TokenTree::Group(crate::Group::_new(Group::Compiler(tt))) + } + proc_macro::TokenTree::Punct(tt) =3D> { + let spacing =3D match tt.spacing() { + proc_macro::Spacing::Joint =3D> Spacing::Joint, + proc_macro::Spacing::Alone =3D> Spacing::Alone, + }; + let mut o =3D Punct::new(tt.as_char(), spacing); + o.set_span(crate::Span::_new(Span::Compiler(tt.span()))); + TokenTree::Punct(o) + } + proc_macro::TokenTree::Ident(s) =3D> { + TokenTree::Ident(crate::Ident::_new(Ident::Compiler(s))) + } + proc_macro::TokenTree::Literal(l) =3D> { + TokenTree::Literal(crate::Literal::_new(Literal::Compiler(= l))) + } + }) + } + + fn size_hint(&self) -> (usize, Option) { + match self { + TokenTreeIter::Compiler(tts) =3D> tts.size_hint(), + TokenTreeIter::Fallback(tts) =3D> tts.size_hint(), + } + } +} + +#[derive(Copy, Clone)] +pub(crate) enum Span { + Compiler(proc_macro::Span), + Fallback(fallback::Span), +} + +impl Span { + pub(crate) fn call_site() -> Self { + if inside_proc_macro() { + Span::Compiler(proc_macro::Span::call_site()) + } else { + Span::Fallback(fallback::Span::call_site()) + } + } + + pub(crate) fn mixed_site() -> Self { + if inside_proc_macro() { + Span::Compiler(proc_macro::Span::mixed_site()) + } else { + Span::Fallback(fallback::Span::mixed_site()) + } + } + + #[cfg(super_unstable)] + pub(crate) fn def_site() -> Self { + if inside_proc_macro() { + Span::Compiler(proc_macro::Span::def_site()) + } else { + Span::Fallback(fallback::Span::def_site()) + } + } + + pub(crate) fn resolved_at(&self, other: Span) -> Span { + match (self, other) { + (Span::Compiler(a), Span::Compiler(b)) =3D> Span::Compiler(a.r= esolved_at(b)), + (Span::Fallback(a), Span::Fallback(b)) =3D> Span::Fallback(a.r= esolved_at(b)), + (Span::Compiler(_), Span::Fallback(_)) =3D> mismatch(line!()), + (Span::Fallback(_), Span::Compiler(_)) =3D> mismatch(line!()), + } + } + + pub(crate) fn located_at(&self, other: Span) -> Span { + match (self, other) { + (Span::Compiler(a), Span::Compiler(b)) =3D> Span::Compiler(a.l= ocated_at(b)), + (Span::Fallback(a), Span::Fallback(b)) =3D> Span::Fallback(a.l= ocated_at(b)), + (Span::Compiler(_), Span::Fallback(_)) =3D> mismatch(line!()), + (Span::Fallback(_), Span::Compiler(_)) =3D> mismatch(line!()), + } + } + + pub(crate) fn unwrap(self) -> proc_macro::Span { + match self { + Span::Compiler(s) =3D> s, + Span::Fallback(_) =3D> panic!("proc_macro::Span is only availa= ble in procedural macros"), + } + } + + #[cfg(span_locations)] + pub(crate) fn byte_range(&self) -> Range { + match self { + #[cfg(proc_macro_span)] + Span::Compiler(s) =3D> proc_macro_span::byte_range(s), + #[cfg(not(proc_macro_span))] + Span::Compiler(_) =3D> 0..0, + Span::Fallback(s) =3D> s.byte_range(), + } + } + + #[cfg(span_locations)] + pub(crate) fn start(&self) -> LineColumn { + match self { + #[cfg(proc_macro_span_location)] + Span::Compiler(s) =3D> LineColumn { + line: proc_macro_span_location::line(s), + column: proc_macro_span_location::column(s).saturating_sub= (1), + }, + #[cfg(not(proc_macro_span_location))] + Span::Compiler(_) =3D> LineColumn { line: 0, column: 0 }, + Span::Fallback(s) =3D> s.start(), + } + } + + #[cfg(span_locations)] + pub(crate) fn end(&self) -> LineColumn { + match self { + #[cfg(proc_macro_span_location)] + Span::Compiler(s) =3D> { + let end =3D proc_macro_span_location::end(s); + LineColumn { + line: proc_macro_span_location::line(&end), + column: proc_macro_span_location::column(&end).saturat= ing_sub(1), + } + } + #[cfg(not(proc_macro_span_location))] + Span::Compiler(_) =3D> LineColumn { line: 0, column: 0 }, + Span::Fallback(s) =3D> s.end(), + } + } + + #[cfg(span_locations)] + pub(crate) fn file(&self) -> String { + match self { + #[cfg(proc_macro_span_file)] + Span::Compiler(s) =3D> proc_macro_span_file::file(s), + #[cfg(not(proc_macro_span_file))] + Span::Compiler(_) =3D> "".to_owned(), + Span::Fallback(s) =3D> s.file(), + } + } + + #[cfg(span_locations)] + pub(crate) fn local_file(&self) -> Option { + match self { + #[cfg(proc_macro_span_file)] + Span::Compiler(s) =3D> proc_macro_span_file::local_file(s), + #[cfg(not(proc_macro_span_file))] + Span::Compiler(_) =3D> None, + Span::Fallback(s) =3D> s.local_file(), + } + } + + pub(crate) fn join(&self, other: Span) -> Option { + let ret =3D match (self, other) { + #[cfg(proc_macro_span)] + (Span::Compiler(a), Span::Compiler(b)) =3D> Span::Compiler(pro= c_macro_span::join(a, b)?), + (Span::Fallback(a), Span::Fallback(b)) =3D> Span::Fallback(a.j= oin(b)?), + _ =3D> return None, + }; + Some(ret) + } + + #[cfg(super_unstable)] + pub(crate) fn eq(&self, other: &Span) -> bool { + match (self, other) { + (Span::Compiler(a), Span::Compiler(b)) =3D> a.eq(b), + (Span::Fallback(a), Span::Fallback(b)) =3D> a.eq(b), + _ =3D> false, + } + } + + pub(crate) fn source_text(&self) -> Option { + match self { + #[cfg(not(no_source_text))] + Span::Compiler(s) =3D> s.source_text(), + #[cfg(no_source_text)] + Span::Compiler(_) =3D> None, + Span::Fallback(s) =3D> s.source_text(), + } + } + + fn unwrap_nightly(self) -> proc_macro::Span { + match self { + Span::Compiler(s) =3D> s, + Span::Fallback(_) =3D> mismatch(line!()), + } + } +} + +impl From for crate::Span { + fn from(proc_span: proc_macro::Span) -> Self { + crate::Span::_new(Span::Compiler(proc_span)) + } +} + +impl From for Span { + fn from(inner: fallback::Span) -> Self { + Span::Fallback(inner) + } +} + +impl Debug for Span { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Span::Compiler(s) =3D> Debug::fmt(s, f), + Span::Fallback(s) =3D> Debug::fmt(s, f), + } + } +} + +pub(crate) fn debug_span_field_if_nontrivial(debug: &mut fmt::DebugStruct,= span: Span) { + match span { + Span::Compiler(s) =3D> { + debug.field("span", &s); + } + Span::Fallback(s) =3D> fallback::debug_span_field_if_nontrivial(de= bug, s), + } +} + +#[derive(Clone)] +pub(crate) enum Group { + Compiler(proc_macro::Group), + Fallback(fallback::Group), +} + +impl Group { + pub(crate) fn new(delimiter: Delimiter, stream: TokenStream) -> Self { + match stream { + TokenStream::Compiler(tts) =3D> { + let delimiter =3D match delimiter { + Delimiter::Parenthesis =3D> proc_macro::Delimiter::Par= enthesis, + Delimiter::Bracket =3D> proc_macro::Delimiter::Bracket, + Delimiter::Brace =3D> proc_macro::Delimiter::Brace, + Delimiter::None =3D> proc_macro::Delimiter::None, + }; + Group::Compiler(proc_macro::Group::new(delimiter, tts.into= _token_stream())) + } + TokenStream::Fallback(stream) =3D> { + Group::Fallback(fallback::Group::new(delimiter, stream)) + } + } + } + + pub(crate) fn delimiter(&self) -> Delimiter { + match self { + Group::Compiler(g) =3D> match g.delimiter() { + proc_macro::Delimiter::Parenthesis =3D> Delimiter::Parenth= esis, + proc_macro::Delimiter::Bracket =3D> Delimiter::Bracket, + proc_macro::Delimiter::Brace =3D> Delimiter::Brace, + proc_macro::Delimiter::None =3D> Delimiter::None, + }, + Group::Fallback(g) =3D> g.delimiter(), + } + } + + pub(crate) fn stream(&self) -> TokenStream { + match self { + Group::Compiler(g) =3D> TokenStream::Compiler(DeferredTokenStr= eam::new(g.stream())), + Group::Fallback(g) =3D> TokenStream::Fallback(g.stream()), + } + } + + pub(crate) fn span(&self) -> Span { + match self { + Group::Compiler(g) =3D> Span::Compiler(g.span()), + Group::Fallback(g) =3D> Span::Fallback(g.span()), + } + } + + pub(crate) fn span_open(&self) -> Span { + match self { + Group::Compiler(g) =3D> Span::Compiler(g.span_open()), + Group::Fallback(g) =3D> Span::Fallback(g.span_open()), + } + } + + pub(crate) fn span_close(&self) -> Span { + match self { + Group::Compiler(g) =3D> Span::Compiler(g.span_close()), + Group::Fallback(g) =3D> Span::Fallback(g.span_close()), + } + } + + pub(crate) fn set_span(&mut self, span: Span) { + match (self, span) { + (Group::Compiler(g), Span::Compiler(s)) =3D> g.set_span(s), + (Group::Fallback(g), Span::Fallback(s)) =3D> g.set_span(s), + (Group::Compiler(_), Span::Fallback(_)) =3D> mismatch(line!()), + (Group::Fallback(_), Span::Compiler(_)) =3D> mismatch(line!()), + } + } + + fn unwrap_nightly(self) -> proc_macro::Group { + match self { + Group::Compiler(g) =3D> g, + Group::Fallback(_) =3D> mismatch(line!()), + } + } +} + +impl From for Group { + fn from(g: fallback::Group) -> Self { + Group::Fallback(g) + } +} + +impl Display for Group { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + match self { + Group::Compiler(group) =3D> Display::fmt(group, formatter), + Group::Fallback(group) =3D> Display::fmt(group, formatter), + } + } +} + +impl Debug for Group { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + match self { + Group::Compiler(group) =3D> Debug::fmt(group, formatter), + Group::Fallback(group) =3D> Debug::fmt(group, formatter), + } + } +} + +#[derive(Clone)] +pub(crate) enum Ident { + Compiler(proc_macro::Ident), + Fallback(fallback::Ident), +} + +impl Ident { + #[track_caller] + pub(crate) fn new_checked(string: &str, span: Span) -> Self { + match span { + Span::Compiler(s) =3D> Ident::Compiler(proc_macro::Ident::new(= string, s)), + Span::Fallback(s) =3D> Ident::Fallback(fallback::Ident::new_ch= ecked(string, s)), + } + } + + #[track_caller] + pub(crate) fn new_raw_checked(string: &str, span: Span) -> Self { + match span { + Span::Compiler(s) =3D> Ident::Compiler(proc_macro::Ident::new_= raw(string, s)), + Span::Fallback(s) =3D> Ident::Fallback(fallback::Ident::new_ra= w_checked(string, s)), + } + } + + pub(crate) fn span(&self) -> Span { + match self { + Ident::Compiler(t) =3D> Span::Compiler(t.span()), + Ident::Fallback(t) =3D> Span::Fallback(t.span()), + } + } + + pub(crate) fn set_span(&mut self, span: Span) { + match (self, span) { + (Ident::Compiler(t), Span::Compiler(s)) =3D> t.set_span(s), + (Ident::Fallback(t), Span::Fallback(s)) =3D> t.set_span(s), + (Ident::Compiler(_), Span::Fallback(_)) =3D> mismatch(line!()), + (Ident::Fallback(_), Span::Compiler(_)) =3D> mismatch(line!()), + } + } + + fn unwrap_nightly(self) -> proc_macro::Ident { + match self { + Ident::Compiler(s) =3D> s, + Ident::Fallback(_) =3D> mismatch(line!()), + } + } +} + +impl From for Ident { + fn from(inner: fallback::Ident) -> Self { + Ident::Fallback(inner) + } +} + +impl PartialEq for Ident { + fn eq(&self, other: &Ident) -> bool { + match (self, other) { + (Ident::Compiler(t), Ident::Compiler(o)) =3D> t.to_string() = =3D=3D o.to_string(), + (Ident::Fallback(t), Ident::Fallback(o)) =3D> t =3D=3D o, + (Ident::Compiler(_), Ident::Fallback(_)) =3D> mismatch(line!()= ), + (Ident::Fallback(_), Ident::Compiler(_)) =3D> mismatch(line!()= ), + } + } +} + +impl PartialEq for Ident +where + T: ?Sized + AsRef, +{ + fn eq(&self, other: &T) -> bool { + let other =3D other.as_ref(); + match self { + Ident::Compiler(t) =3D> t.to_string() =3D=3D other, + Ident::Fallback(t) =3D> t =3D=3D other, + } + } +} + +impl Display for Ident { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Ident::Compiler(t) =3D> Display::fmt(t, f), + Ident::Fallback(t) =3D> Display::fmt(t, f), + } + } +} + +impl Debug for Ident { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Ident::Compiler(t) =3D> Debug::fmt(t, f), + Ident::Fallback(t) =3D> Debug::fmt(t, f), + } + } +} + +#[derive(Clone)] +pub(crate) enum Literal { + Compiler(proc_macro::Literal), + Fallback(fallback::Literal), +} + +macro_rules! suffixed_numbers { + ($($name:ident =3D> $kind:ident,)*) =3D> ($( + pub(crate) fn $name(n: $kind) -> Literal { + if inside_proc_macro() { + Literal::Compiler(proc_macro::Literal::$name(n)) + } else { + Literal::Fallback(fallback::Literal::$name(n)) + } + } + )*) +} + +macro_rules! unsuffixed_integers { + ($($name:ident =3D> $kind:ident,)*) =3D> ($( + pub(crate) fn $name(n: $kind) -> Literal { + if inside_proc_macro() { + Literal::Compiler(proc_macro::Literal::$name(n)) + } else { + Literal::Fallback(fallback::Literal::$name(n)) + } + } + )*) +} + +impl Literal { + pub(crate) fn from_str_checked(repr: &str) -> Result { + if inside_proc_macro() { + let literal =3D proc_macro::Literal::from_str_checked(repr)?; + Ok(Literal::Compiler(literal)) + } else { + let literal =3D fallback::Literal::from_str_checked(repr)?; + Ok(Literal::Fallback(literal)) + } + } + + pub(crate) unsafe fn from_str_unchecked(repr: &str) -> Self { + if inside_proc_macro() { + Literal::Compiler(proc_macro::Literal::from_str_unchecked(repr= )) + } else { + Literal::Fallback(unsafe { fallback::Literal::from_str_uncheck= ed(repr) }) + } + } + + suffixed_numbers! { + u8_suffixed =3D> u8, + u16_suffixed =3D> u16, + u32_suffixed =3D> u32, + u64_suffixed =3D> u64, + u128_suffixed =3D> u128, + usize_suffixed =3D> usize, + i8_suffixed =3D> i8, + i16_suffixed =3D> i16, + i32_suffixed =3D> i32, + i64_suffixed =3D> i64, + i128_suffixed =3D> i128, + isize_suffixed =3D> isize, + + f32_suffixed =3D> f32, + f64_suffixed =3D> f64, + } + + unsuffixed_integers! { + u8_unsuffixed =3D> u8, + u16_unsuffixed =3D> u16, + u32_unsuffixed =3D> u32, + u64_unsuffixed =3D> u64, + u128_unsuffixed =3D> u128, + usize_unsuffixed =3D> usize, + i8_unsuffixed =3D> i8, + i16_unsuffixed =3D> i16, + i32_unsuffixed =3D> i32, + i64_unsuffixed =3D> i64, + i128_unsuffixed =3D> i128, + isize_unsuffixed =3D> isize, + } + + pub(crate) fn f32_unsuffixed(f: f32) -> Literal { + if inside_proc_macro() { + Literal::Compiler(proc_macro::Literal::f32_unsuffixed(f)) + } else { + Literal::Fallback(fallback::Literal::f32_unsuffixed(f)) + } + } + + pub(crate) fn f64_unsuffixed(f: f64) -> Literal { + if inside_proc_macro() { + Literal::Compiler(proc_macro::Literal::f64_unsuffixed(f)) + } else { + Literal::Fallback(fallback::Literal::f64_unsuffixed(f)) + } + } + + pub(crate) fn string(string: &str) -> Literal { + if inside_proc_macro() { + Literal::Compiler(proc_macro::Literal::string(string)) + } else { + Literal::Fallback(fallback::Literal::string(string)) + } + } + + pub(crate) fn character(ch: char) -> Literal { + if inside_proc_macro() { + Literal::Compiler(proc_macro::Literal::character(ch)) + } else { + Literal::Fallback(fallback::Literal::character(ch)) + } + } + + pub(crate) fn byte_character(byte: u8) -> Literal { + if inside_proc_macro() { + Literal::Compiler({ + #[cfg(not(no_literal_byte_character))] + { + proc_macro::Literal::byte_character(byte) + } + + #[cfg(no_literal_byte_character)] + { + let fallback =3D fallback::Literal::byte_character(byt= e); + proc_macro::Literal::from_str_unchecked(&fallback.repr) + } + }) + } else { + Literal::Fallback(fallback::Literal::byte_character(byte)) + } + } + + pub(crate) fn byte_string(bytes: &[u8]) -> Literal { + if inside_proc_macro() { + Literal::Compiler(proc_macro::Literal::byte_string(bytes)) + } else { + Literal::Fallback(fallback::Literal::byte_string(bytes)) + } + } + + pub(crate) fn c_string(string: &CStr) -> Literal { + if inside_proc_macro() { + Literal::Compiler({ + #[cfg(not(no_literal_c_string))] + { + proc_macro::Literal::c_string(string) + } + + #[cfg(no_literal_c_string)] + { + let fallback =3D fallback::Literal::c_string(string); + proc_macro::Literal::from_str_unchecked(&fallback.repr) + } + }) + } else { + Literal::Fallback(fallback::Literal::c_string(string)) + } + } + + pub(crate) fn span(&self) -> Span { + match self { + Literal::Compiler(lit) =3D> Span::Compiler(lit.span()), + Literal::Fallback(lit) =3D> Span::Fallback(lit.span()), + } + } + + pub(crate) fn set_span(&mut self, span: Span) { + match (self, span) { + (Literal::Compiler(lit), Span::Compiler(s)) =3D> lit.set_span(= s), + (Literal::Fallback(lit), Span::Fallback(s)) =3D> lit.set_span(= s), + (Literal::Compiler(_), Span::Fallback(_)) =3D> mismatch(line!(= )), + (Literal::Fallback(_), Span::Compiler(_)) =3D> mismatch(line!(= )), + } + } + + pub(crate) fn subspan>(&self, range: R) -> Optio= n { + match self { + #[cfg(proc_macro_span)] + Literal::Compiler(lit) =3D> proc_macro_span::subspan(lit, rang= e).map(Span::Compiler), + #[cfg(not(proc_macro_span))] + Literal::Compiler(_lit) =3D> None, + Literal::Fallback(lit) =3D> lit.subspan(range).map(Span::Fallb= ack), + } + } + + fn unwrap_nightly(self) -> proc_macro::Literal { + match self { + Literal::Compiler(s) =3D> s, + Literal::Fallback(_) =3D> mismatch(line!()), + } + } +} + +impl From for Literal { + fn from(s: fallback::Literal) -> Self { + Literal::Fallback(s) + } +} + +impl Display for Literal { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Literal::Compiler(t) =3D> Display::fmt(t, f), + Literal::Fallback(t) =3D> Display::fmt(t, f), + } + } +} + +impl Debug for Literal { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Literal::Compiler(t) =3D> Debug::fmt(t, f), + Literal::Fallback(t) =3D> Debug::fmt(t, f), + } + } +} + +#[cfg(span_locations)] +pub(crate) fn invalidate_current_thread_spans() { + if inside_proc_macro() { + panic!( + "proc_macro2::extra::invalidate_current_thread_spans is not av= ailable in procedural macros" + ); + } else { + crate::fallback::invalidate_current_thread_spans(); + } +} --=20 2.52.0 From nobody Tue Dec 2 00:46:11 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 180F8313E3A; Mon, 24 Nov 2025 15:19:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997570; cv=none; b=N4xnb+O26h0Tb0Zoe0l80fUalvxsrLLkCwXt5nExcuNY7k85Km/oxeQoL/0ktPpD61ND9cGlryH2qhNp23MFWD/eB2MN75/uu/GWSr5kBMlpHUcQqsMOqlneElQqR2tjsbSgCfe6f7d1d+dW+YNefRX292g1z3l0tYH8Xmznto0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997570; c=relaxed/simple; bh=2eCN8ceP2/4PN0nBj+y8o269rR90z5C7yVZUfrjfyU8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=LL2+H73HcFARTrOKD9bOw8iMfIuyCP6IqQZ49e13y1MoPEOW2n/UWfUzxAlOlmTIDl1+HTAGpybB0LV0RUb5QHpFL0BUu4LY8pItddQms3s4xOXDg2aUkBq5K7fNmKxDG0jrUyOQed7Pv+IVBDwBdzgvvAxKOtQIZG+LF1oiUyo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=suMwj0PS; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="suMwj0PS" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4CF31C116C6; Mon, 24 Nov 2025 15:19:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763997569; bh=2eCN8ceP2/4PN0nBj+y8o269rR90z5C7yVZUfrjfyU8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=suMwj0PSxVMoyT8OA0JXcywHCq8GfvPlgfFub9K67yyK2pXTqkFwITvTmQaKwc+XW oVPi3t6Pbo8QLpBZLtAb+uCmQkb6h0laz/3FkzHR4hu8CsmtsGTYa1wa96kaJpSrfj 1+IjyTy0ojWFKmC3IyuKU7ruHM7kZqWGpJl0Oi1L0RpyScb9TRvCILmisNv5NsirmO ftmKLNX/MDCk9P004SBlKUD/upnOufr6dXWXc4IegB6cKBt2IGGuG6zga9hYaPAEE+ OlarJUXf97jtVa+lR5o6HEKMd96c4/TsJKdAfreg3Qx9reTi3hR6DB2BKm4flB5Nxi lS/a2weLnWf+Q== From: Miguel Ojeda To: Miguel Ojeda , Alex Gaynor , Nathan Chancellor , Nicolas Schier Cc: Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , rust-for-linux@vger.kernel.org, linux-kbuild@vger.kernel.org, linux-kernel@vger.kernel.org, patches@lists.linux.dev, Jesung Yang Subject: [PATCH v2 07/20] rust: proc-macro2: add SPDX License Identifiers Date: Mon, 24 Nov 2025 16:18:19 +0100 Message-ID: <20251124151837.2184382-8-ojeda@kernel.org> In-Reply-To: <20251124151837.2184382-1-ojeda@kernel.org> References: <20251124151837.2184382-1-ojeda@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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. Reviewed-by: Gary Guo Tested-by: Gary Guo Tested-by: Jesung Yang Signed-off-by: Miguel Ojeda --- 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(+) diff --git a/rust/proc-macro2/detection.rs b/rust/proc-macro2/detection.rs index beba7b237395..3de448cb2dde 100644 --- a/rust/proc-macro2/detection.rs +++ b/rust/proc-macro2/detection.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + use core::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Once; =20 diff --git a/rust/proc-macro2/extra.rs b/rust/proc-macro2/extra.rs index 522a90e136be..55feb5ec7526 100644 --- a/rust/proc-macro2/extra.rs +++ b/rust/proc-macro2/extra.rs @@ -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. =20 diff --git a/rust/proc-macro2/fallback.rs b/rust/proc-macro2/fallback.rs index 1560105cfd25..9e005d67f7f5 100644 --- a/rust/proc-macro2/fallback.rs +++ b/rust/proc-macro2/fallback.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + #[cfg(wrap_proc_macro)] use crate::imp; #[cfg(span_locations)] diff --git a/rust/proc-macro2/lib.rs b/rust/proc-macro2/lib.rs index 2984af335adc..7b78d065d51c 100644 --- a/rust/proc-macro2/lib.rs +++ b/rust/proc-macro2/lib.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + //! [![github]](https://github.com/dtolnay/proc-macro2) [![crates-io]= ](https://crates.io/crates/proc-macro2) [![docs-rs]](crate) //! //! [github]: https://img.shields.io/badge/github-8da0cb?style=3Dfor-the-b= adge&labelColor=3D555555&logo=3Dgithub diff --git a/rust/proc-macro2/location.rs b/rust/proc-macro2/location.rs index 7190e2d05255..3a11871e2943 100644 --- a/rust/proc-macro2/location.rs +++ b/rust/proc-macro2/location.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + use core::cmp::Ordering; =20 /// A line-column pair representing the start or end of a `Span`. diff --git a/rust/proc-macro2/marker.rs b/rust/proc-macro2/marker.rs index 23b94ce6fa85..6f322424808c 100644 --- a/rust/proc-macro2/marker.rs +++ b/rust/proc-macro2/marker.rs @@ -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}; diff --git a/rust/proc-macro2/parse.rs b/rust/proc-macro2/parse.rs index b8be403f842f..a005dea1fe88 100644 --- a/rust/proc-macro2/parse.rs +++ b/rust/proc-macro2/parse.rs @@ -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, Liter= al, Span, TokenStream, TokenStreamBuilder, diff --git a/rust/proc-macro2/probe.rs b/rust/proc-macro2/probe.rs index b67f52036218..d68aa8cfd85e 100644 --- a/rust/proc-macro2/probe.rs +++ b/rust/proc-macro2/probe.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + #![allow(dead_code)] =20 #[cfg(proc_macro_span)] diff --git a/rust/proc-macro2/probe/proc_macro_span.rs b/rust/proc-macro2/p= robe/proc_macro_span.rs index 2d7d44e07708..892a7eb3e5a0 100644 --- a/rust/proc-macro2/probe/proc_macro_span.rs +++ b/rust/proc-macro2/probe/proc_macro_span.rs @@ -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 abl= e to // offer these APIs too. diff --git a/rust/proc-macro2/probe/proc_macro_span_file.rs b/rust/proc-mac= ro2/probe/proc_macro_span_file.rs index 8b76bdf5007b..f2dbc4056ebe 100644 --- a/rust/proc-macro2/probe/proc_macro_span_file.rs +++ b/rust/proc-macro2/probe/proc_macro_span_file.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + // The subset of Span's API stabilized in Rust 1.88. =20 extern crate proc_macro; diff --git a/rust/proc-macro2/probe/proc_macro_span_location.rs b/rust/proc= -macro2/probe/proc_macro_span_location.rs index 79da34af54af..ae19c93394fa 100644 --- a/rust/proc-macro2/probe/proc_macro_span_location.rs +++ b/rust/proc-macro2/probe/proc_macro_span_location.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + // The subset of Span's API stabilized in Rust 1.88. =20 extern crate proc_macro; diff --git a/rust/proc-macro2/rcvec.rs b/rust/proc-macro2/rcvec.rs index 23edc77d597f..e224ebe277ef 100644 --- a/rust/proc-macro2/rcvec.rs +++ b/rust/proc-macro2/rcvec.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + use alloc::rc::Rc; use alloc::vec; use core::mem; diff --git a/rust/proc-macro2/wrapper.rs b/rust/proc-macro2/wrapper.rs index 2e3eb5b4d04e..6792e9834111 100644 --- a/rust/proc-macro2/wrapper.rs +++ b/rust/proc-macro2/wrapper.rs @@ -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)] --=20 2.52.0 From nobody Tue Dec 2 00:46:11 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7F9683148A3; Mon, 24 Nov 2025 15:19:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997574; cv=none; b=HSq6/1/+x3TmESPn+saBwIUq8FBfp3HIen+GzolfSggvOTxRJ2A01E8af97Bj4PXMpwlZSNtXat+rA3K3E3nCYrhEZMw/0Pa1T39dxEtMjATO5TwkxKZNvP6OvQG/pPwmF6Tnzp0w4GFGlKnNliDt2BFapldVhP3XBCR8011Skg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997574; c=relaxed/simple; bh=qP8H4KbldX4ym0wDH2ARhCFeX0lZaVOw4pNpCrhYCM4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=CGDzrxac1TrEtatYQZIh91Zgc7zUyxnNDgBmPCQxYI7eq8iy3JfdQMO77cWS5hF7aunFeakbXt3KQui3tptlnykLOJaXd6vyvR7K3uycZToBLBzEy5N8yXz2L4OiE7H7xbYz/uBZAdt30A4g8s8IE3BTgXOSNYnYYCzZeqik1to= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=qt03R+Tc; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="qt03R+Tc" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 71F99C116C6; Mon, 24 Nov 2025 15:19:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763997574; bh=qP8H4KbldX4ym0wDH2ARhCFeX0lZaVOw4pNpCrhYCM4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=qt03R+TcoCq02jXyWc32Z+65gxQd18fX3aF7hJYZI7j+5qw+sIHOSUlP07iUcK1Sd L6FCEvpS18PlsFYPVNfONW5U9JcXnMRFrf6tcyLiyYreQvRSJHQHWlBdrIglCWT/+a SEWIz6BugePvrKsBKVRZwNqkSpRaVQKkn0Xc+Y2XG7WrqEEq1JItr2i003EI0ODMpn PF8GB5jFC4CuUwPkLwVVnki8UW6KMPDLzAF0qvcG5ID3B5ooBQRiuyD99axW/ASEuJ bT3FSCODZ55dtQHVhiWeWOmp9TaDheWf0HAEsHvkeSKxZByQFqaXIEeoChfbMaiiEM COlTzQ0uTq/Rw== From: Miguel Ojeda To: Miguel Ojeda , Alex Gaynor , Nathan Chancellor , Nicolas Schier Cc: Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , rust-for-linux@vger.kernel.org, linux-kbuild@vger.kernel.org, linux-kernel@vger.kernel.org, patches@lists.linux.dev, Jesung Yang Subject: [PATCH v2 08/20] rust: proc-macro2: remove `unicode_ident` dependency Date: Mon, 24 Nov 2025 16:18:20 +0100 Message-ID: <20251124151837.2184382-9-ojeda@kernel.org> In-Reply-To: <20251124151837.2184382-1-ojeda@kernel.org> References: <20251124151837.2184382-1-ojeda@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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. Reviewed-by: Alice Ryhl Reviewed-by: Gary Guo Tested-by: Gary Guo Tested-by: Jesung Yang Signed-off-by: Miguel Ojeda --- rust/proc-macro2/fallback.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/proc-macro2/fallback.rs b/rust/proc-macro2/fallback.rs index 9e005d67f7f5..9b43c97df97a 100644 --- a/rust/proc-macro2/fallback.rs +++ b/rust/proc-macro2/fallback.rs @@ -818,11 +818,11 @@ pub(crate) fn set_span(&mut self, span: Span) { } =20 pub(crate) fn is_ident_start(c: char) -> bool { - c =3D=3D '_' || unicode_ident::is_xid_start(c) + c =3D=3D '_' || c.is_ascii_alphabetic() } =20 pub(crate) fn is_ident_continue(c: char) -> bool { - unicode_ident::is_xid_continue(c) + c =3D=3D '_' || c.is_ascii_alphanumeric() } =20 #[track_caller] --=20 2.52.0 From nobody Tue Dec 2 00:46:11 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B5C30313E29; Mon, 24 Nov 2025 15:19:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997578; cv=none; b=NudYrb4BxDFR5mhDUiCFzQE6ny2CKTzWNTgMTl88ikR8AR7KGrzPCPeU8QqOCf6pmwViinq2hGT6BiHQkX4rEyQYi98nt19RypoEVJTPDiGyP385fep1BEdgJ+cLL8kQCrTYl6BpRW4CuM61Lq/77gfgChDuIldLMHjFpHYaz0I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997578; c=relaxed/simple; bh=QyWf6un0iyXwuZhP/q/B/xBsbhhCsZajTwpxBQ7zkoE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Is2IY5202ph+IGQt7Rt36AR2gU0R86EC5I0tf1mb92orbkskKlAG86Romvqu2TjedtrLgCzZFJc9/mLHIVQKxpcn9i1Pp5NGIiEVpDh1+rIqmfePdWzKxG/wByq/96YyxGpFE6Nj68/TImKKgtGGsGhTkkBC9p2afJACbyz2CMI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=AiunD9dZ; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="AiunD9dZ" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 95DCAC116D0; Mon, 24 Nov 2025 15:19:34 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763997578; bh=QyWf6un0iyXwuZhP/q/B/xBsbhhCsZajTwpxBQ7zkoE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=AiunD9dZAV6vODOPSjFHDCD67itnOpSv/FwyHLi1yETRHs9AJSdRwjvKGTSHOIXRE SXpv9U5ZDFzeK+ED+fEfG0SnLRmlppoyZteYhhdhxPXpPZ0ip9v/9ncm6QyZhjRX79 VWJwxExJYUdRIEqSYN3p8pvzKTwIBETIJdLjWJGc+8Qorb32hyVHCIXbuM8JXAbZhK dEPL4ZAK5qr/4kE/KOYgKyfRONcx2MZoq+Fpej7pdCnoCHvIrnIS+dylXx2vNkEqVQ N2rIWxIz6nQRPkn+RsQ0ne4c8aY5lLkjqApNXovaXg3c5opPDufEHuXNJ3tKh/ttUh ZV7nPcemqIEgQ== From: Miguel Ojeda To: Miguel Ojeda , Alex Gaynor , Nathan Chancellor , Nicolas Schier Cc: Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , rust-for-linux@vger.kernel.org, linux-kbuild@vger.kernel.org, linux-kernel@vger.kernel.org, patches@lists.linux.dev, Jesung Yang Subject: [PATCH v2 09/20] rust: proc-macro2: add `README.md` Date: Mon, 24 Nov 2025 16:18:21 +0100 Message-ID: <20251124151837.2184382-10-ojeda@kernel.org> In-Reply-To: <20251124151837.2184382-1-ojeda@kernel.org> References: <20251124151837.2184382-1-ojeda@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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. Reviewed-by: Alice Ryhl Reviewed-by: Gary Guo Tested-by: Gary Guo Tested-by: Jesung Yang Signed-off-by: Miguel Ojeda --- rust/proc-macro2/README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 rust/proc-macro2/README.md diff --git a/rust/proc-macro2/README.md b/rust/proc-macro2/README.md new file mode 100644 index 000000000000..af044fee4f59 --- /dev/null +++ b/rust/proc-macro2/README.md @@ -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 + 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 --=20 2.52.0 From nobody Tue Dec 2 00:46:11 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F1E703148C4; Mon, 24 Nov 2025 15:19:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997583; cv=none; b=MyWPNVjc4t9P99F1GZ9O0CoXkZrOD/IVMGdOgw4MJjPaPvyP51wb24M2TzmUUXOawKMxqgecem6CvUArbwtyJac3WiEzaOAcydWKoOziMyFDRaMJXwszqewS+VX0WP2TQiFlAZVJF/wRt++6Itaw54zc2bZTOSc+LyOJHuZ9fgw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997583; c=relaxed/simple; bh=UZXBZPFjmbbXq/Epyw48QKoosy278HOUcsBUKqmT3i4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=q/v7DtoetVq9oSlTEVDn6SPzddnQQjaxDs4bvSsbWiyuAnUhLD3c6AnAVH+K7/VGLEIj7XUFM38M06SqhkcQDai9PzcldHuRvPJjmPPW4NC+A08h143DNa160Xi19rQAPGXslsj/l3YtLyRtMixGQNN8KoIuzqziY+4EgdQZxWg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=CC5FeK6Y; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="CC5FeK6Y" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C3BF0C19421; Mon, 24 Nov 2025 15:19:38 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763997582; bh=UZXBZPFjmbbXq/Epyw48QKoosy278HOUcsBUKqmT3i4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=CC5FeK6Yuf3g1LSGiaEHi5CUVQf4U8Yj1aUaYoyPa24BZ1DN5w9m4BE6qesX3eM7f FogAqlPmNP8S7KDL0pN+cLQdyJHLU0Fc+//fdv41gLjUYt9KjEgz6c2rpvro4R9rGF lbYI6K2QtoYXhtmvU4fMzPbwzSyYfQiMgyN+5hvIKNvARwClmCyXczBA7qm74fdrbu 6A/+DV5IVOF1qmGhGWlGoDWZasvLjqadbXRu0MUELmjo0NQfCy32pNPeoq3mG3TRhx N6zIf0oUFeVZdcMVnrpD3zlHanyDK01li1cqRa1O1DOUMuvZ8MnAwDqR2rGGcsBgmc ZuOVdrv8Q8LWg== From: Miguel Ojeda To: Miguel Ojeda , Alex Gaynor , Nathan Chancellor , Nicolas Schier Cc: Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , rust-for-linux@vger.kernel.org, linux-kbuild@vger.kernel.org, linux-kernel@vger.kernel.org, patches@lists.linux.dev, Jesung Yang Subject: [PATCH v2 10/20] rust: proc-macro2: enable support in kbuild Date: Mon, 24 Nov 2025 16:18:22 +0100 Message-ID: <20251124151837.2184382-11-ojeda@kernel.org> In-Reply-To: <20251124151837.2184382-1-ojeda@kernel.org> References: <20251124151837.2184382-1-ojeda@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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. In addition, skip formatting for this vendored crate and take the chance to add a comment mentioning this. Link: https://github.com/rust-lang/rust/pull/123431 [1] Link: https://github.com/rust-lang/rust/pull/112711 [2] Link: https://github.com/rust-lang/rust/pull/119651 [3] Reviewed-by: Gary Guo Tested-by: Gary Guo Tested-by: Jesung Yang Signed-off-by: Miguel Ojeda --- Makefile | 5 +++++ rust/Makefile | 32 +++++++++++++++++++++++++++++-- scripts/generate_rust_analyzer.py | 7 +++++++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index d14824792227..589bcfe3bf75 100644 --- a/Makefile +++ b/Makefile @@ -1826,10 +1826,15 @@ rusttest: prepare $(Q)$(MAKE) $(build)=3Drust $@ =20 # Formatting targets +# +# Generated files as well as vendored crates are skipped. PHONY +=3D rustfmt rustfmtcheck =20 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) =20 diff --git a/rust/Makefile b/rust/Makefile index 0288ca8d270c..7dd1261ad98f 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -27,6 +27,8 @@ endif =20 obj-$(CONFIG_RUST) +=3D exports.o =20 +always-$(CONFIG_RUST) +=3D libproc_macro2.rlib + always-$(CONFIG_RUST_KERNEL_DOCTESTS) +=3D doctests_kernel_generated.rs always-$(CONFIG_RUST_KERNEL_DOCTESTS) +=3D doctests_kernel_generated_kunit= .c =20 @@ -76,6 +78,17 @@ core-flags :=3D \ --edition=3D$(core-edition) \ $(call cfgs-to-flags,$(core-cfgs)) =20 +proc_macro2-cfgs :=3D \ + feature=3D"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 :=3D \ + --cap-lints=3Dallow \ + -Zcrate-attr=3D'feature(proc_macro_byte_character,proc_macro_c_str_lit= erals)' \ + $(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 :=3D $(if $(call rustc-min-version,108800),-C= unsafe-allow-abi-mismatch=3Dfixed-x18) @@ -125,10 +138,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 =20 +rustdoc-proc_macro2: private rustdoc_host =3D yes +rustdoc-proc_macro2: private rustc_target_flags =3D $(proc_macro2-flags) +rustdoc-proc_macro2: $(src)/proc-macro2/lib.rs rustdoc-clean FORCE + +$(call if_changed,rustdoc) + rustdoc-macros: private rustdoc_host =3D yes rustdoc-macros: private rustc_target_flags =3D --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 FOR= CE +$(call if_changed,rustdoc) =20 # Starting with Rust 1.82.0, skipping `-Wrustdoc::unescaped_backticks` sho= uld @@ -185,6 +203,10 @@ rusttestlib-build_error: $(src)/build_error.rs FORCE rusttestlib-ffi: $(src)/ffi.rs FORCE +$(call if_changed,rustc_test_library) =20 +rusttestlib-proc_macro2: private rustc_target_flags =3D $(proc_macro2-flag= s) +rusttestlib-proc_macro2: $(src)/proc-macro2/lib.rs FORCE + +$(call if_changed,rustc_test_library) + rusttestlib-macros: private rustc_target_flags =3D --extern proc_macro rusttestlib-macros: private rustc_test_library_proc =3D yes rusttestlib-macros: $(src)/macros/lib.rs FORCE @@ -431,6 +453,11 @@ quiet_cmd_rustc_procmacrolibrary =3D $(RUSTC_OR_CLIPPY= _QUIET) PL $@ mv $(objtree)/$(obj)/$(patsubst lib%.rlib,%,$(notdir $@)).d $(depfile); \ sed -i '/^\#/d' $(depfile) =20 +$(obj)/libproc_macro2.rlib: private skip_clippy =3D 1 +$(obj)/libproc_macro2.rlib: private rustc_target_flags =3D $(proc_macro2-f= lags) +$(obj)/libproc_macro2.rlib: $(src)/proc-macro2/lib.rs FORCE + +$(call if_changed_dep,rustc_procmacrolibrary) + quiet_cmd_rustc_procmacro =3D $(RUSTC_OR_CLIPPY_QUIET) P $@ cmd_rustc_procmacro =3D \ $(RUSTC_OR_CLIPPY) $(rust_common_flags) $(rustc_target_flags) \ @@ -442,7 +469,7 @@ quiet_cmd_rustc_procmacro =3D $(RUSTC_OR_CLIPPY_QUIET) = P $@ @$(objtree)/include/generated/rustc_cfg $< =20 # 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) =20 $(obj)/$(libpin_init_internal_name): private rustc_target_flags =3D --cfg = kernel @@ -465,6 +492,7 @@ quiet_cmd_rustc_library =3D $(if $(skip_clippy),RUSTC,$= (RUSTC_OR_CLIPPY_QUIET)) L rust-analyzer: $(Q)MAKEFLAGS=3D $(srctree)/scripts/generate_rust_analyzer.py \ --cfgs=3D'core=3D$(core-cfgs)' $(core-edition) \ + --cfgs=3D'proc_macro2=3D$(proc_macro2-cfgs)' \ $(realpath $(srctree)) $(realpath $(objtree)) \ $(rustc_sysroot) $(RUST_LIB_SRC) $(if $(KBUILD_EXTMOD),$(srcroot)) \ > rust-project.json diff --git a/scripts/generate_rust_analyzer.py b/scripts/generate_rust_anal= yzer.py index dedca470adc1..00c6b7cc94b7 100755 --- a/scripts/generate_rust_analyzer.py +++ b/scripts/generate_rust_analyzer.py @@ -86,6 +86,13 @@ def generate_crates(srctree, objtree, sysroot_src, exter= nal_src, cfgs, core_edit [], ) =20 + append_crate( + "proc_macro2", + srctree / "rust" / "proc-macro2" / "lib.rs", + ["core", "alloc", "std", "proc_macro"], + cfg=3Dcrates_cfgs["proc_macro2"], + ) + append_crate( "macros", srctree / "rust" / "macros" / "lib.rs", --=20 2.52.0 From nobody Tue Dec 2 00:46:11 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5DCD031355F; Mon, 24 Nov 2025 15:19:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997587; cv=none; b=BYd763VAysHOfgtcufawUbgx/4CHUl7dV1FY7FENsh3/rPIDbYCgvq1PdXTEHbF9iDdKnAWY/EyMCXqry+onskvdEt2RtxP82XfPo7wPZB7lq0vvojr53aBH9BzgmtVQyDhVAPVri6H800FYTMokfnD/muFoHLrAPZPLdM3CvQM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997587; c=relaxed/simple; bh=IXQIOIe2K/y4GphPyiyCEUbQ34Nszece1DlDsE/1vh8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=D6ZjkPXt3mf4D7avsGqKsqVCHneE51aSboL5o62AU00hM66TpXFBuwG5ip5easeavOP1OF0djEx1+i2hvuBuu/uxy6Z7775VDCwyT/aSQVCbZS3TnmLwaajSbocQG7cUN9lSk8q53hKdnt9aa1GraKaisRZI6HkiOc+XItqcP3k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=WUXUoTMx; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="WUXUoTMx" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 01D78C4CEF1; Mon, 24 Nov 2025 15:19:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763997586; bh=IXQIOIe2K/y4GphPyiyCEUbQ34Nszece1DlDsE/1vh8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=WUXUoTMxUFQEUf5bpRQxSP57gb+9eWViVzZudsvZXs4ianzzzWeobE1UJRig7JDjk c6409I8O7OEzstCYRAd8pksOTf6ioZKJffnIthhwN2paXmhasnFDIyvo4jmfAgk+vq 7LlYDzi/DbdF+hB7nki7xx4itjW3TaQ4Ns6yeSZTtotMMz37385Cd/QxYxmrBIB5CI 6pxE1YgrBJFAB1mMCcvRur9pSM9YZyf/y2tScano9ZELXXI4dbmqU5c32Q0gcmiXvZ EgMM3JxFGSgigr/9TqAW6wh7qavG/QyofCHp0ASbw/Iw+X8tsp+o5O5hxhw67yEfp6 qW48OTfvKzrFA== From: Miguel Ojeda To: Miguel Ojeda , Alex Gaynor , Nathan Chancellor , Nicolas Schier Cc: Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , rust-for-linux@vger.kernel.org, linux-kbuild@vger.kernel.org, linux-kernel@vger.kernel.org, patches@lists.linux.dev, Jesung Yang Subject: [PATCH v2 11/20] rust: quote: import crate Date: Mon, 24 Nov 2025 16:18:23 +0100 Message-ID: <20251124151837.2184382-12-ojeda@kernel.org> In-Reply-To: <20251124151837.2184382-1-ojeda@kernel.org> References: <20251124151837.2184382-1-ojeda@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable 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 Reviewed-by: Gary Guo Tested-by: Gary Guo Tested-by: Jesung Yang Signed-off-by: Miguel Ojeda --- 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 diff --git a/rust/quote/ext.rs b/rust/quote/ext.rs new file mode 100644 index 000000000000..92c2315b182d --- /dev/null +++ b/rust/quote/ext.rs @@ -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(&mut self, token: U) + where + U: Into; + + /// 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 =3D quote!(#X); + /// assert_eq!(tokens.to_string(), "true false"); + /// ``` + fn append_all(&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 tok= ens + /// `U`. + fn append_separated(&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(&mut self, iter: I, term: U) + where + I: IntoIterator, + I::Item: ToTokens, + U: ToTokens; +} + +impl TokenStreamExt for TokenStream { + fn append(&mut self, token: U) + where + U: Into, + { + self.extend(iter::once(token.into())); + } + + fn append_all(&mut self, iter: I) + where + I: IntoIterator, + I::Item: ToTokens, + { + for token in iter { + token.to_tokens(self); + } + } + + fn append_separated(&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(&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 {} +} diff --git a/rust/quote/format.rs b/rust/quote/format.rs new file mode 100644 index 000000000000..ec0bbf38ba37 --- /dev/null +++ b/rust/quote/format.rs @@ -0,0 +1,168 @@ +/// Formatting macro for constructing `Ident`s. +/// +///
+/// +/// # Syntax +/// +/// Syntax is copied from the [`format!`] macro, supporting both positiona= l and +/// named arguments. +/// +/// Only a limited set of formatting traits are supported. The current map= ping +/// of format types to traits is: +/// +/// * `{}` =E2=87=92 [`IdentFragment`] +/// * `{:o}` =E2=87=92 [`Octal`](std::fmt::Octal) +/// * `{:x}` =E2=87=92 [`LowerHex`](std::fmt::LowerHex) +/// * `{:X}` =E2=87=92 [`UpperHex`](std::fmt::UpperHex) +/// * `{:b}` =E2=87=92 [`Binary`](std::fmt::Binary) +/// +/// See [`std::fmt`] for more information. +/// +///
+/// +/// # IdentFragment +/// +/// Unlike `format!`, this macro uses the [`IdentFragment`] formatting tra= it by +/// default. This trait is like `Display`, with a few differences: +/// +/// * `IdentFragment` is only implemented for a limited set of types, such= as +/// unsigned integers and strings. +/// * [`Ident`] arguments will have their `r#` prefixes stripped, if prese= nt. +/// +/// [`IdentFragment`]: crate::IdentFragment +/// [`Ident`]: proc_macro2::Ident +/// +///
+/// +/// # Hygiene +/// +/// The [`Span`] of the first `Ident` argument is used as the span of the = final +/// identifier, falling back to [`Span::call_site`] when no identifiers are +/// provided. +/// +/// ``` +/// # use quote::format_ident; +/// # let ident =3D format_ident!("Ident"); +/// // If `ident` is an Ident, the span of `my_ident` will be inherited fr= om it. +/// let my_ident =3D format_ident!("My{}{}", ident, "IsCool"); +/// assert_eq!(my_ident, "MyIdentIsCool"); +/// ``` +/// +/// Alternatively, the span can be overridden by passing the `span` named +/// argument. +/// +/// ``` +/// # use quote::format_ident; +/// # const IGNORE_TOKENS: &'static str =3D stringify! { +/// let my_span =3D /* ... */; +/// # }; +/// # let my_span =3D proc_macro2::Span::call_site(); +/// format_ident!("MyIdent", span =3D my_span); +/// ``` +/// +/// [`Span`]: proc_macro2::Span +/// [`Span::call_site`]: proc_macro2::Span::call_site +/// +///


+/// +/// # Panics +/// +/// This method will panic if the resulting formatted string is not a valid +/// identifier. +/// +///
+/// +/// # Examples +/// +/// Composing raw and non-raw identifiers: +/// ``` +/// # use quote::format_ident; +/// let my_ident =3D format_ident!("My{}", "Ident"); +/// assert_eq!(my_ident, "MyIdent"); +/// +/// let raw =3D format_ident!("r#Raw"); +/// assert_eq!(raw, "r#Raw"); +/// +/// let my_ident_raw =3D format_ident!("{}Is{}", my_ident, raw); +/// assert_eq!(my_ident_raw, "MyIdentIsRaw"); +/// ``` +/// +/// Integer formatting options: +/// ``` +/// # use quote::format_ident; +/// let num: u32 =3D 10; +/// +/// let decimal =3D format_ident!("Id_{}", num); +/// assert_eq!(decimal, "Id_10"); +/// +/// let octal =3D format_ident!("Id_{:o}", num); +/// assert_eq!(octal, "Id_12"); +/// +/// let binary =3D format_ident!("Id_{:b}", num); +/// assert_eq!(binary, "Id_1010"); +/// +/// let lower_hex =3D format_ident!("Id_{:x}", num); +/// assert_eq!(lower_hex, "Id_a"); +/// +/// let upper_hex =3D format_ident!("Id_{:X}", num); +/// assert_eq!(upper_hex, "Id_A"); +/// ``` +#[macro_export] +macro_rules! format_ident { + ($fmt:expr) =3D> { + $crate::format_ident_impl!([ + $crate::__private::Option::None, + $fmt + ]) + }; + + ($fmt:expr, $($rest:tt)*) =3D> { + $crate::format_ident_impl!([ + $crate::__private::Option::None, + $fmt + ] $($rest)*) + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! format_ident_impl { + // Final state + ([$span:expr, $($fmt:tt)*]) =3D> { + $crate::__private::mk_ident( + &$crate::__private::format!($($fmt)*), + $span, + ) + }; + + // Span argument + ([$old:expr, $($fmt:tt)*] span =3D $span:expr) =3D> { + $crate::format_ident_impl!([$old, $($fmt)*] span =3D $span,) + }; + ([$old:expr, $($fmt:tt)*] span =3D $span:expr, $($rest:tt)*) =3D> { + $crate::format_ident_impl!([ + $crate::__private::Option::Some::<$crate::__private::Span>($sp= an), + $($fmt)* + ] $($rest)*) + }; + + // Named argument + ([$span:expr, $($fmt:tt)*] $name:ident =3D $arg:expr) =3D> { + $crate::format_ident_impl!([$span, $($fmt)*] $name =3D $arg,) + }; + ([$span:expr, $($fmt:tt)*] $name:ident =3D $arg:expr, $($rest:tt)*) = =3D> { + match $crate::__private::IdentFragmentAdapter(&$arg) { + arg =3D> $crate::format_ident_impl!([$span.or(arg.span()), $($= fmt)*, $name =3D arg] $($rest)*), + } + }; + + // Positional argument + ([$span:expr, $($fmt:tt)*] $arg:expr) =3D> { + $crate::format_ident_impl!([$span, $($fmt)*] $arg,) + }; + ([$span:expr, $($fmt:tt)*] $arg:expr, $($rest:tt)*) =3D> { + match $crate::__private::IdentFragmentAdapter(&$arg) { + arg =3D> $crate::format_ident_impl!([$span.or(arg.span()), $($= fmt)*, arg] $($rest)*), + } + }; +} diff --git a/rust/quote/ident_fragment.rs b/rust/quote/ident_fragment.rs new file mode 100644 index 000000000000..6c2a9a87acb4 --- /dev/null +++ b/rust/quote/ident_fragment.rs @@ -0,0 +1,88 @@ +use alloc::borrow::Cow; +use core::fmt; +use proc_macro2::{Ident, Span}; + +/// Specialized formatting trait used by `format_ident!`. +/// +/// [`Ident`] arguments formatted using this trait will have their `r#` pr= efix +/// stripped, if present. +/// +/// See [`format_ident!`] for more information. +/// +/// [`format_ident!`]: crate::format_ident +pub trait IdentFragment { + /// Format this value as an identifier fragment. + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result; + + /// Span associated with this `IdentFragment`. + /// + /// If non-`None`, may be inherited by formatted identifiers. + fn span(&self) -> Option { + None + } +} + +impl IdentFragment for &T { + fn span(&self) -> Option { + ::span(*self) + } + + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + IdentFragment::fmt(*self, f) + } +} + +impl IdentFragment for &mut T { + fn span(&self) -> Option { + ::span(*self) + } + + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + IdentFragment::fmt(*self, f) + } +} + +impl IdentFragment for Ident { + fn span(&self) -> Option { + Some(self.span()) + } + + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let id =3D self.to_string(); + if let Some(id) =3D id.strip_prefix("r#") { + fmt::Display::fmt(id, f) + } else { + fmt::Display::fmt(&id[..], f) + } + } +} + +impl IdentFragment for Cow<'_, T> +where + T: IdentFragment + ToOwned + ?Sized, +{ + fn span(&self) -> Option { + T::span(self) + } + + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + T::fmt(self, f) + } +} + +// Limited set of types which this is implemented for, as we want to avoid= types +// which will often include non-identifier characters in their `Display` i= mpl. +macro_rules! ident_fragment_display { + ($($T:ty),*) =3D> { + $( + impl IdentFragment for $T { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } + } + )* + }; +} + +ident_fragment_display!(bool, str, String, char); +ident_fragment_display!(u8, u16, u32, u64, u128, usize); diff --git a/rust/quote/lib.rs b/rust/quote/lib.rs new file mode 100644 index 000000000000..0a12d607f279 --- /dev/null +++ b/rust/quote/lib.rs @@ -0,0 +1,1454 @@ +//! [![github]](https://github.com/dtolnay/quote) [![crates-io]](http= s://crates.io/crates/quote) [![docs-rs]](https://docs.rs/quote) +//! +//! [github]: https://img.shields.io/badge/github-8da0cb?style=3Dfor-the-b= adge&labelColor=3D555555&logo=3Dgithub +//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=3Dfor= -the-badge&labelColor=3D555555&logo=3Drust +//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=3Dfor-the= -badge&labelColor=3D555555&logo=3Ddocs.rs +//! +//!
+//! +//! This crate provides the [`quote!`] macro for turning Rust syntax tree = data +//! structures into tokens of source code. +//! +//! Procedural macros in Rust receive a stream of tokens as input, execute +//! arbitrary Rust code to determine how to manipulate those tokens, and p= roduce +//! a stream of tokens to hand back to the compiler to compile into the ca= ller's +//! crate. Quasi-quoting is a solution to one piece of that — produc= ing +//! tokens to return to the compiler. +//! +//! The idea of quasi-quoting is that we write *code* that we treat as *da= ta*. +//! Within the `quote!` macro, we can write what looks like code to our te= xt +//! editor or IDE. We get all the benefits of the editor's brace matching, +//! syntax highlighting, indentation, and maybe autocompletion. But rather= than +//! compiling that as code into the current crate, we can treat it as data= , pass +//! it around, mutate it, and eventually hand it back to the compiler as t= okens +//! to compile into the macro caller's crate. +//! +//! This crate is motivated by the procedural macro use case, but is a +//! general-purpose Rust quasi-quoting library and is not specific to proc= edural +//! macros. +//! +//! ```toml +//! [dependencies] +//! quote =3D "1.0" +//! ``` +//! +//!
+//! +//! # Example +//! +//! The following quasi-quoted block of code is something you might find i= n [a] +//! procedural macro having to do with data structure serialization. The `= #var` +//! syntax performs interpolation of runtime variables into the quoted tok= ens. +//! Check out the documentation of the [`quote!`] macro for more detail ab= out +//! the syntax. See also the [`quote_spanned!`] macro which is important f= or +//! implementing hygienic procedural macros. +//! +//! [a]: https://serde.rs/ +//! +//! ``` +//! # use quote::quote; +//! # +//! # let generics =3D ""; +//! # let where_clause =3D ""; +//! # let field_ty =3D ""; +//! # let item_ty =3D ""; +//! # let path =3D ""; +//! # let value =3D ""; +//! # +//! let tokens =3D quote! { +//! struct SerializeWith #generics #where_clause { +//! value: &'a #field_ty, +//! phantom: core::marker::PhantomData<#item_ty>, +//! } +//! +//! impl #generics serde::Serialize for SerializeWith #generics #where= _clause { +//! fn serialize(&self, serializer: S) -> Result +//! where +//! S: serde::Serializer, +//! { +//! #path(self.value, serializer) +//! } +//! } +//! +//! SerializeWith { +//! value: #value, +//! phantom: core::marker::PhantomData::<#item_ty>, +//! } +//! }; +//! ``` +//! +//!
+//! +//! # Non-macro code generators +//! +//! When using `quote` in a build.rs or main.rs and writing the output out= to a +//! file, consider having the code generator pass the tokens through +//! [prettyplease] before writing. This way if an error occurs in the gene= rated +//! code it is convenient for a human to read and debug. +//! +//! [prettyplease]: https://github.com/dtolnay/prettyplease + +// Quote types in rustdoc of other crates get linked to here. +#![doc(html_root_url =3D "https://docs.rs/quote/1.0.40")] +#![allow( + clippy::doc_markdown, + clippy::elidable_lifetime_names, + clippy::missing_errors_doc, + clippy::missing_panics_doc, + clippy::module_name_repetitions, + clippy::needless_lifetimes, + // false positive https://github.com/rust-lang/rust-clippy/issues/6983 + clippy::wrong_self_convention, +)] + +extern crate alloc; + +#[cfg(feature =3D "proc-macro")] +extern crate proc_macro; + +mod ext; +mod format; +mod ident_fragment; +mod to_tokens; + +// Not public API. +#[doc(hidden)] +#[path =3D "runtime.rs"] +pub mod __private; + +pub use crate::ext::TokenStreamExt; +pub use crate::ident_fragment::IdentFragment; +pub use crate::to_tokens::ToTokens; + +// Not public API. +#[doc(hidden)] +pub mod spanned; + +macro_rules! __quote { + ($quote:item) =3D> { + /// The whole point. + /// + /// Performs variable interpolation against the input and produces= it as + /// [`proc_macro2::TokenStream`]. + /// + /// Note: for returning tokens to the compiler in a procedural mac= ro, use + /// `.into()` on the result to convert to [`proc_macro::TokenStrea= m`]. + /// + ///
+ /// + /// # Interpolation + /// + /// Variable interpolation is done with `#var` (similar to `$var` = in + /// `macro_rules!` macros). This grabs the `var` variable that is = currently in + /// scope and inserts it in that location in the output tokens. An= y type + /// implementing the [`ToTokens`] trait can be interpolated. This = includes most + /// Rust primitive types as well as most of the syntax tree types = from the [Syn] + /// crate. + /// + /// [Syn]: https://github.com/dtolnay/syn + /// + /// Repetition is done using `#(...)*` or `#(...),*` again similar= to + /// `macro_rules!`. This iterates through the elements of any vari= able + /// interpolated within the repetition and inserts a copy of the r= epetition body + /// for each one. The variables in an interpolation may be a `Vec`= , slice, + /// `BTreeSet`, or any `Iterator`. + /// + /// - `#(#var)*` =E2=80=94 no separators + /// - `#(#var),*` =E2=80=94 the character before the asterisk is u= sed as a separator + /// - `#( struct #var; )*` =E2=80=94 the repetition can contain ot= her tokens + /// - `#( #k =3D> println!("{}", #v), )*` =E2=80=94 even multiple = interpolations + /// + ///
+ /// + /// # Hygiene + /// + /// Any interpolated tokens preserve the `Span` information provid= ed by their + /// `ToTokens` implementation. Tokens that originate within the `q= uote!` + /// invocation are spanned with [`Span::call_site()`]. + /// + /// [`Span::call_site()`]: proc_macro2::Span::call_site + /// + /// A different span can be provided through the [`quote_spanned!`= ] macro. + /// + ///
+ /// + /// # Return type + /// + /// The macro evaluates to an expression of type `proc_macro2::Tok= enStream`. + /// Meanwhile Rust procedural macros are expected to return the ty= pe + /// `proc_macro::TokenStream`. + /// + /// The difference between the two types is that `proc_macro` type= s are entirely + /// specific to procedural macros and cannot ever exist in code ou= tside of a + /// procedural macro, while `proc_macro2` types may exist anywhere= including + /// tests and non-macro code like main.rs and build.rs. This is wh= y even the + /// procedural macro ecosystem is largely built around `proc_macro= 2`, because + /// that ensures the libraries are unit testable and accessible in= non-macro + /// contexts. + /// + /// There is a [`From`]-conversion in both directions so returning= the output of + /// `quote!` from a procedural macro usually looks like `tokens.in= to()` or + /// `proc_macro::TokenStream::from(tokens)`. + /// + ///
+ /// + /// # Examples + /// + /// ### Procedural macro + /// + /// The structure of a basic procedural macro is as follows. Refer= to the [Syn] + /// crate for further useful guidance on using `quote!` as part of= a procedural + /// macro. + /// + /// [Syn]: https://github.com/dtolnay/syn + /// + /// ``` + /// # #[cfg(any())] + /// extern crate proc_macro; + /// # extern crate proc_macro2; + /// + /// # #[cfg(any())] + /// use proc_macro::TokenStream; + /// # use proc_macro2::TokenStream; + /// use quote::quote; + /// + /// # const IGNORE_TOKENS: &'static str =3D stringify! { + /// #[proc_macro_derive(HeapSize)] + /// # }; + /// pub fn derive_heap_size(input: TokenStream) -> TokenStream { + /// // Parse the input and figure out what implementation to g= enerate... + /// # const IGNORE_TOKENS: &'static str =3D stringify! { + /// let name =3D /* ... */; + /// let expr =3D /* ... */; + /// # }; + /// # + /// # let name =3D 0; + /// # let expr =3D 0; + /// + /// let expanded =3D quote! { + /// // The generated impl. + /// impl heapsize::HeapSize for #name { + /// fn heap_size_of_children(&self) -> usize { + /// #expr + /// } + /// } + /// }; + /// + /// // Hand the output tokens back to the compiler. + /// TokenStream::from(expanded) + /// } + /// ``` + /// + ///


+ /// + /// ### Combining quoted fragments + /// + /// Usually you don't end up constructing an entire final `TokenSt= ream` in one + /// piece. Different parts may come from different helper function= s. The tokens + /// produced by `quote!` themselves implement `ToTokens` and so ca= n be + /// interpolated into later `quote!` invocations to build up a fin= al result. + /// + /// ``` + /// # use quote::quote; + /// # + /// let type_definition =3D quote! {...}; + /// let methods =3D quote! {...}; + /// + /// let tokens =3D quote! { + /// #type_definition + /// #methods + /// }; + /// ``` + /// + ///


+ /// + /// ### Constructing identifiers + /// + /// Suppose we have an identifier `ident` which came from somewher= e in a macro + /// input and we need to modify it in some way for the macro outpu= t. Let's + /// consider prepending the identifier with an underscore. + /// + /// Simply interpolating the identifier next to an underscore will= not have the + /// behavior of concatenating them. The underscore and the identif= ier will + /// continue to be two separate tokens as if you had written `_ x`. + /// + /// ``` + /// # use proc_macro2::{self as syn, Span}; + /// # use quote::quote; + /// # + /// # let ident =3D syn::Ident::new("i", Span::call_site()); + /// # + /// // incorrect + /// quote! { + /// let mut _#ident =3D 0; + /// } + /// # ; + /// ``` + /// + /// The solution is to build a new identifier token with the corre= ct value. As + /// this is such a common case, the [`format_ident!`] macro provid= es a + /// convenient utility for doing so correctly. + /// + /// ``` + /// # use proc_macro2::{Ident, Span}; + /// # use quote::{format_ident, quote}; + /// # + /// # let ident =3D Ident::new("i", Span::call_site()); + /// # + /// let varname =3D format_ident!("_{}", ident); + /// quote! { + /// let mut #varname =3D 0; + /// } + /// # ; + /// ``` + /// + /// Alternatively, the APIs provided by Syn and proc-macro2 can be= used to + /// directly build the identifier. This is roughly equivalent to t= he above, but + /// will not handle `ident` being a raw identifier. + /// + /// ``` + /// # use proc_macro2::{self as syn, Span}; + /// # use quote::quote; + /// # + /// # let ident =3D syn::Ident::new("i", Span::call_site()); + /// # + /// let concatenated =3D format!("_{}", ident); + /// let varname =3D syn::Ident::new(&concatenated, ident.span()); + /// quote! { + /// let mut #varname =3D 0; + /// } + /// # ; + /// ``` + /// + ///


+ /// + /// ### Making method calls + /// + /// Let's say our macro requires some type specified in the macro = input to have + /// a constructor called `new`. We have the type in a variable cal= led + /// `field_type` of type `syn::Type` and want to invoke the constr= uctor. + /// + /// ``` + /// # use quote::quote; + /// # + /// # let field_type =3D quote!(...); + /// # + /// // incorrect + /// quote! { + /// let value =3D #field_type::new(); + /// } + /// # ; + /// ``` + /// + /// This works only sometimes. If `field_type` is `String`, the ex= panded code + /// contains `String::new()` which is fine. But if `field_type` is= something + /// like `Vec` then the expanded code is `Vec::new()` wh= ich is invalid + /// syntax. Ordinarily in handwritten Rust we would write `Vec::::new()` + /// but for macros often the following is more convenient. + /// + /// ``` + /// # use quote::quote; + /// # + /// # let field_type =3D quote!(...); + /// # + /// quote! { + /// let value =3D <#field_type>::new(); + /// } + /// # ; + /// ``` + /// + /// This expands to `>::new()` which behaves correctly. + /// + /// A similar pattern is appropriate for trait methods. + /// + /// ``` + /// # use quote::quote; + /// # + /// # let field_type =3D quote!(...); + /// # + /// quote! { + /// let value =3D <#field_type as core::default::Default>::def= ault(); + /// } + /// # ; + /// ``` + /// + ///


+ /// + /// ### Interpolating text inside of doc comments + /// + /// Neither doc comments nor string literals get interpolation beh= avior in + /// quote: + /// + /// ```compile_fail + /// quote! { + /// /// try to interpolate: #ident + /// /// + /// /// ... + /// } + /// ``` + /// + /// ```compile_fail + /// quote! { + /// #[doc =3D "try to interpolate: #ident"] + /// } + /// ``` + /// + /// Instead the best way to build doc comments that involve variab= les is by + /// formatting the doc string literal outside of quote. + /// + /// ```rust + /// # use proc_macro2::{Ident, Span}; + /// # use quote::quote; + /// # + /// # const IGNORE: &str =3D stringify! { + /// let msg =3D format!(...); + /// # }; + /// # + /// # let ident =3D Ident::new("var", Span::call_site()); + /// # let msg =3D format!("try to interpolate: {}", ident); + /// quote! { + /// #[doc =3D #msg] + /// /// + /// /// ... + /// } + /// # ; + /// ``` + /// + ///


+ /// + /// ### Indexing into a tuple struct + /// + /// When interpolating indices of a tuple or tuple struct, we need= them not to + /// appears suffixed as integer literals by interpolating them as = [`syn::Index`] + /// instead. + /// + /// [`syn::Index`]: https://docs.rs/syn/2.0/syn/struct.Index.html + /// + /// ```compile_fail + /// let i =3D 0usize..self.fields.len(); + /// + /// // expands to 0 + self.0usize.heap_size() + self.1usize.heap_s= ize() + ... + /// // which is not valid syntax + /// quote! { + /// 0 #( + self.#i.heap_size() )* + /// } + /// ``` + /// + /// ``` + /// # use proc_macro2::{Ident, TokenStream}; + /// # use quote::quote; + /// # + /// # mod syn { + /// # use proc_macro2::{Literal, TokenStream}; + /// # use quote::{ToTokens, TokenStreamExt}; + /// # + /// # pub struct Index(usize); + /// # + /// # impl From for Index { + /// # fn from(i: usize) -> Self { + /// # Index(i) + /// # } + /// # } + /// # + /// # impl ToTokens for Index { + /// # fn to_tokens(&self, tokens: &mut TokenStream) { + /// # tokens.append(Literal::usize_unsuffixed(self.0)); + /// # } + /// # } + /// # } + /// # + /// # struct Struct { + /// # fields: Vec, + /// # } + /// # + /// # impl Struct { + /// # fn example(&self) -> TokenStream { + /// let i =3D (0..self.fields.len()).map(syn::Index::from); + /// + /// // expands to 0 + self.0.heap_size() + self.1.heap_size() + ... + /// quote! { + /// 0 #( + self.#i.heap_size() )* + /// } + /// # } + /// # } + /// ``` + $quote + }; +} + +#[cfg(doc)] +__quote![ + #[macro_export] + macro_rules! quote { + ($($tt:tt)*) =3D> { + ... + }; + } +]; + +#[cfg(not(doc))] +__quote![ + #[macro_export] + macro_rules! quote { + () =3D> { + $crate::__private::TokenStream::new() + }; + + // Special case rule for a single tt, for performance. + ($tt:tt) =3D> {{ + let mut _s =3D $crate::__private::TokenStream::new(); + $crate::quote_token!{$tt _s} + _s + }}; + + // Special case rules for two tts, for performance. + (# $var:ident) =3D> {{ + let mut _s =3D $crate::__private::TokenStream::new(); + $crate::ToTokens::to_tokens(&$var, &mut _s); + _s + }}; + ($tt1:tt $tt2:tt) =3D> {{ + let mut _s =3D $crate::__private::TokenStream::new(); + $crate::quote_token!{$tt1 _s} + $crate::quote_token!{$tt2 _s} + _s + }}; + + // Rule for any other number of tokens. + ($($tt:tt)*) =3D> {{ + let mut _s =3D $crate::__private::TokenStream::new(); + $crate::quote_each_token!{_s $($tt)*} + _s + }}; + } +]; + +macro_rules! __quote_spanned { + ($quote_spanned:item) =3D> { + /// Same as `quote!`, but applies a given span to all tokens origi= nating within + /// the macro invocation. + /// + ///
+ /// + /// # Syntax + /// + /// A span expression of type [`Span`], followed by `=3D>`, follow= ed by the tokens + /// to quote. The span expression should be brief — use a va= riable for + /// anything more than a few characters. There should be no space = before the + /// `=3D>` token. + /// + /// [`Span`]: proc_macro2::Span + /// + /// ``` + /// # use proc_macro2::Span; + /// # use quote::quote_spanned; + /// # + /// # const IGNORE_TOKENS: &'static str =3D stringify! { + /// let span =3D /* ... */; + /// # }; + /// # let span =3D Span::call_site(); + /// # let init =3D 0; + /// + /// // On one line, use parentheses. + /// let tokens =3D quote_spanned!(span=3D> Box::into_raw(Box::new(= #init))); + /// + /// // On multiple lines, place the span at the top and use braces. + /// let tokens =3D quote_spanned! {span=3D> + /// Box::into_raw(Box::new(#init)) + /// }; + /// ``` + /// + /// The lack of space before the `=3D>` should look jarring to Rus= t programmers + /// and this is intentional. The formatting is designed to be visi= bly + /// off-balance and draw the eye a particular way, due to the span= expression + /// being evaluated in the context of the procedural macro and the= remaining + /// tokens being evaluated in the generated code. + /// + ///
+ /// + /// # Hygiene + /// + /// Any interpolated tokens preserve the `Span` information provid= ed by their + /// `ToTokens` implementation. Tokens that originate within the `q= uote_spanned!` + /// invocation are spanned with the given span argument. + /// + ///
+ /// + /// # Example + /// + /// The following procedural macro code uses `quote_spanned!` to a= ssert that a + /// particular Rust type implements the [`Sync`] trait so that ref= erences can be + /// safely shared between threads. + /// + /// ``` + /// # use quote::{quote_spanned, TokenStreamExt, ToTokens}; + /// # use proc_macro2::{Span, TokenStream}; + /// # + /// # struct Type; + /// # + /// # impl Type { + /// # fn span(&self) -> Span { + /// # Span::call_site() + /// # } + /// # } + /// # + /// # impl ToTokens for Type { + /// # fn to_tokens(&self, _tokens: &mut TokenStream) {} + /// # } + /// # + /// # let ty =3D Type; + /// # let call_site =3D Span::call_site(); + /// # + /// let ty_span =3D ty.span(); + /// let assert_sync =3D quote_spanned! {ty_span=3D> + /// struct _AssertSync where #ty: Sync; + /// }; + /// ``` + /// + /// If the assertion fails, the user will see an error like the fo= llowing. The + /// input span of their type is highlighted in the error. + /// + /// ```text + /// error[E0277]: the trait bound `*const (): std::marker::Sync` i= s not satisfied + /// --> src/main.rs:10:21 + /// | + /// 10 | static ref PTR: *const () =3D &(); + /// | ^^^^^^^^^ `*const ()` cannot be share= d between threads safely + /// ``` + /// + /// In this example it is important for the where-clause to be spa= nned with the + /// line/column information of the user's input type so that error= messages are + /// placed appropriately by the compiler. + $quote_spanned + }; +} + +#[cfg(doc)] +__quote_spanned![ + #[macro_export] + macro_rules! quote_spanned { + ($span:expr=3D> $($tt:tt)*) =3D> { + ... + }; + } +]; + +#[cfg(not(doc))] +__quote_spanned![ + #[macro_export] + macro_rules! quote_spanned { + ($span:expr=3D>) =3D> {{ + let _: $crate::__private::Span =3D $crate::__private::get_span= ($span).__into_span(); + $crate::__private::TokenStream::new() + }}; + + // Special case rule for a single tt, for performance. + ($span:expr=3D> $tt:tt) =3D> {{ + let mut _s =3D $crate::__private::TokenStream::new(); + let _span: $crate::__private::Span =3D $crate::__private::get_= span($span).__into_span(); + $crate::quote_token_spanned!{$tt _s _span} + _s + }}; + + // Special case rules for two tts, for performance. + ($span:expr=3D> # $var:ident) =3D> {{ + let mut _s =3D $crate::__private::TokenStream::new(); + let _: $crate::__private::Span =3D $crate::__private::get_span= ($span).__into_span(); + $crate::ToTokens::to_tokens(&$var, &mut _s); + _s + }}; + ($span:expr=3D> $tt1:tt $tt2:tt) =3D> {{ + let mut _s =3D $crate::__private::TokenStream::new(); + let _span: $crate::__private::Span =3D $crate::__private::get_= span($span).__into_span(); + $crate::quote_token_spanned!{$tt1 _s _span} + $crate::quote_token_spanned!{$tt2 _s _span} + _s + }}; + + // Rule for any other number of tokens. + ($span:expr=3D> $($tt:tt)*) =3D> {{ + let mut _s =3D $crate::__private::TokenStream::new(); + let _span: $crate::__private::Span =3D $crate::__private::get_= span($span).__into_span(); + $crate::quote_each_token_spanned!{_s _span $($tt)*} + _s + }}; + } +]; + +// Extract the names of all #metavariables and pass them to the $call macr= o. +// +// in: pounded_var_names!(then!(...) a #b c #( #d )* #e) +// out: then!(... b); +// then!(... d); +// then!(... e); +#[macro_export] +#[doc(hidden)] +macro_rules! pounded_var_names { + ($call:ident! $extra:tt $($tts:tt)*) =3D> { + $crate::pounded_var_names_with_context!{$call! $extra + (@ $($tts)*) + ($($tts)* @) + } + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! pounded_var_names_with_context { + ($call:ident! $extra:tt ($($b1:tt)*) ($($curr:tt)*)) =3D> { + $( + $crate::pounded_var_with_context!{$call! $extra $b1 $curr} + )* + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! pounded_var_with_context { + ($call:ident! $extra:tt $b1:tt ( $($inner:tt)* )) =3D> { + $crate::pounded_var_names!{$call! $extra $($inner)*} + }; + + ($call:ident! $extra:tt $b1:tt [ $($inner:tt)* ]) =3D> { + $crate::pounded_var_names!{$call! $extra $($inner)*} + }; + + ($call:ident! $extra:tt $b1:tt { $($inner:tt)* }) =3D> { + $crate::pounded_var_names!{$call! $extra $($inner)*} + }; + + ($call:ident!($($extra:tt)*) # $var:ident) =3D> { + $crate::$call!($($extra)* $var); + }; + + ($call:ident! $extra:tt $b1:tt $curr:tt) =3D> {}; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! quote_bind_into_iter { + ($has_iter:ident $var:ident) =3D> { + // `mut` may be unused if $var occurs multiple times in the list. + #[allow(unused_mut)] + let (mut $var, i) =3D $var.quote_into_iter(); + let $has_iter =3D $has_iter | i; + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! quote_bind_next_or_break { + ($var:ident) =3D> { + let $var =3D match $var.next() { + Some(_x) =3D> $crate::__private::RepInterp(_x), + None =3D> break, + }; + }; +} + +// The obvious way to write this macro is as a tt muncher. This implementa= tion +// does something more complex for two reasons. +// +// - With a tt muncher it's easy to hit Rust's built-in recursion_limit,= which +// this implementation avoids because it isn't tail recursive. +// +// - Compile times for a tt muncher are quadratic relative to the length= of +// the input. This implementation is linear, so it will be faster +// (potentially much faster) for big inputs. However, the constant fac= tors +// of this implementation are higher than that of a tt muncher, so it = is +// somewhat slower than a tt muncher if there are many invocations with +// short inputs. +// +// An invocation like this: +// +// quote_each_token!(_s a b c d e f g h i j); +// +// expands to this: +// +// quote_tokens_with_context!(_s +// (@ @ @ @ @ @ a b c d e f g h i j) +// (@ @ @ @ @ a b c d e f g h i j @) +// (@ @ @ @ a b c d e f g h i j @ @) +// (@ @ @ (a) (b) (c) (d) (e) (f) (g) (h) (i) (j) @ @ @) +// (@ @ a b c d e f g h i j @ @ @ @) +// (@ a b c d e f g h i j @ @ @ @ @) +// (a b c d e f g h i j @ @ @ @ @ @) +// ); +// +// which gets transposed and expanded to this: +// +// quote_token_with_context!(_s @ @ @ @ @ @ a); +// quote_token_with_context!(_s @ @ @ @ @ a b); +// quote_token_with_context!(_s @ @ @ @ a b c); +// quote_token_with_context!(_s @ @ @ (a) b c d); +// quote_token_with_context!(_s @ @ a (b) c d e); +// quote_token_with_context!(_s @ a b (c) d e f); +// quote_token_with_context!(_s a b c (d) e f g); +// quote_token_with_context!(_s b c d (e) f g h); +// quote_token_with_context!(_s c d e (f) g h i); +// quote_token_with_context!(_s d e f (g) h i j); +// quote_token_with_context!(_s e f g (h) i j @); +// quote_token_with_context!(_s f g h (i) j @ @); +// quote_token_with_context!(_s g h i (j) @ @ @); +// quote_token_with_context!(_s h i j @ @ @ @); +// quote_token_with_context!(_s i j @ @ @ @ @); +// quote_token_with_context!(_s j @ @ @ @ @ @); +// +// Without having used muncher-style recursion, we get one invocation of +// quote_token_with_context for each original tt, with three tts of contex= t on +// either side. This is enough for the longest possible interpolation form= (a +// repetition with separator, as in `# (#var) , *`) to be fully represente= d with +// the first or last tt in the middle. +// +// The middle tt (surrounded by parentheses) is the tt being processed. +// +// - When it is a `#`, quote_token_with_context can do an interpolation.= The +// interpolation kind will depend on the three subsequent tts. +// +// - When it is within a later part of an interpolation, it can be ignor= ed +// because the interpolation has already been done. +// +// - When it is not part of an interpolation it can be pushed as a single +// token into the output. +// +// - When the middle token is an unparenthesized `@`, that call is one o= f the +// first 3 or last 3 calls of quote_token_with_context and does not +// correspond to one of the original input tokens, so turns into nothi= ng. +#[macro_export] +#[doc(hidden)] +macro_rules! quote_each_token { + ($tokens:ident $($tts:tt)*) =3D> { + $crate::quote_tokens_with_context!{$tokens + (@ @ @ @ @ @ $($tts)*) + (@ @ @ @ @ $($tts)* @) + (@ @ @ @ $($tts)* @ @) + (@ @ @ $(($tts))* @ @ @) + (@ @ $($tts)* @ @ @ @) + (@ $($tts)* @ @ @ @ @) + ($($tts)* @ @ @ @ @ @) + } + }; +} + +// See the explanation on quote_each_token. +#[macro_export] +#[doc(hidden)] +macro_rules! quote_each_token_spanned { + ($tokens:ident $span:ident $($tts:tt)*) =3D> { + $crate::quote_tokens_with_context_spanned!{$tokens $span + (@ @ @ @ @ @ $($tts)*) + (@ @ @ @ @ $($tts)* @) + (@ @ @ @ $($tts)* @ @) + (@ @ @ $(($tts))* @ @ @) + (@ @ $($tts)* @ @ @ @) + (@ $($tts)* @ @ @ @ @) + ($($tts)* @ @ @ @ @ @) + } + }; +} + +// See the explanation on quote_each_token. +#[macro_export] +#[doc(hidden)] +macro_rules! quote_tokens_with_context { + ($tokens:ident + ($($b3:tt)*) ($($b2:tt)*) ($($b1:tt)*) + ($($curr:tt)*) + ($($a1:tt)*) ($($a2:tt)*) ($($a3:tt)*) + ) =3D> { + $( + $crate::quote_token_with_context!{$tokens $b3 $b2 $b1 $curr $a= 1 $a2 $a3} + )* + }; +} + +// See the explanation on quote_each_token. +#[macro_export] +#[doc(hidden)] +macro_rules! quote_tokens_with_context_spanned { + ($tokens:ident $span:ident + ($($b3:tt)*) ($($b2:tt)*) ($($b1:tt)*) + ($($curr:tt)*) + ($($a1:tt)*) ($($a2:tt)*) ($($a3:tt)*) + ) =3D> { + $( + $crate::quote_token_with_context_spanned!{$tokens $span $b3 $b= 2 $b1 $curr $a1 $a2 $a3} + )* + }; +} + +// See the explanation on quote_each_token. +#[macro_export] +#[doc(hidden)] +macro_rules! quote_token_with_context { + // Unparenthesized `@` indicates this call does not correspond to one = of the + // original input tokens. Ignore it. + ($tokens:ident $b3:tt $b2:tt $b1:tt @ $a1:tt $a2:tt $a3:tt) =3D> {}; + + // A repetition with no separator. + ($tokens:ident $b3:tt $b2:tt $b1:tt (#) ( $($inner:tt)* ) * $a3:tt) = =3D> {{ + use $crate::__private::ext::*; + let has_iter =3D $crate::__private::ThereIsNoIteratorInRepetition; + $crate::pounded_var_names!{quote_bind_into_iter!(has_iter) () $($i= nner)*} + let _: $crate::__private::HasIterator =3D has_iter; + // This is `while true` instead of `loop` because if there are no + // iterators used inside of this repetition then the body would not + // contain any `break`, so the compiler would emit unreachable code + // warnings on anything below the loop. We use has_iter to detect = and + // fail to compile when there are no iterators, so here we just wo= rk + // around the unneeded extra warning. + while true { + $crate::pounded_var_names!{quote_bind_next_or_break!() () $($i= nner)*} + $crate::quote_each_token!{$tokens $($inner)*} + } + }}; + // ... and one step later. + ($tokens:ident $b3:tt $b2:tt # (( $($inner:tt)* )) * $a2:tt $a3:tt) = =3D> {}; + // ... and one step later. + ($tokens:ident $b3:tt # ( $($inner:tt)* ) (*) $a1:tt $a2:tt $a3:tt) = =3D> {}; + + // A repetition with separator. + ($tokens:ident $b3:tt $b2:tt $b1:tt (#) ( $($inner:tt)* ) $sep:tt *) = =3D> {{ + use $crate::__private::ext::*; + let mut _i =3D 0usize; + let has_iter =3D $crate::__private::ThereIsNoIteratorInRepetition; + $crate::pounded_var_names!{quote_bind_into_iter!(has_iter) () $($i= nner)*} + let _: $crate::__private::HasIterator =3D has_iter; + while true { + $crate::pounded_var_names!{quote_bind_next_or_break!() () $($i= nner)*} + if _i > 0 { + $crate::quote_token!{$sep $tokens} + } + _i +=3D 1; + $crate::quote_each_token!{$tokens $($inner)*} + } + }}; + // ... and one step later. + ($tokens:ident $b3:tt $b2:tt # (( $($inner:tt)* )) $sep:tt * $a3:tt) = =3D> {}; + // ... and one step later. + ($tokens:ident $b3:tt # ( $($inner:tt)* ) ($sep:tt) * $a2:tt $a3:tt) = =3D> {}; + // (A special case for `#(var)**`, where the first `*` is treated as t= he + // repetition symbol and the second `*` is treated as an ordinary toke= n.) + ($tokens:ident # ( $($inner:tt)* ) * (*) $a1:tt $a2:tt $a3:tt) =3D> { + // https://github.com/dtolnay/quote/issues/130 + $crate::quote_token!{* $tokens} + }; + // ... and one step later. + ($tokens:ident # ( $($inner:tt)* ) $sep:tt (*) $a1:tt $a2:tt $a3:tt) = =3D> {}; + + // A non-repetition interpolation. + ($tokens:ident $b3:tt $b2:tt $b1:tt (#) $var:ident $a2:tt $a3:tt) =3D>= { + $crate::ToTokens::to_tokens(&$var, &mut $tokens); + }; + // ... and one step later. + ($tokens:ident $b3:tt $b2:tt # ($var:ident) $a1:tt $a2:tt $a3:tt) =3D>= {}; + + // An ordinary token, not part of any interpolation. + ($tokens:ident $b3:tt $b2:tt $b1:tt ($curr:tt) $a1:tt $a2:tt $a3:tt) = =3D> { + $crate::quote_token!{$curr $tokens} + }; +} + +// See the explanation on quote_each_token, and on the individual rules of +// quote_token_with_context. +#[macro_export] +#[doc(hidden)] +macro_rules! quote_token_with_context_spanned { + ($tokens:ident $span:ident $b3:tt $b2:tt $b1:tt @ $a1:tt $a2:tt $a3:tt= ) =3D> {}; + + ($tokens:ident $span:ident $b3:tt $b2:tt $b1:tt (#) ( $($inner:tt)* ) = * $a3:tt) =3D> {{ + use $crate::__private::ext::*; + let has_iter =3D $crate::__private::ThereIsNoIteratorInRepetition; + $crate::pounded_var_names!{quote_bind_into_iter!(has_iter) () $($i= nner)*} + let _: $crate::__private::HasIterator =3D has_iter; + while true { + $crate::pounded_var_names!{quote_bind_next_or_break!() () $($i= nner)*} + $crate::quote_each_token_spanned!{$tokens $span $($inner)*} + } + }}; + ($tokens:ident $span:ident $b3:tt $b2:tt # (( $($inner:tt)* )) * $a2:t= t $a3:tt) =3D> {}; + ($tokens:ident $span:ident $b3:tt # ( $($inner:tt)* ) (*) $a1:tt $a2:t= t $a3:tt) =3D> {}; + + ($tokens:ident $span:ident $b3:tt $b2:tt $b1:tt (#) ( $($inner:tt)* ) = $sep:tt *) =3D> {{ + use $crate::__private::ext::*; + let mut _i =3D 0usize; + let has_iter =3D $crate::__private::ThereIsNoIteratorInRepetition; + $crate::pounded_var_names!{quote_bind_into_iter!(has_iter) () $($i= nner)*} + let _: $crate::__private::HasIterator =3D has_iter; + while true { + $crate::pounded_var_names!{quote_bind_next_or_break!() () $($i= nner)*} + if _i > 0 { + $crate::quote_token_spanned!{$sep $tokens $span} + } + _i +=3D 1; + $crate::quote_each_token_spanned!{$tokens $span $($inner)*} + } + }}; + ($tokens:ident $span:ident $b3:tt $b2:tt # (( $($inner:tt)* )) $sep:tt= * $a3:tt) =3D> {}; + ($tokens:ident $span:ident $b3:tt # ( $($inner:tt)* ) ($sep:tt) * $a2:= tt $a3:tt) =3D> {}; + ($tokens:ident $span:ident # ( $($inner:tt)* ) * (*) $a1:tt $a2:tt $a3= :tt) =3D> { + // https://github.com/dtolnay/quote/issues/130 + $crate::quote_token_spanned!{* $tokens $span} + }; + ($tokens:ident $span:ident # ( $($inner:tt)* ) $sep:tt (*) $a1:tt $a2:= tt $a3:tt) =3D> {}; + + ($tokens:ident $span:ident $b3:tt $b2:tt $b1:tt (#) $var:ident $a2:tt = $a3:tt) =3D> { + $crate::ToTokens::to_tokens(&$var, &mut $tokens); + }; + ($tokens:ident $span:ident $b3:tt $b2:tt # ($var:ident) $a1:tt $a2:tt = $a3:tt) =3D> {}; + + ($tokens:ident $span:ident $b3:tt $b2:tt $b1:tt ($curr:tt) $a1:tt $a2:= tt $a3:tt) =3D> { + $crate::quote_token_spanned!{$curr $tokens $span} + }; +} + +// These rules are ordered by approximate token frequency, at least for the +// first 10 or so, to improve compile times. Having `ident` first is by fa= r the +// most important because it's typically 2-3x more common than the next mo= st +// common token. +// +// Separately, we put the token being matched in the very front so that fa= iling +// rules may fail to match as quickly as possible. +#[macro_export] +#[doc(hidden)] +macro_rules! quote_token { + ($ident:ident $tokens:ident) =3D> { + $crate::__private::push_ident(&mut $tokens, stringify!($ident)); + }; + + (:: $tokens:ident) =3D> { + $crate::__private::push_colon2(&mut $tokens); + }; + + (( $($inner:tt)* ) $tokens:ident) =3D> { + $crate::__private::push_group( + &mut $tokens, + $crate::__private::Delimiter::Parenthesis, + $crate::quote!($($inner)*), + ); + }; + + ([ $($inner:tt)* ] $tokens:ident) =3D> { + $crate::__private::push_group( + &mut $tokens, + $crate::__private::Delimiter::Bracket, + $crate::quote!($($inner)*), + ); + }; + + ({ $($inner:tt)* } $tokens:ident) =3D> { + $crate::__private::push_group( + &mut $tokens, + $crate::__private::Delimiter::Brace, + $crate::quote!($($inner)*), + ); + }; + + (# $tokens:ident) =3D> { + $crate::__private::push_pound(&mut $tokens); + }; + + (, $tokens:ident) =3D> { + $crate::__private::push_comma(&mut $tokens); + }; + + (. $tokens:ident) =3D> { + $crate::__private::push_dot(&mut $tokens); + }; + + (; $tokens:ident) =3D> { + $crate::__private::push_semi(&mut $tokens); + }; + + (: $tokens:ident) =3D> { + $crate::__private::push_colon(&mut $tokens); + }; + + (+ $tokens:ident) =3D> { + $crate::__private::push_add(&mut $tokens); + }; + + (+=3D $tokens:ident) =3D> { + $crate::__private::push_add_eq(&mut $tokens); + }; + + (& $tokens:ident) =3D> { + $crate::__private::push_and(&mut $tokens); + }; + + (&& $tokens:ident) =3D> { + $crate::__private::push_and_and(&mut $tokens); + }; + + (&=3D $tokens:ident) =3D> { + $crate::__private::push_and_eq(&mut $tokens); + }; + + (@ $tokens:ident) =3D> { + $crate::__private::push_at(&mut $tokens); + }; + + (! $tokens:ident) =3D> { + $crate::__private::push_bang(&mut $tokens); + }; + + (^ $tokens:ident) =3D> { + $crate::__private::push_caret(&mut $tokens); + }; + + (^=3D $tokens:ident) =3D> { + $crate::__private::push_caret_eq(&mut $tokens); + }; + + (/ $tokens:ident) =3D> { + $crate::__private::push_div(&mut $tokens); + }; + + (/=3D $tokens:ident) =3D> { + $crate::__private::push_div_eq(&mut $tokens); + }; + + (.. $tokens:ident) =3D> { + $crate::__private::push_dot2(&mut $tokens); + }; + + (... $tokens:ident) =3D> { + $crate::__private::push_dot3(&mut $tokens); + }; + + (..=3D $tokens:ident) =3D> { + $crate::__private::push_dot_dot_eq(&mut $tokens); + }; + + (=3D $tokens:ident) =3D> { + $crate::__private::push_eq(&mut $tokens); + }; + + (=3D=3D $tokens:ident) =3D> { + $crate::__private::push_eq_eq(&mut $tokens); + }; + + (>=3D $tokens:ident) =3D> { + $crate::__private::push_ge(&mut $tokens); + }; + + (> $tokens:ident) =3D> { + $crate::__private::push_gt(&mut $tokens); + }; + + (<=3D $tokens:ident) =3D> { + $crate::__private::push_le(&mut $tokens); + }; + + (< $tokens:ident) =3D> { + $crate::__private::push_lt(&mut $tokens); + }; + + (*=3D $tokens:ident) =3D> { + $crate::__private::push_mul_eq(&mut $tokens); + }; + + (!=3D $tokens:ident) =3D> { + $crate::__private::push_ne(&mut $tokens); + }; + + (| $tokens:ident) =3D> { + $crate::__private::push_or(&mut $tokens); + }; + + (|=3D $tokens:ident) =3D> { + $crate::__private::push_or_eq(&mut $tokens); + }; + + (|| $tokens:ident) =3D> { + $crate::__private::push_or_or(&mut $tokens); + }; + + (? $tokens:ident) =3D> { + $crate::__private::push_question(&mut $tokens); + }; + + (-> $tokens:ident) =3D> { + $crate::__private::push_rarrow(&mut $tokens); + }; + + (<- $tokens:ident) =3D> { + $crate::__private::push_larrow(&mut $tokens); + }; + + (% $tokens:ident) =3D> { + $crate::__private::push_rem(&mut $tokens); + }; + + (%=3D $tokens:ident) =3D> { + $crate::__private::push_rem_eq(&mut $tokens); + }; + + (=3D> $tokens:ident) =3D> { + $crate::__private::push_fat_arrow(&mut $tokens); + }; + + (<< $tokens:ident) =3D> { + $crate::__private::push_shl(&mut $tokens); + }; + + (<<=3D $tokens:ident) =3D> { + $crate::__private::push_shl_eq(&mut $tokens); + }; + + (>> $tokens:ident) =3D> { + $crate::__private::push_shr(&mut $tokens); + }; + + (>>=3D $tokens:ident) =3D> { + $crate::__private::push_shr_eq(&mut $tokens); + }; + + (* $tokens:ident) =3D> { + $crate::__private::push_star(&mut $tokens); + }; + + (- $tokens:ident) =3D> { + $crate::__private::push_sub(&mut $tokens); + }; + + (-=3D $tokens:ident) =3D> { + $crate::__private::push_sub_eq(&mut $tokens); + }; + + ($lifetime:lifetime $tokens:ident) =3D> { + $crate::__private::push_lifetime(&mut $tokens, stringify!($lifetim= e)); + }; + + (_ $tokens:ident) =3D> { + $crate::__private::push_underscore(&mut $tokens); + }; + + ($other:tt $tokens:ident) =3D> { + $crate::__private::parse(&mut $tokens, stringify!($other)); + }; +} + +// See the comment above `quote_token!` about the rule ordering. +#[macro_export] +#[doc(hidden)] +macro_rules! quote_token_spanned { + ($ident:ident $tokens:ident $span:ident) =3D> { + $crate::__private::push_ident_spanned(&mut $tokens, $span, stringi= fy!($ident)); + }; + + (:: $tokens:ident $span:ident) =3D> { + $crate::__private::push_colon2_spanned(&mut $tokens, $span); + }; + + (( $($inner:tt)* ) $tokens:ident $span:ident) =3D> { + $crate::__private::push_group_spanned( + &mut $tokens, + $span, + $crate::__private::Delimiter::Parenthesis, + $crate::quote_spanned!($span=3D> $($inner)*), + ); + }; + + ([ $($inner:tt)* ] $tokens:ident $span:ident) =3D> { + $crate::__private::push_group_spanned( + &mut $tokens, + $span, + $crate::__private::Delimiter::Bracket, + $crate::quote_spanned!($span=3D> $($inner)*), + ); + }; + + ({ $($inner:tt)* } $tokens:ident $span:ident) =3D> { + $crate::__private::push_group_spanned( + &mut $tokens, + $span, + $crate::__private::Delimiter::Brace, + $crate::quote_spanned!($span=3D> $($inner)*), + ); + }; + + (# $tokens:ident $span:ident) =3D> { + $crate::__private::push_pound_spanned(&mut $tokens, $span); + }; + + (, $tokens:ident $span:ident) =3D> { + $crate::__private::push_comma_spanned(&mut $tokens, $span); + }; + + (. $tokens:ident $span:ident) =3D> { + $crate::__private::push_dot_spanned(&mut $tokens, $span); + }; + + (; $tokens:ident $span:ident) =3D> { + $crate::__private::push_semi_spanned(&mut $tokens, $span); + }; + + (: $tokens:ident $span:ident) =3D> { + $crate::__private::push_colon_spanned(&mut $tokens, $span); + }; + + (+ $tokens:ident $span:ident) =3D> { + $crate::__private::push_add_spanned(&mut $tokens, $span); + }; + + (+=3D $tokens:ident $span:ident) =3D> { + $crate::__private::push_add_eq_spanned(&mut $tokens, $span); + }; + + (& $tokens:ident $span:ident) =3D> { + $crate::__private::push_and_spanned(&mut $tokens, $span); + }; + + (&& $tokens:ident $span:ident) =3D> { + $crate::__private::push_and_and_spanned(&mut $tokens, $span); + }; + + (&=3D $tokens:ident $span:ident) =3D> { + $crate::__private::push_and_eq_spanned(&mut $tokens, $span); + }; + + (@ $tokens:ident $span:ident) =3D> { + $crate::__private::push_at_spanned(&mut $tokens, $span); + }; + + (! $tokens:ident $span:ident) =3D> { + $crate::__private::push_bang_spanned(&mut $tokens, $span); + }; + + (^ $tokens:ident $span:ident) =3D> { + $crate::__private::push_caret_spanned(&mut $tokens, $span); + }; + + (^=3D $tokens:ident $span:ident) =3D> { + $crate::__private::push_caret_eq_spanned(&mut $tokens, $span); + }; + + (/ $tokens:ident $span:ident) =3D> { + $crate::__private::push_div_spanned(&mut $tokens, $span); + }; + + (/=3D $tokens:ident $span:ident) =3D> { + $crate::__private::push_div_eq_spanned(&mut $tokens, $span); + }; + + (.. $tokens:ident $span:ident) =3D> { + $crate::__private::push_dot2_spanned(&mut $tokens, $span); + }; + + (... $tokens:ident $span:ident) =3D> { + $crate::__private::push_dot3_spanned(&mut $tokens, $span); + }; + + (..=3D $tokens:ident $span:ident) =3D> { + $crate::__private::push_dot_dot_eq_spanned(&mut $tokens, $span); + }; + + (=3D $tokens:ident $span:ident) =3D> { + $crate::__private::push_eq_spanned(&mut $tokens, $span); + }; + + (=3D=3D $tokens:ident $span:ident) =3D> { + $crate::__private::push_eq_eq_spanned(&mut $tokens, $span); + }; + + (>=3D $tokens:ident $span:ident) =3D> { + $crate::__private::push_ge_spanned(&mut $tokens, $span); + }; + + (> $tokens:ident $span:ident) =3D> { + $crate::__private::push_gt_spanned(&mut $tokens, $span); + }; + + (<=3D $tokens:ident $span:ident) =3D> { + $crate::__private::push_le_spanned(&mut $tokens, $span); + }; + + (< $tokens:ident $span:ident) =3D> { + $crate::__private::push_lt_spanned(&mut $tokens, $span); + }; + + (*=3D $tokens:ident $span:ident) =3D> { + $crate::__private::push_mul_eq_spanned(&mut $tokens, $span); + }; + + (!=3D $tokens:ident $span:ident) =3D> { + $crate::__private::push_ne_spanned(&mut $tokens, $span); + }; + + (| $tokens:ident $span:ident) =3D> { + $crate::__private::push_or_spanned(&mut $tokens, $span); + }; + + (|=3D $tokens:ident $span:ident) =3D> { + $crate::__private::push_or_eq_spanned(&mut $tokens, $span); + }; + + (|| $tokens:ident $span:ident) =3D> { + $crate::__private::push_or_or_spanned(&mut $tokens, $span); + }; + + (? $tokens:ident $span:ident) =3D> { + $crate::__private::push_question_spanned(&mut $tokens, $span); + }; + + (-> $tokens:ident $span:ident) =3D> { + $crate::__private::push_rarrow_spanned(&mut $tokens, $span); + }; + + (<- $tokens:ident $span:ident) =3D> { + $crate::__private::push_larrow_spanned(&mut $tokens, $span); + }; + + (% $tokens:ident $span:ident) =3D> { + $crate::__private::push_rem_spanned(&mut $tokens, $span); + }; + + (%=3D $tokens:ident $span:ident) =3D> { + $crate::__private::push_rem_eq_spanned(&mut $tokens, $span); + }; + + (=3D> $tokens:ident $span:ident) =3D> { + $crate::__private::push_fat_arrow_spanned(&mut $tokens, $span); + }; + + (<< $tokens:ident $span:ident) =3D> { + $crate::__private::push_shl_spanned(&mut $tokens, $span); + }; + + (<<=3D $tokens:ident $span:ident) =3D> { + $crate::__private::push_shl_eq_spanned(&mut $tokens, $span); + }; + + (>> $tokens:ident $span:ident) =3D> { + $crate::__private::push_shr_spanned(&mut $tokens, $span); + }; + + (>>=3D $tokens:ident $span:ident) =3D> { + $crate::__private::push_shr_eq_spanned(&mut $tokens, $span); + }; + + (* $tokens:ident $span:ident) =3D> { + $crate::__private::push_star_spanned(&mut $tokens, $span); + }; + + (- $tokens:ident $span:ident) =3D> { + $crate::__private::push_sub_spanned(&mut $tokens, $span); + }; + + (-=3D $tokens:ident $span:ident) =3D> { + $crate::__private::push_sub_eq_spanned(&mut $tokens, $span); + }; + + ($lifetime:lifetime $tokens:ident $span:ident) =3D> { + $crate::__private::push_lifetime_spanned(&mut $tokens, $span, stri= ngify!($lifetime)); + }; + + (_ $tokens:ident $span:ident) =3D> { + $crate::__private::push_underscore_spanned(&mut $tokens, $span); + }; + + ($other:tt $tokens:ident $span:ident) =3D> { + $crate::__private::parse_spanned(&mut $tokens, $span, stringify!($= other)); + }; +} diff --git a/rust/quote/runtime.rs b/rust/quote/runtime.rs new file mode 100644 index 000000000000..c704ca89411f --- /dev/null +++ b/rust/quote/runtime.rs @@ -0,0 +1,492 @@ +use self::get_span::{GetSpan, GetSpanBase, GetSpanInner}; +use crate::{IdentFragment, ToTokens, TokenStreamExt}; +use core::fmt; +use core::iter; +use core::ops::BitOr; +use proc_macro2::{Group, Ident, Punct, Spacing, TokenTree}; + +#[doc(hidden)] +pub use alloc::format; +#[doc(hidden)] +pub use core::option::Option; + +#[doc(hidden)] +pub type Delimiter =3D proc_macro2::Delimiter; +#[doc(hidden)] +pub type Span =3D proc_macro2::Span; +#[doc(hidden)] +pub type TokenStream =3D proc_macro2::TokenStream; + +#[doc(hidden)] +pub struct HasIterator; // True +#[doc(hidden)] +pub struct ThereIsNoIteratorInRepetition; // False + +impl BitOr for ThereIsNoIteratorInRepetitio= n { + type Output =3D ThereIsNoIteratorInRepetition; + fn bitor(self, _rhs: ThereIsNoIteratorInRepetition) -> ThereIsNoIterat= orInRepetition { + ThereIsNoIteratorInRepetition + } +} + +impl BitOr for HasIterator { + type Output =3D HasIterator; + fn bitor(self, _rhs: ThereIsNoIteratorInRepetition) -> HasIterator { + HasIterator + } +} + +impl BitOr for ThereIsNoIteratorInRepetition { + type Output =3D HasIterator; + fn bitor(self, _rhs: HasIterator) -> HasIterator { + HasIterator + } +} + +impl BitOr for HasIterator { + type Output =3D HasIterator; + fn bitor(self, _rhs: HasIterator) -> HasIterator { + HasIterator + } +} + +/// Extension traits used by the implementation of `quote!`. These are def= ined +/// in separate traits, rather than as a single trait due to ambiguity iss= ues. +/// +/// These traits expose a `quote_into_iter` method which should allow call= ing +/// whichever impl happens to be applicable. Calling that method repeatedl= y on +/// the returned value should be idempotent. +#[doc(hidden)] +pub mod ext { + use super::RepInterp; + use super::{HasIterator as HasIter, ThereIsNoIteratorInRepetition as D= oesNotHaveIter}; + use crate::ToTokens; + use alloc::collections::btree_set::{self, BTreeSet}; + use core::slice; + + /// Extension trait providing the `quote_into_iter` method on iterator= s. + #[doc(hidden)] + pub trait RepIteratorExt: Iterator + Sized { + fn quote_into_iter(self) -> (Self, HasIter) { + (self, HasIter) + } + } + + impl RepIteratorExt for T {} + + /// Extension trait providing the `quote_into_iter` method for + /// non-iterable types. These types interpolate the same value in each + /// iteration of the repetition. + #[doc(hidden)] + pub trait RepToTokensExt { + /// Pretend to be an iterator for the purposes of `quote_into_iter= `. + /// This allows repeated calls to `quote_into_iter` to continue + /// correctly returning DoesNotHaveIter. + fn next(&self) -> Option<&Self> { + Some(self) + } + + fn quote_into_iter(&self) -> (&Self, DoesNotHaveIter) { + (self, DoesNotHaveIter) + } + } + + impl RepToTokensExt for T {} + + /// Extension trait providing the `quote_into_iter` method for types t= hat + /// can be referenced as an iterator. + #[doc(hidden)] + pub trait RepAsIteratorExt<'q> { + type Iter: Iterator; + + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter); + } + + impl<'q, T: RepAsIteratorExt<'q> + ?Sized> RepAsIteratorExt<'q> for &T= { + type Iter =3D T::Iter; + + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { + ::quote_into_iter(*self) + } + } + + impl<'q, T: RepAsIteratorExt<'q> + ?Sized> RepAsIteratorExt<'q> for &m= ut T { + type Iter =3D T::Iter; + + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { + ::quote_into_iter(*self) + } + } + + impl<'q, T: 'q> RepAsIteratorExt<'q> for [T] { + type Iter =3D slice::Iter<'q, T>; + + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { + (self.iter(), HasIter) + } + } + + impl<'q, T: 'q, const N: usize> RepAsIteratorExt<'q> for [T; N] { + type Iter =3D slice::Iter<'q, T>; + + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { + (self.iter(), HasIter) + } + } + + impl<'q, T: 'q> RepAsIteratorExt<'q> for Vec { + type Iter =3D slice::Iter<'q, T>; + + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { + (self.iter(), HasIter) + } + } + + impl<'q, T: 'q> RepAsIteratorExt<'q> for BTreeSet { + type Iter =3D btree_set::Iter<'q, T>; + + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { + (self.iter(), HasIter) + } + } + + impl<'q, T: RepAsIteratorExt<'q>> RepAsIteratorExt<'q> for RepInterp { + type Iter =3D T::Iter; + + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { + self.0.quote_into_iter() + } + } +} + +// Helper type used within interpolations to allow for repeated binding na= mes. +// Implements the relevant traits, and exports a dummy `next()` method. +#[derive(Copy, Clone)] +#[doc(hidden)] +pub struct RepInterp(pub T); + +impl RepInterp { + // This method is intended to look like `Iterator::next`, and is calle= d when + // a name is bound multiple times, as the previous binding will shadow= the + // original `Iterator` object. This allows us to avoid advancing the + // iterator multiple times per iteration. + pub fn next(self) -> Option { + Some(self.0) + } +} + +impl Iterator for RepInterp { + type Item =3D T::Item; + + fn next(&mut self) -> Option { + self.0.next() + } +} + +impl ToTokens for RepInterp { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.0.to_tokens(tokens); + } +} + +#[doc(hidden)] +#[inline] +pub fn get_span(span: T) -> GetSpan { + GetSpan(GetSpanInner(GetSpanBase(span))) +} + +mod get_span { + use core::ops::Deref; + use proc_macro2::extra::DelimSpan; + use proc_macro2::Span; + + pub struct GetSpan(pub(crate) GetSpanInner); + + pub struct GetSpanInner(pub(crate) GetSpanBase); + + pub struct GetSpanBase(pub(crate) T); + + impl GetSpan { + #[inline] + pub fn __into_span(self) -> Span { + ((self.0).0).0 + } + } + + impl GetSpanInner { + #[inline] + pub fn __into_span(&self) -> Span { + (self.0).0.join() + } + } + + impl GetSpanBase { + #[allow(clippy::unused_self)] + pub fn __into_span(&self) -> T { + unreachable!() + } + } + + impl Deref for GetSpan { + type Target =3D GetSpanInner; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl Deref for GetSpanInner { + type Target =3D GetSpanBase; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } + } +} + +#[doc(hidden)] +pub fn push_group(tokens: &mut TokenStream, delimiter: Delimiter, inner: T= okenStream) { + tokens.append(Group::new(delimiter, inner)); +} + +#[doc(hidden)] +pub fn push_group_spanned( + tokens: &mut TokenStream, + span: Span, + delimiter: Delimiter, + inner: TokenStream, +) { + let mut g =3D Group::new(delimiter, inner); + g.set_span(span); + tokens.append(g); +} + +#[doc(hidden)] +pub fn parse(tokens: &mut TokenStream, s: &str) { + let s: TokenStream =3D s.parse().expect("invalid token stream"); + tokens.extend(iter::once(s)); +} + +#[doc(hidden)] +pub fn parse_spanned(tokens: &mut TokenStream, span: Span, s: &str) { + let s: TokenStream =3D s.parse().expect("invalid token stream"); + tokens.extend(s.into_iter().map(|t| respan_token_tree(t, span))); +} + +// Token tree with every span replaced by the given one. +fn respan_token_tree(mut token: TokenTree, span: Span) -> TokenTree { + match &mut token { + TokenTree::Group(g) =3D> { + let stream =3D g + .stream() + .into_iter() + .map(|token| respan_token_tree(token, span)) + .collect(); + *g =3D Group::new(g.delimiter(), stream); + g.set_span(span); + } + other =3D> other.set_span(span), + } + token +} + +#[doc(hidden)] +pub fn push_ident(tokens: &mut TokenStream, s: &str) { + let span =3D Span::call_site(); + push_ident_spanned(tokens, span, s); +} + +#[doc(hidden)] +pub fn push_ident_spanned(tokens: &mut TokenStream, span: Span, s: &str) { + tokens.append(ident_maybe_raw(s, span)); +} + +#[doc(hidden)] +pub fn push_lifetime(tokens: &mut TokenStream, lifetime: &str) { + tokens.extend([ + TokenTree::Punct(Punct::new('\'', Spacing::Joint)), + TokenTree::Ident(Ident::new(&lifetime[1..], Span::call_site())), + ]); +} + +#[doc(hidden)] +pub fn push_lifetime_spanned(tokens: &mut TokenStream, span: Span, lifetim= e: &str) { + tokens.extend([ + TokenTree::Punct({ + let mut apostrophe =3D Punct::new('\'', Spacing::Joint); + apostrophe.set_span(span); + apostrophe + }), + TokenTree::Ident(Ident::new(&lifetime[1..], span)), + ]); +} + +macro_rules! push_punct { + ($name:ident $spanned:ident $char1:tt) =3D> { + #[doc(hidden)] + pub fn $name(tokens: &mut TokenStream) { + tokens.append(Punct::new($char1, Spacing::Alone)); + } + #[doc(hidden)] + pub fn $spanned(tokens: &mut TokenStream, span: Span) { + let mut punct =3D Punct::new($char1, Spacing::Alone); + punct.set_span(span); + tokens.append(punct); + } + }; + ($name:ident $spanned:ident $char1:tt $char2:tt) =3D> { + #[doc(hidden)] + pub fn $name(tokens: &mut TokenStream) { + tokens.append(Punct::new($char1, Spacing::Joint)); + tokens.append(Punct::new($char2, Spacing::Alone)); + } + #[doc(hidden)] + pub fn $spanned(tokens: &mut TokenStream, span: Span) { + let mut punct =3D Punct::new($char1, Spacing::Joint); + punct.set_span(span); + tokens.append(punct); + let mut punct =3D Punct::new($char2, Spacing::Alone); + punct.set_span(span); + tokens.append(punct); + } + }; + ($name:ident $spanned:ident $char1:tt $char2:tt $char3:tt) =3D> { + #[doc(hidden)] + pub fn $name(tokens: &mut TokenStream) { + tokens.append(Punct::new($char1, Spacing::Joint)); + tokens.append(Punct::new($char2, Spacing::Joint)); + tokens.append(Punct::new($char3, Spacing::Alone)); + } + #[doc(hidden)] + pub fn $spanned(tokens: &mut TokenStream, span: Span) { + let mut punct =3D Punct::new($char1, Spacing::Joint); + punct.set_span(span); + tokens.append(punct); + let mut punct =3D Punct::new($char2, Spacing::Joint); + punct.set_span(span); + tokens.append(punct); + let mut punct =3D Punct::new($char3, Spacing::Alone); + punct.set_span(span); + tokens.append(punct); + } + }; +} + +push_punct!(push_add push_add_spanned '+'); +push_punct!(push_add_eq push_add_eq_spanned '+' '=3D'); +push_punct!(push_and push_and_spanned '&'); +push_punct!(push_and_and push_and_and_spanned '&' '&'); +push_punct!(push_and_eq push_and_eq_spanned '&' '=3D'); +push_punct!(push_at push_at_spanned '@'); +push_punct!(push_bang push_bang_spanned '!'); +push_punct!(push_caret push_caret_spanned '^'); +push_punct!(push_caret_eq push_caret_eq_spanned '^' '=3D'); +push_punct!(push_colon push_colon_spanned ':'); +push_punct!(push_colon2 push_colon2_spanned ':' ':'); +push_punct!(push_comma push_comma_spanned ','); +push_punct!(push_div push_div_spanned '/'); +push_punct!(push_div_eq push_div_eq_spanned '/' '=3D'); +push_punct!(push_dot push_dot_spanned '.'); +push_punct!(push_dot2 push_dot2_spanned '.' '.'); +push_punct!(push_dot3 push_dot3_spanned '.' '.' '.'); +push_punct!(push_dot_dot_eq push_dot_dot_eq_spanned '.' '.' '=3D'); +push_punct!(push_eq push_eq_spanned '=3D'); +push_punct!(push_eq_eq push_eq_eq_spanned '=3D' '=3D'); +push_punct!(push_ge push_ge_spanned '>' '=3D'); +push_punct!(push_gt push_gt_spanned '>'); +push_punct!(push_le push_le_spanned '<' '=3D'); +push_punct!(push_lt push_lt_spanned '<'); +push_punct!(push_mul_eq push_mul_eq_spanned '*' '=3D'); +push_punct!(push_ne push_ne_spanned '!' '=3D'); +push_punct!(push_or push_or_spanned '|'); +push_punct!(push_or_eq push_or_eq_spanned '|' '=3D'); +push_punct!(push_or_or push_or_or_spanned '|' '|'); +push_punct!(push_pound push_pound_spanned '#'); +push_punct!(push_question push_question_spanned '?'); +push_punct!(push_rarrow push_rarrow_spanned '-' '>'); +push_punct!(push_larrow push_larrow_spanned '<' '-'); +push_punct!(push_rem push_rem_spanned '%'); +push_punct!(push_rem_eq push_rem_eq_spanned '%' '=3D'); +push_punct!(push_fat_arrow push_fat_arrow_spanned '=3D' '>'); +push_punct!(push_semi push_semi_spanned ';'); +push_punct!(push_shl push_shl_spanned '<' '<'); +push_punct!(push_shl_eq push_shl_eq_spanned '<' '<' '=3D'); +push_punct!(push_shr push_shr_spanned '>' '>'); +push_punct!(push_shr_eq push_shr_eq_spanned '>' '>' '=3D'); +push_punct!(push_star push_star_spanned '*'); +push_punct!(push_sub push_sub_spanned '-'); +push_punct!(push_sub_eq push_sub_eq_spanned '-' '=3D'); + +#[doc(hidden)] +pub fn push_underscore(tokens: &mut TokenStream) { + push_underscore_spanned(tokens, Span::call_site()); +} + +#[doc(hidden)] +pub fn push_underscore_spanned(tokens: &mut TokenStream, span: Span) { + tokens.append(Ident::new("_", span)); +} + +// Helper method for constructing identifiers from the `format_ident!` mac= ro, +// handling `r#` prefixes. +#[doc(hidden)] +pub fn mk_ident(id: &str, span: Option) -> Ident { + let span =3D span.unwrap_or_else(Span::call_site); + ident_maybe_raw(id, span) +} + +fn ident_maybe_raw(id: &str, span: Span) -> Ident { + if let Some(id) =3D id.strip_prefix("r#") { + Ident::new_raw(id, span) + } else { + Ident::new(id, span) + } +} + +// Adapts from `IdentFragment` to `fmt::Display` for use by the `format_id= ent!` +// macro, and exposes span information from these fragments. +// +// This struct also has forwarding implementations of the formatting traits +// `Octal`, `LowerHex`, `UpperHex`, and `Binary` to allow for their use wi= thin +// `format_ident!`. +#[derive(Copy, Clone)] +#[doc(hidden)] +pub struct IdentFragmentAdapter(pub T); + +impl IdentFragmentAdapter { + pub fn span(&self) -> Option { + self.0.span() + } +} + +impl fmt::Display for IdentFragmentAdapter { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + IdentFragment::fmt(&self.0, f) + } +} + +impl fmt::Octal for IdentFragmentAdapter= { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Octal::fmt(&self.0, f) + } +} + +impl fmt::LowerHex for IdentFragmentAdap= ter { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::LowerHex::fmt(&self.0, f) + } +} + +impl fmt::UpperHex for IdentFragmentAdap= ter { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::UpperHex::fmt(&self.0, f) + } +} + +impl fmt::Binary for IdentFragmentAdapter<= T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Binary::fmt(&self.0, f) + } +} diff --git a/rust/quote/spanned.rs b/rust/quote/spanned.rs new file mode 100644 index 000000000000..6eba64445d89 --- /dev/null +++ b/rust/quote/spanned.rs @@ -0,0 +1,50 @@ +use crate::ToTokens; +use proc_macro2::extra::DelimSpan; +use proc_macro2::{Span, TokenStream}; + +// Not public API other than via the syn crate. Use syn::spanned::Spanned. +pub trait Spanned: private::Sealed { + fn __span(&self) -> Span; +} + +impl Spanned for Span { + fn __span(&self) -> Span { + *self + } +} + +impl Spanned for DelimSpan { + fn __span(&self) -> Span { + self.join() + } +} + +impl Spanned for T { + fn __span(&self) -> Span { + join_spans(self.into_token_stream()) + } +} + +fn join_spans(tokens: TokenStream) -> Span { + let mut iter =3D tokens.into_iter().map(|tt| tt.span()); + + let first =3D match iter.next() { + Some(span) =3D> span, + None =3D> return Span::call_site(), + }; + + iter.fold(None, |_prev, next| Some(next)) + .and_then(|last| first.join(last)) + .unwrap_or(first) +} + +mod private { + use crate::ToTokens; + use proc_macro2::extra::DelimSpan; + use proc_macro2::Span; + + pub trait Sealed {} + impl Sealed for Span {} + impl Sealed for DelimSpan {} + impl Sealed for T {} +} diff --git a/rust/quote/to_tokens.rs b/rust/quote/to_tokens.rs new file mode 100644 index 000000000000..f373092b650f --- /dev/null +++ b/rust/quote/to_tokens.rs @@ -0,0 +1,271 @@ +use super::TokenStreamExt; +use alloc::borrow::Cow; +use alloc::rc::Rc; +use core::iter; +use proc_macro2::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTr= ee}; +use std::ffi::{CStr, CString}; + +/// Types that can be interpolated inside a `quote!` invocation. +pub trait ToTokens { + /// Write `self` to the given `TokenStream`. + /// + /// The token append methods provided by the [`TokenStreamExt`] extens= ion + /// trait may be useful for implementing `ToTokens`. + /// + /// # Example + /// + /// Example implementation for a struct representing Rust paths like + /// `std::cmp::PartialEq`: + /// + /// ``` + /// use proc_macro2::{TokenTree, Spacing, Span, Punct, TokenStream}; + /// use quote::{TokenStreamExt, ToTokens}; + /// + /// pub struct Path { + /// pub global: bool, + /// pub segments: Vec, + /// } + /// + /// impl ToTokens for Path { + /// fn to_tokens(&self, tokens: &mut TokenStream) { + /// for (i, segment) in self.segments.iter().enumerate() { + /// if i > 0 || self.global { + /// // Double colon `::` + /// tokens.append(Punct::new(':', Spacing::Joint)); + /// tokens.append(Punct::new(':', Spacing::Alone)); + /// } + /// segment.to_tokens(tokens); + /// } + /// } + /// } + /// # + /// # pub struct PathSegment; + /// # + /// # impl ToTokens for PathSegment { + /// # fn to_tokens(&self, tokens: &mut TokenStream) { + /// # unimplemented!() + /// # } + /// # } + /// ``` + fn to_tokens(&self, tokens: &mut TokenStream); + + /// Convert `self` directly into a `TokenStream` object. + /// + /// This method is implicitly implemented using `to_tokens`, and acts = as a + /// convenience method for consumers of the `ToTokens` trait. + fn to_token_stream(&self) -> TokenStream { + let mut tokens =3D TokenStream::new(); + self.to_tokens(&mut tokens); + tokens + } + + /// Convert `self` directly into a `TokenStream` object. + /// + /// This method is implicitly implemented using `to_tokens`, and acts = as a + /// convenience method for consumers of the `ToTokens` trait. + fn into_token_stream(self) -> TokenStream + where + Self: Sized, + { + self.to_token_stream() + } +} + +impl ToTokens for &T { + fn to_tokens(&self, tokens: &mut TokenStream) { + (**self).to_tokens(tokens); + } +} + +impl ToTokens for &mut T { + fn to_tokens(&self, tokens: &mut TokenStream) { + (**self).to_tokens(tokens); + } +} + +impl<'a, T: ?Sized + ToOwned + ToTokens> ToTokens for Cow<'a, T> { + fn to_tokens(&self, tokens: &mut TokenStream) { + (**self).to_tokens(tokens); + } +} + +impl ToTokens for Box { + fn to_tokens(&self, tokens: &mut TokenStream) { + (**self).to_tokens(tokens); + } +} + +impl ToTokens for Rc { + fn to_tokens(&self, tokens: &mut TokenStream) { + (**self).to_tokens(tokens); + } +} + +impl ToTokens for Option { + fn to_tokens(&self, tokens: &mut TokenStream) { + if let Some(t) =3D self { + t.to_tokens(tokens); + } + } +} + +impl ToTokens for str { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append(Literal::string(self)); + } +} + +impl ToTokens for String { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.as_str().to_tokens(tokens); + } +} + +impl ToTokens for i8 { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append(Literal::i8_suffixed(*self)); + } +} + +impl ToTokens for i16 { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append(Literal::i16_suffixed(*self)); + } +} + +impl ToTokens for i32 { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append(Literal::i32_suffixed(*self)); + } +} + +impl ToTokens for i64 { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append(Literal::i64_suffixed(*self)); + } +} + +impl ToTokens for i128 { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append(Literal::i128_suffixed(*self)); + } +} + +impl ToTokens for isize { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append(Literal::isize_suffixed(*self)); + } +} + +impl ToTokens for u8 { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append(Literal::u8_suffixed(*self)); + } +} + +impl ToTokens for u16 { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append(Literal::u16_suffixed(*self)); + } +} + +impl ToTokens for u32 { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append(Literal::u32_suffixed(*self)); + } +} + +impl ToTokens for u64 { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append(Literal::u64_suffixed(*self)); + } +} + +impl ToTokens for u128 { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append(Literal::u128_suffixed(*self)); + } +} + +impl ToTokens for usize { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append(Literal::usize_suffixed(*self)); + } +} + +impl ToTokens for f32 { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append(Literal::f32_suffixed(*self)); + } +} + +impl ToTokens for f64 { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append(Literal::f64_suffixed(*self)); + } +} + +impl ToTokens for char { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append(Literal::character(*self)); + } +} + +impl ToTokens for bool { + fn to_tokens(&self, tokens: &mut TokenStream) { + let word =3D if *self { "true" } else { "false" }; + tokens.append(Ident::new(word, Span::call_site())); + } +} + +impl ToTokens for CStr { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append(Literal::c_string(self)); + } +} + +impl ToTokens for CString { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append(Literal::c_string(self)); + } +} + +impl ToTokens for Group { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append(self.clone()); + } +} + +impl ToTokens for Ident { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append(self.clone()); + } +} + +impl ToTokens for Punct { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append(self.clone()); + } +} + +impl ToTokens for Literal { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append(self.clone()); + } +} + +impl ToTokens for TokenTree { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append(self.clone()); + } +} + +impl ToTokens for TokenStream { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.extend(iter::once(self.clone())); + } + + fn into_token_stream(self) -> TokenStream { + self + } +} --=20 2.52.0 From nobody Tue Dec 2 00:46:11 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4E8173148DF; Mon, 24 Nov 2025 15:19:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997591; cv=none; b=ZfBRCRMaeRinwxuHRCZz+714WrijQpddIBbt2VmtTWV6R88BM0WVr61nLuSRiMuJJXWX+ufU59K/cU0PmDdP1rA2nej1C+0c+S5qdYMVAnjL3pg0ICM7ifoWy0IIPxybHcK9u/HSkUiK2ATXN73WDhNDg92FJ4A0+HiJqvIm9zk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997591; c=relaxed/simple; bh=h+uXhzfxLe+WQBro751yLnx+zqgOzC+COhhonF/XjoA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jLYYBCUr5spP7MHxK0qUuZr5tZ76e8SB5zVzSviglDTL7dfUqLK+lG/XYB/z6JYdyp/wGr6sjSR6nVffjyF0b4wpd2QLz8Z/sGd81HuzB+J/iAF5VUUFDKqrZaNmEZu41Lp/tfRaNDkBsQ2jmSFH9mCXdQA2k7xH6XtoNhRBXTU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=nAeyLd3p; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="nAeyLd3p" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 46863C19421; Mon, 24 Nov 2025 15:19:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763997590; bh=h+uXhzfxLe+WQBro751yLnx+zqgOzC+COhhonF/XjoA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=nAeyLd3pZxz7xnwKEvgBgKkJb3Rmw0FIuD36fupN1UXxNfdearJ4WaTmwWODgs5zF +WG1XZBFM/0UsRC28tnmjrqQVDTA4AYYRGX+GirPe98H9NVnPzSdW4dl0yJMjc9xHE 0UMSDW7HqGDJXRGEgm/KBD0633DuDdsG8mceBgo6KgAe8qkze+5/bbhSSqqyVw9t+U LL7IELEae5hcaMAsDLdc9QsCbqeUylPmmIpV4QiN9Vve5CktTlqKyhUbU/9Nqm2odL xYztoov/qjiubfvoyd8cmEEAK9OQK5xajEy8XtCxckljU4/ymUbDNeW9/HYsBIfBLN EWgtZJd/iuwnw== From: Miguel Ojeda To: Miguel Ojeda , Alex Gaynor , Nathan Chancellor , Nicolas Schier Cc: Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , rust-for-linux@vger.kernel.org, linux-kbuild@vger.kernel.org, linux-kernel@vger.kernel.org, patches@lists.linux.dev, Jesung Yang Subject: [PATCH v2 12/20] rust: quote: add SPDX License Identifiers Date: Mon, 24 Nov 2025 16:18:24 +0100 Message-ID: <20251124151837.2184382-13-ojeda@kernel.org> In-Reply-To: <20251124151837.2184382-1-ojeda@kernel.org> References: <20251124151837.2184382-1-ojeda@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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. Reviewed-by: Gary Guo Tested-by: Gary Guo Tested-by: Jesung Yang Signed-off-by: Miguel Ojeda --- 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(+) diff --git a/rust/quote/ext.rs b/rust/quote/ext.rs index 92c2315b182d..977d2f0c5919 100644 --- a/rust/quote/ext.rs +++ b/rust/quote/ext.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + use super::ToTokens; use core::iter; use proc_macro2::{TokenStream, TokenTree}; diff --git a/rust/quote/format.rs b/rust/quote/format.rs index ec0bbf38ba37..6e3d55b6e427 100644 --- a/rust/quote/format.rs +++ b/rust/quote/format.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + /// Formatting macro for constructing `Ident`s. /// ///
diff --git a/rust/quote/ident_fragment.rs b/rust/quote/ident_fragment.rs index 6c2a9a87acb4..d98106f17666 100644 --- a/rust/quote/ident_fragment.rs +++ b/rust/quote/ident_fragment.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + use alloc::borrow::Cow; use core::fmt; use proc_macro2::{Ident, Span}; diff --git a/rust/quote/lib.rs b/rust/quote/lib.rs index 0a12d607f279..cc1637660a75 100644 --- a/rust/quote/lib.rs +++ b/rust/quote/lib.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + //! [![github]](https://github.com/dtolnay/quote) [![crates-io]](http= s://crates.io/crates/quote) [![docs-rs]](https://docs.rs/quote) //! //! [github]: https://img.shields.io/badge/github-8da0cb?style=3Dfor-the-b= adge&labelColor=3D555555&logo=3Dgithub diff --git a/rust/quote/runtime.rs b/rust/quote/runtime.rs index c704ca89411f..09a94f5dd4fe 100644 --- a/rust/quote/runtime.rs +++ b/rust/quote/runtime.rs @@ -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; diff --git a/rust/quote/spanned.rs b/rust/quote/spanned.rs index 6eba64445d89..54ce9177f45e 100644 --- a/rust/quote/spanned.rs +++ b/rust/quote/spanned.rs @@ -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}; diff --git a/rust/quote/to_tokens.rs b/rust/quote/to_tokens.rs index f373092b650f..1af1089e1423 100644 --- a/rust/quote/to_tokens.rs +++ b/rust/quote/to_tokens.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + use super::TokenStreamExt; use alloc::borrow::Cow; use alloc::rc::Rc; --=20 2.52.0 From nobody Tue Dec 2 00:46:11 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 79507313E0C; Mon, 24 Nov 2025 15:19:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997595; cv=none; b=Ay9hDKvOTak8B/xqMmcgZ686nskDDcZlNddrA7cJxgEkMXRi5QZsIdY1sZjS+Wvu2Ht7RbQfu+YI0qm+v4fm/X651nicIlVvbKwSnmqH6Q1eGGuBee4yIJMXrsv9XlII1wvNMX1wYREguywaZxGVRdMYCsHaSJbBXZaIokc+jKw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997595; c=relaxed/simple; bh=wRcdRtTXxPKRHOS3E56w6Kk4IiSME0GgcnJ2nXkKYL8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=X2AU5kIvMA7xrJQVYzVVoa5bUzSiHl8rrQJBo9ibIE9dE2YHI4ETnmkyjXQOP/l4P8+YCqowg7FVfZceSFt+mozdn6ua5yp88h2odi59P4XrewXeG4TwNm7hTf+afyitXPTMhjUr3GUIImHV+ZJXIWxAVbanx0FWobUXytTUTag= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=WoLkt/iA; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="WoLkt/iA" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 68C44C4CEF1; Mon, 24 Nov 2025 15:19:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763997595; bh=wRcdRtTXxPKRHOS3E56w6Kk4IiSME0GgcnJ2nXkKYL8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=WoLkt/iAdGrSFMnzhd6xzYsEhnpv18k066He9lyuudcnRMEFo4bRL51k4KKjMj7Cq aTyTht3GGEw7EAgsv4PY7tCFn3qV15ZIT6G6DCPB3lwMytefd3XJjnKuESb3Zv+0RZ erObp6FcneaEn8kG8IuC+r5JJs4bkVN32y08o7F7DUlb4XmuQj6BKWifHEynr3LsX4 0EoDi5sye5lqAl8BtuFVoG0/VhUdl2eUTxVe6iEQVFc2sPSmNHGP3KIAmwX1CKGxkg SXO4x8sqpxulhYTfGeIIxzMv+2eQ73qlLTE3FIXQUyCMhqurHV7au30n2lYzu7p7y0 PmPVajmASUjhQ== From: Miguel Ojeda To: Miguel Ojeda , Alex Gaynor , Nathan Chancellor , Nicolas Schier Cc: Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , rust-for-linux@vger.kernel.org, linux-kbuild@vger.kernel.org, linux-kernel@vger.kernel.org, patches@lists.linux.dev, Jesung Yang Subject: [PATCH v2 13/20] rust: quote: add `README.md` Date: Mon, 24 Nov 2025 16:18:25 +0100 Message-ID: <20251124151837.2184382-14-ojeda@kernel.org> In-Reply-To: <20251124151837.2184382-1-ojeda@kernel.org> References: <20251124151837.2184382-1-ojeda@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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. Reviewed-by: Gary Guo Tested-by: Gary Guo Tested-by: Jesung Yang Signed-off-by: Miguel Ojeda --- rust/quote/README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 rust/quote/README.md diff --git a/rust/quote/README.md b/rust/quote/README.md new file mode 100644 index 000000000000..186123eff1cb --- /dev/null +++ b/rust/quote/README.md @@ -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 +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 --=20 2.52.0 From nobody Tue Dec 2 00:46:11 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 65B2631355A; Mon, 24 Nov 2025 15:19:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997599; cv=none; b=EdHw5R48CZhElzCGueimKrgowRSxcNlnUsFSQJRE0dYTVviWNJ3OEQnAq5wtFNq5txPOaDg8ccokorQRMfdRWZ/xuhGR7OrArvJkVh7DxY31lia7jkxuIq+D6YG7f2FobOUTS9+FaF06LqF2NxOR41fmAvQ+9DLkXGVIdPu/ufk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997599; c=relaxed/simple; bh=9o7Mk4OPTg3U83R+FC8FwjwEWbG4Bl7tPSgERthXmXs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MWtBTxZCY8Wjj9H89hMOj0Br8KE4em/C5wVtoVfUZqrpbEWMOPW3JBl+Wti1r+wyjPOHw2HGVim5HtUiOv9/z+ED58IufAgE4Avw1h26/6Po5/2v8C3faU3TFXY7GgJI3hBY6tie9zE47Lb0FpwpGNCz6rE4v/WFSpSYk+DAA3M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=auZhmb4K; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="auZhmb4K" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 8C070C116C6; Mon, 24 Nov 2025 15:19:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763997599; bh=9o7Mk4OPTg3U83R+FC8FwjwEWbG4Bl7tPSgERthXmXs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=auZhmb4KiLqbM2mUSC9CZ3NcG7BhP/vedqZelOKVJbXLxlNpoTtLvh8DZ701Rlf5z BVdc66orhQW06k3g/zoNaBp0yBuH7iLYOwP3FiO9ZVZ7/3jGwUL9cFtYfcziv2o8cn GHSQE55h9pBfKGWyzBOmN8nju5migXE5gT4yjc4G8E0WOZ07MmZdXq3MCDQl07Say3 awfjOZO88Np6EX3xoY7u5rWRyuOINCeBAIjPTTYBWEMyH4oxhkvp0S3q3yN3IGeZjC Gpkhiq13up02uaDL5dBTgNpr249JEBXbZJujBmCd+4LldVYGDhpup3FMI9XdImIv7U /I4IfKs6C0D6Q== From: Miguel Ojeda To: Miguel Ojeda , Alex Gaynor , Nathan Chancellor , Nicolas Schier Cc: Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , rust-for-linux@vger.kernel.org, linux-kbuild@vger.kernel.org, linux-kernel@vger.kernel.org, patches@lists.linux.dev, Jesung Yang Subject: [PATCH v2 14/20] rust: quote: enable support in kbuild Date: Mon, 24 Nov 2025 16:18:26 +0100 Message-ID: <20251124151837.2184382-15-ojeda@kernel.org> In-Reply-To: <20251124151837.2184382-1-ojeda@kernel.org> References: <20251124151837.2184382-1-ojeda@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" With all the new files in place and ready from the new crate, enable the support for it in the build system. Reviewed-by: Alice Ryhl Reviewed-by: Gary Guo Tested-by: Gary Guo Tested-by: Jesung Yang Signed-off-by: Miguel Ojeda --- Makefile | 1 + rust/Makefile | 38 ++++++++++++++++++++++++++++--- scripts/generate_rust_analyzer.py | 7 ++++++ 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 589bcfe3bf75..85da055e4f00 100644 --- a/Makefile +++ b/Makefile @@ -1834,6 +1834,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) diff --git a/rust/Makefile b/rust/Makefile index 7dd1261ad98f..4f4a00594142 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -27,7 +27,7 @@ endif =20 obj-$(CONFIG_RUST) +=3D exports.o =20 -always-$(CONFIG_RUST) +=3D libproc_macro2.rlib +always-$(CONFIG_RUST) +=3D libproc_macro2.rlib libquote.rlib =20 always-$(CONFIG_RUST_KERNEL_DOCTESTS) +=3D doctests_kernel_generated.rs always-$(CONFIG_RUST_KERNEL_DOCTESTS) +=3D doctests_kernel_generated_kunit= .c @@ -89,6 +89,18 @@ proc_macro2-flags :=3D \ -Zcrate-attr=3D'feature(proc_macro_byte_character,proc_macro_c_str_lit= erals)' \ $(call cfgs-to-flags,$(proc_macro2-cfgs)) =20 +quote-cfgs :=3D \ + feature=3D"proc-macro" + +quote-skip_flags :=3D \ + --edition=3D2021 + +quote-flags :=3D \ + --edition=3D2018 \ + --cap-lints=3Dallow \ + --extern proc_macro2 \ + $(call cfgs-to-flags,$(quote-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 :=3D $(if $(call rustc-min-version,108800),-C= unsafe-allow-abi-mismatch=3Dfixed-x18) @@ -143,10 +155,17 @@ rustdoc-proc_macro2: private rustc_target_flags =3D $= (proc_macro2-flags) rustdoc-proc_macro2: $(src)/proc-macro2/lib.rs rustdoc-clean FORCE +$(call if_changed,rustdoc) =20 +rustdoc-quote: private rustdoc_host =3D yes +rustdoc-quote: private rustc_target_flags =3D $(quote-flags) +rustdoc-quote: private skip_flags =3D $(quote-skip_flags) +rustdoc-quote: $(src)/quote/lib.rs rustdoc-clean rustdoc-proc_macro2 FORCE + +$(call if_changed,rustdoc) + rustdoc-macros: private rustdoc_host =3D yes rustdoc-macros: private rustc_target_flags =3D --crate-type proc-macro \ --extern proc_macro -rustdoc-macros: $(src)/macros/lib.rs rustdoc-clean rustdoc-proc_macro2 FOR= CE +rustdoc-macros: $(src)/macros/lib.rs rustdoc-clean rustdoc-proc_macro2 \ + rustdoc-quote FORCE +$(call if_changed,rustdoc) =20 # Starting with Rust 1.82.0, skipping `-Wrustdoc::unescaped_backticks` sho= uld @@ -207,6 +226,11 @@ rusttestlib-proc_macro2: private rustc_target_flags = =3D $(proc_macro2-flags) rusttestlib-proc_macro2: $(src)/proc-macro2/lib.rs FORCE +$(call if_changed,rustc_test_library) =20 +rusttestlib-quote: private skip_flags =3D $(quote-skip_flags) +rusttestlib-quote: private rustc_target_flags =3D $(quote-flags) +rusttestlib-quote: $(src)/quote/lib.rs rusttestlib-proc_macro2 FORCE + +$(call if_changed,rustc_test_library) + rusttestlib-macros: private rustc_target_flags =3D --extern proc_macro rusttestlib-macros: private rustc_test_library_proc =3D yes rusttestlib-macros: $(src)/macros/lib.rs FORCE @@ -458,6 +482,12 @@ $(obj)/libproc_macro2.rlib: private rustc_target_flags= =3D $(proc_macro2-flags) $(obj)/libproc_macro2.rlib: $(src)/proc-macro2/lib.rs FORCE +$(call if_changed_dep,rustc_procmacrolibrary) =20 +$(obj)/libquote.rlib: private skip_clippy =3D 1 +$(obj)/libquote.rlib: private skip_flags =3D $(quote-skip_flags) +$(obj)/libquote.rlib: private rustc_target_flags =3D $(quote-flags) +$(obj)/libquote.rlib: $(src)/quote/lib.rs $(obj)/libproc_macro2.rlib FORCE + +$(call if_changed_dep,rustc_procmacrolibrary) + quiet_cmd_rustc_procmacro =3D $(RUSTC_OR_CLIPPY_QUIET) P $@ cmd_rustc_procmacro =3D \ $(RUSTC_OR_CLIPPY) $(rust_common_flags) $(rustc_target_flags) \ @@ -469,7 +499,8 @@ quiet_cmd_rustc_procmacro =3D $(RUSTC_OR_CLIPPY_QUIET) = P $@ @$(objtree)/include/generated/rustc_cfg $< =20 # Procedural macros can only be used with the `rustc` that compiled it. -$(obj)/$(libmacros_name): $(src)/macros/lib.rs $(obj)/libproc_macro2.rlib = FORCE +$(obj)/$(libmacros_name): $(src)/macros/lib.rs $(obj)/libproc_macro2.rlib \ + $(obj)/libquote.rlib FORCE +$(call if_changed_dep,rustc_procmacro) =20 $(obj)/$(libpin_init_internal_name): private rustc_target_flags =3D --cfg = kernel @@ -493,6 +524,7 @@ rust-analyzer: $(Q)MAKEFLAGS=3D $(srctree)/scripts/generate_rust_analyzer.py \ --cfgs=3D'core=3D$(core-cfgs)' $(core-edition) \ --cfgs=3D'proc_macro2=3D$(proc_macro2-cfgs)' \ + --cfgs=3D'quote=3D$(quote-cfgs)' \ $(realpath $(srctree)) $(realpath $(objtree)) \ $(rustc_sysroot) $(RUST_LIB_SRC) $(if $(KBUILD_EXTMOD),$(srcroot)) \ > rust-project.json diff --git a/scripts/generate_rust_analyzer.py b/scripts/generate_rust_anal= yzer.py index 00c6b7cc94b7..4faf153ed2ee 100755 --- a/scripts/generate_rust_analyzer.py +++ b/scripts/generate_rust_analyzer.py @@ -93,6 +93,13 @@ def generate_crates(srctree, objtree, sysroot_src, exter= nal_src, cfgs, core_edit cfg=3Dcrates_cfgs["proc_macro2"], ) =20 + append_crate( + "quote", + srctree / "rust" / "quote" / "lib.rs", + ["alloc", "proc_macro", "proc_macro2"], + cfg=3Dcrates_cfgs["quote"], + ) + append_crate( "macros", srctree / "rust" / "macros" / "lib.rs", --=20 2.52.0 From nobody Tue Dec 2 00:46:11 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 39D483191DA; Mon, 24 Nov 2025 15:20:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997605; cv=none; b=l4oSxgznYiwfVD/iegmswQoJePZN49eCQUAwp4zmVDa1k0hHSdK+ezrYYu1/7CrR58LH2uBJVk1a0ams3T/GpMzdlI7QHiNSSEEXW3vw7yDO/xPWVVTDuJNIDmyUoAnxQz5vq4cziwGnUxRXA6ptqb7jJHaXv3rT17kZDdeROcA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763997605; c=relaxed/simple; bh=XdEyRhyhBrbsjoW6ZL2/en9W2NQ3C0Z87wZPGzR0r3o=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=iYSzZjGfJAQC8FC7kQwCtRZPNfcrCtQmuycIVA1BVCPs+Dm8/1m/GPMODtfIYoONUmtPslplEESrqiVgyLKGDnVDlzcnXC1MrYkrnwVVzDzgdA6fRnKk4boPaXhjMb+iEbn+qQp3WJ9OWgfuOrqdL0xBT0HtQBHwVHcLhRHucCY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=R3i2Jr4L; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="R3i2Jr4L" Received: by smtp.kernel.org (Postfix) with ESMTPSA id CD9ECC4CEF1; Mon, 24 Nov 2025 15:19:59 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763997604; bh=XdEyRhyhBrbsjoW6ZL2/en9W2NQ3C0Z87wZPGzR0r3o=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=R3i2Jr4LrvDtgusKa8F+2AUIjWo2P/cxllwNHXPMsz8nLtftGR+/S2xXAt0BNCSCs Yh7rOk2G6peYEWq6eTF10FWHubaEHxIjnfPlzApayFEkukdUp6rgVV/+H9pvx6kKlQ ecapAgAllJVG1wzC+2Np4o62vwUHbL4+sQi9qkp7du3MB3GMr/9tk8AoPiQldZq3QV hKbmBS/ObQyVhXMre+PDCYZNqViD/HfA7y9409CVbh6rMv/n9lNFLOxn+HFA7LrPSt HatWU/6s8hXyMc+Q1ozxDdHyw4GzOb+nQz4VFvHyzX9YQbOPM49JCOLL1M5p/2qnDi GWGib2oflhUjw== From: Miguel Ojeda To: Miguel Ojeda , Alex Gaynor , Nathan Chancellor , Nicolas Schier Cc: Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , rust-for-linux@vger.kernel.org, linux-kbuild@vger.kernel.org, linux-kernel@vger.kernel.org, patches@lists.linux.dev, Jesung Yang Subject: [PATCH v2 15/20] rust: syn: import crate Date: Mon, 24 Nov 2025 16:18:27 +0100 Message-ID: <20251124151837.2184382-16-ojeda@kernel.org> In-Reply-To: <20251124151837.2184382-1-ojeda@kernel.org> References: <20251124151837.2184382-1-ojeda@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable 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 Reviewed-by: Gary Guo Tested-by: Gary Guo Tested-by: Jesung Yang Signed-off-by: Miguel Ojeda --- 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 diff --git a/rust/syn/attr.rs b/rust/syn/attr.rs new file mode 100644 index 000000000000..2bdf96ee7fa9 --- /dev/null +++ b/rust/syn/attr.rs @@ -0,0 +1,836 @@ +#[cfg(feature =3D "parsing")] +use crate::error::Error; +#[cfg(feature =3D "parsing")] +use crate::error::Result; +use crate::expr::Expr; +use crate::mac::MacroDelimiter; +#[cfg(feature =3D "parsing")] +use crate::meta::{self, ParseNestedMeta}; +#[cfg(feature =3D "parsing")] +use crate::parse::{Parse, ParseStream, Parser}; +use crate::path::Path; +use crate::token; +use proc_macro2::TokenStream; +#[cfg(feature =3D "printing")] +use std::iter; +#[cfg(feature =3D "printing")] +use std::slice; + +ast_struct! { + /// An attribute, like `#[repr(transparent)]`. + /// + ///
+ /// + /// # Syntax + /// + /// Rust has six types of attributes. + /// + /// - Outer attributes like `#[repr(transparent)]`. These appear outsi= de or + /// in front of the item they describe. + /// + /// - Inner attributes like `#![feature(proc_macro)]`. These appear in= side + /// 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 att= ribute + /// is outer or inner. + /// + /// Every attribute has a `path` that indicates the intended interpret= ation + /// of the rest of the attribute's contents. The path and the optional + /// additional contents are represented together in the `meta` field o= f the + /// attribute in three possible varieties: + /// + /// - Meta::Path — attributes whose information content conveys = just a + /// path, for example the `#[test]` attribute. + /// + /// - Meta::List — 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 — attributes with an `=3D` sign after the = path, + /// followed by a Rust expression. For example `#[path =3D + /// "sys/windows.rs"]`. + /// + /// All doc comments are represented in the NameValue style with a pat= h 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 =3D "sys/windows.rs"] + /// ~~~~Path + /// ^^^^^^^^^^^^^^^^^^^^^^^Meta::NameValue + /// + /// #[test] + /// ^^^^Meta::Path + /// ``` + /// + ///
+ /// + /// # 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 =3D "s.tmpl"] + /// // struct S; + /// struct UnitStruct { + /// attrs: Vec, + /// struct_token: Token![struct], + /// name: Ident, + /// semi_token: Token![;], + /// } + /// + /// impl Parse for UnitStruct { + /// fn parse(input: ParseStream) -> Result { + /// Ok(UnitStruct { + /// attrs: input.call(Attribute::parse_outer)?, + /// struct_token: input.parse()?, + /// name: input.parse()?, + /// semi_token: input.parse()?, + /// }) + /// } + /// } + /// ``` + /// + ///


+ /// + /// # 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 o= f the + /// `Meta::List` variety of attribute are held in an arbitrary `tokens: + /// TokenStream`. Macros are expected to check the `path` of the attri= bute, + /// decide whether they recognize it, and then parse the remaining tok= ens + /// according to whatever grammar they wish to require for that kind of + /// attribute. Use [`parse_args()`] to parse those tokens into the exp= ected + /// data structure. + /// + /// [`parse_args()`]: Attribute::parse_args + /// + ///


+ /// + /// # Doc comments + /// + /// The compiler transforms doc comments, such as `/// comment` and `/= *! + /// comment */`, into attributes before macros are expanded. Each comm= ent is + /// expanded into an attribute of the form `#[doc =3D r"comment"]`. + /// + /// As an example, the following `mod` items are expanded identically: + /// + /// ``` + /// # use syn::{ItemMod, parse_quote}; + /// let doc: ItemMod =3D 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 =3D parse_quote! { + /// #[doc =3D r" Single line doc comments"] + /// #[doc =3D r" We write so many!"] + /// #[doc =3D r" + /// * Multi-line comments... + /// * May span many lines + /// "] + /// mod example { + /// #![doc =3D r" Of course, they can be inner too"] + /// #![doc =3D r" And fit in a single line "] + /// } + /// }; + /// assert_eq!(doc, attr); + /// ``` + #[cfg_attr(docsrs, doc(cfg(any(feature =3D "full", feature =3D "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 attrib= ute. + /// + /// For example this would return the `test` in `#[test]`, the `derive= ` in + /// `#[derive(Copy)]`, and the `path` in `#[path =3D "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::(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 =3D parse_quote! { + /// #[precondition(value < 5)] + /// }; + /// + /// if attr.path().is_ident("precondition") { + /// let precondition: Expr =3D attr.parse_args()?; + /// // ... + /// } + /// # anyhow::Ok(()) + /// ``` + #[cfg(feature =3D "parsing")] + #[cfg_attr(docsrs, doc(cfg(feature =3D "parsing")))] + pub fn parse_args(&self) -> Result { + 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 =3D parse_quote! { + /// #[inception { #[brrrrrrraaaaawwwwrwrrrmrmrmmrmrmmmmm] }] + /// }; + /// + /// let bwom =3D attr.parse_args_with(Attribute::parse_outer)?; + /// + /// // Attribute does not have a Parse impl, so we couldn't directly d= o: + /// // let bwom: Attribute =3D attr.parse_args()?; + /// # anyhow::Ok(()) + /// ``` + #[cfg(feature =3D "parsing")] + #[cfg_attr(docsrs, doc(cfg(feature =3D "parsing")))] + pub fn parse_args_with(&self, parser: F) -> Result { + match &self.meta { + Meta::Path(path) =3D> 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) =3D> Err(Error::new( + meta.eq_token.span, + format_args!( + "expected parentheses: {}[{}(...)]", + parsing::DisplayAttrStyle(&self.style), + parsing::DisplayPath(&meta.path), + ), + )), + Meta::List(meta) =3D> 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 ref= erence + /// 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]` attr= ibute + /// syntax. + /// + /// ``` + /// use syn::{parenthesized, parse_quote, token, ItemStruct, LitInt}; + /// + /// let input: ItemStruct =3D parse_quote! { + /// #[repr(C, align(4))] + /// pub struct MyStruct(u16, u32); + /// }; + /// + /// let mut repr_c =3D false; + /// let mut repr_transparent =3D false; + /// let mut repr_align =3D None::; + /// let mut repr_packed =3D None::; + /// 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 =3D true; + /// return Ok(()); + /// } + /// + /// // #[repr(transparent)] + /// if meta.path.is_ident("transparent") { + /// repr_transparent =3D true; + /// return Ok(()); + /// } + /// + /// // #[repr(align(N))] + /// if meta.path.is_ident("align") { + /// let content; + /// parenthesized!(content in meta.input); + /// let lit: LitInt =3D content.parse()?; + /// let n: usize =3D lit.base10_parse()?; + /// repr_align =3D Some(n); + /// return Ok(()); + /// } + /// + /// // #[repr(packed)] or #[repr(packed(N))], omitted N me= ans 1 + /// if meta.path.is_ident("packed") { + /// if meta.input.peek(token::Paren) { + /// let content; + /// parenthesized!(content in meta.input); + /// let lit: LitInt =3D content.parse()?; + /// let n: usize =3D lit.base10_parse()?; + /// repr_packed =3D Some(n); + /// } else { + /// repr_packed =3D Some(1); + /// } + /// return Ok(()); + /// } + /// + /// Err(meta.error("unrecognized repr")) + /// })?; + /// } + /// } + /// # anyhow::Ok(()) + /// ``` + /// + /// # Alternatives + /// + /// In some cases, for attributes which have nested layers of structur= ed + /// content, the following less flexible approach might be more conven= ient: + /// + /// ``` + /// # use syn::{parse_quote, ItemStruct}; + /// # + /// # let input: ItemStruct =3D 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 =3D false; + /// let mut repr_transparent =3D false; + /// let mut repr_align =3D None::; + /// let mut repr_packed =3D None::; + /// for attr in &input.attrs { + /// if attr.path().is_ident("repr") { + /// let nested =3D attr.parse_args_with(Punctuated::::parse_terminated)?; + /// for meta in nested { + /// match meta { + /// // #[repr(C)] + /// Meta::Path(path) if path.is_ident("C") =3D> { + /// repr_c =3D true; + /// } + /// + /// // #[repr(align(N))] + /// Meta::List(meta) if meta.path.is_ident("align") = =3D> { + /// let lit: LitInt =3D meta.parse_args()?; + /// let n: usize =3D lit.base10_parse()?; + /// repr_align =3D Some(n); + /// } + /// + /// /* ... */ + /// + /// _ =3D> { + /// return Err(Error::new_spanned(meta, "unrecogni= zed repr")); + /// } + /// } + /// } + /// } + /// } + /// # Ok(()) + /// ``` + #[cfg(feature =3D "parsing")] + #[cfg_attr(docsrs, doc(cfg(feature =3D "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-attri= bute). + #[cfg(feature =3D "parsing")] + #[cfg_attr(docsrs, doc(cfg(feature =3D "parsing")))] + pub fn parse_outer(input: ParseStream) -> Result> { + let mut attrs =3D 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-attri= bute). + #[cfg(feature =3D "parsing")] + #[cfg_attr(docsrs, doc(cfg(feature =3D "parsing")))] + pub fn parse_inner(input: ParseStream) -> Result> { + let mut attrs =3D Vec::new(); + parsing::parse_inner(input, &mut attrs)?; + Ok(attrs) + } +} + +ast_enum! { + /// Distinguishes between attributes that decorate an item and attribu= tes + /// 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 =3D "full", feature =3D "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 =3D "..."` in `#[path =3D + /// "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 =3D "full", feature =3D "derive= "))))] + pub enum Meta { + Path(Path), + + /// A structured list within an attribute, like `derive(Copy, Clon= e)`. + List(MetaList), + + /// A name-value pair within an attribute, like `feature =3D "nigh= tly"`. + NameValue(MetaNameValue), + } +} + +ast_struct! { + /// A structured list within an attribute, like `derive(Copy, Clone)`. + #[cfg_attr(docsrs, doc(cfg(any(feature =3D "full", feature =3D "derive= "))))] + pub struct MetaList { + pub path: Path, + pub delimiter: MacroDelimiter, + pub tokens: TokenStream, + } +} + +ast_struct! { + /// A name-value pair within an attribute, like `feature =3D "nightly"= `. + #[cfg_attr(docsrs, doc(cfg(any(feature =3D "full", feature =3D "derive= "))))] + pub struct MetaNameValue { + pub path: Path, + pub eq_token: Token![=3D], + 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 =3D "sys/windows.rs"]= `. + pub fn path(&self) -> &Path { + match self { + Meta::Path(path) =3D> path, + Meta::List(meta) =3D> &meta.path, + Meta::NameValue(meta) =3D> &meta.path, + } + } + + /// Error if this is a `Meta::List` or `Meta::NameValue`. + #[cfg(feature =3D "parsing")] + #[cfg_attr(docsrs, doc(cfg(feature =3D "parsing")))] + pub fn require_path_only(&self) -> Result<&Path> { + let error_span =3D match self { + Meta::Path(path) =3D> return Ok(path), + Meta::List(meta) =3D> meta.delimiter.span().open(), + Meta::NameValue(meta) =3D> 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 =3D "parsing")] + #[cfg_attr(docsrs, doc(cfg(feature =3D "parsing")))] + pub fn require_list(&self) -> Result<&MetaList> { + match self { + Meta::List(meta) =3D> Ok(meta), + Meta::Path(path) =3D> 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) =3D> Err(Error::new(meta.eq_token.span, = "expected `(`")), + } + } + + /// Error if this is a `Meta::Path` or `Meta::List`. + #[cfg(feature =3D "parsing")] + #[cfg_attr(docsrs, doc(cfg(feature =3D "parsing")))] + pub fn require_name_value(&self) -> Result<&MetaNameValue> { + match self { + Meta::NameValue(meta) =3D> Ok(meta), + Meta::Path(path) =3D> Err(crate::error::new2( + path.segments.first().unwrap().ident.span(), + path.segments.last().unwrap().ident.span(), + format!( + "expected a value for this attribute: `{} =3D ...`", + parsing::DisplayPath(path), + ), + )), + Meta::List(meta) =3D> Err(Error::new(meta.delimiter.span().ope= n(), "expected `=3D`")), + } + } +} + +impl MetaList { + /// See [`Attribute::parse_args`]. + #[cfg(feature =3D "parsing")] + #[cfg_attr(docsrs, doc(cfg(feature =3D "parsing")))] + pub fn parse_args(&self) -> Result { + self.parse_args_with(T::parse) + } + + /// See [`Attribute::parse_args_with`]. + #[cfg(feature =3D "parsing")] + #[cfg_attr(docsrs, doc(cfg(feature =3D "parsing")))] + pub fn parse_args_with(&self, parser: F) -> Result { + let scope =3D self.delimiter.span().close(); + crate::parse::parse_scoped(parser, scope, self.tokens.clone()) + } + + /// See [`Attribute::parse_nested_meta`]. + #[cfg(feature =3D "parsing")] + #[cfg_attr(docsrs, doc(cfg(feature =3D "parsing")))] + pub fn parse_nested_meta( + &self, + logic: impl FnMut(ParseNestedMeta) -> Result<()>, + ) -> Result<()> { + self.parse_args_with(meta::parser(logic)) + } +} + +#[cfg(feature =3D "printing")] +pub(crate) trait FilterAttrs<'a> { + type Ret: Iterator; + + fn outer(self) -> Self::Ret; + #[cfg(feature =3D "full")] + fn inner(self) -> Self::Ret; +} + +#[cfg(feature =3D "printing")] +impl<'a> FilterAttrs<'a> for &'a [Attribute] { + type Ret =3D iter::Filter, fn(&&Attribute) = -> bool>; + + fn outer(self) -> Self::Ret { + fn is_outer(attr: &&Attribute) -> bool { + match attr.style { + AttrStyle::Outer =3D> true, + AttrStyle::Inner(_) =3D> false, + } + } + self.iter().filter(is_outer) + } + + #[cfg(feature =3D "full")] + fn inner(self) -> Self::Ret { + fn is_inner(attr: &&Attribute) -> bool { + match attr.style { + AttrStyle::Inner(_) =3D> true, + AttrStyle::Outer =3D> false, + } + } + self.iter().filter(is_inner) + } +} + +impl From for Meta { + fn from(meta: Path) -> Meta { + Meta::Path(meta) + } +} + +impl From for Meta { + fn from(meta: MetaList) -> Meta { + Meta::List(meta) + } +} + +impl From for Meta { + fn from(meta: MetaNameValue) -> Meta { + Meta::NameValue(meta) + } +} + +#[cfg(feature =3D "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) -> 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 { + 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 { + 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 =3D "parsing")))] + impl Parse for Meta { + fn parse(input: ParseStream) -> Result { + let path =3D parse_outermost_meta_path(input)?; + parse_meta_after_path(path, input) + } + } + + #[cfg_attr(docsrs, doc(cfg(feature =3D "parsing")))] + impl Parse for MetaList { + fn parse(input: ParseStream) -> Result { + let path =3D parse_outermost_meta_path(input)?; + parse_meta_list_after_path(path, input) + } + } + + #[cfg_attr(docsrs, doc(cfg(feature =3D "parsing")))] + impl Parse for MetaNameValue { + fn parse(input: ParseStream) -> Result { + let path =3D parse_outermost_meta_path(input)?; + parse_meta_name_value_after_path(path, input) + } + } + + // Unlike meta::parse_meta_path which accepts arbitrary keywords in th= e path, + // only the `unsafe` keyword is accepted as an attribute's outermost p= ath. + fn parse_outermost_meta_path(input: ParseStream) -> Result { + if input.peek(Token![unsafe]) { + let unsafe_token: Token![unsafe] =3D 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 { + 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![=3D]) { + parse_meta_name_value_after_path(path, input).map(Meta::NameVa= lue) + } else { + Ok(Meta::Path(path)) + } + } + + fn parse_meta_list_after_path(path: Path, input: ParseStream) -> Resul= t { + let (delimiter, tokens) =3D mac::parse_delimiter(input)?; + Ok(MetaList { + path, + delimiter, + tokens, + }) + } + + fn parse_meta_name_value_after_path(path: Path, input: ParseStream) ->= Result { + let eq_token: Token![=3D] =3D input.parse()?; + let ahead =3D input.fork(); + let lit: Option =3D ahead.parse()?; + let value =3D if let (Some(lit), true) =3D (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 attribu= te")); + } 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 =3D> "#", + AttrStyle::Inner(_) =3D> "#!", + }) + } + } + + 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 =3D "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 =3D "printing")))] + impl ToTokens for Attribute { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.pound_token.to_tokens(tokens); + if let AttrStyle::Inner(b) =3D &self.style { + b.to_tokens(tokens); + } + self.bracket_token.surround(tokens, |tokens| { + self.meta.to_tokens(tokens); + }); + } + } + + #[cfg_attr(docsrs, doc(cfg(feature =3D "printing")))] + impl ToTokens for Meta { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Meta::Path(path) =3D> path::printing::print_path(tokens, p= ath, PathStyle::Mod), + Meta::List(meta_list) =3D> meta_list.to_tokens(tokens), + Meta::NameValue(meta_name_value) =3D> meta_name_value.to_t= okens(tokens), + } + } + } + + #[cfg_attr(docsrs, doc(cfg(feature =3D "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 =3D "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); + } + } +} diff --git a/rust/syn/bigint.rs b/rust/syn/bigint.rs new file mode 100644 index 000000000000..66aaa9372540 --- /dev/null +++ b/rust/syn/bigint.rs @@ -0,0 +1,66 @@ +use std::ops::{AddAssign, MulAssign}; + +// For implementing base10_digits() accessor on LitInt. +pub(crate) struct BigInt { + digits: Vec, +} + +impl BigInt { + pub(crate) fn new() -> Self { + BigInt { digits: Vec::new() } + } + + pub(crate) fn to_string(&self) -> String { + let mut repr =3D String::with_capacity(self.digits.len()); + + let mut has_nonzero =3D false; + for digit in self.digits.iter().rev() { + has_nonzero |=3D *digit !=3D 0; + if has_nonzero { + repr.push((*digit + b'0') as char); + } + } + + if repr.is_empty() { + repr.push('0'); + } + + repr + } + + fn reserve_two_digits(&mut self) { + let len =3D self.digits.len(); + let desired =3D + len + !self.digits.ends_with(&[0, 0]) as usize + !self.digits.= ends_with(&[0]) as usize; + self.digits.resize(desired, 0); + } +} + +impl AddAssign for BigInt { + // Assumes increment <16. + fn add_assign(&mut self, mut increment: u8) { + self.reserve_two_digits(); + + let mut i =3D 0; + while increment > 0 { + let sum =3D self.digits[i] + increment; + self.digits[i] =3D sum % 10; + increment =3D sum / 10; + i +=3D 1; + } + } +} + +impl MulAssign for BigInt { + // Assumes base <=3D16. + fn mul_assign(&mut self, base: u8) { + self.reserve_two_digits(); + + let mut carry =3D 0; + for digit in &mut self.digits { + let prod =3D *digit * base + carry; + *digit =3D prod % 10; + carry =3D prod / 10; + } + } +} diff --git a/rust/syn/buffer.rs b/rust/syn/buffer.rs new file mode 100644 index 000000000000..7b6a504eeb7c --- /dev/null +++ b/rust/syn/buffer.rs @@ -0,0 +1,434 @@ +//! A stably addressed token buffer supporting efficient traversal based o= n a +//! cheaply copyable cursor. + +// This module is heavily commented as it contains most of the unsafe code= in +// Syn, and caution should be used when editing it. The public-facing inte= rface +// is 100% safe but the implementation is fragile internally. + +use crate::Lifetime; +use proc_macro2::extra::DelimSpan; +use proc_macro2::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, = TokenStream, TokenTree}; +use std::cmp::Ordering; +use std::marker::PhantomData; +use std::ptr; + +/// Internal type which is used instead of `TokenTree` to represent a toke= n tree +/// within a `TokenBuffer`. +enum Entry { + // Mimicking types from proc-macro. + // Group entries contain the offset to the matching End entry. + Group(Group, usize), + Ident(Ident), + Punct(Punct), + Literal(Literal), + // End entries contain the offset (negative) to the start of the buffe= r, and + // offset (negative) to the matching Group entry. + End(isize, isize), +} + +/// A buffer that can be efficiently traversed multiple times, unlike +/// `TokenStream` which requires a deep copy in order to traverse more than +/// once. +pub struct TokenBuffer { + // NOTE: Do not implement clone on this - while the current design cou= ld be + // cloned, other designs which could be desirable may not be cloneable. + entries: Box<[Entry]>, +} + +impl TokenBuffer { + fn recursive_new(entries: &mut Vec, stream: TokenStream) { + for tt in stream { + match tt { + TokenTree::Ident(ident) =3D> entries.push(Entry::Ident(ide= nt)), + TokenTree::Punct(punct) =3D> entries.push(Entry::Punct(pun= ct)), + TokenTree::Literal(literal) =3D> entries.push(Entry::Liter= al(literal)), + TokenTree::Group(group) =3D> { + let group_start_index =3D entries.len(); + entries.push(Entry::End(0, 0)); // we replace this bel= ow + Self::recursive_new(entries, group.stream()); + let group_end_index =3D entries.len(); + let group_offset =3D group_end_index - group_start_ind= ex; + entries.push(Entry::End( + -(group_end_index as isize), + -(group_offset as isize), + )); + entries[group_start_index] =3D Entry::Group(group, gro= up_offset); + } + } + } + } + + /// Creates a `TokenBuffer` containing all the tokens from the input + /// `proc_macro::TokenStream`. + #[cfg(feature =3D "proc-macro")] + #[cfg_attr(docsrs, doc(cfg(feature =3D "proc-macro")))] + pub fn new(stream: proc_macro::TokenStream) -> Self { + Self::new2(stream.into()) + } + + /// Creates a `TokenBuffer` containing all the tokens from the input + /// `proc_macro2::TokenStream`. + pub fn new2(stream: TokenStream) -> Self { + let mut entries =3D Vec::new(); + Self::recursive_new(&mut entries, stream); + entries.push(Entry::End(-(entries.len() as isize), 0)); + Self { + entries: entries.into_boxed_slice(), + } + } + + /// Creates a cursor referencing the first token in the buffer and abl= e to + /// traverse until the end of the buffer. + pub fn begin(&self) -> Cursor { + let ptr =3D self.entries.as_ptr(); + unsafe { Cursor::create(ptr, ptr.add(self.entries.len() - 1)) } + } +} + +/// A cheaply copyable cursor into a `TokenBuffer`. +/// +/// This cursor holds a shared reference into the immutable data which is = used +/// internally to represent a `TokenStream`, and can be efficiently manipu= lated +/// and copied around. +/// +/// An empty `Cursor` can be created directly, or one may create a `TokenB= uffer` +/// object and get a cursor to its first token with `begin()`. +pub struct Cursor<'a> { + // The current entry which the `Cursor` is pointing at. + ptr: *const Entry, + // This is the only `Entry::End` object which this cursor is allowed to + // point at. All other `End` objects are skipped over in `Cursor::crea= te`. + scope: *const Entry, + // Cursor is covariant in 'a. This field ensures that our pointers are= still + // valid. + marker: PhantomData<&'a Entry>, +} + +impl<'a> Cursor<'a> { + /// Creates a cursor referencing a static empty TokenStream. + pub fn empty() -> Self { + // It's safe in this situation for us to put an `Entry` object in = global + // storage, despite it not actually being safe to send across thre= ads + // (`Ident` is a reference into a thread-local table). This is bec= ause + // this entry never includes a `Ident` object. + // + // This wrapper struct allows us to break the rules and put a `Syn= c` + // object in global storage. + struct UnsafeSyncEntry(Entry); + unsafe impl Sync for UnsafeSyncEntry {} + static EMPTY_ENTRY: UnsafeSyncEntry =3D UnsafeSyncEntry(Entry::End= (0, 0)); + + Cursor { + ptr: &EMPTY_ENTRY.0, + scope: &EMPTY_ENTRY.0, + marker: PhantomData, + } + } + + /// This create method intelligently exits non-explicitly-entered + /// `None`-delimited scopes when the cursor reaches the end of them, + /// allowing for them to be treated transparently. + unsafe fn create(mut ptr: *const Entry, scope: *const Entry) -> Self { + // NOTE: If we're looking at a `End`, we want to advance the cursor + // past it, unless `ptr =3D=3D scope`, which means that we're at t= he edge of + // our cursor's scope. We should only have `ptr !=3D scope` at the= exit + // from None-delimited groups entered with `ignore_none`. + while let Entry::End(..) =3D unsafe { &*ptr } { + if ptr::eq(ptr, scope) { + break; + } + ptr =3D unsafe { ptr.add(1) }; + } + + Cursor { + ptr, + scope, + marker: PhantomData, + } + } + + /// Get the current entry. + fn entry(self) -> &'a Entry { + unsafe { &*self.ptr } + } + + /// Bump the cursor to point at the next token after the current one. = This + /// is undefined behavior if the cursor is currently looking at an + /// `Entry::End`. + /// + /// If the cursor is looking at an `Entry::Group`, the bumped cursor w= ill + /// point at the first token in the group (with the same scope end). + unsafe fn bump_ignore_group(self) -> Cursor<'a> { + unsafe { Cursor::create(self.ptr.offset(1), self.scope) } + } + + /// While the cursor is looking at a `None`-delimited group, move it t= o look + /// at the first token inside instead. If the group is empty, this wil= l move + /// the cursor past the `None`-delimited group. + /// + /// WARNING: This mutates its argument. + fn ignore_none(&mut self) { + while let Entry::Group(group, _) =3D self.entry() { + if group.delimiter() =3D=3D Delimiter::None { + unsafe { *self =3D self.bump_ignore_group() }; + } else { + break; + } + } + } + + /// Checks whether the cursor is currently pointing at the end of its = valid + /// scope. + pub fn eof(self) -> bool { + // We're at eof if we're at the end of our scope. + ptr::eq(self.ptr, self.scope) + } + + /// If the cursor is pointing at a `Ident`, returns it along with a cu= rsor + /// pointing at the next `TokenTree`. + pub fn ident(mut self) -> Option<(Ident, Cursor<'a>)> { + self.ignore_none(); + match self.entry() { + Entry::Ident(ident) =3D> Some((ident.clone(), unsafe { self.bu= mp_ignore_group() })), + _ =3D> None, + } + } + + /// If the cursor is pointing at a `Punct`, returns it along with a cu= rsor + /// pointing at the next `TokenTree`. + pub fn punct(mut self) -> Option<(Punct, Cursor<'a>)> { + self.ignore_none(); + match self.entry() { + Entry::Punct(punct) if punct.as_char() !=3D '\'' =3D> { + Some((punct.clone(), unsafe { self.bump_ignore_group() })) + } + _ =3D> None, + } + } + + /// If the cursor is pointing at a `Literal`, return it along with a c= ursor + /// pointing at the next `TokenTree`. + pub fn literal(mut self) -> Option<(Literal, Cursor<'a>)> { + self.ignore_none(); + match self.entry() { + Entry::Literal(literal) =3D> Some((literal.clone(), unsafe { s= elf.bump_ignore_group() })), + _ =3D> None, + } + } + + /// If the cursor is pointing at a `Lifetime`, returns it along with a + /// cursor pointing at the next `TokenTree`. + pub fn lifetime(mut self) -> Option<(Lifetime, Cursor<'a>)> { + self.ignore_none(); + match self.entry() { + Entry::Punct(punct) if punct.as_char() =3D=3D '\'' && punct.sp= acing() =3D=3D Spacing::Joint =3D> { + let next =3D unsafe { self.bump_ignore_group() }; + let (ident, rest) =3D next.ident()?; + let lifetime =3D Lifetime { + apostrophe: punct.span(), + ident, + }; + Some((lifetime, rest)) + } + _ =3D> None, + } + } + + /// If the cursor is pointing at a `Group` with the given delimiter, r= eturns + /// a cursor into that group and one pointing to the next `TokenTree`. + pub fn group(mut self, delim: Delimiter) -> Option<(Cursor<'a>, DelimS= pan, Cursor<'a>)> { + // If we're not trying to enter a none-delimited group, we want to + // ignore them. We have to make sure to _not_ ignore them when we = want + // to enter them, of course. For obvious reasons. + if delim !=3D Delimiter::None { + self.ignore_none(); + } + + if let Entry::Group(group, end_offset) =3D self.entry() { + if group.delimiter() =3D=3D delim { + let span =3D group.delim_span(); + let end_of_group =3D unsafe { self.ptr.add(*end_offset) }; + let inside_of_group =3D unsafe { Cursor::create(self.ptr.a= dd(1), end_of_group) }; + let after_group =3D unsafe { Cursor::create(end_of_group, = self.scope) }; + return Some((inside_of_group, span, after_group)); + } + } + + None + } + + /// If the cursor is pointing at a `Group`, returns a cursor into the = group + /// and one pointing to the next `TokenTree`. + pub fn any_group(self) -> Option<(Cursor<'a>, Delimiter, DelimSpan, Cu= rsor<'a>)> { + if let Entry::Group(group, end_offset) =3D self.entry() { + let delimiter =3D group.delimiter(); + let span =3D group.delim_span(); + let end_of_group =3D unsafe { self.ptr.add(*end_offset) }; + let inside_of_group =3D unsafe { Cursor::create(self.ptr.add(1= ), end_of_group) }; + let after_group =3D unsafe { Cursor::create(end_of_group, self= .scope) }; + return Some((inside_of_group, delimiter, span, after_group)); + } + + None + } + + pub(crate) fn any_group_token(self) -> Option<(Group, Cursor<'a>)> { + if let Entry::Group(group, end_offset) =3D self.entry() { + let end_of_group =3D unsafe { self.ptr.add(*end_offset) }; + let after_group =3D unsafe { Cursor::create(end_of_group, self= .scope) }; + return Some((group.clone(), after_group)); + } + + None + } + + /// Copies all remaining tokens visible from this cursor into a + /// `TokenStream`. + pub fn token_stream(self) -> TokenStream { + let mut tts =3D Vec::new(); + let mut cursor =3D self; + while let Some((tt, rest)) =3D cursor.token_tree() { + tts.push(tt); + cursor =3D rest; + } + tts.into_iter().collect() + } + + /// If the cursor is pointing at a `TokenTree`, returns it along with a + /// cursor pointing at the next `TokenTree`. + /// + /// Returns `None` if the cursor has reached the end of its stream. + /// + /// This method does not treat `None`-delimited groups as transparent,= and + /// will return a `Group(None, ..)` if the cursor is looking at one. + pub fn token_tree(self) -> Option<(TokenTree, Cursor<'a>)> { + let (tree, len) =3D match self.entry() { + Entry::Group(group, end_offset) =3D> (group.clone().into(), *e= nd_offset), + Entry::Literal(literal) =3D> (literal.clone().into(), 1), + Entry::Ident(ident) =3D> (ident.clone().into(), 1), + Entry::Punct(punct) =3D> (punct.clone().into(), 1), + Entry::End(..) =3D> return None, + }; + + let rest =3D unsafe { Cursor::create(self.ptr.add(len), self.scope= ) }; + Some((tree, rest)) + } + + /// Returns the `Span` of the current token, or `Span::call_site()` if= this + /// cursor points to eof. + pub fn span(mut self) -> Span { + match self.entry() { + Entry::Group(group, _) =3D> group.span(), + Entry::Literal(literal) =3D> literal.span(), + Entry::Ident(ident) =3D> ident.span(), + Entry::Punct(punct) =3D> punct.span(), + Entry::End(_, offset) =3D> { + self.ptr =3D unsafe { self.ptr.offset(*offset) }; + if let Entry::Group(group, _) =3D self.entry() { + group.span_close() + } else { + Span::call_site() + } + } + } + } + + /// Returns the `Span` of the token immediately prior to the position = of + /// this cursor, or of the current token if there is no previous one. + #[cfg(any(feature =3D "full", feature =3D "derive"))] + pub(crate) fn prev_span(mut self) -> Span { + if start_of_buffer(self) < self.ptr { + self.ptr =3D unsafe { self.ptr.offset(-1) }; + } + self.span() + } + + /// Skip over the next token that is not a None-delimited group, witho= ut + /// cloning it. Returns `None` if this cursor points to eof. + /// + /// This method treats `'lifetimes` as a single token. + pub(crate) fn skip(mut self) -> Option> { + self.ignore_none(); + + let len =3D match self.entry() { + Entry::End(..) =3D> return None, + + // Treat lifetimes as a single tt for the purposes of 'skip'. + Entry::Punct(punct) if punct.as_char() =3D=3D '\'' && punct.sp= acing() =3D=3D Spacing::Joint =3D> { + match unsafe { &*self.ptr.add(1) } { + Entry::Ident(_) =3D> 2, + _ =3D> 1, + } + } + + Entry::Group(_, end_offset) =3D> *end_offset, + _ =3D> 1, + }; + + Some(unsafe { Cursor::create(self.ptr.add(len), self.scope) }) + } + + pub(crate) fn scope_delimiter(self) -> Delimiter { + match unsafe { &*self.scope } { + Entry::End(_, offset) =3D> match unsafe { &*self.scope.offset(= *offset) } { + Entry::Group(group, _) =3D> group.delimiter(), + _ =3D> Delimiter::None, + }, + _ =3D> unreachable!(), + } + } +} + +impl<'a> Copy for Cursor<'a> {} + +impl<'a> Clone for Cursor<'a> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a> Eq for Cursor<'a> {} + +impl<'a> PartialEq for Cursor<'a> { + fn eq(&self, other: &Self) -> bool { + ptr::eq(self.ptr, other.ptr) + } +} + +impl<'a> PartialOrd for Cursor<'a> { + fn partial_cmp(&self, other: &Self) -> Option { + if same_buffer(*self, *other) { + Some(cmp_assuming_same_buffer(*self, *other)) + } else { + None + } + } +} + +pub(crate) fn same_scope(a: Cursor, b: Cursor) -> bool { + ptr::eq(a.scope, b.scope) +} + +pub(crate) fn same_buffer(a: Cursor, b: Cursor) -> bool { + ptr::eq(start_of_buffer(a), start_of_buffer(b)) +} + +fn start_of_buffer(cursor: Cursor) -> *const Entry { + unsafe { + match &*cursor.scope { + Entry::End(offset, _) =3D> cursor.scope.offset(*offset), + _ =3D> unreachable!(), + } + } +} + +pub(crate) fn cmp_assuming_same_buffer(a: Cursor, b: Cursor) -> Ordering { + a.ptr.cmp(&b.ptr) +} + +pub(crate) fn open_span_of_group(cursor: Cursor) -> Span { + match cursor.entry() { + Entry::Group(group, _) =3D> group.span_open(), + _ =3D> cursor.span(), + } +} diff --git a/rust/syn/classify.rs b/rust/syn/classify.rs new file mode 100644 index 000000000000..8eab19dbc37c --- /dev/null +++ b/rust/syn/classify.rs @@ -0,0 +1,311 @@ +#[cfg(feature =3D "full")] +use crate::expr::Expr; +#[cfg(any(feature =3D "printing", feature =3D "full"))] +use crate::generics::TypeParamBound; +#[cfg(any(feature =3D "printing", feature =3D "full"))] +use crate::path::{Path, PathArguments}; +#[cfg(any(feature =3D "printing", feature =3D "full"))] +use crate::punctuated::Punctuated; +#[cfg(any(feature =3D "printing", feature =3D "full"))] +use crate::ty::{ReturnType, Type}; +#[cfg(feature =3D "full")] +use proc_macro2::{Delimiter, TokenStream, TokenTree}; +#[cfg(any(feature =3D "printing", feature =3D "full"))] +use std::ops::ControlFlow; + +#[cfg(feature =3D "full")] +pub(crate) fn requires_semi_to_be_stmt(expr: &Expr) -> bool { + match expr { + Expr::Macro(expr) =3D> !expr.mac.delimiter.is_brace(), + _ =3D> requires_comma_to_be_match_arm(expr), + } +} + +#[cfg(feature =3D "full")] +pub(crate) fn requires_comma_to_be_match_arm(expr: &Expr) -> bool { + match expr { + Expr::If(_) + | Expr::Match(_) + | Expr::Block(_) | Expr::Unsafe(_) // both under ExprKind::Block i= n rustc + | Expr::While(_) + | Expr::Loop(_) + | Expr::ForLoop(_) + | Expr::TryBlock(_) + | Expr::Const(_) =3D> false, + + Expr::Array(_) + | Expr::Assign(_) + | Expr::Async(_) + | Expr::Await(_) + | Expr::Binary(_) + | Expr::Break(_) + | Expr::Call(_) + | Expr::Cast(_) + | Expr::Closure(_) + | Expr::Continue(_) + | Expr::Field(_) + | Expr::Group(_) + | Expr::Index(_) + | Expr::Infer(_) + | Expr::Let(_) + | Expr::Lit(_) + | Expr::Macro(_) + | Expr::MethodCall(_) + | Expr::Paren(_) + | Expr::Path(_) + | Expr::Range(_) + | Expr::RawAddr(_) + | Expr::Reference(_) + | Expr::Repeat(_) + | Expr::Return(_) + | Expr::Struct(_) + | Expr::Try(_) + | Expr::Tuple(_) + | Expr::Unary(_) + | Expr::Yield(_) + | Expr::Verbatim(_) =3D> true, + } +} + +#[cfg(feature =3D "printing")] +pub(crate) fn trailing_unparameterized_path(mut ty: &Type) -> bool { + loop { + match ty { + Type::BareFn(t) =3D> match &t.output { + ReturnType::Default =3D> return false, + ReturnType::Type(_, ret) =3D> ty =3D ret, + }, + Type::ImplTrait(t) =3D> match last_type_in_bounds(&t.bounds) { + ControlFlow::Break(trailing_path) =3D> return trailing_pat= h, + ControlFlow::Continue(t) =3D> ty =3D t, + }, + Type::Path(t) =3D> match last_type_in_path(&t.path) { + ControlFlow::Break(trailing_path) =3D> return trailing_pat= h, + ControlFlow::Continue(t) =3D> ty =3D t, + }, + Type::Ptr(t) =3D> ty =3D &t.elem, + Type::Reference(t) =3D> ty =3D &t.elem, + Type::TraitObject(t) =3D> match last_type_in_bounds(&t.bounds)= { + ControlFlow::Break(trailing_path) =3D> return trailing_pat= h, + ControlFlow::Continue(t) =3D> ty =3D t, + }, + + Type::Array(_) + | Type::Group(_) + | Type::Infer(_) + | Type::Macro(_) + | Type::Never(_) + | Type::Paren(_) + | Type::Slice(_) + | Type::Tuple(_) + | Type::Verbatim(_) =3D> return false, + } + } + + fn last_type_in_path(path: &Path) -> ControlFlow { + match &path.segments.last().unwrap().arguments { + PathArguments::None =3D> ControlFlow::Break(true), + PathArguments::AngleBracketed(_) =3D> ControlFlow::Break(false= ), + PathArguments::Parenthesized(arg) =3D> match &arg.output { + ReturnType::Default =3D> ControlFlow::Break(false), + ReturnType::Type(_, ret) =3D> ControlFlow::Continue(ret), + }, + } + } + + fn last_type_in_bounds( + bounds: &Punctuated, + ) -> ControlFlow { + match bounds.last().unwrap() { + TypeParamBound::Trait(t) =3D> last_type_in_path(&t.path), + TypeParamBound::Lifetime(_) + | TypeParamBound::PreciseCapture(_) + | TypeParamBound::Verbatim(_) =3D> ControlFlow::Break(false), + } + } +} + +/// Whether the expression's first token is the label of a loop/block. +#[cfg(all(feature =3D "printing", feature =3D "full"))] +pub(crate) fn expr_leading_label(mut expr: &Expr) -> bool { + loop { + match expr { + Expr::Block(e) =3D> return e.label.is_some(), + Expr::ForLoop(e) =3D> return e.label.is_some(), + Expr::Loop(e) =3D> return e.label.is_some(), + Expr::While(e) =3D> return e.label.is_some(), + + Expr::Assign(e) =3D> expr =3D &e.left, + Expr::Await(e) =3D> expr =3D &e.base, + Expr::Binary(e) =3D> expr =3D &e.left, + Expr::Call(e) =3D> expr =3D &e.func, + Expr::Cast(e) =3D> expr =3D &e.expr, + Expr::Field(e) =3D> expr =3D &e.base, + Expr::Index(e) =3D> expr =3D &e.expr, + Expr::MethodCall(e) =3D> expr =3D &e.receiver, + Expr::Range(e) =3D> match &e.start { + Some(start) =3D> expr =3D start, + None =3D> return false, + }, + Expr::Try(e) =3D> expr =3D &e.expr, + + Expr::Array(_) + | Expr::Async(_) + | Expr::Break(_) + | Expr::Closure(_) + | Expr::Const(_) + | Expr::Continue(_) + | Expr::Group(_) + | Expr::If(_) + | Expr::Infer(_) + | Expr::Let(_) + | Expr::Lit(_) + | Expr::Macro(_) + | Expr::Match(_) + | Expr::Paren(_) + | Expr::Path(_) + | Expr::RawAddr(_) + | Expr::Reference(_) + | Expr::Repeat(_) + | Expr::Return(_) + | Expr::Struct(_) + | Expr::TryBlock(_) + | Expr::Tuple(_) + | Expr::Unary(_) + | Expr::Unsafe(_) + | Expr::Verbatim(_) + | Expr::Yield(_) =3D> return false, + } + } +} + +/// Whether the expression's last token is `}`. +#[cfg(feature =3D "full")] +pub(crate) fn expr_trailing_brace(mut expr: &Expr) -> bool { + loop { + match expr { + Expr::Async(_) + | Expr::Block(_) + | Expr::Const(_) + | Expr::ForLoop(_) + | Expr::If(_) + | Expr::Loop(_) + | Expr::Match(_) + | Expr::Struct(_) + | Expr::TryBlock(_) + | Expr::Unsafe(_) + | Expr::While(_) =3D> return true, + + Expr::Assign(e) =3D> expr =3D &e.right, + Expr::Binary(e) =3D> expr =3D &e.right, + Expr::Break(e) =3D> match &e.expr { + Some(e) =3D> expr =3D e, + None =3D> return false, + }, + Expr::Cast(e) =3D> return type_trailing_brace(&e.ty), + Expr::Closure(e) =3D> expr =3D &e.body, + Expr::Let(e) =3D> expr =3D &e.expr, + Expr::Macro(e) =3D> return e.mac.delimiter.is_brace(), + Expr::Range(e) =3D> match &e.end { + Some(end) =3D> expr =3D end, + None =3D> return false, + }, + Expr::RawAddr(e) =3D> expr =3D &e.expr, + Expr::Reference(e) =3D> expr =3D &e.expr, + Expr::Return(e) =3D> match &e.expr { + Some(e) =3D> expr =3D e, + None =3D> return false, + }, + Expr::Unary(e) =3D> expr =3D &e.expr, + Expr::Verbatim(e) =3D> return tokens_trailing_brace(e), + Expr::Yield(e) =3D> match &e.expr { + Some(e) =3D> expr =3D e, + None =3D> return false, + }, + + Expr::Array(_) + | Expr::Await(_) + | Expr::Call(_) + | Expr::Continue(_) + | Expr::Field(_) + | Expr::Group(_) + | Expr::Index(_) + | Expr::Infer(_) + | Expr::Lit(_) + | Expr::MethodCall(_) + | Expr::Paren(_) + | Expr::Path(_) + | Expr::Repeat(_) + | Expr::Try(_) + | Expr::Tuple(_) =3D> return false, + } + } + + fn type_trailing_brace(mut ty: &Type) -> bool { + loop { + match ty { + Type::BareFn(t) =3D> match &t.output { + ReturnType::Default =3D> return false, + ReturnType::Type(_, ret) =3D> ty =3D ret, + }, + Type::ImplTrait(t) =3D> match last_type_in_bounds(&t.bound= s) { + ControlFlow::Break(trailing_brace) =3D> return trailin= g_brace, + ControlFlow::Continue(t) =3D> ty =3D t, + }, + Type::Macro(t) =3D> return t.mac.delimiter.is_brace(), + Type::Path(t) =3D> match last_type_in_path(&t.path) { + Some(t) =3D> ty =3D t, + None =3D> return false, + }, + Type::Ptr(t) =3D> ty =3D &t.elem, + Type::Reference(t) =3D> ty =3D &t.elem, + Type::TraitObject(t) =3D> match last_type_in_bounds(&t.bou= nds) { + ControlFlow::Break(trailing_brace) =3D> return trailin= g_brace, + ControlFlow::Continue(t) =3D> ty =3D t, + }, + Type::Verbatim(t) =3D> return tokens_trailing_brace(t), + + Type::Array(_) + | Type::Group(_) + | Type::Infer(_) + | Type::Never(_) + | Type::Paren(_) + | Type::Slice(_) + | Type::Tuple(_) =3D> return false, + } + } + } + + fn last_type_in_path(path: &Path) -> Option<&Type> { + match &path.segments.last().unwrap().arguments { + PathArguments::None | PathArguments::AngleBracketed(_) =3D> No= ne, + PathArguments::Parenthesized(arg) =3D> match &arg.output { + ReturnType::Default =3D> None, + ReturnType::Type(_, ret) =3D> Some(ret), + }, + } + } + + fn last_type_in_bounds( + bounds: &Punctuated, + ) -> ControlFlow { + match bounds.last().unwrap() { + TypeParamBound::Trait(t) =3D> match last_type_in_path(&t.path)= { + Some(t) =3D> ControlFlow::Continue(t), + None =3D> ControlFlow::Break(false), + }, + TypeParamBound::Lifetime(_) | TypeParamBound::PreciseCapture(_= ) =3D> { + ControlFlow::Break(false) + } + TypeParamBound::Verbatim(t) =3D> ControlFlow::Break(tokens_tra= iling_brace(t)), + } + } + + fn tokens_trailing_brace(tokens: &TokenStream) -> bool { + if let Some(TokenTree::Group(last)) =3D tokens.clone().into_iter()= .last() { + last.delimiter() =3D=3D Delimiter::Brace + } else { + false + } + } +} diff --git a/rust/syn/custom_keyword.rs b/rust/syn/custom_keyword.rs new file mode 100644 index 000000000000..cc4f632c981a --- /dev/null +++ b/rust/syn/custom_keyword.rs @@ -0,0 +1,260 @@ +/// Define a type that supports parsing and printing a given identifier as= if it +/// were a keyword. +/// +/// # Usage +/// +/// As a convention, it is recommended that this macro be invoked within a +/// module called `kw` or `keyword` and that the resulting parser be invok= ed +/// with a `kw::` or `keyword::` prefix. +/// +/// ``` +/// mod kw { +/// syn::custom_keyword!(whatever); +/// } +/// ``` +/// +/// The generated syntax tree node supports the following operations just = like +/// any built-in keyword token. +/// +/// - [Peeking] =E2=80=94 `input.peek(kw::whatever)` +/// +/// - [Parsing] =E2=80=94 `input.parse::()?` +/// +/// - [Printing] =E2=80=94 `quote!( ... #whatever_token ... )` +/// +/// - Construction from a [`Span`] =E2=80=94 `let whatever_token =3D kw::w= hatever(sp)` +/// +/// - Field access to its span =E2=80=94 `let sp =3D whatever_token.span` +/// +/// [Peeking]: crate::parse::ParseBuffer::peek +/// [Parsing]: crate::parse::ParseBuffer::parse +/// [Printing]: quote::ToTokens +/// [`Span`]: proc_macro2::Span +/// +/// # Example +/// +/// This example parses input that looks like `bool =3D true` or `str =3D = "value"`. +/// The key must be either the identifier `bool` or the identifier `str`. = If +/// `bool`, the value may be either `true` or `false`. If `str`, the value= may +/// be any string literal. +/// +/// The symbols `bool` and `str` are not reserved keywords in Rust so thes= e are +/// not considered keywords in the `syn::token` module. Like any other +/// identifier that is not a keyword, these can be declared as custom keyw= ords +/// by crates that need to use them as such. +/// +/// ``` +/// use syn::{LitBool, LitStr, Result, Token}; +/// use syn::parse::{Parse, ParseStream}; +/// +/// mod kw { +/// syn::custom_keyword!(bool); +/// syn::custom_keyword!(str); +/// } +/// +/// enum Argument { +/// Bool { +/// bool_token: kw::bool, +/// eq_token: Token![=3D], +/// value: LitBool, +/// }, +/// Str { +/// str_token: kw::str, +/// eq_token: Token![=3D], +/// value: LitStr, +/// }, +/// } +/// +/// impl Parse for Argument { +/// fn parse(input: ParseStream) -> Result { +/// let lookahead =3D input.lookahead1(); +/// if lookahead.peek(kw::bool) { +/// Ok(Argument::Bool { +/// bool_token: input.parse::()?, +/// eq_token: input.parse()?, +/// value: input.parse()?, +/// }) +/// } else if lookahead.peek(kw::str) { +/// Ok(Argument::Str { +/// str_token: input.parse::()?, +/// eq_token: input.parse()?, +/// value: input.parse()?, +/// }) +/// } else { +/// Err(lookahead.error()) +/// } +/// } +/// } +/// ``` +#[macro_export] +macro_rules! custom_keyword { + ($ident:ident) =3D> { + #[allow(non_camel_case_types)] + pub struct $ident { + #[allow(dead_code)] + pub span: $crate::__private::Span, + } + + #[doc(hidden)] + #[allow(dead_code, non_snake_case)] + pub fn $ident<__S: $crate::__private::IntoSpans<$crate::__private:= :Span>>( + span: __S, + ) -> $ident { + $ident { + span: $crate::__private::IntoSpans::into_spans(span), + } + } + + const _: () =3D { + impl $crate::__private::Default for $ident { + fn default() -> Self { + $ident { + span: $crate::__private::Span::call_site(), + } + } + } + + $crate::impl_parse_for_custom_keyword!($ident); + $crate::impl_to_tokens_for_custom_keyword!($ident); + $crate::impl_clone_for_custom_keyword!($ident); + $crate::impl_extra_traits_for_custom_keyword!($ident); + }; + }; +} + +// Not public API. +#[cfg(feature =3D "parsing")] +#[doc(hidden)] +#[macro_export] +macro_rules! impl_parse_for_custom_keyword { + ($ident:ident) =3D> { + // For peek. + impl $crate::__private::CustomToken for $ident { + fn peek(cursor: $crate::buffer::Cursor) -> $crate::__private::= bool { + if let $crate::__private::Some((ident, _rest)) =3D cursor.= ident() { + ident =3D=3D $crate::__private::stringify!($ident) + } else { + false + } + } + + fn display() -> &'static $crate::__private::str { + $crate::__private::concat!("`", $crate::__private::stringi= fy!($ident), "`") + } + } + + impl $crate::parse::Parse for $ident { + fn parse(input: $crate::parse::ParseStream) -> $crate::parse::= Result<$ident> { + input.step(|cursor| { + if let $crate::__private::Some((ident, rest)) =3D curs= or.ident() { + if ident =3D=3D $crate::__private::stringify!($ide= nt) { + return $crate::__private::Ok(($ident { span: i= dent.span() }, rest)); + } + } + $crate::__private::Err(cursor.error($crate::__private:= :concat!( + "expected `", + $crate::__private::stringify!($ident), + "`", + ))) + }) + } + } + }; +} + +// Not public API. +#[cfg(not(feature =3D "parsing"))] +#[doc(hidden)] +#[macro_export] +macro_rules! impl_parse_for_custom_keyword { + ($ident:ident) =3D> {}; +} + +// Not public API. +#[cfg(feature =3D "printing")] +#[doc(hidden)] +#[macro_export] +macro_rules! impl_to_tokens_for_custom_keyword { + ($ident:ident) =3D> { + impl $crate::__private::ToTokens for $ident { + fn to_tokens(&self, tokens: &mut $crate::__private::TokenStrea= m2) { + let ident =3D $crate::Ident::new($crate::__private::string= ify!($ident), self.span); + $crate::__private::TokenStreamExt::append(tokens, ident); + } + } + }; +} + +// Not public API. +#[cfg(not(feature =3D "printing"))] +#[doc(hidden)] +#[macro_export] +macro_rules! impl_to_tokens_for_custom_keyword { + ($ident:ident) =3D> {}; +} + +// Not public API. +#[cfg(feature =3D "clone-impls")] +#[doc(hidden)] +#[macro_export] +macro_rules! impl_clone_for_custom_keyword { + ($ident:ident) =3D> { + impl $crate::__private::Copy for $ident {} + + #[allow(clippy::expl_impl_clone_on_copy)] + impl $crate::__private::Clone for $ident { + fn clone(&self) -> Self { + *self + } + } + }; +} + +// Not public API. +#[cfg(not(feature =3D "clone-impls"))] +#[doc(hidden)] +#[macro_export] +macro_rules! impl_clone_for_custom_keyword { + ($ident:ident) =3D> {}; +} + +// Not public API. +#[cfg(feature =3D "extra-traits")] +#[doc(hidden)] +#[macro_export] +macro_rules! impl_extra_traits_for_custom_keyword { + ($ident:ident) =3D> { + impl $crate::__private::Debug for $ident { + fn fmt(&self, f: &mut $crate::__private::Formatter) -> $crate:= :__private::FmtResult { + $crate::__private::Formatter::write_str( + f, + $crate::__private::concat!( + "Keyword [", + $crate::__private::stringify!($ident), + "]", + ), + ) + } + } + + impl $crate::__private::Eq for $ident {} + + impl $crate::__private::PartialEq for $ident { + fn eq(&self, _other: &Self) -> $crate::__private::bool { + true + } + } + + impl $crate::__private::Hash for $ident { + fn hash<__H: $crate::__private::Hasher>(&self, _state: &mut __= H) {} + } + }; +} + +// Not public API. +#[cfg(not(feature =3D "extra-traits"))] +#[doc(hidden)] +#[macro_export] +macro_rules! impl_extra_traits_for_custom_keyword { + ($ident:ident) =3D> {}; +} diff --git a/rust/syn/custom_punctuation.rs b/rust/syn/custom_punctuation.rs new file mode 100644 index 000000000000..eef5f5458459 --- /dev/null +++ b/rust/syn/custom_punctuation.rs @@ -0,0 +1,304 @@ +/// Define a type that supports parsing and printing a multi-character sym= bol +/// as if it were a punctuation token. +/// +/// # Usage +/// +/// ``` +/// syn::custom_punctuation!(LeftRightArrow, <=3D>); +/// ``` +/// +/// The generated syntax tree node supports the following operations just = like +/// any built-in punctuation token. +/// +/// - [Peeking] =E2=80=94 `input.peek(LeftRightArrow)` +/// +/// - [Parsing] =E2=80=94 `input.parse::()?` +/// +/// - [Printing] =E2=80=94 `quote!( ... #lrarrow ... )` +/// +/// - Construction from a [`Span`] =E2=80=94 `let lrarrow =3D LeftRightArr= ow(sp)` +/// +/// - Construction from multiple [`Span`] =E2=80=94 `let lrarrow =3D LeftR= ightArrow([sp, sp, sp])` +/// +/// - Field access to its spans =E2=80=94 `let spans =3D lrarrow.spans` +/// +/// [Peeking]: crate::parse::ParseBuffer::peek +/// [Parsing]: crate::parse::ParseBuffer::parse +/// [Printing]: quote::ToTokens +/// [`Span`]: proc_macro2::Span +/// +/// # Example +/// +/// ``` +/// use proc_macro2::{TokenStream, TokenTree}; +/// use syn::parse::{Parse, ParseStream, Peek, Result}; +/// use syn::punctuated::Punctuated; +/// use syn::Expr; +/// +/// syn::custom_punctuation!(PathSeparator, ); +/// +/// // expr expr expr ... +/// struct PathSegments { +/// segments: Punctuated, +/// } +/// +/// impl Parse for PathSegments { +/// fn parse(input: ParseStream) -> Result { +/// let mut segments =3D Punctuated::new(); +/// +/// let first =3D parse_until(input, PathSeparator)?; +/// segments.push_value(syn::parse2(first)?); +/// +/// while input.peek(PathSeparator) { +/// segments.push_punct(input.parse()?); +/// +/// let next =3D parse_until(input, PathSeparator)?; +/// segments.push_value(syn::parse2(next)?); +/// } +/// +/// Ok(PathSegments { segments }) +/// } +/// } +/// +/// fn parse_until(input: ParseStream, end: E) -> Result { +/// let mut tokens =3D TokenStream::new(); +/// while !input.is_empty() && !input.peek(end) { +/// let next: TokenTree =3D input.parse()?; +/// tokens.extend(Some(next)); +/// } +/// Ok(tokens) +/// } +/// +/// fn main() { +/// let input =3D r#" a::b c::d::e "#; +/// let _: PathSegments =3D syn::parse_str(input).unwrap(); +/// } +/// ``` +#[macro_export] +macro_rules! custom_punctuation { + ($ident:ident, $($tt:tt)+) =3D> { + pub struct $ident { + #[allow(dead_code)] + pub spans: $crate::custom_punctuation_repr!($($tt)+), + } + + #[doc(hidden)] + #[allow(dead_code, non_snake_case)] + pub fn $ident<__S: $crate::__private::IntoSpans<$crate::custom_pun= ctuation_repr!($($tt)+)>>( + spans: __S, + ) -> $ident { + let _validate_len =3D 0 $(+ $crate::custom_punctuation_len!(st= rict, $tt))*; + $ident { + spans: $crate::__private::IntoSpans::into_spans(spans) + } + } + + const _: () =3D { + impl $crate::__private::Default for $ident { + fn default() -> Self { + $ident($crate::__private::Span::call_site()) + } + } + + $crate::impl_parse_for_custom_punctuation!($ident, $($tt)+); + $crate::impl_to_tokens_for_custom_punctuation!($ident, $($tt)+= ); + $crate::impl_clone_for_custom_punctuation!($ident, $($tt)+); + $crate::impl_extra_traits_for_custom_punctuation!($ident, $($t= t)+); + }; + }; +} + +// Not public API. +#[cfg(feature =3D "parsing")] +#[doc(hidden)] +#[macro_export] +macro_rules! impl_parse_for_custom_punctuation { + ($ident:ident, $($tt:tt)+) =3D> { + impl $crate::__private::CustomToken for $ident { + fn peek(cursor: $crate::buffer::Cursor) -> $crate::__private::= bool { + $crate::__private::peek_punct(cursor, $crate::stringify_pu= nct!($($tt)+)) + } + + fn display() -> &'static $crate::__private::str { + $crate::__private::concat!("`", $crate::stringify_punct!($= ($tt)+), "`") + } + } + + impl $crate::parse::Parse for $ident { + fn parse(input: $crate::parse::ParseStream) -> $crate::parse::= Result<$ident> { + let spans: $crate::custom_punctuation_repr!($($tt)+) =3D + $crate::__private::parse_punct(input, $crate::stringif= y_punct!($($tt)+))?; + Ok($ident(spans)) + } + } + }; +} + +// Not public API. +#[cfg(not(feature =3D "parsing"))] +#[doc(hidden)] +#[macro_export] +macro_rules! impl_parse_for_custom_punctuation { + ($ident:ident, $($tt:tt)+) =3D> {}; +} + +// Not public API. +#[cfg(feature =3D "printing")] +#[doc(hidden)] +#[macro_export] +macro_rules! impl_to_tokens_for_custom_punctuation { + ($ident:ident, $($tt:tt)+) =3D> { + impl $crate::__private::ToTokens for $ident { + fn to_tokens(&self, tokens: &mut $crate::__private::TokenStrea= m2) { + $crate::__private::print_punct($crate::stringify_punct!($(= $tt)+), &self.spans, tokens) + } + } + }; +} + +// Not public API. +#[cfg(not(feature =3D "printing"))] +#[doc(hidden)] +#[macro_export] +macro_rules! impl_to_tokens_for_custom_punctuation { + ($ident:ident, $($tt:tt)+) =3D> {}; +} + +// Not public API. +#[cfg(feature =3D "clone-impls")] +#[doc(hidden)] +#[macro_export] +macro_rules! impl_clone_for_custom_punctuation { + ($ident:ident, $($tt:tt)+) =3D> { + impl $crate::__private::Copy for $ident {} + + #[allow(clippy::expl_impl_clone_on_copy)] + impl $crate::__private::Clone for $ident { + fn clone(&self) -> Self { + *self + } + } + }; +} + +// Not public API. +#[cfg(not(feature =3D "clone-impls"))] +#[doc(hidden)] +#[macro_export] +macro_rules! impl_clone_for_custom_punctuation { + ($ident:ident, $($tt:tt)+) =3D> {}; +} + +// Not public API. +#[cfg(feature =3D "extra-traits")] +#[doc(hidden)] +#[macro_export] +macro_rules! impl_extra_traits_for_custom_punctuation { + ($ident:ident, $($tt:tt)+) =3D> { + impl $crate::__private::Debug for $ident { + fn fmt(&self, f: &mut $crate::__private::Formatter) -> $crate:= :__private::FmtResult { + $crate::__private::Formatter::write_str(f, $crate::__priva= te::stringify!($ident)) + } + } + + impl $crate::__private::Eq for $ident {} + + impl $crate::__private::PartialEq for $ident { + fn eq(&self, _other: &Self) -> $crate::__private::bool { + true + } + } + + impl $crate::__private::Hash for $ident { + fn hash<__H: $crate::__private::Hasher>(&self, _state: &mut __= H) {} + } + }; +} + +// Not public API. +#[cfg(not(feature =3D "extra-traits"))] +#[doc(hidden)] +#[macro_export] +macro_rules! impl_extra_traits_for_custom_punctuation { + ($ident:ident, $($tt:tt)+) =3D> {}; +} + +// Not public API. +#[doc(hidden)] +#[macro_export] +macro_rules! custom_punctuation_repr { + ($($tt:tt)+) =3D> { + [$crate::__private::Span; 0 $(+ $crate::custom_punctuation_len!(le= nient, $tt))+] + }; +} + +// Not public API. +#[doc(hidden)] +#[macro_export] +#[rustfmt::skip] +macro_rules! custom_punctuation_len { + ($mode:ident, &) =3D> { 1 }; + ($mode:ident, &&) =3D> { 2 }; + ($mode:ident, &=3D) =3D> { 2 }; + ($mode:ident, @) =3D> { 1 }; + ($mode:ident, ^) =3D> { 1 }; + ($mode:ident, ^=3D) =3D> { 2 }; + ($mode:ident, :) =3D> { 1 }; + ($mode:ident, ,) =3D> { 1 }; + ($mode:ident, $) =3D> { 1 }; + ($mode:ident, .) =3D> { 1 }; + ($mode:ident, ..) =3D> { 2 }; + ($mode:ident, ...) =3D> { 3 }; + ($mode:ident, ..=3D) =3D> { 3 }; + ($mode:ident, =3D) =3D> { 1 }; + ($mode:ident, =3D=3D) =3D> { 2 }; + ($mode:ident, =3D>) =3D> { 2 }; + ($mode:ident, >=3D) =3D> { 2 }; + ($mode:ident, >) =3D> { 1 }; + ($mode:ident, <-) =3D> { 2 }; + ($mode:ident, <=3D) =3D> { 2 }; + ($mode:ident, <) =3D> { 1 }; + ($mode:ident, -) =3D> { 1 }; + ($mode:ident, -=3D) =3D> { 2 }; + ($mode:ident, !=3D) =3D> { 2 }; + ($mode:ident, !) =3D> { 1 }; + ($mode:ident, |) =3D> { 1 }; + ($mode:ident, |=3D) =3D> { 2 }; + ($mode:ident, ||) =3D> { 2 }; + ($mode:ident, ::) =3D> { 2 }; + ($mode:ident, %) =3D> { 1 }; + ($mode:ident, %=3D) =3D> { 2 }; + ($mode:ident, +) =3D> { 1 }; + ($mode:ident, +=3D) =3D> { 2 }; + ($mode:ident, #) =3D> { 1 }; + ($mode:ident, ?) =3D> { 1 }; + ($mode:ident, ->) =3D> { 2 }; + ($mode:ident, ;) =3D> { 1 }; + ($mode:ident, <<) =3D> { 2 }; + ($mode:ident, <<=3D) =3D> { 3 }; + ($mode:ident, >>) =3D> { 2 }; + ($mode:ident, >>=3D) =3D> { 3 }; + ($mode:ident, /) =3D> { 1 }; + ($mode:ident, /=3D) =3D> { 2 }; + ($mode:ident, *) =3D> { 1 }; + ($mode:ident, *=3D) =3D> { 2 }; + ($mode:ident, ~) =3D> { 1 }; + (lenient, $tt:tt) =3D> { 0 }; + (strict, $tt:tt) =3D> {{ $crate::custom_punctuation_unexpected!($t= t); 0 }}; +} + +// Not public API. +#[doc(hidden)] +#[macro_export] +macro_rules! custom_punctuation_unexpected { + () =3D> {}; +} + +// Not public API. +#[doc(hidden)] +#[macro_export] +macro_rules! stringify_punct { + ($($tt:tt)+) =3D> { + $crate::__private::concat!($($crate::__private::stringify!($tt)),+) + }; +} diff --git a/rust/syn/data.rs b/rust/syn/data.rs new file mode 100644 index 000000000000..96db2a0b7c6f --- /dev/null +++ b/rust/syn/data.rs @@ -0,0 +1,424 @@ +use crate::attr::Attribute; +use crate::expr::{Expr, Index, Member}; +use crate::ident::Ident; +use crate::punctuated::{self, Punctuated}; +use crate::restriction::{FieldMutability, Visibility}; +use crate::token; +use crate::ty::Type; + +ast_struct! { + /// An enum variant. + #[cfg_attr(docsrs, doc(cfg(any(feature =3D "full", feature =3D "derive= "))))] + pub struct Variant { + pub attrs: Vec, + + /// Name of the variant. + pub ident: Ident, + + /// Content stored in the variant. + pub fields: Fields, + + /// Explicit discriminant: `Variant =3D 1` + pub discriminant: Option<(Token![=3D], Expr)>, + } +} + +ast_enum_of_structs! { + /// Data stored within an enum variant or struct. + /// + /// # 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 =3D "full", feature =3D "derive= "))))] + pub enum Fields { + /// Named fields of a struct or struct variant such as `Point { x:= f64, + /// y: f64 }`. + Named(FieldsNamed), + + /// Unnamed fields of a tuple struct or tuple variant such as `Som= e(T)`. + Unnamed(FieldsUnnamed), + + /// Unit struct or unit variant such as `None`. + Unit, + } +} + +ast_struct! { + /// Named fields of a struct or struct variant such as `Point { x: f64, + /// y: f64 }`. + #[cfg_attr(docsrs, doc(cfg(any(feature =3D "full", feature =3D "derive= "))))] + pub struct FieldsNamed { + pub brace_token: token::Brace, + pub named: Punctuated, + } +} + +ast_struct! { + /// Unnamed fields of a tuple struct or tuple variant such as `Some(T)= `. + #[cfg_attr(docsrs, doc(cfg(any(feature =3D "full", feature =3D "derive= "))))] + pub struct FieldsUnnamed { + pub paren_token: token::Paren, + pub unnamed: Punctuated, + } +} + +impl Fields { + /// Get an iterator over the borrowed [`Field`] items in this object. = This + /// iterator can be used to iterate over a named or unnamed struct or + /// variant's fields uniformly. + pub fn iter(&self) -> punctuated::Iter { + match self { + Fields::Unit =3D> crate::punctuated::empty_punctuated_iter(), + Fields::Named(f) =3D> f.named.iter(), + Fields::Unnamed(f) =3D> f.unnamed.iter(), + } + } + + /// Get an iterator over the mutably borrowed [`Field`] items in this + /// object. This iterator can be used to iterate over a named or unnam= ed + /// struct or variant's fields uniformly. + pub fn iter_mut(&mut self) -> punctuated::IterMut { + match self { + Fields::Unit =3D> crate::punctuated::empty_punctuated_iter_mut= (), + Fields::Named(f) =3D> f.named.iter_mut(), + Fields::Unnamed(f) =3D> f.unnamed.iter_mut(), + } + } + + /// Returns the number of fields. + pub fn len(&self) -> usize { + match self { + Fields::Unit =3D> 0, + Fields::Named(f) =3D> f.named.len(), + Fields::Unnamed(f) =3D> f.unnamed.len(), + } + } + + /// Returns `true` if there are zero fields. + pub fn is_empty(&self) -> bool { + match self { + Fields::Unit =3D> true, + Fields::Named(f) =3D> f.named.is_empty(), + Fields::Unnamed(f) =3D> f.unnamed.is_empty(), + } + } + + return_impl_trait! { + /// Get an iterator over the fields of a struct or variant as [`Me= mber`]s. + /// This iterator can be used to iterate over a named or unnamed s= truct or + /// variant's fields uniformly. + /// + /// # Example + /// + /// The following is a simplistic [`Clone`] derive for structs. (A= more + /// complete implementation would additionally want to infer trait= bounds on + /// the generic type parameters.) + /// + /// ``` + /// # use quote::quote; + /// # + /// fn derive_clone(input: &syn::ItemStruct) -> proc_macro2::Token= Stream { + /// let ident =3D &input.ident; + /// let members =3D input.fields.members(); + /// let (impl_generics, ty_generics, where_clause) =3D input.g= enerics.split_for_impl(); + /// quote! { + /// impl #impl_generics Clone for #ident #ty_generics #whe= re_clause { + /// fn clone(&self) -> Self { + /// Self { + /// #(#members: self.#members.clone()),* + /// } + /// } + /// } + /// } + /// } + /// ``` + /// + /// For structs with named fields, it produces an expression like = `Self { a: + /// self.a.clone() }`. For structs with unnamed fields, `Self { 0: + /// self.0.clone() }`. And for unit structs, `Self {}`. + pub fn members(&self) -> impl Iterator + Clone + = '_ [Members] { + Members { + fields: self.iter(), + index: 0, + } + } + } +} + +impl IntoIterator for Fields { + type Item =3D Field; + type IntoIter =3D punctuated::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + match self { + Fields::Unit =3D> Punctuated::::new().into_iter(), + Fields::Named(f) =3D> f.named.into_iter(), + Fields::Unnamed(f) =3D> f.unnamed.into_iter(), + } + } +} + +impl<'a> IntoIterator for &'a Fields { + type Item =3D &'a Field; + type IntoIter =3D punctuated::Iter<'a, Field>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'a> IntoIterator for &'a mut Fields { + type Item =3D &'a mut Field; + type IntoIter =3D punctuated::IterMut<'a, Field>; + + fn into_iter(self) -> Self::IntoIter { + self.iter_mut() + } +} + +ast_struct! { + /// A field of a struct or enum variant. + #[cfg_attr(docsrs, doc(cfg(any(feature =3D "full", feature =3D "derive= "))))] + pub struct Field { + pub attrs: Vec, + + pub vis: Visibility, + + pub mutability: FieldMutability, + + /// Name of the field, if any. + /// + /// Fields of tuple structs have no names. + pub ident: Option, + + pub colon_token: Option, + + pub ty: Type, + } +} + +pub struct Members<'a> { + fields: punctuated::Iter<'a, Field>, + index: u32, +} + +impl<'a> Iterator for Members<'a> { + type Item =3D Member; + + fn next(&mut self) -> Option { + let field =3D self.fields.next()?; + let member =3D match &field.ident { + Some(ident) =3D> Member::Named(ident.clone()), + None =3D> { + #[cfg(all(feature =3D "parsing", feature =3D "printing"))] + let span =3D crate::spanned::Spanned::span(&field.ty); + #[cfg(not(all(feature =3D "parsing", feature =3D "printing= ")))] + let span =3D proc_macro2::Span::call_site(); + Member::Unnamed(Index { + index: self.index, + span, + }) + } + }; + self.index +=3D 1; + Some(member) + } +} + +impl<'a> Clone for Members<'a> { + fn clone(&self) -> Self { + Members { + fields: self.fields.clone(), + index: self.index, + } + } +} + +#[cfg(feature =3D "parsing")] +pub(crate) mod parsing { + use crate::attr::Attribute; + use crate::data::{Field, Fields, FieldsNamed, FieldsUnnamed, Variant}; + use crate::error::Result; + use crate::expr::Expr; + use crate::ext::IdentExt as _; + use crate::ident::Ident; + #[cfg(not(feature =3D "full"))] + use crate::parse::discouraged::Speculative as _; + use crate::parse::{Parse, ParseStream}; + use crate::restriction::{FieldMutability, Visibility}; + #[cfg(not(feature =3D "full"))] + use crate::scan_expr::scan_expr; + use crate::token; + use crate::ty::Type; + use crate::verbatim; + + #[cfg_attr(docsrs, doc(cfg(feature =3D "parsing")))] + impl Parse for Variant { + fn parse(input: ParseStream) -> Result { + let attrs =3D input.call(Attribute::parse_outer)?; + let _visibility: Visibility =3D input.parse()?; + let ident: Ident =3D input.parse()?; + let fields =3D if input.peek(token::Brace) { + Fields::Named(input.parse()?) + } else if input.peek(token::Paren) { + Fields::Unnamed(input.parse()?) + } else { + Fields::Unit + }; + let discriminant =3D if input.peek(Token![=3D]) { + let eq_token: Token![=3D] =3D input.parse()?; + #[cfg(feature =3D "full")] + let discriminant: Expr =3D input.parse()?; + #[cfg(not(feature =3D "full"))] + let discriminant =3D { + let begin =3D input.fork(); + let ahead =3D input.fork(); + let mut discriminant: Result =3D ahead.parse(); + if discriminant.is_ok() { + input.advance_to(&ahead); + } else if scan_expr(input).is_ok() { + discriminant =3D Ok(Expr::Verbatim(verbatim::betwe= en(&begin, input))); + } + discriminant? + }; + Some((eq_token, discriminant)) + } else { + None + }; + Ok(Variant { + attrs, + ident, + fields, + discriminant, + }) + } + } + + #[cfg_attr(docsrs, doc(cfg(feature =3D "parsing")))] + impl Parse for FieldsNamed { + fn parse(input: ParseStream) -> Result { + let content; + Ok(FieldsNamed { + brace_token: braced!(content in input), + named: content.parse_terminated(Field::parse_named, Token!= [,])?, + }) + } + } + + #[cfg_attr(docsrs, doc(cfg(feature =3D "parsing")))] + impl Parse for FieldsUnnamed { + fn parse(input: ParseStream) -> Result { + let content; + Ok(FieldsUnnamed { + paren_token: parenthesized!(content in input), + unnamed: content.parse_terminated(Field::parse_unnamed, To= ken![,])?, + }) + } + } + + impl Field { + /// Parses a named (braced struct) field. + #[cfg_attr(docsrs, doc(cfg(feature =3D "parsing")))] + pub fn parse_named(input: ParseStream) -> Result { + let attrs =3D input.call(Attribute::parse_outer)?; + let vis: Visibility =3D input.parse()?; + + let unnamed_field =3D cfg!(feature =3D "full") && input.peek(T= oken![_]); + let ident =3D if unnamed_field { + input.call(Ident::parse_any) + } else { + input.parse() + }?; + + let colon_token: Token![:] =3D input.parse()?; + + let ty: Type =3D if unnamed_field + && (input.peek(Token![struct]) + || input.peek(Token![union]) && input.peek2(token::Bra= ce)) + { + let begin =3D input.fork(); + input.call(Ident::parse_any)?; + input.parse::()?; + Type::Verbatim(verbatim::between(&begin, input)) + } else { + input.parse()? + }; + + Ok(Field { + attrs, + vis, + mutability: FieldMutability::None, + ident: Some(ident), + colon_token: Some(colon_token), + ty, + }) + } + + /// Parses an unnamed (tuple struct) field. + #[cfg_attr(docsrs, doc(cfg(feature =3D "parsing")))] + pub fn parse_unnamed(input: ParseStream) -> Result { + Ok(Field { + attrs: input.call(Attribute::parse_outer)?, + vis: input.parse()?, + mutability: FieldMutability::None, + ident: None, + colon_token: None, + ty: input.parse()?, + }) + } + } +} + +#[cfg(feature =3D "printing")] +mod printing { + use crate::data::{Field, FieldsNamed, FieldsUnnamed, Variant}; + use crate::print::TokensOrDefault; + use proc_macro2::TokenStream; + use quote::{ToTokens, TokenStreamExt}; + + #[cfg_attr(docsrs, doc(cfg(feature =3D "printing")))] + impl ToTokens for Variant { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append_all(&self.attrs); + self.ident.to_tokens(tokens); + self.fields.to_tokens(tokens); + if let Some((eq_token, disc)) =3D &self.discriminant { + eq_token.to_tokens(tokens); + disc.to_tokens(tokens); + } + } + } + + #[cfg_attr(docsrs, doc(cfg(feature =3D "printing")))] + impl ToTokens for FieldsNamed { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.brace_token.surround(tokens, |tokens| { + self.named.to_tokens(tokens); + }); + } + } + + #[cfg_attr(docsrs, doc(cfg(feature =3D "printing")))] + impl ToTokens for FieldsUnnamed { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.paren_token.surround(tokens, |tokens| { + self.unnamed.to_tokens(tokens); + }); + } + } + + #[cfg_attr(docsrs, doc(cfg(feature =3D "printing")))] + impl ToTokens for Field { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append_all(&self.attrs); + self.vis.to_tokens(tokens); + if let Some(ident) =3D &self.ident { + ident.to_tokens(tokens); + TokensOrDefault(&self.colon_token).to_tokens(tokens); + } + self.ty.to_tokens(tokens); + } + } +} diff --git a/rust/syn/derive.rs b/rust/syn/derive.rs new file mode 100644 index 000000000000..3443ecfc05cb --- /dev/null +++ b/rust/syn/derive.rs @@ -0,0 +1,259 @@ +use crate::attr::Attribute; +use crate::data::{Fields, FieldsNamed, Variant}; +use crate::generics::Generics; +use crate::ident::Ident; +use crate::punctuated::Punctuated; +use crate::restriction::Visibility; +use crate::token; + +ast_struct! { + /// Data structure sent to a `proc_macro_derive` macro. + #[cfg_attr(docsrs, doc(cfg(feature =3D "derive")))] + pub struct DeriveInput { + pub attrs: Vec, + pub vis: Visibility, + pub ident: Ident, + pub generics: Generics, + pub data: Data, + } +} + +ast_enum! { + /// The storage of a struct, enum or union data structure. + /// + /// # Syntax tree enum + /// + /// This type is a [syntax tree enum]. + /// + /// [syntax tree enum]: crate::expr::Expr#syntax-tree-enums + #[cfg_attr(docsrs, doc(cfg(feature =3D "derive")))] + pub enum Data { + Struct(DataStruct), + Enum(DataEnum), + Union(DataUnion), + } +} + +ast_struct! { + /// A struct input to a `proc_macro_derive` macro. + #[cfg_attr(docsrs, doc(cfg(feature =3D "derive")))] + pub struct DataStruct { + pub struct_token: Token![struct], + pub fields: Fields, + pub semi_token: Option, + } +} + +ast_struct! { + /// An enum input to a `proc_macro_derive` macro. + #[cfg_attr(docsrs, doc(cfg(feature =3D "derive")))] + pub struct DataEnum { + pub enum_token: Token![enum], + pub brace_token: token::Brace, + pub variants: Punctuated, + } +} + +ast_struct! { + /// An untagged union input to a `proc_macro_derive` macro. + #[cfg_attr(docsrs, doc(cfg(feature =3D "derive")))] + pub struct DataUnion { + pub union_token: Token![union], + pub fields: FieldsNamed, + } +} + +#[cfg(feature =3D "parsing")] +pub(crate) mod parsing { + use crate::attr::Attribute; + use crate::data::{Fields, FieldsNamed, Variant}; + use crate::derive::{Data, DataEnum, DataStruct, DataUnion, DeriveInput= }; + use crate::error::Result; + use crate::generics::{Generics, WhereClause}; + use crate::ident::Ident; + use crate::parse::{Parse, ParseStream}; + use crate::punctuated::Punctuated; + use crate::restriction::Visibility; + use crate::token; + + #[cfg_attr(docsrs, doc(cfg(feature =3D "parsing")))] + impl Parse for DeriveInput { + fn parse(input: ParseStream) -> Result { + let attrs =3D input.call(Attribute::parse_outer)?; + let vis =3D input.parse::()?; + + let lookahead =3D input.lookahead1(); + if lookahead.peek(Token![struct]) { + let struct_token =3D input.parse::()?; + let ident =3D input.parse::()?; + let generics =3D input.parse::()?; + let (where_clause, fields, semi) =3D data_struct(input)?; + Ok(DeriveInput { + attrs, + vis, + ident, + generics: Generics { + where_clause, + ..generics + }, + data: Data::Struct(DataStruct { + struct_token, + fields, + semi_token: semi, + }), + }) + } else if lookahead.peek(Token![enum]) { + let enum_token =3D input.parse::()?; + let ident =3D input.parse::()?; + let generics =3D input.parse::()?; + let (where_clause, brace, variants) =3D data_enum(input)?; + Ok(DeriveInput { + attrs, + vis, + ident, + generics: Generics { + where_clause, + ..generics + }, + data: Data::Enum(DataEnum { + enum_token, + brace_token: brace, + variants, + }), + }) + } else if lookahead.peek(Token![union]) { + let union_token =3D input.parse::()?; + let ident =3D input.parse::()?; + let generics =3D input.parse::()?; + let (where_clause, fields) =3D data_union(input)?; + Ok(DeriveInput { + attrs, + vis, + ident, + generics: Generics { + where_clause, + ..generics + }, + data: Data::Union(DataUnion { + union_token, + fields, + }), + }) + } else { + Err(lookahead.error()) + } + } + } + + pub(crate) fn data_struct( + input: ParseStream, + ) -> Result<(Option, Fields, Option)> { + let mut lookahead =3D input.lookahead1(); + let mut where_clause =3D None; + if lookahead.peek(Token![where]) { + where_clause =3D Some(input.parse()?); + lookahead =3D input.lookahead1(); + } + + if where_clause.is_none() && lookahead.peek(token::Paren) { + let fields =3D input.parse()?; + + lookahead =3D input.lookahead1(); + if lookahead.peek(Token![where]) { + where_clause =3D Some(input.parse()?); + lookahead =3D input.lookahead1(); + } + + if lookahead.peek(Token![;]) { + let semi =3D input.parse()?; + Ok((where_clause, Fields::Unnamed(fields), Some(semi))) + } else { + Err(lookahead.error()) + } + } else if lookahead.peek(token::Brace) { + let fields =3D input.parse()?; + Ok((where_clause, Fields::Named(fields), None)) + } else if lookahead.peek(Token![;]) { + let semi =3D input.parse()?; + Ok((where_clause, Fields::Unit, Some(semi))) + } else { + Err(lookahead.error()) + } + } + + pub(crate) fn data_enum( + input: ParseStream, + ) -> Result<( + Option, + token::Brace, + Punctuated, + )> { + let where_clause =3D input.parse()?; + + let content; + let brace =3D braced!(content in input); + let variants =3D content.parse_terminated(Variant::parse, Token![,= ])?; + + Ok((where_clause, brace, variants)) + } + + pub(crate) fn data_union(input: ParseStream) -> Result<(Option, FieldsNamed)> { + let where_clause =3D input.parse()?; + let fields =3D input.parse()?; + Ok((where_clause, fields)) + } +} + +#[cfg(feature =3D "printing")] +mod printing { + use crate::attr::FilterAttrs; + use crate::data::Fields; + use crate::derive::{Data, DeriveInput}; + use crate::print::TokensOrDefault; + use proc_macro2::TokenStream; + use quote::ToTokens; + + #[cfg_attr(docsrs, doc(cfg(feature =3D "printing")))] + impl ToTokens for DeriveInput { + fn to_tokens(&self, tokens: &mut TokenStream) { + for attr in self.attrs.outer() { + attr.to_tokens(tokens); + } + self.vis.to_tokens(tokens); + match &self.data { + Data::Struct(d) =3D> d.struct_token.to_tokens(tokens), + Data::Enum(d) =3D> d.enum_token.to_tokens(tokens), + Data::Union(d) =3D> d.union_token.to_tokens(tokens), + } + self.ident.to_tokens(tokens); + self.generics.to_tokens(tokens); + match &self.data { + Data::Struct(data) =3D> match &data.fields { + Fields::Named(fields) =3D> { + self.generics.where_clause.to_tokens(tokens); + fields.to_tokens(tokens); + } + Fields::Unnamed(fields) =3D> { + fields.to_tokens(tokens); + self.generics.where_clause.to_tokens(tokens); + TokensOrDefault(&data.semi_token).to_tokens(tokens= ); + } + Fields::Unit =3D> { + self.generics.where_clause.to_tokens(tokens); + TokensOrDefault(&data.semi_token).to_tokens(tokens= ); + } + }, + Data::Enum(data) =3D> { + self.generics.where_clause.to_tokens(tokens); + data.brace_token.surround(tokens, |tokens| { + data.variants.to_tokens(tokens); + }); + } + Data::Union(data) =3D> { + self.generics.where_clause.to_tokens(tokens); + data.fields.to_tokens(tokens); + } + } + } + } +} diff --git a/rust/syn/discouraged.rs b/rust/syn/discouraged.rs new file mode 100644 index 000000000000..c8d6bfe89a14 --- /dev/null +++ b/rust/syn/discouraged.rs @@ -0,0 +1,225 @@ +//! Extensions to the parsing API with niche applicability. + +use crate::buffer::Cursor; +use crate::error::Result; +use crate::parse::{inner_unexpected, ParseBuffer, Unexpected}; +use proc_macro2::extra::DelimSpan; +use proc_macro2::Delimiter; +use std::cell::Cell; +use std::mem; +use std::rc::Rc; + +/// Extensions to the `ParseStream` API to support speculative parsing. +pub trait Speculative { + /// Advance this parse stream to the position of a forked parse stream. + /// + /// This is the opposite operation to [`ParseStream::fork`]. You can f= ork a + /// parse stream, perform some speculative parsing, then join the orig= inal + /// stream to the fork to "commit" the parsing from the fork to the ma= in + /// stream. + /// + /// If you can avoid doing this, you should, as it limits the ability = to + /// generate useful errors. That said, it is often the only way to par= se + /// syntax of the form `A* B*` for arbitrary syntax `A` and `B`. The p= roblem + /// is that when the fork fails to parse an `A`, it's impossible to te= ll + /// whether that was because of a syntax error and the user meant to p= rovide + /// an `A`, or that the `A`s are finished and it's time to start parsi= ng + /// `B`s. Use with care. + /// + /// Also note that if `A` is a subset of `B`, `A* B*` can be parsed by + /// parsing `B*` and removing the leading members of `A` from the + /// repetition, bypassing the need to involve the downsides associated= with + /// speculative parsing. + /// + /// [`ParseStream::fork`]: ParseBuffer::fork + /// + /// # Example + /// + /// There has been chatter about the possibility of making the colons = in the + /// turbofish syntax like `path::to::` no longer required by accept= ing + /// `path::to` in expression position. Specifically, according to [= RFC + /// 2544], [`PathSegment`] parsing should always try to consume a foll= owing + /// `<` token as the start of generic arguments, and reset to the `<` = if + /// that fails (e.g. the token is acting as a less-than operator). + /// + /// This is the exact kind of parsing behavior which requires the "for= k, + /// try, commit" behavior that [`ParseStream::fork`] discourages. With + /// `advance_to`, we can avoid having to parse the speculatively parsed + /// content a second time. + /// + /// This change in behavior can be implemented in syn by replacing jus= t the + /// `Parse` implementation for `PathSegment`: + /// + /// ``` + /// # use syn::ext::IdentExt; + /// use syn::parse::discouraged::Speculative; + /// # use syn::parse::{Parse, ParseStream}; + /// # use syn::{Ident, PathArguments, Result, Token}; + /// + /// pub struct PathSegment { + /// pub ident: Ident, + /// pub arguments: PathArguments, + /// } + /// # + /// # impl From for PathSegment + /// # where + /// # T: Into, + /// # { + /// # fn from(ident: T) -> Self { + /// # PathSegment { + /// # ident: ident.into(), + /// # arguments: PathArguments::None, + /// # } + /// # } + /// # } + /// + /// impl Parse for PathSegment { + /// fn parse(input: ParseStream) -> Result { + /// if input.peek(Token![super]) + /// || input.peek(Token![self]) + /// || input.peek(Token![Self]) + /// || input.peek(Token![crate]) + /// { + /// let ident =3D input.call(Ident::parse_any)?; + /// return Ok(PathSegment::from(ident)); + /// } + /// + /// let ident =3D input.parse()?; + /// if input.peek(Token![::]) && input.peek3(Token![<]) { + /// return Ok(PathSegment { + /// ident, + /// arguments: PathArguments::AngleBracketed(input.par= se()?), + /// }); + /// } + /// if input.peek(Token![<]) && !input.peek(Token![<=3D]) { + /// let fork =3D input.fork(); + /// if let Ok(arguments) =3D fork.parse() { + /// input.advance_to(&fork); + /// return Ok(PathSegment { + /// ident, + /// arguments: PathArguments::AngleBracketed(argum= ents), + /// }); + /// } + /// } + /// Ok(PathSegment::from(ident)) + /// } + /// } + /// + /// # syn::parse_str::("a").unwrap(); + /// ``` + /// + /// # Drawbacks + /// + /// The main drawback of this style of speculative parsing is in error + /// presentation. Even if the lookahead is the "correct" parse, the er= ror + /// that is shown is that of the "fallback" parse. To use the same exa= mple + /// as the turbofish above, take the following unfinished "turbofish": + /// + /// ```text + /// let _ =3D f<&'a fn(), for<'a> serde::>(); + /// ``` + /// + /// If this is parsed as generic arguments, we can provide the error m= essage + /// + /// ```text + /// error: expected identifier + /// --> src.rs:L:C + /// | + /// L | let _ =3D f<&'a fn(), for<'a> serde::>(); + /// | ^ + /// ``` + /// + /// but if parsed using the above speculative parsing, it falls back to + /// assuming that the `<` is a less-than when it fails to parse the ge= neric + /// arguments, and tries to interpret the `&'a` as the start of a labe= lled + /// loop, resulting in the much less helpful error + /// + /// ```text + /// error: expected `:` + /// --> src.rs:L:C + /// | + /// L | let _ =3D f<&'a fn(), for<'a> serde::>(); + /// | ^^ + /// ``` + /// + /// This can be mitigated with various heuristics (two examples: show = both + /// forks' parse errors, or show the one that consumed more tokens), b= ut + /// when you can control the grammar, sticking to something that can be + /// parsed LL(3) and without the LL(*) speculative parsing this makes + /// possible, displaying reasonable errors becomes much more simple. + /// + /// [RFC 2544]: https://github.com/rust-lang/rfcs/pull/2544 + /// [`PathSegment`]: crate::PathSegment + /// + /// # Performance + /// + /// This method performs a cheap fixed amount of work that does not de= pend + /// on how far apart the two streams are positioned. + /// + /// # Panics + /// + /// The forked stream in the argument of `advance_to` must have been + /// obtained by forking `self`. Attempting to advance to any other str= eam + /// will cause a panic. + fn advance_to(&self, fork: &Self); +} + +impl<'a> Speculative for ParseBuffer<'a> { + fn advance_to(&self, fork: &Self) { + if !crate::buffer::same_scope(self.cursor(), fork.cursor()) { + panic!("fork was not derived from the advancing parse stream"); + } + + let (self_unexp, self_sp) =3D inner_unexpected(self); + let (fork_unexp, fork_sp) =3D inner_unexpected(fork); + if !Rc::ptr_eq(&self_unexp, &fork_unexp) { + match (fork_sp, self_sp) { + // Unexpected set on the fork, but not on `self`, copy it = over. + (Some((span, delimiter)), None) =3D> { + self_unexp.set(Unexpected::Some(span, delimiter)); + } + // Unexpected unset. Use chain to propagate errors from fo= rk. + (None, None) =3D> { + fork_unexp.set(Unexpected::Chain(self_unexp)); + + // Ensure toplevel 'unexpected' tokens from the fork d= on't + // propagate up the chain by replacing the root `unexp= ected` + // pointer, only 'unexpected' tokens from existing gro= up + // parsers should propagate. + fork.unexpected + .set(Some(Rc::new(Cell::new(Unexpected::None)))); + } + // Unexpected has been set on `self`. No changes needed. + (_, Some(_)) =3D> {} + } + } + + // See comment on `cell` in the struct definition. + self.cell + .set(unsafe { mem::transmute::>(fork.c= ursor()) }); + } +} + +/// Extensions to the `ParseStream` API to support manipulating invisible +/// delimiters the same as if they were visible. +pub trait AnyDelimiter { + /// Returns the delimiter, the span of the delimiter token, and the ne= sted + /// contents for further parsing. + fn parse_any_delimiter(&self) -> Result<(Delimiter, DelimSpan, ParseBu= ffer)>; +} + +impl<'a> AnyDelimiter for ParseBuffer<'a> { + fn parse_any_delimiter(&self) -> Result<(Delimiter, DelimSpan, ParseBu= ffer)> { + self.step(|cursor| { + if let Some((content, delimiter, span, rest)) =3D cursor.any_g= roup() { + let scope =3D span.close(); + let nested =3D crate::parse::advance_step_cursor(cursor, c= ontent); + let unexpected =3D crate::parse::get_unexpected(self); + let content =3D crate::parse::new_parse_buffer(scope, nest= ed, unexpected); + Ok(((delimiter, span, content), rest)) + } else { + Err(cursor.error("expected any delimiter")) + } + }) + } +} diff --git a/rust/syn/drops.rs b/rust/syn/drops.rs new file mode 100644 index 000000000000..c54308f02c13 --- /dev/null +++ b/rust/syn/drops.rs @@ -0,0 +1,58 @@ +use std::iter; +use std::mem::ManuallyDrop; +use std::ops::{Deref, DerefMut}; +use std::option; +use std::slice; + +#[repr(transparent)] +pub(crate) struct NoDrop(ManuallyDrop); + +impl NoDrop { + pub(crate) fn new(value: T) -> Self + where + T: TrivialDrop, + { + NoDrop(ManuallyDrop::new(value)) + } +} + +impl Deref for NoDrop { + type Target =3D T; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for NoDrop { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +pub(crate) trait TrivialDrop {} + +impl TrivialDrop for iter::Empty {} +impl TrivialDrop for slice::Iter<'_, T> {} +impl TrivialDrop for slice::IterMut<'_, T> {} +impl TrivialDrop for option::IntoIter<&T> {} +impl TrivialDrop for option::IntoIter<&mut T> {} + +#[test] +fn test_needs_drop() { + use std::mem::needs_drop; + + struct NeedsDrop; + + impl Drop for NeedsDrop { + fn drop(&mut self) {} + } + + assert!(needs_drop::()); + + // Test each of the types with a handwritten TrivialDrop impl above. + assert!(!needs_drop::>()); + assert!(!needs_drop::>()); + assert!(!needs_drop::>()); + assert!(!needs_drop::>()); + assert!(!needs_drop::>()); +} diff --git a/rust/syn/error.rs b/rust/syn/error.rs new file mode 100644 index 000000000000..63310543a3b4 --- /dev/null +++ b/rust/syn/error.rs @@ -0,0 +1,467 @@ +#[cfg(feature =3D "parsing")] +use crate::buffer::Cursor; +use crate::thread::ThreadBound; +use proc_macro2::{ + Delimiter, Group, Ident, LexError, Literal, Punct, Spacing, Span, Toke= nStream, TokenTree, +}; +#[cfg(feature =3D "printing")] +use quote::ToTokens; +use std::fmt::{self, Debug, Display}; +use std::slice; +use std::vec; + +/// The result of a Syn parser. +pub type Result =3D std::result::Result; + +/// Error returned when a Syn parser cannot parse the input tokens. +/// +/// # Error reporting in proc macros +/// +/// The correct way to report errors back to the compiler from a procedural +/// macro is by emitting an appropriately spanned invocation of +/// [`compile_error!`] in the generated code. This produces a better diagn= ostic +/// message than simply panicking the macro. +/// +/// [`compile_error!`]: std::compile_error! +/// +/// When parsing macro input, the [`parse_macro_input!`] macro handles the +/// conversion to `compile_error!` automatically. +/// +/// [`parse_macro_input!`]: crate::parse_macro_input! +/// +/// ``` +/// # extern crate proc_macro; +/// # +/// use proc_macro::TokenStream; +/// use syn::parse::{Parse, ParseStream, Result}; +/// use syn::{parse_macro_input, ItemFn}; +/// +/// # const IGNORE: &str =3D stringify! { +/// #[proc_macro_attribute] +/// # }; +/// pub fn my_attr(args: TokenStream, input: TokenStream) -> TokenStream { +/// let args =3D parse_macro_input!(args as MyAttrArgs); +/// let input =3D parse_macro_input!(input as ItemFn); +/// +/// /* ... */ +/// # TokenStream::new() +/// } +/// +/// struct MyAttrArgs { +/// # _k: [(); { stringify! { +/// ... +/// # }; 0 }] +/// } +/// +/// impl Parse for MyAttrArgs { +/// fn parse(input: ParseStream) -> Result { +/// # stringify! { +/// ... +/// # }; +/// # unimplemented!() +/// } +/// } +/// ``` +/// +/// For errors that arise later than the initial parsing stage, the +/// [`.to_compile_error()`] or [`.into_compile_error()`] methods can be us= ed to +/// perform an explicit conversion to `compile_error!`. +/// +/// [`.to_compile_error()`]: Error::to_compile_error +/// [`.into_compile_error()`]: Error::into_compile_error +/// +/// ``` +/// # extern crate proc_macro; +/// # +/// # use proc_macro::TokenStream; +/// # use syn::{parse_macro_input, DeriveInput}; +/// # +/// # const IGNORE: &str =3D stringify! { +/// #[proc_macro_derive(MyDerive)] +/// # }; +/// pub fn my_derive(input: TokenStream) -> TokenStream { +/// let input =3D parse_macro_input!(input as DeriveInput); +/// +/// // fn(DeriveInput) -> syn::Result +/// expand::my_derive(input) +/// .unwrap_or_else(syn::Error::into_compile_error) +/// .into() +/// } +/// # +/// # mod expand { +/// # use proc_macro2::TokenStream; +/// # use syn::{DeriveInput, Result}; +/// # +/// # pub fn my_derive(input: DeriveInput) -> Result { +/// # unimplemented!() +/// # } +/// # } +/// ``` +pub struct Error { + messages: Vec, +} + +struct ErrorMessage { + // Span is implemented as an index into a thread-local interner to kee= p the + // size small. It is not safe to access from a different thread. We wa= nt + // errors to be Send and Sync to play nicely with ecosystem crates for= error + // handling, so pin the span we're given to its original thread and as= sume + // it is Span::call_site if accessed from any other thread. + span: ThreadBound, + message: String, +} + +// Cannot use std::ops::Range because that does not implement Copy, +// whereas ThreadBound requires a Copy impl as a way to ensure no Drop = impls +// are involved. +struct SpanRange { + start: Span, + end: Span, +} + +#[cfg(test)] +struct _Test +where + Error: Send + Sync; + +impl Error { + /// Usually the [`ParseStream::error`] method will be used instead, wh= ich + /// automatically uses the correct span from the current position of t= he + /// parse stream. + /// + /// Use `Error::new` when the error needs to be triggered on some span= other + /// than where the parse stream is currently positioned. + /// + /// [`ParseStream::error`]: crate::parse::ParseBuffer::error + /// + /// # Example + /// + /// ``` + /// use syn::{Error, Ident, LitStr, Result, Token}; + /// use syn::parse::ParseStream; + /// + /// // Parses input that looks like `name =3D "string"` where the key = must be + /// // the identifier `name` and the value may be any string literal. + /// // Returns the string literal. + /// fn parse_name(input: ParseStream) -> Result { + /// let name_token: Ident =3D input.parse()?; + /// if name_token !=3D "name" { + /// // Trigger an error not on the current position of the str= eam, + /// // but on the position of the unexpected identifier. + /// return Err(Error::new(name_token.span(), "expected `name`"= )); + /// } + /// input.parse::()?; + /// let s: LitStr =3D input.parse()?; + /// Ok(s) + /// } + /// ``` + pub fn new(span: Span, message: T) -> Self { + return new(span, message.to_string()); + + fn new(span: Span, message: String) -> Error { + Error { + messages: vec![ErrorMessage { + span: ThreadBound::new(SpanRange { + start: span, + end: span, + }), + message, + }], + } + } + } + + /// Creates an error with the specified message spanning the given syn= tax + /// tree node. + /// + /// Unlike the `Error::new` constructor, this constructor takes an arg= ument + /// `tokens` which is a syntax tree node. This allows the resulting `E= rror` + /// to attempt to span all tokens inside of `tokens`. While you would + /// typically be able to use the `Spanned` trait with the above `Error= ::new` + /// constructor, implementation limitations today mean that + /// `Error::new_spanned` may provide a higher-quality error message on + /// stable Rust. + /// + /// When in doubt it's recommended to stick to `Error::new` (or + /// `ParseStream::error`)! + #[cfg(feature =3D "printing")] + #[cfg_attr(docsrs, doc(cfg(feature =3D "printing")))] + pub fn new_spanned(tokens: T, message: U) -> = Self { + return new_spanned(tokens.into_token_stream(), message.to_string()= ); + + fn new_spanned(tokens: TokenStream, message: String) -> Error { + let mut iter =3D tokens.into_iter(); + let start =3D iter.next().map_or_else(Span::call_site, |t| t.s= pan()); + let end =3D iter.last().map_or(start, |t| t.span()); + Error { + messages: vec![ErrorMessage { + span: ThreadBound::new(SpanRange { start, end }), + message, + }], + } + } + } + + /// The source location of the error. + /// + /// Spans are not thread-safe so this function returns `Span::call_sit= e()` + /// if called from a different thread than the one on which the `Error= ` was + /// originally created. + pub fn span(&self) -> Span { + let SpanRange { start, end } =3D match self.messages[0].span.get()= { + Some(span) =3D> *span, + None =3D> return Span::call_site(), + }; + start.join(end).unwrap_or(start) + } + + /// Render the error as an invocation of [`compile_error!`]. + /// + /// The [`parse_macro_input!`] macro provides a convenient way to invo= ke + /// this method correctly in a procedural macro. + /// + /// [`compile_error!`]: std::compile_error! + /// [`parse_macro_input!`]: crate::parse_macro_input! + pub fn to_compile_error(&self) -> TokenStream { + self.messages + .iter() + .map(ErrorMessage::to_compile_error) + .collect() + } + + /// Render the error as an invocation of [`compile_error!`]. + /// + /// [`compile_error!`]: std::compile_error! + /// + /// # Example + /// + /// ``` + /// # extern crate proc_macro; + /// # + /// use proc_macro::TokenStream; + /// use syn::{parse_macro_input, DeriveInput, Error}; + /// + /// # const _: &str =3D stringify! { + /// #[proc_macro_derive(MyTrait)] + /// # }; + /// pub fn derive_my_trait(input: TokenStream) -> TokenStream { + /// let input =3D parse_macro_input!(input as DeriveInput); + /// my_trait::expand(input) + /// .unwrap_or_else(Error::into_compile_error) + /// .into() + /// } + /// + /// mod my_trait { + /// use proc_macro2::TokenStream; + /// use syn::{DeriveInput, Result}; + /// + /// pub(crate) fn expand(input: DeriveInput) -> Result { + /// /* ... */ + /// # unimplemented!() + /// } + /// } + /// ``` + pub fn into_compile_error(self) -> TokenStream { + self.to_compile_error() + } + + /// Add another error message to self such that when `to_compile_error= ()` is + /// called, both errors will be emitted together. + pub fn combine(&mut self, another: Error) { + self.messages.extend(another.messages); + } +} + +impl ErrorMessage { + fn to_compile_error(&self) -> TokenStream { + let (start, end) =3D match self.span.get() { + Some(range) =3D> (range.start, range.end), + None =3D> (Span::call_site(), Span::call_site()), + }; + + // ::core::compile_error!($message) + TokenStream::from_iter([ + TokenTree::Punct({ + let mut punct =3D Punct::new(':', Spacing::Joint); + punct.set_span(start); + punct + }), + TokenTree::Punct({ + let mut punct =3D Punct::new(':', Spacing::Alone); + punct.set_span(start); + punct + }), + TokenTree::Ident(Ident::new("core", start)), + TokenTree::Punct({ + let mut punct =3D Punct::new(':', Spacing::Joint); + punct.set_span(start); + punct + }), + TokenTree::Punct({ + let mut punct =3D Punct::new(':', Spacing::Alone); + punct.set_span(start); + punct + }), + TokenTree::Ident(Ident::new("compile_error", start)), + TokenTree::Punct({ + let mut punct =3D Punct::new('!', Spacing::Alone); + punct.set_span(start); + punct + }), + TokenTree::Group({ + let mut group =3D Group::new(Delimiter::Brace, { + TokenStream::from_iter([TokenTree::Literal({ + let mut string =3D Literal::string(&self.message); + string.set_span(end); + string + })]) + }); + group.set_span(end); + group + }), + ]) + } +} + +#[cfg(feature =3D "parsing")] +pub(crate) fn new_at(scope: Span, cursor: Cursor, message: T) = -> Error { + if cursor.eof() { + Error::new(scope, format!("unexpected end of input, {}", message)) + } else { + let span =3D crate::buffer::open_span_of_group(cursor); + Error::new(span, message) + } +} + +#[cfg(all(feature =3D "parsing", any(feature =3D "full", feature =3D "deri= ve")))] +pub(crate) fn new2(start: Span, end: Span, message: T) -> Erro= r { + return new2(start, end, message.to_string()); + + fn new2(start: Span, end: Span, message: String) -> Error { + Error { + messages: vec![ErrorMessage { + span: ThreadBound::new(SpanRange { start, end }), + message, + }], + } + } +} + +impl Debug for Error { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + if self.messages.len() =3D=3D 1 { + formatter + .debug_tuple("Error") + .field(&self.messages[0]) + .finish() + } else { + formatter + .debug_tuple("Error") + .field(&self.messages) + .finish() + } + } +} + +impl Debug for ErrorMessage { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(&self.message, formatter) + } +} + +impl Display for Error { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(&self.messages[0].message) + } +} + +impl Clone for Error { + fn clone(&self) -> Self { + Error { + messages: self.messages.clone(), + } + } +} + +impl Clone for ErrorMessage { + fn clone(&self) -> Self { + ErrorMessage { + span: self.span, + message: self.message.clone(), + } + } +} + +impl Clone for SpanRange { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for SpanRange {} + +impl std::error::Error for Error {} + +impl From for Error { + fn from(err: LexError) -> Self { + Error::new(err.span(), err) + } +} + +impl IntoIterator for Error { + type Item =3D Error; + type IntoIter =3D IntoIter; + + fn into_iter(self) -> Self::IntoIter { + IntoIter { + messages: self.messages.into_iter(), + } + } +} + +pub struct IntoIter { + messages: vec::IntoIter, +} + +impl Iterator for IntoIter { + type Item =3D Error; + + fn next(&mut self) -> Option { + Some(Error { + messages: vec![self.messages.next()?], + }) + } +} + +impl<'a> IntoIterator for &'a Error { + type Item =3D Error; + type IntoIter =3D Iter<'a>; + + fn into_iter(self) -> Self::IntoIter { + Iter { + messages: self.messages.iter(), + } + } +} + +pub struct Iter<'a> { + messages: slice::Iter<'a, ErrorMessage>, +} + +impl<'a> Iterator for Iter<'a> { + type Item =3D Error; + + fn next(&mut self) -> Option { + Some(Error { + messages: vec![self.messages.next()?.clone()], + }) + } +} + +impl Extend for Error { + fn extend>(&mut self, iter: T) { + for err in iter { + self.combine(err); + } + } +} diff --git a/rust/syn/export.rs b/rust/syn/export.rs new file mode 100644 index 000000000000..b9ea5c747b75 --- /dev/null +++ b/rust/syn/export.rs @@ -0,0 +1,73 @@ +#[doc(hidden)] +pub use std::clone::Clone; +#[doc(hidden)] +pub use std::cmp::{Eq, PartialEq}; +#[doc(hidden)] +pub use std::concat; +#[doc(hidden)] +pub use std::default::Default; +#[doc(hidden)] +pub use std::fmt::Debug; +#[doc(hidden)] +pub use std::hash::{Hash, Hasher}; +#[doc(hidden)] +pub use std::marker::Copy; +#[doc(hidden)] +pub use std::option::Option::{None, Some}; +#[doc(hidden)] +pub use std::result::Result::{Err, Ok}; +#[doc(hidden)] +pub use std::stringify; + +#[doc(hidden)] +pub type Formatter<'a> =3D std::fmt::Formatter<'a>; +#[doc(hidden)] +pub type FmtResult =3D std::fmt::Result; + +#[doc(hidden)] +pub type bool =3D std::primitive::bool; +#[doc(hidden)] +pub type str =3D std::primitive::str; + +#[cfg(feature =3D "printing")] +#[doc(hidden)] +pub use quote; + +#[doc(hidden)] +pub type Span =3D proc_macro2::Span; +#[doc(hidden)] +pub type TokenStream2 =3D proc_macro2::TokenStream; + +#[cfg(feature =3D "parsing")] +#[doc(hidden)] +pub use crate::group::{parse_braces, parse_brackets, parse_parens}; + +#[doc(hidden)] +pub use crate::span::IntoSpans; + +#[cfg(all(feature =3D "parsing", feature =3D "printing"))] +#[doc(hidden)] +pub use crate::parse_quote::parse as parse_quote; + +#[cfg(feature =3D "parsing")] +#[doc(hidden)] +pub use crate::token::parsing::{peek_punct, punct as parse_punct}; + +#[cfg(feature =3D "printing")] +#[doc(hidden)] +pub use crate::token::printing::punct as print_punct; + +#[cfg(feature =3D "parsing")] +#[doc(hidden)] +pub use crate::token::private::CustomToken; + +#[cfg(feature =3D "proc-macro")] +#[doc(hidden)] +pub type TokenStream =3D proc_macro::TokenStream; + +#[cfg(feature =3D "printing")] +#[doc(hidden)] +pub use quote::{ToTokens, TokenStreamExt}; + +#[doc(hidden)] +pub struct private(pub(crate) ()); diff --git a/rust/syn/expr.rs b/rust/syn/expr.rs new file mode 100644 index 000000000000..1e49d9a6633e --- /dev/null +++ b/rust/syn/expr.rs @@ -0,0 +1,4173 @@ +use crate::attr::Attribute; +#[cfg(all(feature =3D "parsing", feature =3D "full"))] +use crate::error::Result; +#[cfg(feature =3D "parsing")] +use crate::ext::IdentExt as _; +#[cfg(feature =3D "full")] +use crate::generics::BoundLifetimes; +use crate::ident::Ident; +#[cfg(any(feature =3D "parsing", feature =3D "full"))] +use crate::lifetime::Lifetime; +use crate::lit::Lit; +use crate::mac::Macro; +use crate::op::{BinOp, UnOp}; +#[cfg(feature =3D "parsing")] +use crate::parse::ParseStream; +#[cfg(feature =3D "full")] +use crate::pat::Pat; +use crate::path::{AngleBracketedGenericArguments, Path, QSelf}; +use crate::punctuated::Punctuated; +#[cfg(feature =3D "full")] +use crate::stmt::Block; +use crate::token; +#[cfg(feature =3D "full")] +use crate::ty::ReturnType; +use crate::ty::Type; +use proc_macro2::{Span, TokenStream}; +#[cfg(feature =3D "printing")] +use quote::IdentFragment; +#[cfg(feature =3D "printing")] +use std::fmt::{self, Display}; +use std::hash::{Hash, Hasher}; +#[cfg(all(feature =3D "parsing", feature =3D "full"))] +use std::mem; + +ast_enum_of_structs! { + /// A Rust expression. + /// + /// *This type is available only if Syn is built with the `"derive"` o= r `"full"` + /// feature, but most of the variants are not available unless "full" = is enabled.* + /// + /// # Syntax tree enums + /// + /// This type is a syntax tree enum. In Syn this and other syntax tree= enums + /// are designed to be traversed using the following rebinding idiom. + /// + /// ``` + /// # use syn::Expr; + /// # + /// # fn example(expr: Expr) { + /// # const IGNORE: &str =3D stringify! { + /// let expr: Expr =3D /* ... */; + /// # }; + /// match expr { + /// Expr::MethodCall(expr) =3D> { + /// /* ... */ + /// } + /// Expr::Cast(expr) =3D> { + /// /* ... */ + /// } + /// Expr::If(expr) =3D> { + /// /* ... */ + /// } + /// + /// /* ... */ + /// # _ =3D> {} + /// # } + /// # } + /// ``` + /// + /// We begin with a variable `expr` of type `Expr` that has no fields + /// (because it is an enum), and by matching on it and rebinding a var= iable + /// with the same name `expr` we effectively imbue our variable with a= ll of + /// the data fields provided by the variant that it turned out to be. = So for + /// example above if we ended up in the `MethodCall` case then we get = to use + /// `expr.receiver`, `expr.args` etc; if we ended up in the `If` case = we get + /// to use `expr.cond`, `expr.then_branch`, `expr.else_branch`. + /// + /// This approach avoids repeating the variant names twice on every li= ne. + /// + /// ``` + /// # use syn::{Expr, ExprMethodCall}; + /// # + /// # fn example(expr: Expr) { + /// // Repetitive; recommend not doing this. + /// match expr { + /// Expr::MethodCall(ExprMethodCall { method, args, .. }) =3D> { + /// # } + /// # _ =3D> {} + /// # } + /// # } + /// ``` + /// + /// In general, the name to which a syntax tree enum variant is bound = should + /// be a suitable name for the complete syntax tree enum type. + /// + /// ``` + /// # use syn::{Expr, ExprField}; + /// # + /// # fn example(discriminant: ExprField) { + /// // Binding is called `base` which is the name I would use if I were + /// // assigning `*discriminant.base` without an `if let`. + /// if let Expr::Tuple(base) =3D *discriminant.base { + /// # } + /// # } + /// ``` + /// + /// A sign that you may not be choosing the right variable names is if= you + /// see names getting repeated in your code, like accessing + /// `receiver.receiver` or `pat.pat` or `cond.cond`. + #[cfg_attr(docsrs, doc(cfg(any(feature =3D "full", feature =3D "derive= "))))] + #[non_exhaustive] + pub enum Expr { + /// A slice literal expression: `[a, b, c, d]`. + Array(ExprArray), + + /// An assignment expression: `a =3D compute()`. + Assign(ExprAssign), + + /// An async block: `async { ... }`. + Async(ExprAsync), + + /// An await expression: `fut.await`. + Await(ExprAwait), + + /// A binary operation: `a + b`, `a +=3D b`. + Binary(ExprBinary), + + /// A blocked scope: `{ ... }`. + Block(ExprBlock), + + /// A `break`, with an optional label to break and an optional + /// expression. + Break(ExprBreak), + + /// A function call expression: `invoke(a, b)`. + Call(ExprCall), + + /// A cast expression: `foo as f64`. + Cast(ExprCast), + + /// A closure expression: `|a, b| a + b`. + Closure(ExprClosure), + + /// A const block: `const { ... }`. + Const(ExprConst), + + /// A `continue`, with an optional label. + Continue(ExprContinue), + + /// Access of a named struct field (`obj.k`) or unnamed tuple stru= ct + /// field (`obj.0`). + Field(ExprField), + + /// A for loop: `for pat in expr { ... }`. + ForLoop(ExprForLoop), + + /// An expression contained within invisible delimiters. + /// + /// This variant is important for faithfully representing the prec= edence + /// of expressions and is related to `None`-delimited spans in a + /// `TokenStream`. + Group(ExprGroup), + + /// An `if` expression with an optional `else` block: `if expr { .= .. } + /// else { ... }`. + /// + /// The `else` branch expression may only be an `If` or `Block` + /// expression, not any of the other types of expression. + If(ExprIf), + + /// A square bracketed indexing expression: `vector[2]`. + Index(ExprIndex), + + /// The inferred value of a const generic argument, denoted `_`. + Infer(ExprInfer), + + /// A `let` guard: `let Some(x) =3D opt`. + Let(ExprLet), + + /// A literal in place of an expression: `1`, `"foo"`. + Lit(ExprLit), + + /// Conditionless loop: `loop { ... }`. + Loop(ExprLoop), + + /// A macro invocation expression: `format!("{}", q)`. + Macro(ExprMacro), + + /// A `match` expression: `match n { Some(n) =3D> {}, None =3D> {}= }`. + Match(ExprMatch), + + /// A method call expression: `x.foo::(a, b)`. + MethodCall(ExprMethodCall), + + /// A parenthesized expression: `(a + b)`. + Paren(ExprParen), + + /// A path like `std::mem::replace` possibly containing generic + /// parameters and a qualified self-type. + /// + /// A plain identifier like `x` is a path of length 1. + Path(ExprPath), + + /// A range expression: `1..2`, `1..`, `..2`, `1..=3D2`, `..=3D2`. + Range(ExprRange), + + /// Address-of operation: `&raw const place` or `&raw mut place`. + RawAddr(ExprRawAddr), + + /// A referencing operation: `&a` or `&mut a`. + Reference(ExprReference), + + /// An array literal constructed from one repeated element: `[0u8;= N]`. + Repeat(ExprRepeat), + + /// A `return`, with an optional value to be returned. + Return(ExprReturn), + + /// A struct literal expression: `Point { x: 1, y: 1 }`. + /// + /// The `rest` provides the value of the remaining fields as in `S= { a: + /// 1, b: 1, ..rest }`. + Struct(ExprStruct), + + /// A try-expression: `expr?`. + Try(ExprTry), + + /// A try block: `try { ... }`. + TryBlock(ExprTryBlock), + + /// A tuple expression: `(a, b, c, d)`. + Tuple(ExprTuple), + + /// A unary operation: `!x`, `*x`. + Unary(ExprUnary), + + /// An unsafe block: `unsafe { ... }`. + Unsafe(ExprUnsafe), + + /// Tokens in expression position not interpreted by Syn. + Verbatim(TokenStream), + + /// A while loop: `while expr { ... }`. + While(ExprWhile), + + /// A yield expression: `yield expr`. + Yield(ExprYield), + + // For testing exhaustiveness in downstream code, use the followin= g idiom: + // + // match expr { + // #![cfg_attr(test, deny(non_exhaustive_omitted_patterns)= )] + // + // Expr::Array(expr) =3D> {...} + // Expr::Assign(expr) =3D> {...} + // ... + // Expr::Yield(expr) =3D> {...} + // + // _ =3D> { /* some sane fallback */ } + // } + // + // This way we fail your tests but don't break your library when a= dding + // a variant. You will be notified by a test failure when a varian= t is + // added, so that you can add code to handle it, but your library = will + // continue to compile and work for downstream users in the interi= m. + } +} + +ast_struct! { + /// A slice literal expression: `[a, b, c, d]`. + #[cfg_attr(docsrs, doc(cfg(feature =3D "full")))] + pub struct ExprArray #full { + pub attrs: Vec, + pub bracket_token: token::Bracket, + pub elems: Punctuated, + } +} + +ast_struct! { + /// An assignment expression: `a =3D compute()`. + #[cfg_attr(docsrs, doc(cfg(feature =3D "full")))] + pub struct ExprAssign #full { + pub attrs: Vec, + pub left: Box, + pub eq_token: Token![=3D], + pub right: Box, + } +} + +ast_struct! { + /// An async block: `async { ... }`. + #[cfg_attr(docsrs, doc(cfg(feature =3D "full")))] + pub struct ExprAsync #full { + pub attrs: Vec, + pub async_token: Token![async], + pub capture: Option, + pub block: Block, + } +} + +ast_struct! { + /// An await expression: `fut.await`. + #[cfg_attr(docsrs, doc(cfg(feature =3D "full")))] + pub struct ExprAwait #full { + pub attrs: Vec, + pub base: Box, + pub dot_token: Token![.], + pub await_token: Token![await], + } +} + +ast_struct! { + /// A binary operation: `a + b`, `a +=3D b`. + #[cfg_attr(docsrs, doc(cfg(any(feature =3D "full", feature =3D "derive= "))))] + pub struct ExprBinary { + pub attrs: Vec, + pub left: Box, + pub op: BinOp, + pub right: Box, + } +} + +ast_struct! { + /// A blocked scope: `{ ... }`. + #[cfg_attr(docsrs, doc(cfg(feature =3D "full")))] + pub struct ExprBlock #full { + pub attrs: Vec, + pub label: Option