mirror of
https://github.com/openharmony/third_party_rust_proc-macro2.git
synced 2026-07-01 20:24:03 -04:00
!14 merge master into master
proc-macro2升级到1.0.105版本 Created-by: dragonswordy Commit-by: ljy9810 Merged-by: openharmony_ci Description: ### 一、内容说明(相关的Issue) https://gitcode.com/openharmony/third_party_rust_unicode-ident/issues/2 ### 二、建议测试周期和提测地址 建议测试完成时间:xxxx.xx.xx 投产上线时间:xxxx.xx.xx 提测地址:CI环境/压测环境 测试账号: ### 三、变更内容 * 3.1 关联PR列表 * 3.2 数据库和部署说明 1. 常规更新 2. 重启unicorn 3. 重启sidekiq 4. 迁移任务:是否有迁移任务,没有写 "无" 5. rake脚本:`bundle exec xxx RAILS_ENV = production`;没有写 "无" * 3.4 其他技术优化内容(做了什么,变更了什么) - 重构了 xxxx 代码 - xxxx 算法优化 * 3.5 废弃通知(什么字段、方法弃用?) * 3.6 后向不兼容变更(是否有无法向后兼容的变更?) ### 四、研发自测点(自测哪些?冒烟用例全部自测?) 自测测试结论: ### 五、测试关注点(需要提醒QA重点关注的、可能会忽略的地方) 检查点: | 需求名称 | 是否影响xx公共模块 | 是否需要xx功能 | 需求升级是否依赖其他子产品 | |------|------------|----------|---------------| | xxx | 否 | 需要 | 不需要 | | | | | | 接口测试: 性能测试: 并发测试: 其他: See merge request: openharmony/third_party_rust_proc-macro2!14
This commit is contained in:
+23
-17
@@ -24,10 +24,10 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
rust: [1.63.0, stable, beta]
|
||||
rust: [1.80.0, stable, beta]
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{matrix.rust}}
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
components: rust-src
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
run: cargo test
|
||||
env:
|
||||
RUSTFLAGS: -Z allow-features= --cfg procmacro2_backtrace ${{env.RUSTFLAGS}}
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v6
|
||||
if: always()
|
||||
with:
|
||||
name: Cargo.lock
|
||||
@@ -91,7 +91,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
components: rust-src
|
||||
@@ -101,14 +101,14 @@ jobs:
|
||||
- run: cargo test --test test_size --no-default-features --features span-locations
|
||||
|
||||
msrv:
|
||||
name: Rust 1.56.0
|
||||
name: Rust 1.68.0
|
||||
needs: pre_ci
|
||||
if: needs.pre_ci.outputs.continue
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@1.56.0
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@1.68.0
|
||||
with:
|
||||
components: rust-src
|
||||
- run: cargo check
|
||||
@@ -130,7 +130,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- run: cargo generate-lockfile -Z minimal-versions
|
||||
- run: cargo check --locked
|
||||
@@ -142,11 +142,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
target: wasm32-unknown-unknown
|
||||
components: rust-src
|
||||
- name: Ignore WebAssembly linker warning
|
||||
run: echo RUSTFLAGS=${RUSTFLAGS}\ -Alinker_messages >> $GITHUB_ENV
|
||||
- run: cargo test --target wasm32-unknown-unknown --no-run
|
||||
|
||||
fuzz:
|
||||
@@ -156,7 +158,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
components: rust-src
|
||||
@@ -165,8 +167,12 @@ jobs:
|
||||
- run: cargo check --no-default-features --features afl
|
||||
working-directory: fuzz
|
||||
- uses: dtolnay/install@honggfuzz
|
||||
- run: sudo apt-get update # https://github.com/actions/runner-images/issues/8953
|
||||
- run: sudo apt-get install binutils-dev libunwind-dev
|
||||
- name: Run apt install binutils-dev libunwind-dev
|
||||
run: |
|
||||
sudo sed -i 's/^update_initramfs=yes$/update_initramfs=no/' /etc/initramfs-tools/update-initramfs.conf
|
||||
sudo rm -f /var/lib/man-db/auto-update
|
||||
sudo apt-get update
|
||||
sudo apt-get install binutils-dev libunwind-dev
|
||||
- run: cargo hfuzz build --no-default-features --features honggfuzz
|
||||
working-directory: fuzz
|
||||
|
||||
@@ -179,7 +185,7 @@ jobs:
|
||||
env:
|
||||
RUSTDOCFLAGS: -Dwarnings
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
components: rust-src
|
||||
@@ -192,7 +198,7 @@ jobs:
|
||||
if: github.event_name != 'pull_request'
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
components: clippy, rust-src
|
||||
@@ -206,7 +212,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@miri
|
||||
- run: cargo miri setup
|
||||
- run: cargo miri test
|
||||
@@ -219,7 +225,7 @@ jobs:
|
||||
if: github.event_name != 'pull_request'
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: dtolnay/install@cargo-outdated
|
||||
- run: cargo outdated --workspace --exit-code 1
|
||||
|
||||
+2
-3
@@ -1,3 +1,2 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
/target/
|
||||
/Cargo.lock
|
||||
|
||||
@@ -20,7 +20,7 @@ ohos_cargo_crate("lib") {
|
||||
|
||||
sources = [ "src/lib.rs" ]
|
||||
edition = "2021"
|
||||
cargo_pkg_version = "1.0.92"
|
||||
cargo_pkg_version = "1.0.105"
|
||||
cargo_pkg_authors =
|
||||
"David Tolnay <dtolnay@gmail.com>, Alex Crichton <alex@alexcrichton.com>"
|
||||
cargo_pkg_name = "proc-macro2"
|
||||
|
||||
+12
-7
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.92"
|
||||
version = "1.0.105"
|
||||
authors = ["David Tolnay <dtolnay@gmail.com>", "Alex Crichton <alex@alexcrichton.com>"]
|
||||
autobenches = false
|
||||
categories = ["development-tools::procedural-macro-helpers"]
|
||||
@@ -10,12 +10,20 @@ edition = "2021"
|
||||
keywords = ["macros", "syn"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/dtolnay/proc-macro2"
|
||||
rust-version = "1.56"
|
||||
rust-version = "1.68"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
rustc-args = ["--cfg", "procmacro2_semver_exempt"]
|
||||
rustdoc-args = ["--cfg", "procmacro2_semver_exempt", "--generate-link-to-definition"]
|
||||
rustc-args = ["--cfg=procmacro2_semver_exempt"]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
rustdoc-args = [
|
||||
"--cfg=procmacro2_semver_exempt",
|
||||
"--generate-link-to-definition",
|
||||
"--generate-macro-expansion",
|
||||
"--extern-html-root-url=core=https://doc.rust-lang.org",
|
||||
"--extern-html-root-url=alloc=https://doc.rust-lang.org",
|
||||
"--extern-html-root-url=std=https://doc.rust-lang.org",
|
||||
"--extern-html-root-url=proc_macro=https://doc.rust-lang.org",
|
||||
]
|
||||
|
||||
[package.metadata.playground]
|
||||
features = ["span-locations"]
|
||||
@@ -41,9 +49,6 @@ span-locations = []
|
||||
# This feature no longer means anything.
|
||||
nightly = []
|
||||
|
||||
[lib]
|
||||
doc-scrape-examples = false
|
||||
|
||||
[workspace]
|
||||
members = ["benches/bench-libproc-macro", "tests/ui"]
|
||||
|
||||
|
||||
+5
-2
@@ -3,9 +3,12 @@
|
||||
"Name": "proc-macro2",
|
||||
"License": "Apache License V2.0, MIT",
|
||||
"License File": "LICENSE-APACHE, LICENSE-MIT",
|
||||
"Version Number": "1.0.92",
|
||||
"Version Number": "1.0.105",
|
||||
"Owner": "fangting12@huawei.com",
|
||||
"Upstream URL": "https://github.com/dtolnay/proc-macro2",
|
||||
"Description": "A Rust library that provides support for error handling in procedural macros."
|
||||
"Description": "A Rust library that provides support for error handling in procedural macros.",
|
||||
"Dependencies": [
|
||||
"unicode-ident"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -62,7 +62,7 @@ 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
|
||||
polyfill those nightly-only APIs back to Rust 1.68.0. As these are unstable APIs
|
||||
that track the nightly compiler, minor versions of proc-macro2 may make breaking
|
||||
changes to them at any time.
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name = "bench-libproc-macro"
|
||||
version = "0.0.0"
|
||||
authors = ["David Tolnay <dtolnay@gmail.com>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#![allow(unknown_lints)]
|
||||
#![allow(unexpected_cfgs)]
|
||||
#![allow(clippy::uninlined_format_args)]
|
||||
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
@@ -20,7 +21,10 @@ fn main() {
|
||||
println!("cargo:rustc-check-cfg=cfg(no_literal_c_string)");
|
||||
println!("cargo:rustc-check-cfg=cfg(no_source_text)");
|
||||
println!("cargo:rustc-check-cfg=cfg(proc_macro_span)");
|
||||
println!("cargo:rustc-check-cfg=cfg(proc_macro_span_file)");
|
||||
println!("cargo:rustc-check-cfg=cfg(proc_macro_span_location)");
|
||||
println!("cargo:rustc-check-cfg=cfg(procmacro2_backtrace)");
|
||||
println!("cargo:rustc-check-cfg=cfg(procmacro2_build_probe)");
|
||||
println!("cargo:rustc-check-cfg=cfg(procmacro2_nightly_testing)");
|
||||
println!("cargo:rustc-check-cfg=cfg(procmacro2_semver_exempt)");
|
||||
println!("cargo:rustc-check-cfg=cfg(randomize_layout)");
|
||||
@@ -29,8 +33,7 @@ fn main() {
|
||||
println!("cargo:rustc-check-cfg=cfg(wrap_proc_macro)");
|
||||
}
|
||||
|
||||
let docs_rs = env::var_os("DOCS_RS").is_some();
|
||||
let semver_exempt = cfg!(procmacro2_semver_exempt) || docs_rs;
|
||||
let semver_exempt = cfg!(procmacro2_semver_exempt);
|
||||
if semver_exempt {
|
||||
// https://github.com/dtolnay/proc-macro2/issues/147
|
||||
println!("cargo:rustc-cfg=procmacro2_semver_exempt");
|
||||
@@ -68,18 +71,16 @@ fn main() {
|
||||
return;
|
||||
}
|
||||
|
||||
println!("cargo:rerun-if-changed=build/probe.rs");
|
||||
|
||||
let proc_macro_span;
|
||||
let consider_rustc_bootstrap;
|
||||
if compile_probe(false) {
|
||||
if compile_probe_unstable("proc_macro_span", false) {
|
||||
// This is a nightly or dev compiler, so it supports unstable features
|
||||
// regardless of RUSTC_BOOTSTRAP. No need to rerun build script if
|
||||
// RUSTC_BOOTSTRAP is changed.
|
||||
proc_macro_span = true;
|
||||
consider_rustc_bootstrap = false;
|
||||
} else if let Some(rustc_bootstrap) = env::var_os("RUSTC_BOOTSTRAP") {
|
||||
if compile_probe(true) {
|
||||
if compile_probe_unstable("proc_macro_span", true) {
|
||||
// This is a stable or beta compiler for which the user has set
|
||||
// RUSTC_BOOTSTRAP to turn on unstable features. Rerun build script
|
||||
// if they change it.
|
||||
@@ -116,13 +117,25 @@ fn main() {
|
||||
}
|
||||
|
||||
if proc_macro_span {
|
||||
// Enable non-dummy behavior of Span::start and Span::end methods which
|
||||
// requires an unstable compiler feature. Enabled when building with
|
||||
// nightly, unless `-Z allow-feature` in RUSTFLAGS disallows unstable
|
||||
// features.
|
||||
// Enable non-dummy behavior of Span::byte_range and Span::join methods
|
||||
// which requires an unstable compiler feature. Enabled when building
|
||||
// with nightly, unless `-Z allow-feature` in RUSTFLAGS disallows
|
||||
// unstable features.
|
||||
println!("cargo:rustc-cfg=proc_macro_span");
|
||||
}
|
||||
|
||||
if proc_macro_span || (rustc >= 88 && compile_probe_stable("proc_macro_span_location")) {
|
||||
// Enable non-dummy behavior of Span::start and Span::end methods on
|
||||
// Rust 1.88+.
|
||||
println!("cargo:rustc-cfg=proc_macro_span_location");
|
||||
}
|
||||
|
||||
if proc_macro_span || (rustc >= 88 && compile_probe_stable("proc_macro_span_file")) {
|
||||
// Enable non-dummy behavior of Span::file and Span::local_file methods
|
||||
// on Rust 1.88+.
|
||||
println!("cargo:rustc-cfg=proc_macro_span_file");
|
||||
}
|
||||
|
||||
if semver_exempt && proc_macro_span {
|
||||
// Implement the semver exempt API in terms of the nightly-only
|
||||
// proc_macro API.
|
||||
@@ -134,22 +147,31 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
fn compile_probe(rustc_bootstrap: bool) -> bool {
|
||||
if env::var_os("RUSTC_STAGE").is_some() {
|
||||
// We are running inside rustc bootstrap. This is a highly non-standard
|
||||
// environment with issues such as:
|
||||
//
|
||||
// https://github.com/rust-lang/cargo/issues/11138
|
||||
// https://github.com/rust-lang/rust/issues/114839
|
||||
//
|
||||
// Let's just not use nightly features here.
|
||||
return false;
|
||||
}
|
||||
fn compile_probe_unstable(feature: &str, rustc_bootstrap: bool) -> bool {
|
||||
// RUSTC_STAGE indicates that this crate is being compiled as a dependency
|
||||
// of a multistage rustc bootstrap. This environment uses Cargo in a highly
|
||||
// non-standard way with issues such as:
|
||||
//
|
||||
// https://github.com/rust-lang/cargo/issues/11138
|
||||
// https://github.com/rust-lang/rust/issues/114839
|
||||
//
|
||||
env::var_os("RUSTC_STAGE").is_none() && do_compile_probe(feature, rustc_bootstrap)
|
||||
}
|
||||
|
||||
fn compile_probe_stable(feature: &str) -> bool {
|
||||
env::var_os("RUSTC_STAGE").is_some() || do_compile_probe(feature, true)
|
||||
}
|
||||
|
||||
fn do_compile_probe(feature: &str, rustc_bootstrap: bool) -> bool {
|
||||
println!("cargo:rerun-if-changed=src/probe/{}.rs", feature);
|
||||
|
||||
let rustc = cargo_env_var("RUSTC");
|
||||
let out_dir = cargo_env_var("OUT_DIR");
|
||||
let out_subdir = Path::new(&out_dir).join("probe");
|
||||
let probefile = Path::new("build").join("probe.rs");
|
||||
let probefile = Path::new("src")
|
||||
.join("probe")
|
||||
.join(feature)
|
||||
.with_extension("rs");
|
||||
|
||||
if let Err(err) = fs::create_dir(&out_subdir) {
|
||||
if err.kind() != ErrorKind::AlreadyExists {
|
||||
@@ -173,6 +195,7 @@ fn compile_probe(rustc_bootstrap: bool) -> bool {
|
||||
}
|
||||
|
||||
cmd.stderr(Stdio::null())
|
||||
.arg("--cfg=procmacro2_build_probe")
|
||||
.arg("--edition=2021")
|
||||
.arg("--crate-name=proc_macro2")
|
||||
.arg("--crate-type=lib")
|
||||
@@ -204,7 +227,16 @@ fn compile_probe(rustc_bootstrap: bool) -> bool {
|
||||
// file in OUT_DIR, which causes nonreproducible builds in build systems
|
||||
// that treat the entire OUT_DIR as an artifact.
|
||||
if let Err(err) = fs::remove_dir_all(&out_subdir) {
|
||||
if err.kind() != ErrorKind::NotFound {
|
||||
// libc::ENOTEMPTY
|
||||
// Some filesystems (NFSv3) have timing issues under load where '.nfs*'
|
||||
// dummy files can continue to get created for a short period after the
|
||||
// probe command completes, breaking remove_dir_all.
|
||||
// To be replaced with ErrorKind::DirectoryNotEmpty (Rust 1.83+).
|
||||
const ENOTEMPTY: i32 = 39;
|
||||
|
||||
if !(err.kind() == ErrorKind::NotFound
|
||||
|| (cfg!(target_os = "linux") && err.raw_os_error() == Some(ENOTEMPTY)))
|
||||
{
|
||||
eprintln!("Failed to clean up {}: {}", out_subdir.display(), err);
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
+9
-8
@@ -1,8 +1,9 @@
|
||||
artifacts/
|
||||
corpus/
|
||||
coverage/
|
||||
hfuzz_target/
|
||||
hfuzz_workspace/
|
||||
in/
|
||||
out/
|
||||
target/
|
||||
/artifacts/
|
||||
/corpus/
|
||||
/coverage/
|
||||
/hfuzz_target/
|
||||
/hfuzz_workspace/
|
||||
/in/
|
||||
/out/
|
||||
/target/
|
||||
/Cargo.lock
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ publish = false
|
||||
cargo-fuzz = true
|
||||
|
||||
[dependencies]
|
||||
afl = { version = "0.15", optional = true }
|
||||
afl = { version = "0.17", optional = true }
|
||||
honggfuzz = { version = "0.5", optional = true }
|
||||
libfuzzer-sys = { version = "0.4.7", optional = true }
|
||||
proc-macro2 = { path = "..", default-features = false }
|
||||
|
||||
+84
-85
@@ -5,12 +5,22 @@ use crate::location::LineColumn;
|
||||
use crate::parse::{self, Cursor};
|
||||
use crate::rcvec::{RcVec, RcVecBuilder, RcVecIntoIter, RcVecMut};
|
||||
use crate::{Delimiter, Spacing, TokenTree};
|
||||
use alloc::borrow::ToOwned as _;
|
||||
use alloc::boxed::Box;
|
||||
#[cfg(all(span_locations, not(fuzzing)))]
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::format;
|
||||
use alloc::string::{String, ToString as _};
|
||||
#[cfg(all(span_locations, not(fuzzing)))]
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
#[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::ffi::CStr;
|
||||
use core::fmt::{self, Debug, Display, Write};
|
||||
use core::mem::ManuallyDrop;
|
||||
#[cfg(span_locations)]
|
||||
@@ -20,11 +30,12 @@ use core::ptr;
|
||||
use core::str;
|
||||
#[cfg(feature = "proc-macro")]
|
||||
use core::str::FromStr;
|
||||
use std::ffi::CStr;
|
||||
#[cfg(wrap_proc_macro)]
|
||||
use std::panic;
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[cfg(span_locations)]
|
||||
use std::path::PathBuf;
|
||||
#[cfg(all(span_locations, not(fuzzing)))]
|
||||
use std::thread_local;
|
||||
|
||||
/// Force use of proc-macro2's fallback implementation of the API for now, even
|
||||
/// if the compiler's implementation is available.
|
||||
@@ -125,21 +136,32 @@ fn push_token_from_proc_macro(mut vec: RcVecMut<TokenTree>, token: TokenTree) {
|
||||
// Nonrecursive to prevent stack overflow.
|
||||
impl Drop for TokenStream {
|
||||
fn drop(&mut self) {
|
||||
let mut inner = match self.inner.get_mut() {
|
||||
Some(inner) => inner,
|
||||
let mut stack = Vec::new();
|
||||
let mut current = match self.inner.get_mut() {
|
||||
Some(inner) => inner.take().into_iter(),
|
||||
None => return,
|
||||
};
|
||||
while let Some(token) = inner.pop() {
|
||||
let group = match token {
|
||||
TokenTree::Group(group) => group.inner,
|
||||
_ => continue,
|
||||
};
|
||||
#[cfg(wrap_proc_macro)]
|
||||
let group = match group {
|
||||
crate::imp::Group::Fallback(group) => group,
|
||||
crate::imp::Group::Compiler(_) => continue,
|
||||
};
|
||||
inner.extend(group.stream.take_inner());
|
||||
loop {
|
||||
while let Some(token) = current.next() {
|
||||
let group = match token {
|
||||
TokenTree::Group(group) => group.inner,
|
||||
_ => continue,
|
||||
};
|
||||
#[cfg(wrap_proc_macro)]
|
||||
let group = match group {
|
||||
crate::imp::Group::Fallback(group) => group,
|
||||
crate::imp::Group::Compiler(_) => continue,
|
||||
};
|
||||
let mut group = group;
|
||||
if let Some(inner) = group.stream.inner.get_mut() {
|
||||
stack.push(current);
|
||||
current = inner.take().into_iter();
|
||||
}
|
||||
}
|
||||
match stack.pop() {
|
||||
Some(next) => current = next,
|
||||
None => return,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -209,13 +231,13 @@ impl Display for TokenStream {
|
||||
}
|
||||
joint = false;
|
||||
match tt {
|
||||
TokenTree::Group(tt) => Display::fmt(tt, f),
|
||||
TokenTree::Ident(tt) => Display::fmt(tt, f),
|
||||
TokenTree::Group(tt) => write!(f, "{}", tt),
|
||||
TokenTree::Ident(tt) => write!(f, "{}", tt),
|
||||
TokenTree::Punct(tt) => {
|
||||
joint = tt.spacing() == Spacing::Joint;
|
||||
Display::fmt(tt, f)
|
||||
write!(f, "{}", tt)
|
||||
}
|
||||
TokenTree::Literal(tt) => Display::fmt(tt, f),
|
||||
TokenTree::Literal(tt) => write!(f, "{}", tt),
|
||||
}?;
|
||||
}
|
||||
|
||||
@@ -300,34 +322,6 @@ impl IntoIterator for TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub(crate) struct SourceFile {
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
impl SourceFile {
|
||||
/// Get the path to this source file as a string.
|
||||
pub(crate) fn path(&self) -> PathBuf {
|
||||
self.path.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn is_real(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
impl Debug for SourceFile {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("SourceFile")
|
||||
.field("path", &self.path())
|
||||
.field("is_real", &self.is_real())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(span_locations, not(fuzzing)))]
|
||||
thread_local! {
|
||||
static SOURCE_MAP: RefCell<SourceMap> = RefCell::new(SourceMap {
|
||||
@@ -472,36 +466,39 @@ impl SourceMap {
|
||||
span
|
||||
}
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
fn filepath(&self, span: Span) -> PathBuf {
|
||||
for (i, file) in self.files.iter().enumerate() {
|
||||
if file.span_within(span) {
|
||||
return PathBuf::from(if i == 0 {
|
||||
"<unspecified>".to_owned()
|
||||
} else {
|
||||
format!("<parsed string {}>", i)
|
||||
});
|
||||
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) => i,
|
||||
Err(_) => unreachable!("Invalid span with no related FileInfo!"),
|
||||
}
|
||||
}
|
||||
|
||||
fn filepath(&self, span: Span) -> String {
|
||||
let i = self.find(span);
|
||||
if i == 0 {
|
||||
"<unspecified>".to_owned()
|
||||
} else {
|
||||
format!("<parsed string {}>", i)
|
||||
}
|
||||
unreachable!("Invalid span with no related FileInfo!");
|
||||
}
|
||||
|
||||
fn fileinfo(&self, span: Span) -> &FileInfo {
|
||||
for file in &self.files {
|
||||
if file.span_within(span) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
unreachable!("Invalid span with no related FileInfo!");
|
||||
let i = self.find(span);
|
||||
&self.files[i]
|
||||
}
|
||||
|
||||
fn fileinfo_mut(&mut self, span: Span) -> &mut FileInfo {
|
||||
for file in &mut self.files {
|
||||
if file.span_within(span) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
unreachable!("Invalid span with no related FileInfo!");
|
||||
let i = self.find(span);
|
||||
&mut self.files[i]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -544,21 +541,6 @@ impl Span {
|
||||
other
|
||||
}
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
pub(crate) fn source_file(&self) -> SourceFile {
|
||||
#[cfg(fuzzing)]
|
||||
return SourceFile {
|
||||
path: PathBuf::from("<unspecified>"),
|
||||
};
|
||||
|
||||
#[cfg(not(fuzzing))]
|
||||
SOURCE_MAP.with(|sm| {
|
||||
let sm = sm.borrow();
|
||||
let path = sm.filepath(*self);
|
||||
SourceFile { path }
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(span_locations)]
|
||||
pub(crate) fn byte_range(&self) -> Range<usize> {
|
||||
#[cfg(fuzzing)]
|
||||
@@ -600,6 +582,23 @@ impl Span {
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(span_locations)]
|
||||
pub(crate) fn file(&self) -> String {
|
||||
#[cfg(fuzzing)]
|
||||
return "<unspecified>".to_owned();
|
||||
|
||||
#[cfg(not(fuzzing))]
|
||||
SOURCE_MAP.with(|sm| {
|
||||
let sm = sm.borrow();
|
||||
sm.filepath(*self)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(span_locations)]
|
||||
pub(crate) fn local_file(&self) -> Option<PathBuf> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(not(span_locations))]
|
||||
pub(crate) fn join(&self, _other: Span) -> Option<Span> {
|
||||
Some(Span {})
|
||||
@@ -899,7 +898,7 @@ impl Display for Ident {
|
||||
if self.raw {
|
||||
f.write_str("r#")?;
|
||||
}
|
||||
Display::fmt(&self.sym, f)
|
||||
f.write_str(&self.sym)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+215
-77
@@ -9,8 +9,6 @@
|
||||
//! A wrapper around the procedural macro API of the compiler's [`proc_macro`]
|
||||
//! crate. This library serves two purposes:
|
||||
//!
|
||||
//! [`proc_macro`]: https://doc.rust-lang.org/proc_macro/
|
||||
//!
|
||||
//! - **Bring proc-macro-like functionality to other contexts like build.rs and
|
||||
//! main.rs.** Types from `proc_macro` are entirely specific to procedural
|
||||
//! macros and cannot ever exist in code outside of a procedural macro.
|
||||
@@ -65,7 +63,7 @@
|
||||
//!
|
||||
//! 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
|
||||
//! rustc. We will polyfill those nightly-only APIs back to Rust 1.68.0. As
|
||||
//! these are unstable APIs that track the nightly compiler, minor versions of
|
||||
//! proc-macro2 may make breaking changes to them at any time.
|
||||
//!
|
||||
@@ -85,8 +83,8 @@
|
||||
//! 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 = "https://docs.rs/proc-macro2/1.0.92")]
|
||||
#![no_std]
|
||||
#![doc(html_root_url = "https://docs.rs/proc-macro2/1.0.105")]
|
||||
#![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))]
|
||||
@@ -96,6 +94,7 @@
|
||||
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,
|
||||
@@ -111,11 +110,13 @@
|
||||
clippy::return_self_not_must_use,
|
||||
clippy::shadow_unrelated,
|
||||
clippy::trivially_copy_pass_by_ref,
|
||||
clippy::uninlined_format_args,
|
||||
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! {"\
|
||||
@@ -135,12 +136,14 @@ compile_error! {"\
|
||||
"}
|
||||
|
||||
extern crate alloc;
|
||||
extern crate std;
|
||||
|
||||
#[cfg(feature = "proc-macro")]
|
||||
extern crate proc_macro;
|
||||
|
||||
mod marker;
|
||||
mod parse;
|
||||
mod probe;
|
||||
mod rcvec;
|
||||
|
||||
#[cfg(wrap_proc_macro)]
|
||||
@@ -162,9 +165,23 @@ mod imp;
|
||||
#[cfg(span_locations)]
|
||||
mod location;
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
mod num;
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[allow(dead_code)]
|
||||
mod rustc_literal_escaper;
|
||||
|
||||
use crate::extra::DelimSpan;
|
||||
use crate::marker::{ProcMacroAutoTraits, MARKER};
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
use crate::rustc_literal_escaper::MixedUnit;
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
use alloc::borrow::ToOwned as _;
|
||||
use alloc::string::{String, ToString as _};
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
use alloc::vec::Vec;
|
||||
use core::cmp::Ordering;
|
||||
use core::ffi::CStr;
|
||||
use core::fmt::{self, Debug, Display};
|
||||
use core::hash::{Hash, Hasher};
|
||||
#[cfg(span_locations)]
|
||||
@@ -172,14 +189,17 @@ use core::ops::Range;
|
||||
use core::ops::RangeBounds;
|
||||
use core::str::FromStr;
|
||||
use std::error::Error;
|
||||
use std::ffi::CStr;
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[cfg(span_locations)]
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[cfg(span_locations)]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "span-locations")))]
|
||||
pub use crate::location::LineColumn;
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[cfg_attr(docsrs, doc(cfg(procmacro2_semver_exempt)))]
|
||||
pub use crate::rustc_literal_escaper::EscapeError;
|
||||
|
||||
/// An abstract stream of tokens, or more concretely a sequence of token trees.
|
||||
///
|
||||
/// This type provides interfaces for iterating over token trees and for
|
||||
@@ -278,8 +298,8 @@ impl From<TokenTree> for TokenStream {
|
||||
}
|
||||
|
||||
impl Extend<TokenTree> for TokenStream {
|
||||
fn extend<I: IntoIterator<Item = TokenTree>>(&mut self, streams: I) {
|
||||
self.inner.extend(streams);
|
||||
fn extend<I: IntoIterator<Item = TokenTree>>(&mut self, tokens: I) {
|
||||
self.inner.extend(tokens);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,12 +310,38 @@ impl Extend<TokenStream> for TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
/// Collects a number of token trees into a single stream.
|
||||
impl FromIterator<TokenTree> for TokenStream {
|
||||
fn from_iter<I: IntoIterator<Item = TokenTree>>(streams: I) -> Self {
|
||||
TokenStream::_new(streams.into_iter().collect())
|
||||
impl Extend<Group> for TokenStream {
|
||||
fn extend<I: IntoIterator<Item = Group>>(&mut self, tokens: I) {
|
||||
self.inner.extend(tokens.into_iter().map(TokenTree::Group));
|
||||
}
|
||||
}
|
||||
|
||||
impl Extend<Ident> for TokenStream {
|
||||
fn extend<I: IntoIterator<Item = Ident>>(&mut self, tokens: I) {
|
||||
self.inner.extend(tokens.into_iter().map(TokenTree::Ident));
|
||||
}
|
||||
}
|
||||
|
||||
impl Extend<Punct> for TokenStream {
|
||||
fn extend<I: IntoIterator<Item = Punct>>(&mut self, tokens: I) {
|
||||
self.inner.extend(tokens.into_iter().map(TokenTree::Punct));
|
||||
}
|
||||
}
|
||||
|
||||
impl Extend<Literal> for TokenStream {
|
||||
fn extend<I: IntoIterator<Item = Literal>>(&mut self, tokens: I) {
|
||||
self.inner
|
||||
.extend(tokens.into_iter().map(TokenTree::Literal));
|
||||
}
|
||||
}
|
||||
|
||||
/// Collects a number of token trees into a single stream.
|
||||
impl FromIterator<TokenTree> for TokenStream {
|
||||
fn from_iter<I: IntoIterator<Item = TokenTree>>(tokens: I) -> Self {
|
||||
TokenStream::_new(tokens.into_iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<TokenStream> for TokenStream {
|
||||
fn from_iter<I: IntoIterator<Item = TokenStream>>(streams: I) -> Self {
|
||||
TokenStream::_new(streams.into_iter().map(|i| i.inner).collect())
|
||||
@@ -339,57 +385,6 @@ impl Display for LexError {
|
||||
|
||||
impl Error for LexError {}
|
||||
|
||||
/// The source file of a given `Span`.
|
||||
///
|
||||
/// This type is semver exempt and not exposed by default.
|
||||
#[cfg(all(procmacro2_semver_exempt, any(not(wrap_proc_macro), super_unstable)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(procmacro2_semver_exempt)))]
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct SourceFile {
|
||||
inner: imp::SourceFile,
|
||||
_marker: ProcMacroAutoTraits,
|
||||
}
|
||||
|
||||
#[cfg(all(procmacro2_semver_exempt, any(not(wrap_proc_macro), super_unstable)))]
|
||||
impl SourceFile {
|
||||
fn _new(inner: imp::SourceFile) -> Self {
|
||||
SourceFile {
|
||||
inner,
|
||||
_marker: MARKER,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the path to this source file.
|
||||
///
|
||||
/// ### Note
|
||||
///
|
||||
/// If the code span associated with this `SourceFile` was generated by an
|
||||
/// external macro, this may not be an actual path on the filesystem. Use
|
||||
/// [`is_real`] to check.
|
||||
///
|
||||
/// Also note that even if `is_real` returns `true`, if
|
||||
/// `--remap-path-prefix` was passed on the command line, the path as given
|
||||
/// may not actually be valid.
|
||||
///
|
||||
/// [`is_real`]: #method.is_real
|
||||
pub fn path(&self) -> PathBuf {
|
||||
self.inner.path()
|
||||
}
|
||||
|
||||
/// Returns `true` if this source file is a real source file, and not
|
||||
/// generated by an external macro's expansion.
|
||||
pub fn is_real(&self) -> bool {
|
||||
self.inner.is_real()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(procmacro2_semver_exempt, any(not(wrap_proc_macro), super_unstable)))]
|
||||
impl Debug for SourceFile {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
Debug::fmt(&self.inner, f)
|
||||
}
|
||||
}
|
||||
|
||||
/// A region of source code, along with macro expansion information.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Span {
|
||||
@@ -471,15 +466,6 @@ impl Span {
|
||||
self.unwrap()
|
||||
}
|
||||
|
||||
/// The original source file into which this span points.
|
||||
///
|
||||
/// This method is semver exempt and not exposed by default.
|
||||
#[cfg(all(procmacro2_semver_exempt, any(not(wrap_proc_macro), super_unstable)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(procmacro2_semver_exempt)))]
|
||||
pub fn source_file(&self) -> SourceFile {
|
||||
SourceFile::_new(self.inner.source_file())
|
||||
}
|
||||
|
||||
/// Returns the span's byte position range in the source file.
|
||||
///
|
||||
/// This method requires the `"span-locations"` feature to be enabled.
|
||||
@@ -525,6 +511,29 @@ impl Span {
|
||||
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 `"<macro expansion>"`.
|
||||
#[cfg(span_locations)]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "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 remapping.
|
||||
///
|
||||
/// This path should not be embedded in the output of the macro; prefer
|
||||
/// `file()` instead.
|
||||
#[cfg(span_locations)]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "span-locations")))]
|
||||
pub fn local_file(&self) -> Option<PathBuf> {
|
||||
self.inner.local_file()
|
||||
}
|
||||
|
||||
/// Create a new span encompassing `self` and `other`.
|
||||
///
|
||||
/// Returns `None` if `self` and `other` are from different files.
|
||||
@@ -532,8 +541,6 @@ impl Span {
|
||||
/// 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`.
|
||||
///
|
||||
/// [`proc_macro::Span::join`]: https://doc.rust-lang.org/proc_macro/struct.Span.html#method.join
|
||||
pub fn join(&self, other: Span) -> Option<Span> {
|
||||
self.inner.join(other.inner).map(Span::_new)
|
||||
}
|
||||
@@ -1296,12 +1303,116 @@ impl Literal {
|
||||
/// 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`.
|
||||
///
|
||||
/// [`proc_macro::Literal::subspan`]: https://doc.rust-lang.org/proc_macro/struct.Literal.html#method.subspan
|
||||
pub fn subspan<R: RangeBounds<usize>>(&self, range: R) -> Option<Span> {
|
||||
self.inner.subspan(range).map(Span::_new)
|
||||
}
|
||||
|
||||
/// Returns the unescaped string value if this is a string literal.
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
pub fn str_value(&self) -> Result<String, ConversionErrorKind> {
|
||||
let repr = self.to_string();
|
||||
|
||||
if repr.starts_with('"') && repr[1..].ends_with('"') {
|
||||
let quoted = &repr[1..repr.len() - 1];
|
||||
let mut value = String::with_capacity(quoted.len());
|
||||
let mut error = None;
|
||||
rustc_literal_escaper::unescape_str(quoted, |_range, res| match res {
|
||||
Ok(ch) => value.push(ch),
|
||||
Err(err) => {
|
||||
if err.is_fatal() {
|
||||
error = Some(ConversionErrorKind::FailedToUnescape(err));
|
||||
}
|
||||
}
|
||||
});
|
||||
return match error {
|
||||
Some(error) => Err(error),
|
||||
None => Ok(value),
|
||||
};
|
||||
}
|
||||
|
||||
if repr.starts_with('r') {
|
||||
if let Some(raw) = get_raw(&repr[1..]) {
|
||||
return Ok(raw.to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
Err(ConversionErrorKind::InvalidLiteralKind)
|
||||
}
|
||||
|
||||
/// Returns the unescaped string value (including nul terminator) if this is
|
||||
/// a c-string literal.
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
pub fn cstr_value(&self) -> Result<Vec<u8>, ConversionErrorKind> {
|
||||
let repr = self.to_string();
|
||||
|
||||
if repr.starts_with("c\"") && repr[2..].ends_with('"') {
|
||||
let quoted = &repr[2..repr.len() - 1];
|
||||
let mut value = Vec::with_capacity(quoted.len());
|
||||
let mut error = None;
|
||||
rustc_literal_escaper::unescape_c_str(quoted, |_range, res| match res {
|
||||
Ok(MixedUnit::Char(ch)) => {
|
||||
value.extend_from_slice(ch.get().encode_utf8(&mut [0; 4]).as_bytes());
|
||||
}
|
||||
Ok(MixedUnit::HighByte(byte)) => value.push(byte.get()),
|
||||
Err(err) => {
|
||||
if err.is_fatal() {
|
||||
error = Some(ConversionErrorKind::FailedToUnescape(err));
|
||||
}
|
||||
}
|
||||
});
|
||||
return match error {
|
||||
Some(error) => Err(error),
|
||||
None => {
|
||||
value.push(b'\0');
|
||||
Ok(value)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if repr.starts_with("cr") {
|
||||
if let Some(raw) = get_raw(&repr[2..]) {
|
||||
let mut value = Vec::with_capacity(raw.len() + 1);
|
||||
value.extend_from_slice(raw.as_bytes());
|
||||
value.push(b'\0');
|
||||
return Ok(value);
|
||||
}
|
||||
}
|
||||
|
||||
Err(ConversionErrorKind::InvalidLiteralKind)
|
||||
}
|
||||
|
||||
/// Returns the unescaped string value if this is a byte string literal.
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
pub fn byte_str_value(&self) -> Result<Vec<u8>, ConversionErrorKind> {
|
||||
let repr = self.to_string();
|
||||
|
||||
if repr.starts_with("b\"") && repr[2..].ends_with('"') {
|
||||
let quoted = &repr[2..repr.len() - 1];
|
||||
let mut value = Vec::with_capacity(quoted.len());
|
||||
let mut error = None;
|
||||
rustc_literal_escaper::unescape_byte_str(quoted, |_range, res| match res {
|
||||
Ok(byte) => value.push(byte),
|
||||
Err(err) => {
|
||||
if err.is_fatal() {
|
||||
error = Some(ConversionErrorKind::FailedToUnescape(err));
|
||||
}
|
||||
}
|
||||
});
|
||||
return match error {
|
||||
Some(error) => Err(error),
|
||||
None => Ok(value),
|
||||
};
|
||||
}
|
||||
|
||||
if repr.starts_with("br") {
|
||||
if let Some(raw) = get_raw(&repr[2..]) {
|
||||
return Ok(raw.as_bytes().to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
Err(ConversionErrorKind::InvalidLiteralKind)
|
||||
}
|
||||
|
||||
// Intended for the `quote!` macro to use when constructing a proc-macro2
|
||||
// token out of a macro_rules $:literal token, which is already known to be
|
||||
// a valid literal. This avoids reparsing/validating the literal's string
|
||||
@@ -1338,6 +1449,33 @@ impl Display for Literal {
|
||||
}
|
||||
}
|
||||
|
||||
/// Error when retrieving a string literal's unescaped value.
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum ConversionErrorKind {
|
||||
/// The literal is of the right string kind, but its contents are malformed
|
||||
/// in a way that cannot be unescaped to a value.
|
||||
FailedToUnescape(EscapeError),
|
||||
/// The literal is not of the string kind whose value was requested, for
|
||||
/// example byte string vs UTF-8 string.
|
||||
InvalidLiteralKind,
|
||||
}
|
||||
|
||||
// ###"..."### -> ...
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
fn get_raw(repr: &str) -> Option<&str> {
|
||||
let pounds = repr.len() - repr.trim_start_matches('#').len();
|
||||
if repr.len() >= pounds + 1 + 1 + pounds
|
||||
&& repr[pounds..].starts_with('"')
|
||||
&& repr.trim_end_matches('#').len() + pounds == repr.len()
|
||||
&& repr[..repr.len() - pounds].ends_with('"')
|
||||
{
|
||||
Some(&repr[pounds + 1..repr.len() - pounds - 1])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Public implementation details for the `TokenStream` type, such as iterators.
|
||||
pub mod token_stream {
|
||||
use crate::marker::{ProcMacroAutoTraits, MARKER};
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// TODO: use NonZero<char> in Rust 1.89+
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct NonZeroChar(char);
|
||||
|
||||
impl NonZeroChar {
|
||||
pub fn new(ch: char) -> Option<Self> {
|
||||
if ch == '\0' {
|
||||
None
|
||||
} else {
|
||||
Some(NonZeroChar(ch))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(self) -> char {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
+32
-33
@@ -3,6 +3,9 @@ use crate::fallback::{
|
||||
TokenStreamBuilder,
|
||||
};
|
||||
use crate::{Delimiter, Punct, Spacing, TokenTree};
|
||||
use alloc::borrow::ToOwned as _;
|
||||
use alloc::string::ToString as _;
|
||||
use alloc::vec::Vec;
|
||||
use core::char;
|
||||
use core::str::{Bytes, CharIndices, Chars};
|
||||
|
||||
@@ -166,13 +169,13 @@ fn word_break(input: Cursor) -> Result<Cursor, Reject> {
|
||||
const ERROR: &str = "(/*ERROR*/)";
|
||||
|
||||
pub(crate) fn token_stream(mut input: Cursor) -> Result<TokenStream, LexError> {
|
||||
let mut trees = TokenStreamBuilder::new();
|
||||
let mut tokens = TokenStreamBuilder::new();
|
||||
let mut stack = Vec::new();
|
||||
|
||||
loop {
|
||||
input = skip_whitespace(input);
|
||||
|
||||
if let Ok((rest, ())) = doc_comment(input, &mut trees) {
|
||||
if let Ok((rest, ())) = doc_comment(input, &mut tokens) {
|
||||
input = rest;
|
||||
continue;
|
||||
}
|
||||
@@ -180,19 +183,16 @@ pub(crate) fn token_stream(mut input: Cursor) -> Result<TokenStream, LexError> {
|
||||
#[cfg(span_locations)]
|
||||
let lo = input.off;
|
||||
|
||||
let first = match input.bytes().next() {
|
||||
Some(first) => first,
|
||||
None => match stack.last() {
|
||||
None => return Ok(trees.build()),
|
||||
let Some(first) = input.bytes().next() else {
|
||||
return match stack.last() {
|
||||
None => Ok(tokens.build()),
|
||||
#[cfg(span_locations)]
|
||||
Some((lo, _frame)) => {
|
||||
return Err(LexError {
|
||||
span: Span { lo: *lo, hi: *lo },
|
||||
})
|
||||
}
|
||||
Some((lo, _frame)) => Err(LexError {
|
||||
span: Span { lo: *lo, hi: *lo },
|
||||
}),
|
||||
#[cfg(not(span_locations))]
|
||||
Some(_frame) => return Err(LexError { span: Span {} }),
|
||||
},
|
||||
Some(_frame) => Err(LexError { span: Span {} }),
|
||||
};
|
||||
};
|
||||
|
||||
if let Some(open_delimiter) = match first {
|
||||
@@ -202,20 +202,19 @@ pub(crate) fn token_stream(mut input: Cursor) -> Result<TokenStream, LexError> {
|
||||
_ => None,
|
||||
} {
|
||||
input = input.advance(1);
|
||||
let frame = (open_delimiter, trees);
|
||||
let frame = (open_delimiter, tokens);
|
||||
#[cfg(span_locations)]
|
||||
let frame = (lo, frame);
|
||||
stack.push(frame);
|
||||
trees = TokenStreamBuilder::new();
|
||||
tokens = TokenStreamBuilder::new();
|
||||
} else if let Some(close_delimiter) = match first {
|
||||
b')' => Some(Delimiter::Parenthesis),
|
||||
b']' => Some(Delimiter::Bracket),
|
||||
b'}' => Some(Delimiter::Brace),
|
||||
_ => None,
|
||||
} {
|
||||
let frame = match stack.pop() {
|
||||
Some(frame) => frame,
|
||||
None => return Err(lex_error(input)),
|
||||
let Some(frame) = stack.pop() else {
|
||||
return Err(lex_error(input));
|
||||
};
|
||||
#[cfg(span_locations)]
|
||||
let (lo, frame) = frame;
|
||||
@@ -224,15 +223,15 @@ pub(crate) fn token_stream(mut input: Cursor) -> Result<TokenStream, LexError> {
|
||||
return Err(lex_error(input));
|
||||
}
|
||||
input = input.advance(1);
|
||||
let mut g = Group::new(open_delimiter, trees.build());
|
||||
let mut g = Group::new(open_delimiter, tokens.build());
|
||||
g.set_span(Span {
|
||||
#[cfg(span_locations)]
|
||||
lo,
|
||||
#[cfg(span_locations)]
|
||||
hi: input.off,
|
||||
});
|
||||
trees = outer;
|
||||
trees.push_token_from_parser(TokenTree::Group(crate::Group::_new_fallback(g)));
|
||||
tokens = outer;
|
||||
tokens.push_token_from_parser(TokenTree::Group(crate::Group::_new_fallback(g)));
|
||||
} else {
|
||||
let (rest, mut tt) = match leaf_token(input) {
|
||||
Ok((rest, tt)) => (rest, tt),
|
||||
@@ -244,7 +243,7 @@ pub(crate) fn token_stream(mut input: Cursor) -> Result<TokenStream, LexError> {
|
||||
#[cfg(span_locations)]
|
||||
hi: rest.off,
|
||||
}));
|
||||
trees.push_token_from_parser(tt);
|
||||
tokens.push_token_from_parser(tt);
|
||||
input = rest;
|
||||
}
|
||||
}
|
||||
@@ -857,7 +856,7 @@ fn digits(mut input: Cursor) -> Result<Cursor, Reject> {
|
||||
continue;
|
||||
}
|
||||
_ => break,
|
||||
};
|
||||
}
|
||||
len += 1;
|
||||
empty = false;
|
||||
}
|
||||
@@ -871,7 +870,10 @@ fn digits(mut input: Cursor) -> Result<Cursor, Reject> {
|
||||
fn punct(input: Cursor) -> PResult<Punct> {
|
||||
let (rest, ch) = punct_char(input)?;
|
||||
if ch == '\'' {
|
||||
if ident_any(rest)?.0.starts_with_char('\'') {
|
||||
let (after_lifetime, _ident) = 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)))
|
||||
@@ -892,11 +894,8 @@ fn punct_char(input: Cursor) -> PResult<char> {
|
||||
}
|
||||
|
||||
let mut chars = input.chars();
|
||||
let first = match chars.next() {
|
||||
Some(ch) => ch,
|
||||
None => {
|
||||
return Err(Reject);
|
||||
}
|
||||
let Some(first) = chars.next() else {
|
||||
return Err(Reject);
|
||||
};
|
||||
let recognized = "~!@#$%^&*-=+|;:,<.>/?'";
|
||||
if recognized.contains(first) {
|
||||
@@ -906,7 +905,7 @@ fn punct_char(input: Cursor) -> PResult<char> {
|
||||
}
|
||||
}
|
||||
|
||||
fn doc_comment<'a>(input: Cursor<'a>, trees: &mut TokenStreamBuilder) -> PResult<'a, ()> {
|
||||
fn doc_comment<'a>(input: Cursor<'a>, tokens: &mut TokenStreamBuilder) -> PResult<'a, ()> {
|
||||
#[cfg(span_locations)]
|
||||
let lo = input.off;
|
||||
let (rest, (comment, inner)) = doc_comment_contents(input)?;
|
||||
@@ -929,12 +928,12 @@ fn doc_comment<'a>(input: Cursor<'a>, trees: &mut TokenStreamBuilder) -> PResult
|
||||
|
||||
let mut pound = Punct::new('#', Spacing::Alone);
|
||||
pound.set_span(span);
|
||||
trees.push_token_from_parser(TokenTree::Punct(pound));
|
||||
tokens.push_token_from_parser(TokenTree::Punct(pound));
|
||||
|
||||
if inner {
|
||||
let mut bang = Punct::new('!', Spacing::Alone);
|
||||
bang.set_span(span);
|
||||
trees.push_token_from_parser(TokenTree::Punct(bang));
|
||||
tokens.push_token_from_parser(TokenTree::Punct(bang));
|
||||
}
|
||||
|
||||
let doc_ident = crate::Ident::_new_fallback(Ident::new_unchecked("doc", fallback_span));
|
||||
@@ -949,7 +948,7 @@ fn doc_comment<'a>(input: Cursor<'a>, trees: &mut TokenStreamBuilder) -> PResult
|
||||
let group = Group::new(Delimiter::Bracket, bracketed.build());
|
||||
let mut group = crate::Group::_new_fallback(group);
|
||||
group.set_span(span);
|
||||
trees.push_token_from_parser(TokenTree::Group(group));
|
||||
tokens.push_token_from_parser(TokenTree::Group(group));
|
||||
|
||||
Ok((rest, ()))
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -2,12 +2,17 @@
|
||||
// If the current toolchain is able to compile it, then proc-macro2 is able to
|
||||
// offer these APIs too.
|
||||
|
||||
#![feature(proc_macro_span)]
|
||||
#![cfg_attr(procmacro2_build_probe, no_std)]
|
||||
#![cfg_attr(procmacro2_build_probe, feature(proc_macro_span))]
|
||||
|
||||
extern crate alloc;
|
||||
extern crate proc_macro;
|
||||
extern crate std;
|
||||
|
||||
use alloc::string::String;
|
||||
use core::ops::{Range, RangeBounds};
|
||||
use proc_macro::{Literal, Span};
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub fn byte_range(this: &Span) -> Range<usize> {
|
||||
this.byte_range()
|
||||
@@ -29,6 +34,14 @@ pub fn column(this: &Span) -> usize {
|
||||
this.column()
|
||||
}
|
||||
|
||||
pub fn file(this: &Span) -> String {
|
||||
this.file()
|
||||
}
|
||||
|
||||
pub fn local_file(this: &Span) -> Option<PathBuf> {
|
||||
this.local_file()
|
||||
}
|
||||
|
||||
pub fn join(this: &Span, other: Span) -> Option<Span> {
|
||||
this.join(other)
|
||||
}
|
||||
@@ -38,4 +51,5 @@ pub fn subspan<R: RangeBounds<usize>>(this: &Literal, range: R) -> Option<Span>
|
||||
}
|
||||
|
||||
// Include in sccache cache key.
|
||||
#[cfg(procmacro2_build_probe)]
|
||||
const _: Option<&str> = option_env!("RUSTC_BOOTSTRAP");
|
||||
@@ -0,0 +1,19 @@
|
||||
// The subset of Span's API stabilized in Rust 1.88.
|
||||
|
||||
#![cfg_attr(procmacro2_build_probe, no_std)]
|
||||
|
||||
extern crate alloc;
|
||||
extern crate proc_macro;
|
||||
extern crate std;
|
||||
|
||||
use alloc::string::String;
|
||||
use proc_macro::Span;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub fn file(this: &Span) -> String {
|
||||
this.file()
|
||||
}
|
||||
|
||||
pub fn local_file(this: &Span) -> Option<PathBuf> {
|
||||
this.local_file()
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// The subset of Span's API stabilized in Rust 1.88.
|
||||
|
||||
#![cfg_attr(procmacro2_build_probe, no_std)]
|
||||
|
||||
extern crate alloc;
|
||||
extern crate proc_macro;
|
||||
extern crate std;
|
||||
|
||||
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()
|
||||
}
|
||||
+6
-5
@@ -1,5 +1,5 @@
|
||||
use alloc::rc::Rc;
|
||||
use alloc::vec;
|
||||
use alloc::vec::{self, Vec};
|
||||
use core::mem;
|
||||
use core::panic::RefUnwindSafe;
|
||||
use core::slice;
|
||||
@@ -102,13 +102,14 @@ impl<'a, T> RcVecMut<'a, T> {
|
||||
self.inner.extend(iter);
|
||||
}
|
||||
|
||||
pub(crate) fn pop(&mut self) -> Option<T> {
|
||||
self.inner.pop()
|
||||
}
|
||||
|
||||
pub(crate) fn as_mut(&mut self) -> RcVecMut<T> {
|
||||
RcVecMut { inner: self.inner }
|
||||
}
|
||||
|
||||
pub(crate) fn take(self) -> RcVecBuilder<T> {
|
||||
let vec = mem::take(self.inner);
|
||||
RcVecBuilder { inner: vec }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for RcVec<T> {
|
||||
|
||||
@@ -0,0 +1,701 @@
|
||||
// Vendored from rustc-literal-escaper v0.0.5
|
||||
// https://github.com/rust-lang/literal-escaper/tree/v0.0.5
|
||||
|
||||
//! Utilities for validating (raw) string, char, and byte literals and
|
||||
//! turning escape sequences into the values they represent.
|
||||
|
||||
use crate::num::NonZeroChar;
|
||||
use core::ffi::CStr;
|
||||
use core::num::NonZeroU8;
|
||||
use core::ops::Range;
|
||||
use core::str::Chars;
|
||||
|
||||
/// Errors and warnings that can occur during string, char, and byte unescaping.
|
||||
///
|
||||
/// Mostly relating to malformed escape sequences, but also a few other problems.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum EscapeError {
|
||||
/// Expected 1 char, but 0 were found.
|
||||
ZeroChars,
|
||||
/// Expected 1 char, but more than 1 were found.
|
||||
MoreThanOneChar,
|
||||
|
||||
/// Escaped '\' character without continuation.
|
||||
LoneSlash,
|
||||
/// Invalid escape character (e.g. '\z').
|
||||
InvalidEscape,
|
||||
/// Raw '\r' encountered.
|
||||
BareCarriageReturn,
|
||||
/// Raw '\r' encountered in raw string.
|
||||
BareCarriageReturnInRawString,
|
||||
/// Unescaped character that was expected to be escaped (e.g. raw '\t').
|
||||
EscapeOnlyChar,
|
||||
|
||||
/// Numeric character escape is too short (e.g. '\x1').
|
||||
TooShortHexEscape,
|
||||
/// Invalid character in numeric escape (e.g. '\xz')
|
||||
InvalidCharInHexEscape,
|
||||
/// Character code in numeric escape is non-ascii (e.g. '\xFF').
|
||||
OutOfRangeHexEscape,
|
||||
|
||||
/// '\u' not followed by '{'.
|
||||
NoBraceInUnicodeEscape,
|
||||
/// Non-hexadecimal value in '\u{..}'.
|
||||
InvalidCharInUnicodeEscape,
|
||||
/// '\u{}'
|
||||
EmptyUnicodeEscape,
|
||||
/// No closing brace in '\u{..}', e.g. '\u{12'.
|
||||
UnclosedUnicodeEscape,
|
||||
/// '\u{_12}'
|
||||
LeadingUnderscoreUnicodeEscape,
|
||||
/// More than 6 characters in '\u{..}', e.g. '\u{10FFFF_FF}'
|
||||
OverlongUnicodeEscape,
|
||||
/// Invalid in-bound unicode character code, e.g. '\u{DFFF}'.
|
||||
LoneSurrogateUnicodeEscape,
|
||||
/// Out of bounds unicode character code, e.g. '\u{FFFFFF}'.
|
||||
OutOfRangeUnicodeEscape,
|
||||
|
||||
/// Unicode escape code in byte literal.
|
||||
UnicodeEscapeInByte,
|
||||
/// Non-ascii character in byte literal, byte string literal, or raw byte string literal.
|
||||
NonAsciiCharInByte,
|
||||
|
||||
/// `\0` in a C string literal.
|
||||
NulInCStr,
|
||||
|
||||
/// After a line ending with '\', the next line contains whitespace
|
||||
/// characters that are not skipped.
|
||||
UnskippedWhitespaceWarning,
|
||||
|
||||
/// After a line ending with '\', multiple lines are skipped.
|
||||
MultipleSkippedLinesWarning,
|
||||
}
|
||||
|
||||
impl EscapeError {
|
||||
/// Returns true for actual errors, as opposed to warnings.
|
||||
pub fn is_fatal(&self) -> bool {
|
||||
!matches!(
|
||||
self,
|
||||
EscapeError::UnskippedWhitespaceWarning | EscapeError::MultipleSkippedLinesWarning
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Check a raw string literal for validity
|
||||
///
|
||||
/// Takes the contents of a raw string literal (without quotes)
|
||||
/// and produces a sequence of characters or errors,
|
||||
/// which are returned by invoking `callback`.
|
||||
/// NOTE: Does no escaping, but produces errors for bare carriage return ('\r').
|
||||
pub fn check_raw_str(src: &str, callback: impl FnMut(Range<usize>, Result<char, EscapeError>)) {
|
||||
str::check_raw(src, callback);
|
||||
}
|
||||
|
||||
/// Check a raw byte string literal for validity
|
||||
///
|
||||
/// Takes the contents of a raw byte string literal (without quotes)
|
||||
/// and produces a sequence of bytes or errors,
|
||||
/// which are returned by invoking `callback`.
|
||||
/// NOTE: Does no escaping, but produces errors for bare carriage return ('\r').
|
||||
pub fn check_raw_byte_str(src: &str, callback: impl FnMut(Range<usize>, Result<u8, EscapeError>)) {
|
||||
<[u8]>::check_raw(src, callback);
|
||||
}
|
||||
|
||||
/// Check a raw C string literal for validity
|
||||
///
|
||||
/// Takes the contents of a raw C string literal (without quotes)
|
||||
/// and produces a sequence of characters or errors,
|
||||
/// which are returned by invoking `callback`.
|
||||
/// NOTE: Does no escaping, but produces errors for bare carriage return ('\r').
|
||||
pub fn check_raw_c_str(
|
||||
src: &str,
|
||||
callback: impl FnMut(Range<usize>, Result<NonZeroChar, EscapeError>),
|
||||
) {
|
||||
CStr::check_raw(src, callback);
|
||||
}
|
||||
|
||||
/// Trait for checking raw string literals for validity
|
||||
trait CheckRaw {
|
||||
/// Unit type of the implementing string type (`char` for string, `u8` for byte string)
|
||||
type RawUnit;
|
||||
|
||||
/// Converts chars to the unit type of the literal type
|
||||
fn char2raw_unit(c: char) -> Result<Self::RawUnit, EscapeError>;
|
||||
|
||||
/// Takes the contents of a raw literal (without quotes)
|
||||
/// and produces a sequence of `Result<Self::RawUnit, EscapeError>`
|
||||
/// which are returned via `callback`.
|
||||
///
|
||||
/// NOTE: Does no escaping, but produces errors for bare carriage return ('\r').
|
||||
fn check_raw(
|
||||
src: &str,
|
||||
mut callback: impl FnMut(Range<usize>, Result<Self::RawUnit, EscapeError>),
|
||||
) {
|
||||
let mut chars = src.chars();
|
||||
while let Some(c) = chars.next() {
|
||||
let start = src.len() - chars.as_str().len() - c.len_utf8();
|
||||
let res = match c {
|
||||
'\r' => Err(EscapeError::BareCarriageReturnInRawString),
|
||||
_ => Self::char2raw_unit(c),
|
||||
};
|
||||
let end = src.len() - chars.as_str().len();
|
||||
callback(start..end, res);
|
||||
}
|
||||
|
||||
// Unfortunately, it is a bit unclear whether the following equivalent code is slower or faster: bug 141855
|
||||
// src.char_indices().for_each(|(pos, c)| {
|
||||
// callback(
|
||||
// pos..pos + c.len_utf8(),
|
||||
// if c == '\r' {
|
||||
// Err(EscapeError::BareCarriageReturnInRawString)
|
||||
// } else {
|
||||
// Self::char2raw_unit(c)
|
||||
// },
|
||||
// );
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
impl CheckRaw for str {
|
||||
type RawUnit = char;
|
||||
|
||||
#[inline]
|
||||
fn char2raw_unit(c: char) -> Result<Self::RawUnit, EscapeError> {
|
||||
Ok(c)
|
||||
}
|
||||
}
|
||||
|
||||
impl CheckRaw for [u8] {
|
||||
type RawUnit = u8;
|
||||
|
||||
#[inline]
|
||||
fn char2raw_unit(c: char) -> Result<Self::RawUnit, EscapeError> {
|
||||
char2byte(c)
|
||||
}
|
||||
}
|
||||
|
||||
/// Turn an ascii char into a byte
|
||||
#[inline]
|
||||
fn char2byte(c: char) -> Result<u8, EscapeError> {
|
||||
// do NOT do: c.try_into().ok_or(EscapeError::NonAsciiCharInByte)
|
||||
if c.is_ascii() {
|
||||
Ok(c as u8)
|
||||
} else {
|
||||
Err(EscapeError::NonAsciiCharInByte)
|
||||
}
|
||||
}
|
||||
|
||||
impl CheckRaw for CStr {
|
||||
type RawUnit = NonZeroChar;
|
||||
|
||||
#[inline]
|
||||
fn char2raw_unit(c: char) -> Result<Self::RawUnit, EscapeError> {
|
||||
NonZeroChar::new(c).ok_or(EscapeError::NulInCStr)
|
||||
}
|
||||
}
|
||||
|
||||
/// Unescape a char literal
|
||||
///
|
||||
/// Takes the contents of a char literal (without quotes),
|
||||
/// and returns an unescaped char or an error.
|
||||
#[inline]
|
||||
pub fn unescape_char(src: &str) -> Result<char, EscapeError> {
|
||||
str::unescape_single(&mut src.chars())
|
||||
}
|
||||
|
||||
/// Unescape a byte literal
|
||||
///
|
||||
/// Takes the contents of a byte literal (without quotes),
|
||||
/// and returns an unescaped byte or an error.
|
||||
#[inline]
|
||||
pub fn unescape_byte(src: &str) -> Result<u8, EscapeError> {
|
||||
<[u8]>::unescape_single(&mut src.chars())
|
||||
}
|
||||
|
||||
/// Unescape a string literal
|
||||
///
|
||||
/// Takes the contents of a string literal (without quotes)
|
||||
/// and produces a sequence of escaped characters or errors,
|
||||
/// which are returned by invoking `callback`.
|
||||
pub fn unescape_str(src: &str, callback: impl FnMut(Range<usize>, Result<char, EscapeError>)) {
|
||||
str::unescape(src, callback)
|
||||
}
|
||||
|
||||
/// Unescape a byte string literal
|
||||
///
|
||||
/// Takes the contents of a byte string literal (without quotes)
|
||||
/// and produces a sequence of escaped bytes or errors,
|
||||
/// which are returned by invoking `callback`.
|
||||
pub fn unescape_byte_str(src: &str, callback: impl FnMut(Range<usize>, Result<u8, EscapeError>)) {
|
||||
<[u8]>::unescape(src, callback)
|
||||
}
|
||||
|
||||
/// Unescape a C string literal
|
||||
///
|
||||
/// Takes the contents of a C string literal (without quotes)
|
||||
/// and produces a sequence of escaped MixedUnits or errors,
|
||||
/// which are returned by invoking `callback`.
|
||||
pub fn unescape_c_str(
|
||||
src: &str,
|
||||
callback: impl FnMut(Range<usize>, Result<MixedUnit, EscapeError>),
|
||||
) {
|
||||
CStr::unescape(src, callback)
|
||||
}
|
||||
|
||||
/// Enum representing either a char or a byte
|
||||
///
|
||||
/// Used for mixed utf8 string literals, i.e. those that allow both unicode
|
||||
/// chars and high bytes.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum MixedUnit {
|
||||
/// Used for ASCII chars (written directly or via `\x00`..`\x7f` escapes)
|
||||
/// and Unicode chars (written directly or via `\u` escapes).
|
||||
///
|
||||
/// For example, if '¥' appears in a string it is represented here as
|
||||
/// `MixedUnit::Char('¥')`, and it will be appended to the relevant byte
|
||||
/// string as the two-byte UTF-8 sequence `[0xc2, 0xa5]`
|
||||
Char(NonZeroChar),
|
||||
|
||||
/// Used for high bytes (`\x80`..`\xff`).
|
||||
///
|
||||
/// For example, if `\xa5` appears in a string it is represented here as
|
||||
/// `MixedUnit::HighByte(0xa5)`, and it will be appended to the relevant
|
||||
/// byte string as the single byte `0xa5`.
|
||||
HighByte(NonZeroU8),
|
||||
}
|
||||
|
||||
impl From<NonZeroChar> for MixedUnit {
|
||||
#[inline]
|
||||
fn from(c: NonZeroChar) -> Self {
|
||||
MixedUnit::Char(c)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NonZeroU8> for MixedUnit {
|
||||
#[inline]
|
||||
fn from(byte: NonZeroU8) -> Self {
|
||||
if byte.get().is_ascii() {
|
||||
MixedUnit::Char(NonZeroChar::new(byte.get() as char).unwrap())
|
||||
} else {
|
||||
MixedUnit::HighByte(byte)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<char> for MixedUnit {
|
||||
type Error = EscapeError;
|
||||
|
||||
#[inline]
|
||||
fn try_from(c: char) -> Result<Self, EscapeError> {
|
||||
NonZeroChar::new(c)
|
||||
.map(MixedUnit::Char)
|
||||
.ok_or(EscapeError::NulInCStr)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for MixedUnit {
|
||||
type Error = EscapeError;
|
||||
|
||||
#[inline]
|
||||
fn try_from(byte: u8) -> Result<Self, EscapeError> {
|
||||
NonZeroU8::new(byte)
|
||||
.map(From::from)
|
||||
.ok_or(EscapeError::NulInCStr)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for unescaping escape sequences in strings
|
||||
trait Unescape {
|
||||
/// Unit type of the implementing string type (`char` for string, `u8` for byte string)
|
||||
type Unit;
|
||||
|
||||
/// Result of unescaping the zero char ('\0')
|
||||
const ZERO_RESULT: Result<Self::Unit, EscapeError>;
|
||||
|
||||
/// Converts non-zero bytes to the unit type
|
||||
fn nonzero_byte2unit(b: NonZeroU8) -> Self::Unit;
|
||||
|
||||
/// Converts chars to the unit type
|
||||
fn char2unit(c: char) -> Result<Self::Unit, EscapeError>;
|
||||
|
||||
/// Converts the byte of a hex escape to the unit type
|
||||
fn hex2unit(b: u8) -> Result<Self::Unit, EscapeError>;
|
||||
|
||||
/// Converts the result of a unicode escape to the unit type
|
||||
fn unicode2unit(r: Result<char, EscapeError>) -> Result<Self::Unit, EscapeError>;
|
||||
|
||||
/// Unescape a single unit (single quote syntax)
|
||||
fn unescape_single(chars: &mut Chars<'_>) -> Result<Self::Unit, EscapeError> {
|
||||
let res = match chars.next().ok_or(EscapeError::ZeroChars)? {
|
||||
'\\' => Self::unescape_1(chars),
|
||||
'\n' | '\t' | '\'' => Err(EscapeError::EscapeOnlyChar),
|
||||
'\r' => Err(EscapeError::BareCarriageReturn),
|
||||
c => Self::char2unit(c),
|
||||
}?;
|
||||
if chars.next().is_some() {
|
||||
return Err(EscapeError::MoreThanOneChar);
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Unescape the first unit of a string (double quoted syntax)
|
||||
fn unescape_1(chars: &mut Chars<'_>) -> Result<Self::Unit, EscapeError> {
|
||||
// Previous character was '\\', unescape what follows.
|
||||
let c = chars.next().ok_or(EscapeError::LoneSlash)?;
|
||||
if c == '0' {
|
||||
Self::ZERO_RESULT
|
||||
} else {
|
||||
simple_escape(c)
|
||||
.map(|b| Self::nonzero_byte2unit(b))
|
||||
.or_else(|c| match c {
|
||||
'x' => Self::hex2unit(hex_escape(chars)?),
|
||||
'u' => Self::unicode2unit({
|
||||
let value = unicode_escape(chars)?;
|
||||
if value > char::MAX as u32 {
|
||||
Err(EscapeError::OutOfRangeUnicodeEscape)
|
||||
} else {
|
||||
char::from_u32(value).ok_or(EscapeError::LoneSurrogateUnicodeEscape)
|
||||
}
|
||||
}),
|
||||
_ => Err(EscapeError::InvalidEscape),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Unescape a string literal
|
||||
///
|
||||
/// Takes the contents of a raw string literal (without quotes)
|
||||
/// and produces a sequence of `Result<Self::Unit, EscapeError>`
|
||||
/// which are returned via `callback`.
|
||||
fn unescape(
|
||||
src: &str,
|
||||
mut callback: impl FnMut(Range<usize>, Result<Self::Unit, EscapeError>),
|
||||
) {
|
||||
let mut chars = src.chars();
|
||||
while let Some(c) = chars.next() {
|
||||
let start = src.len() - chars.as_str().len() - c.len_utf8();
|
||||
let res = match c {
|
||||
'\\' => {
|
||||
if let Some(b'\n') = chars.as_str().as_bytes().first() {
|
||||
let _ = chars.next();
|
||||
// skip whitespace for backslash newline, see [Rust language reference]
|
||||
// (https://doc.rust-lang.org/reference/tokens.html#string-literals).
|
||||
let callback_err = |range, err| callback(range, Err(err));
|
||||
skip_ascii_whitespace(&mut chars, start, callback_err);
|
||||
continue;
|
||||
} else {
|
||||
Self::unescape_1(&mut chars)
|
||||
}
|
||||
}
|
||||
'"' => Err(EscapeError::EscapeOnlyChar),
|
||||
'\r' => Err(EscapeError::BareCarriageReturn),
|
||||
c => Self::char2unit(c),
|
||||
};
|
||||
let end = src.len() - chars.as_str().len();
|
||||
callback(start..end, res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Interpret a non-nul ASCII escape
|
||||
///
|
||||
/// Parses the character of an ASCII escape (except nul) without the leading backslash.
|
||||
#[inline] // single use in Unescape::unescape_1
|
||||
fn simple_escape(c: char) -> Result<NonZeroU8, char> {
|
||||
// Previous character was '\\', unescape what follows.
|
||||
Ok(NonZeroU8::new(match c {
|
||||
'"' => b'"',
|
||||
'n' => b'\n',
|
||||
'r' => b'\r',
|
||||
't' => b'\t',
|
||||
'\\' => b'\\',
|
||||
'\'' => b'\'',
|
||||
_ => Err(c)?,
|
||||
})
|
||||
.unwrap())
|
||||
}
|
||||
|
||||
/// Interpret a hexadecimal escape
|
||||
///
|
||||
/// Parses the two hexadecimal characters of a hexadecimal escape without the leading r"\x".
|
||||
#[inline] // single use in Unescape::unescape_1
|
||||
fn hex_escape(chars: &mut impl Iterator<Item = char>) -> Result<u8, EscapeError> {
|
||||
let hi = chars.next().ok_or(EscapeError::TooShortHexEscape)?;
|
||||
let hi = hi.to_digit(16).ok_or(EscapeError::InvalidCharInHexEscape)?;
|
||||
|
||||
let lo = chars.next().ok_or(EscapeError::TooShortHexEscape)?;
|
||||
let lo = lo.to_digit(16).ok_or(EscapeError::InvalidCharInHexEscape)?;
|
||||
|
||||
Ok((hi * 16 + lo) as u8)
|
||||
}
|
||||
|
||||
/// Interpret a unicode escape
|
||||
///
|
||||
/// Parse the braces with hexadecimal characters (and underscores) part of a unicode escape.
|
||||
/// This r"{...}" normally comes after r"\u" and cannot start with an underscore.
|
||||
#[inline] // single use in Unescape::unescape_1
|
||||
fn unicode_escape(chars: &mut impl Iterator<Item = char>) -> Result<u32, EscapeError> {
|
||||
if chars.next() != Some('{') {
|
||||
return Err(EscapeError::NoBraceInUnicodeEscape);
|
||||
}
|
||||
|
||||
// First character must be a hexadecimal digit.
|
||||
let mut value: u32 = match chars.next().ok_or(EscapeError::UnclosedUnicodeEscape)? {
|
||||
'_' => return Err(EscapeError::LeadingUnderscoreUnicodeEscape),
|
||||
'}' => return Err(EscapeError::EmptyUnicodeEscape),
|
||||
c => c
|
||||
.to_digit(16)
|
||||
.ok_or(EscapeError::InvalidCharInUnicodeEscape)?,
|
||||
};
|
||||
|
||||
// First character is valid, now parse the rest of the number
|
||||
// and closing brace.
|
||||
let mut n_digits = 1;
|
||||
loop {
|
||||
match chars.next() {
|
||||
None => return Err(EscapeError::UnclosedUnicodeEscape),
|
||||
Some('_') => continue,
|
||||
Some('}') => {
|
||||
// Incorrect syntax has higher priority for error reporting
|
||||
// than unallowed value for a literal.
|
||||
return if n_digits > 6 {
|
||||
Err(EscapeError::OverlongUnicodeEscape)
|
||||
} else {
|
||||
Ok(value)
|
||||
};
|
||||
}
|
||||
Some(c) => {
|
||||
let digit: u32 = c
|
||||
.to_digit(16)
|
||||
.ok_or(EscapeError::InvalidCharInUnicodeEscape)?;
|
||||
n_digits += 1;
|
||||
if n_digits > 6 {
|
||||
// Stop updating value since we're sure that it's incorrect already.
|
||||
continue;
|
||||
}
|
||||
value = value * 16 + digit;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Interpret a string continuation escape (https://doc.rust-lang.org/reference/expressions/literal-expr.html#string-continuation-escapes)
|
||||
///
|
||||
/// Skip ASCII whitespace, except for the formfeed character
|
||||
/// (see [this issue](https://github.com/rust-lang/rust/issues/136600)).
|
||||
/// Warns on unescaped newline and following non-ASCII whitespace.
|
||||
#[inline] // single use in Unescape::unescape
|
||||
fn skip_ascii_whitespace(
|
||||
chars: &mut Chars<'_>,
|
||||
start: usize,
|
||||
mut callback: impl FnMut(Range<usize>, EscapeError),
|
||||
) {
|
||||
let rest = chars.as_str();
|
||||
let first_non_space = rest
|
||||
.bytes()
|
||||
.position(|b| b != b' ' && b != b'\t' && b != b'\n' && b != b'\r')
|
||||
.unwrap_or(rest.len());
|
||||
let (space, rest) = rest.split_at(first_non_space);
|
||||
// backslash newline adds 2 bytes
|
||||
let end = start + 2 + first_non_space;
|
||||
if space.contains('\n') {
|
||||
callback(start..end, EscapeError::MultipleSkippedLinesWarning);
|
||||
}
|
||||
*chars = rest.chars();
|
||||
if let Some(c) = chars.clone().next() {
|
||||
if c.is_whitespace() {
|
||||
// for error reporting, include the character that was not skipped in the span
|
||||
callback(
|
||||
start..end + c.len_utf8(),
|
||||
EscapeError::UnskippedWhitespaceWarning,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Unescape for str {
|
||||
type Unit = char;
|
||||
|
||||
const ZERO_RESULT: Result<Self::Unit, EscapeError> = Ok('\0');
|
||||
|
||||
#[inline]
|
||||
fn nonzero_byte2unit(b: NonZeroU8) -> Self::Unit {
|
||||
b.get().into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn char2unit(c: char) -> Result<Self::Unit, EscapeError> {
|
||||
Ok(c)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn hex2unit(b: u8) -> Result<Self::Unit, EscapeError> {
|
||||
if b.is_ascii() {
|
||||
Ok(b as char)
|
||||
} else {
|
||||
Err(EscapeError::OutOfRangeHexEscape)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn unicode2unit(r: Result<char, EscapeError>) -> Result<Self::Unit, EscapeError> {
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
impl Unescape for [u8] {
|
||||
type Unit = u8;
|
||||
|
||||
const ZERO_RESULT: Result<Self::Unit, EscapeError> = Ok(b'\0');
|
||||
|
||||
#[inline]
|
||||
fn nonzero_byte2unit(b: NonZeroU8) -> Self::Unit {
|
||||
b.get()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn char2unit(c: char) -> Result<Self::Unit, EscapeError> {
|
||||
char2byte(c)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn hex2unit(b: u8) -> Result<Self::Unit, EscapeError> {
|
||||
Ok(b)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn unicode2unit(_r: Result<char, EscapeError>) -> Result<Self::Unit, EscapeError> {
|
||||
Err(EscapeError::UnicodeEscapeInByte)
|
||||
}
|
||||
}
|
||||
|
||||
impl Unescape for CStr {
|
||||
type Unit = MixedUnit;
|
||||
|
||||
const ZERO_RESULT: Result<Self::Unit, EscapeError> = Err(EscapeError::NulInCStr);
|
||||
|
||||
#[inline]
|
||||
fn nonzero_byte2unit(b: NonZeroU8) -> Self::Unit {
|
||||
b.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn char2unit(c: char) -> Result<Self::Unit, EscapeError> {
|
||||
c.try_into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn hex2unit(byte: u8) -> Result<Self::Unit, EscapeError> {
|
||||
byte.try_into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn unicode2unit(r: Result<char, EscapeError>) -> Result<Self::Unit, EscapeError> {
|
||||
Self::char2unit(r?)
|
||||
}
|
||||
}
|
||||
|
||||
/// Enum of the different kinds of literal
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Mode {
|
||||
/// `'a'`
|
||||
Char,
|
||||
|
||||
/// `b'a'`
|
||||
Byte,
|
||||
|
||||
/// `"hello"`
|
||||
Str,
|
||||
/// `r"hello"`
|
||||
RawStr,
|
||||
|
||||
/// `b"hello"`
|
||||
ByteStr,
|
||||
/// `br"hello"`
|
||||
RawByteStr,
|
||||
|
||||
/// `c"hello"`
|
||||
CStr,
|
||||
/// `cr"hello"`
|
||||
RawCStr,
|
||||
}
|
||||
|
||||
impl Mode {
|
||||
pub fn in_double_quotes(self) -> bool {
|
||||
match self {
|
||||
Mode::Str
|
||||
| Mode::RawStr
|
||||
| Mode::ByteStr
|
||||
| Mode::RawByteStr
|
||||
| Mode::CStr
|
||||
| Mode::RawCStr => true,
|
||||
Mode::Char | Mode::Byte => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prefix_noraw(self) -> &'static str {
|
||||
match self {
|
||||
Mode::Char | Mode::Str | Mode::RawStr => "",
|
||||
Mode::Byte | Mode::ByteStr | Mode::RawByteStr => "b",
|
||||
Mode::CStr | Mode::RawCStr => "c",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check a literal only for errors
|
||||
///
|
||||
/// Takes the contents of a literal (without quotes)
|
||||
/// and produces a sequence of only errors,
|
||||
/// which are returned by invoking `error_callback`.
|
||||
///
|
||||
/// NB Does not produce any output other than errors
|
||||
pub fn check_for_errors(
|
||||
src: &str,
|
||||
mode: Mode,
|
||||
mut error_callback: impl FnMut(Range<usize>, EscapeError),
|
||||
) {
|
||||
match mode {
|
||||
Mode::Char => {
|
||||
let mut chars = src.chars();
|
||||
if let Err(e) = str::unescape_single(&mut chars) {
|
||||
error_callback(0..(src.len() - chars.as_str().len()), e);
|
||||
}
|
||||
}
|
||||
Mode::Byte => {
|
||||
let mut chars = src.chars();
|
||||
if let Err(e) = <[u8]>::unescape_single(&mut chars) {
|
||||
error_callback(0..(src.len() - chars.as_str().len()), e);
|
||||
}
|
||||
}
|
||||
Mode::Str => unescape_str(src, |range, res| {
|
||||
if let Err(e) = res {
|
||||
error_callback(range, e);
|
||||
}
|
||||
}),
|
||||
Mode::ByteStr => unescape_byte_str(src, |range, res| {
|
||||
if let Err(e) = res {
|
||||
error_callback(range, e);
|
||||
}
|
||||
}),
|
||||
Mode::CStr => unescape_c_str(src, |range, res| {
|
||||
if let Err(e) = res {
|
||||
error_callback(range, e);
|
||||
}
|
||||
}),
|
||||
Mode::RawStr => check_raw_str(src, |range, res| {
|
||||
if let Err(e) = res {
|
||||
error_callback(range, e);
|
||||
}
|
||||
}),
|
||||
Mode::RawByteStr => check_raw_byte_str(src, |range, res| {
|
||||
if let Err(e) = res {
|
||||
error_callback(range, e);
|
||||
}
|
||||
}),
|
||||
Mode::RawCStr => check_raw_c_str(src, |range, res| {
|
||||
if let Err(e) = res {
|
||||
error_callback(range, e);
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
+52
-67
@@ -2,13 +2,23 @@ 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};
|
||||
#[cfg(all(span_locations, not(proc_macro_span_file)))]
|
||||
use alloc::borrow::ToOwned as _;
|
||||
use alloc::string::{String, ToString as _};
|
||||
use alloc::vec::Vec;
|
||||
use core::ffi::CStr;
|
||||
use core::fmt::{self, Debug, Display};
|
||||
#[cfg(span_locations)]
|
||||
use core::ops::Range;
|
||||
use core::ops::RangeBounds;
|
||||
use std::ffi::CStr;
|
||||
#[cfg(super_unstable)]
|
||||
#[cfg(span_locations)]
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -182,13 +192,13 @@ impl From<TokenTree> for TokenStream {
|
||||
}
|
||||
|
||||
impl FromIterator<TokenTree> for TokenStream {
|
||||
fn from_iter<I: IntoIterator<Item = TokenTree>>(trees: I) -> Self {
|
||||
fn from_iter<I: IntoIterator<Item = TokenTree>>(tokens: I) -> Self {
|
||||
if inside_proc_macro() {
|
||||
TokenStream::Compiler(DeferredTokenStream::new(
|
||||
trees.into_iter().map(into_compiler_token).collect(),
|
||||
tokens.into_iter().map(into_compiler_token).collect(),
|
||||
))
|
||||
} else {
|
||||
TokenStream::Fallback(trees.into_iter().collect())
|
||||
TokenStream::Fallback(tokens.into_iter().collect())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -218,15 +228,15 @@ impl FromIterator<TokenStream> for TokenStream {
|
||||
}
|
||||
|
||||
impl Extend<TokenTree> for TokenStream {
|
||||
fn extend<I: IntoIterator<Item = TokenTree>>(&mut self, stream: I) {
|
||||
fn extend<I: IntoIterator<Item = TokenTree>>(&mut self, tokens: I) {
|
||||
match self {
|
||||
TokenStream::Compiler(tts) => {
|
||||
// Here is the reason for DeferredTokenStream.
|
||||
for token in stream {
|
||||
for token in tokens {
|
||||
tts.extra.push(into_compiler_token(token));
|
||||
}
|
||||
}
|
||||
TokenStream::Fallback(tts) => tts.extend(stream),
|
||||
TokenStream::Fallback(tts) => tts.extend(tokens),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -360,45 +370,6 @@ impl Iterator for TokenTreeIter {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg(super_unstable)]
|
||||
pub(crate) enum SourceFile {
|
||||
Compiler(proc_macro::SourceFile),
|
||||
Fallback(fallback::SourceFile),
|
||||
}
|
||||
|
||||
#[cfg(super_unstable)]
|
||||
impl SourceFile {
|
||||
fn nightly(sf: proc_macro::SourceFile) -> Self {
|
||||
SourceFile::Compiler(sf)
|
||||
}
|
||||
|
||||
/// Get the path to this source file as a string.
|
||||
pub(crate) fn path(&self) -> PathBuf {
|
||||
match self {
|
||||
SourceFile::Compiler(a) => a.path(),
|
||||
SourceFile::Fallback(a) => a.path(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_real(&self) -> bool {
|
||||
match self {
|
||||
SourceFile::Compiler(a) => a.is_real(),
|
||||
SourceFile::Fallback(a) => a.is_real(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(super_unstable)]
|
||||
impl Debug for SourceFile {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
SourceFile::Compiler(a) => Debug::fmt(a, f),
|
||||
SourceFile::Fallback(a) => Debug::fmt(a, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) enum Span {
|
||||
Compiler(proc_macro::Span),
|
||||
@@ -456,19 +427,11 @@ impl Span {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(super_unstable)]
|
||||
pub(crate) fn source_file(&self) -> SourceFile {
|
||||
match self {
|
||||
Span::Compiler(s) => SourceFile::nightly(s.source_file()),
|
||||
Span::Fallback(s) => SourceFile::Fallback(s.source_file()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(span_locations)]
|
||||
pub(crate) fn byte_range(&self) -> Range<usize> {
|
||||
match self {
|
||||
#[cfg(proc_macro_span)]
|
||||
Span::Compiler(s) => s.byte_range(),
|
||||
Span::Compiler(s) => proc_macro_span::byte_range(s),
|
||||
#[cfg(not(proc_macro_span))]
|
||||
Span::Compiler(_) => 0..0,
|
||||
Span::Fallback(s) => s.byte_range(),
|
||||
@@ -478,12 +441,12 @@ impl Span {
|
||||
#[cfg(span_locations)]
|
||||
pub(crate) fn start(&self) -> LineColumn {
|
||||
match self {
|
||||
#[cfg(proc_macro_span)]
|
||||
#[cfg(proc_macro_span_location)]
|
||||
Span::Compiler(s) => LineColumn {
|
||||
line: s.line(),
|
||||
column: s.column().saturating_sub(1),
|
||||
line: proc_macro_span_location::line(s),
|
||||
column: proc_macro_span_location::column(s).saturating_sub(1),
|
||||
},
|
||||
#[cfg(not(proc_macro_span))]
|
||||
#[cfg(not(proc_macro_span_location))]
|
||||
Span::Compiler(_) => LineColumn { line: 0, column: 0 },
|
||||
Span::Fallback(s) => s.start(),
|
||||
}
|
||||
@@ -492,24 +455,46 @@ impl Span {
|
||||
#[cfg(span_locations)]
|
||||
pub(crate) fn end(&self) -> LineColumn {
|
||||
match self {
|
||||
#[cfg(proc_macro_span)]
|
||||
#[cfg(proc_macro_span_location)]
|
||||
Span::Compiler(s) => {
|
||||
let end = s.end();
|
||||
let end = proc_macro_span_location::end(s);
|
||||
LineColumn {
|
||||
line: end.line(),
|
||||
column: end.column().saturating_sub(1),
|
||||
line: proc_macro_span_location::line(&end),
|
||||
column: proc_macro_span_location::column(&end).saturating_sub(1),
|
||||
}
|
||||
}
|
||||
#[cfg(not(proc_macro_span))]
|
||||
#[cfg(not(proc_macro_span_location))]
|
||||
Span::Compiler(_) => LineColumn { line: 0, column: 0 },
|
||||
Span::Fallback(s) => s.end(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(span_locations)]
|
||||
pub(crate) fn file(&self) -> String {
|
||||
match self {
|
||||
#[cfg(proc_macro_span_file)]
|
||||
Span::Compiler(s) => proc_macro_span_file::file(s),
|
||||
#[cfg(not(proc_macro_span_file))]
|
||||
Span::Compiler(_) => "<token stream>".to_owned(),
|
||||
Span::Fallback(s) => s.file(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(span_locations)]
|
||||
pub(crate) fn local_file(&self) -> Option<PathBuf> {
|
||||
match self {
|
||||
#[cfg(proc_macro_span_file)]
|
||||
Span::Compiler(s) => proc_macro_span_file::local_file(s),
|
||||
#[cfg(not(proc_macro_span_file))]
|
||||
Span::Compiler(_) => None,
|
||||
Span::Fallback(s) => s.local_file(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn join(&self, other: Span) -> Option<Span> {
|
||||
let ret = match (self, other) {
|
||||
#[cfg(proc_macro_span)]
|
||||
(Span::Compiler(a), Span::Compiler(b)) => Span::Compiler(a.join(b)?),
|
||||
(Span::Compiler(a), Span::Compiler(b)) => Span::Compiler(proc_macro_span::join(a, b)?),
|
||||
(Span::Fallback(a), Span::Fallback(b)) => Span::Fallback(a.join(b)?),
|
||||
_ => return None,
|
||||
};
|
||||
@@ -952,7 +937,7 @@ impl Literal {
|
||||
pub(crate) fn subspan<R: RangeBounds<usize>>(&self, range: R) -> Option<Span> {
|
||||
match self {
|
||||
#[cfg(proc_macro_span)]
|
||||
Literal::Compiler(lit) => lit.subspan(range).map(Span::Compiler),
|
||||
Literal::Compiler(lit) => proc_macro_span::subspan(lit, range).map(Span::Compiler),
|
||||
#[cfg(not(proc_macro_span))]
|
||||
Literal::Compiler(_lit) => None,
|
||||
Literal::Fallback(lit) => lit.subspan(range).map(Span::Fallback),
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#![allow(clippy::assertions_on_result_states)]
|
||||
#![allow(clippy::assertions_on_result_states, clippy::uninlined_format_args)]
|
||||
|
||||
use proc_macro2::{Delimiter, Literal, Spacing, TokenStream, TokenTree};
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::assertions_on_constants, clippy::ignore_without_reason)]
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn make_sure_no_proc_macro() {
|
||||
|
||||
+3
-6
@@ -56,19 +56,17 @@ assert_impl!(TokenTree is not Send or Sync);
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
mod semver_exempt {
|
||||
use proc_macro2::{LineColumn, SourceFile};
|
||||
use proc_macro2::LineColumn;
|
||||
|
||||
assert_impl!(LineColumn is Send and Sync);
|
||||
|
||||
assert_impl!(SourceFile is not Send or Sync);
|
||||
}
|
||||
|
||||
mod unwind_safe {
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
use proc_macro2::LineColumn;
|
||||
use proc_macro2::{
|
||||
Delimiter, Group, Ident, LexError, Literal, Punct, Spacing, Span, TokenStream, TokenTree,
|
||||
};
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
use proc_macro2::{LineColumn, SourceFile};
|
||||
use std::panic::{RefUnwindSafe, UnwindSafe};
|
||||
|
||||
macro_rules! assert_unwind_safe {
|
||||
@@ -95,6 +93,5 @@ mod unwind_safe {
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
assert_unwind_safe! {
|
||||
LineColumn
|
||||
SourceFile
|
||||
}
|
||||
}
|
||||
|
||||
+211
-23
@@ -4,7 +4,8 @@
|
||||
clippy::needless_pass_by_value,
|
||||
clippy::needless_raw_string_hashes,
|
||||
clippy::non_ascii_literal,
|
||||
clippy::octal_escapes
|
||||
clippy::octal_escapes,
|
||||
clippy::uninlined_format_args
|
||||
)]
|
||||
|
||||
use proc_macro2::{Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
|
||||
@@ -145,6 +146,30 @@ fn literal_raw_string() {
|
||||
.unwrap_err();
|
||||
}
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[test]
|
||||
fn literal_string_value() {
|
||||
for string in ["", "...", "...\t...", "...\\...", "...\0...", "...\u{1}..."] {
|
||||
assert_eq!(string, Literal::string(string).str_value().unwrap());
|
||||
assert_eq!(
|
||||
string,
|
||||
format!("r\"{string}\"")
|
||||
.parse::<Literal>()
|
||||
.unwrap()
|
||||
.str_value()
|
||||
.unwrap(),
|
||||
);
|
||||
assert_eq!(
|
||||
string,
|
||||
format!("r##\"{string}\"##")
|
||||
.parse::<Literal>()
|
||||
.unwrap()
|
||||
.str_value()
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn literal_byte_character() {
|
||||
#[track_caller]
|
||||
@@ -190,6 +215,42 @@ fn literal_byte_string() {
|
||||
"br\"\u{a0}\"".parse::<TokenStream>().unwrap_err();
|
||||
}
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[test]
|
||||
fn literal_byte_string_value() {
|
||||
for bytestr in [
|
||||
&b""[..],
|
||||
b"...",
|
||||
b"...\t...",
|
||||
b"...\\...",
|
||||
b"...\0...",
|
||||
b"...\xF0...",
|
||||
] {
|
||||
assert_eq!(
|
||||
bytestr,
|
||||
Literal::byte_string(bytestr).byte_str_value().unwrap(),
|
||||
);
|
||||
if let Ok(string) = str::from_utf8(bytestr) {
|
||||
assert_eq!(
|
||||
bytestr,
|
||||
format!("br\"{string}\"")
|
||||
.parse::<Literal>()
|
||||
.unwrap()
|
||||
.byte_str_value()
|
||||
.unwrap(),
|
||||
);
|
||||
assert_eq!(
|
||||
bytestr,
|
||||
format!("br##\"{string}\"##")
|
||||
.parse::<Literal>()
|
||||
.unwrap()
|
||||
.byte_str_value()
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn literal_c_string() {
|
||||
#[track_caller]
|
||||
@@ -261,6 +322,42 @@ fn literal_c_string() {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[test]
|
||||
fn literal_c_string_value() {
|
||||
for cstr in [
|
||||
c"",
|
||||
c"...",
|
||||
c"...\t...",
|
||||
c"...\\...",
|
||||
c"...\u{1}...",
|
||||
c"...\xF0...",
|
||||
] {
|
||||
assert_eq!(
|
||||
cstr.to_bytes_with_nul(),
|
||||
Literal::c_string(cstr).cstr_value().unwrap(),
|
||||
);
|
||||
if let Ok(string) = cstr.to_str() {
|
||||
assert_eq!(
|
||||
cstr.to_bytes_with_nul(),
|
||||
format!("cr\"{string}\"")
|
||||
.parse::<Literal>()
|
||||
.unwrap()
|
||||
.cstr_value()
|
||||
.unwrap(),
|
||||
);
|
||||
assert_eq!(
|
||||
cstr.to_bytes_with_nul(),
|
||||
format!("cr##\"{string}\"##")
|
||||
.parse::<Literal>()
|
||||
.unwrap()
|
||||
.cstr_value()
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn literal_character() {
|
||||
#[track_caller]
|
||||
@@ -453,6 +550,81 @@ fn source_text() {
|
||||
assert_eq!("a", second.span().source_text().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lifetimes() {
|
||||
let mut tokens = "'a 'static 'struct 'r#gen 'r#prefix#lifetime"
|
||||
.parse::<TokenStream>()
|
||||
.unwrap()
|
||||
.into_iter();
|
||||
assert!(match tokens.next() {
|
||||
Some(TokenTree::Punct(punct)) => {
|
||||
punct.as_char() == '\'' && punct.spacing() == Spacing::Joint
|
||||
}
|
||||
_ => false,
|
||||
});
|
||||
assert!(match tokens.next() {
|
||||
Some(TokenTree::Ident(ident)) => ident == "a",
|
||||
_ => false,
|
||||
});
|
||||
assert!(match tokens.next() {
|
||||
Some(TokenTree::Punct(punct)) => {
|
||||
punct.as_char() == '\'' && punct.spacing() == Spacing::Joint
|
||||
}
|
||||
_ => false,
|
||||
});
|
||||
assert!(match tokens.next() {
|
||||
Some(TokenTree::Ident(ident)) => ident == "static",
|
||||
_ => false,
|
||||
});
|
||||
assert!(match tokens.next() {
|
||||
Some(TokenTree::Punct(punct)) => {
|
||||
punct.as_char() == '\'' && punct.spacing() == Spacing::Joint
|
||||
}
|
||||
_ => false,
|
||||
});
|
||||
assert!(match tokens.next() {
|
||||
Some(TokenTree::Ident(ident)) => ident == "struct",
|
||||
_ => false,
|
||||
});
|
||||
assert!(match tokens.next() {
|
||||
Some(TokenTree::Punct(punct)) => {
|
||||
punct.as_char() == '\'' && punct.spacing() == Spacing::Joint
|
||||
}
|
||||
_ => false,
|
||||
});
|
||||
assert!(match tokens.next() {
|
||||
Some(TokenTree::Ident(ident)) => ident == "r#gen",
|
||||
_ => false,
|
||||
});
|
||||
assert!(match tokens.next() {
|
||||
Some(TokenTree::Punct(punct)) => {
|
||||
punct.as_char() == '\'' && punct.spacing() == Spacing::Joint
|
||||
}
|
||||
_ => false,
|
||||
});
|
||||
assert!(match tokens.next() {
|
||||
Some(TokenTree::Ident(ident)) => ident == "r#prefix",
|
||||
_ => false,
|
||||
});
|
||||
assert!(match tokens.next() {
|
||||
Some(TokenTree::Punct(punct)) => {
|
||||
punct.as_char() == '#' && punct.spacing() == Spacing::Alone
|
||||
}
|
||||
_ => false,
|
||||
});
|
||||
assert!(match tokens.next() {
|
||||
Some(TokenTree::Ident(ident)) => ident == "lifetime",
|
||||
_ => false,
|
||||
});
|
||||
|
||||
"' a".parse::<TokenStream>().unwrap_err();
|
||||
"' r#gen".parse::<TokenStream>().unwrap_err();
|
||||
"' prefix#lifetime".parse::<TokenStream>().unwrap_err();
|
||||
"'prefix#lifetime".parse::<TokenStream>().unwrap_err();
|
||||
"'aa'bb".parse::<TokenStream>().unwrap_err();
|
||||
"'r#gen'a".parse::<TokenStream>().unwrap_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn roundtrip() {
|
||||
fn roundtrip(p: &str) {
|
||||
@@ -549,9 +721,8 @@ fn default_span() {
|
||||
let end = Span::call_site().end();
|
||||
assert_eq!(end.line, 1);
|
||||
assert_eq!(end.column, 0);
|
||||
let source_file = Span::call_site().source_file();
|
||||
assert_eq!(source_file.path().to_string_lossy(), "<unspecified>");
|
||||
assert!(!source_file.is_real());
|
||||
assert_eq!(Span::call_site().file(), "<unspecified>");
|
||||
assert!(Span::call_site().local_file().is_none());
|
||||
}
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
@@ -568,11 +739,8 @@ fn span_join() {
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert!(source1[0].span().source_file() != source2[0].span().source_file());
|
||||
assert_eq!(
|
||||
source1[0].span().source_file(),
|
||||
source1[1].span().source_file()
|
||||
);
|
||||
assert!(source1[0].span().file() != source2[0].span().file());
|
||||
assert_eq!(source1[0].span().file(), source1[1].span().file());
|
||||
|
||||
let joined1 = source1[0].span().join(source1[1].span());
|
||||
let joined2 = source1[0].span().join(source2[0].span());
|
||||
@@ -586,10 +754,7 @@ fn span_join() {
|
||||
assert_eq!(end.line, 2);
|
||||
assert_eq!(end.column, 3);
|
||||
|
||||
assert_eq!(
|
||||
joined1.unwrap().source_file(),
|
||||
source1[0].span().source_file()
|
||||
);
|
||||
assert_eq!(joined1.unwrap().file(), source1[0].span().file());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -619,9 +784,8 @@ fn joint_last_token() {
|
||||
|
||||
let joint_punct = Punct::new(':', Spacing::Joint);
|
||||
let stream = TokenStream::from(TokenTree::Punct(joint_punct));
|
||||
let punct = match stream.into_iter().next().unwrap() {
|
||||
TokenTree::Punct(punct) => punct,
|
||||
_ => unreachable!(),
|
||||
let TokenTree::Punct(punct) = stream.into_iter().next().unwrap() else {
|
||||
unreachable!();
|
||||
};
|
||||
assert_eq!(punct.spacing(), Spacing::Joint);
|
||||
}
|
||||
@@ -636,17 +800,41 @@ fn raw_identifier() {
|
||||
assert!(tts.next().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_display_ident() {
|
||||
let ident = Ident::new("proc_macro", Span::call_site());
|
||||
assert_eq!(format!("{ident}"), "proc_macro");
|
||||
assert_eq!(format!("{ident:-^14}"), "proc_macro");
|
||||
|
||||
let ident = Ident::new_raw("proc_macro", Span::call_site());
|
||||
assert_eq!(format!("{ident}"), "r#proc_macro");
|
||||
assert_eq!(format!("{ident:-^14}"), "r#proc_macro");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug_ident() {
|
||||
let ident = Ident::new("proc_macro", Span::call_site());
|
||||
|
||||
#[cfg(not(span_locations))]
|
||||
let expected = "Ident(proc_macro)";
|
||||
|
||||
#[cfg(span_locations)]
|
||||
let expected = "Ident { sym: proc_macro }";
|
||||
|
||||
let expected = if cfg!(span_locations) {
|
||||
"Ident { sym: proc_macro }"
|
||||
} else {
|
||||
"Ident(proc_macro)"
|
||||
};
|
||||
assert_eq!(expected, format!("{:?}", ident));
|
||||
|
||||
let ident = Ident::new_raw("proc_macro", Span::call_site());
|
||||
let expected = if cfg!(span_locations) {
|
||||
"Ident { sym: r#proc_macro }"
|
||||
} else {
|
||||
"Ident(r#proc_macro)"
|
||||
};
|
||||
assert_eq!(expected, format!("{:?}", ident));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_display_tokenstream() {
|
||||
let tts = TokenStream::from_str("[a + 1]").unwrap();
|
||||
assert_eq!(format!("{tts}"), "[a + 1]");
|
||||
assert_eq!(format!("{tts:-^5}"), "[a + 1]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -4,7 +4,6 @@ extern crate proc_macro;
|
||||
|
||||
use std::mem;
|
||||
|
||||
#[rustversion::attr(before(1.64), ignore = "requires Rust 1.64+")]
|
||||
#[cfg_attr(not(target_pointer_width = "64"), ignore = "only applicable to 64-bit")]
|
||||
#[cfg_attr(randomize_layout, ignore = "disabled due to randomized layout")]
|
||||
#[test]
|
||||
@@ -64,7 +63,6 @@ fn test_proc_macro2_wrapper_size_without_locations() {
|
||||
assert_eq!(mem::size_of::<proc_macro2::TokenStream>(), 32);
|
||||
}
|
||||
|
||||
#[rustversion::attr(before(1.65), ignore = "requires Rust 1.65+")]
|
||||
#[cfg_attr(not(target_pointer_width = "64"), ignore = "only applicable to 64-bit")]
|
||||
#[cfg_attr(randomize_layout, ignore = "disabled due to randomized layout")]
|
||||
#[cfg_attr(not(wrap_proc_macro), ignore = "fallback mode")]
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
name = "proc-macro2-ui-test"
|
||||
version = "0.0.0"
|
||||
authors = ["David Tolnay <dtolnay@gmail.com>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[[test]]
|
||||
@@ -12,4 +12,4 @@ path = "compiletest.rs"
|
||||
[dev-dependencies]
|
||||
proc-macro2 = { path = "../.." }
|
||||
rustversion = "1.0"
|
||||
trybuild = { version = "1.0.49", features = ["diff"] }
|
||||
trybuild = { version = "1.0.108", features = ["diff"] }
|
||||
|
||||
@@ -31,7 +31,7 @@ error[E0277]: `Rc<()>` cannot be sent between threads safely
|
||||
note: required because it appears within the type `PhantomData<Rc<()>>`
|
||||
--> $RUST/core/src/marker.rs
|
||||
|
|
||||
| pub struct PhantomData<T: ?Sized>;
|
||||
| pub struct PhantomData<T: PointeeSized>;
|
||||
| ^^^^^^^^^^^
|
||||
note: required because it appears within the type `proc_macro2::marker::ProcMacroAutoTraits`
|
||||
--> $WORKSPACE/src/marker.rs
|
||||
|
||||
Reference in New Issue
Block a user