about summary refs log tree commit diff
path: root/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2024-04-21 14:40:10 +0200
committerLukas Wirth <lukastw97@gmail.com>2024-04-21 16:26:55 +0200
commit7c3926345004c4d679c835760dbea5438a4f9d7b (patch)
tree600a079cdebc5cdd91b5ed272f796e472401e5af /src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test
parent55d9a533b309119c8acd13061581b43ae8840823 (diff)
downloadrust-7c3926345004c4d679c835760dbea5438a4f9d7b.tar.gz
rust-7c3926345004c4d679c835760dbea5438a4f9d7b.zip
Allow rust files to be used linkedProjects
Diffstat (limited to 'src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test')
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/Cargo.toml13
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs126
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/.gitignore2
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/Cargo.toml16
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/build.rs5
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs139
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/src/lib.rs6
7 files changed, 307 insertions, 0 deletions
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/Cargo.toml
new file mode 100644
index 00000000000..7c6a1ba46b5
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "proc-macro-test"
+version = "0.0.0"
+publish = false
+
+edition = "2021"
+license = "MIT OR Apache-2.0"
+
+[lib]
+doctest = false
+
+[build-dependencies]
+cargo_metadata = "0.18.1"
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs
new file mode 100644
index 00000000000..6a0ae362d88
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs
@@ -0,0 +1,126 @@
+//! This will build the proc macro in `imp`, and copy the resulting dylib artifact into the
+//! `OUT_DIR`.
+//!
+//! `proc-macro-test` itself contains only a path to that artifact.
+//!
+//! The `PROC_MACRO_TEST_TOOLCHAIN` environment variable can be exported to use
+//! a specific rustup toolchain: this allows testing against older ABIs (e.g.
+//! 1.58) and future ABIs (stage1, nightly)
+
+use std::{
+    env, fs,
+    path::{Path, PathBuf},
+    process::Command,
+};
+
+use cargo_metadata::Message;
+
+fn main() {
+    println!("cargo:rerun-if-changed=imp");
+
+    let cargo = env::var_os("CARGO").unwrap_or_else(|| "cargo".into());
+
+    let has_features = env::var_os("RUSTC_BOOTSTRAP").is_some()
+        || String::from_utf8(Command::new(&cargo).arg("--version").output().unwrap().stdout)
+            .unwrap()
+            .contains("nightly");
+
+    let out_dir = env::var_os("OUT_DIR").unwrap();
+    let out_dir = Path::new(&out_dir);
+
+    if !has_features {
+        println!("proc-macro-test testing only works on nightly toolchains");
+        let info_path = out_dir.join("proc_macro_test_location.txt");
+        fs::File::create(info_path).unwrap();
+        return;
+    }
+
+    let name = "proc-macro-test-impl";
+    let version = "0.0.0";
+
+    let imp_dir = std::env::current_dir().unwrap().join("imp");
+
+    let staging_dir = out_dir.join("proc-macro-test-imp-staging");
+    // this'll error out if the staging dir didn't previously exist. using
+    // `std::fs::exists` would suffer from TOCTOU so just do our best to
+    // wipe it and ignore errors.
+    let _ = std::fs::remove_dir_all(&staging_dir);
+
+    println!("Creating {}", staging_dir.display());
+    std::fs::create_dir_all(&staging_dir).unwrap();
+
+    let src_dir = staging_dir.join("src");
+    println!("Creating {}", src_dir.display());
+    std::fs::create_dir_all(src_dir).unwrap();
+
+    for item_els in [&["Cargo.toml"][..], &["build.rs"][..], &["src", "lib.rs"]] {
+        let mut src = imp_dir.clone();
+        let mut dst = staging_dir.clone();
+        for el in item_els {
+            src.push(el);
+            dst.push(el);
+        }
+        println!("Copying {} to {}", src.display(), dst.display());
+        std::fs::copy(src, dst).unwrap();
+    }
+
+    let target_dir = out_dir.join("target");
+
+    let mut cmd = Command::new(&cargo);
+    cmd.current_dir(&staging_dir)
+        .args(["build", "-p", "proc-macro-test-impl", "--message-format", "json"])
+        // Explicit override the target directory to avoid using the same one which the parent
+        // cargo is using, or we'll deadlock.
+        // This can happen when `CARGO_TARGET_DIR` is set or global config forces all cargo
+        // instance to use the same target directory.
+        .arg("--target-dir")
+        .arg(&target_dir);
+
+    if let Ok(target) = std::env::var("TARGET") {
+        cmd.args(["--target", &target]);
+    }
+
+    println!("Running {cmd:?}");
+
+    let output = cmd.output().unwrap();
+    if !output.status.success() {
+        println!("proc-macro-test-impl failed to build");
+        println!("============ stdout ============");
+        println!("{}", String::from_utf8_lossy(&output.stdout));
+        println!("============ stderr ============");
+        println!("{}", String::from_utf8_lossy(&output.stderr));
+        panic!("proc-macro-test-impl failed to build");
+    }
+
+    // Old Package ID Spec
+    let repr = format!("{name} {version}");
+    // New Package Id Spec since rust-lang/cargo#13311
+    let pkgid = String::from_utf8(
+        Command::new(cargo)
+            .current_dir(&staging_dir)
+            .args(["pkgid", name])
+            .output()
+            .unwrap()
+            .stdout,
+    )
+    .unwrap();
+    let pkgid = pkgid.trim();
+
+    let mut artifact_path = None;
+    for message in Message::parse_stream(output.stdout.as_slice()) {
+        if let Message::CompilerArtifact(artifact) = message.unwrap() {
+            if artifact.target.kind.contains(&"proc-macro".to_string())
+                && (artifact.package_id.repr.starts_with(&repr)
+                    || artifact.package_id.repr == pkgid)
+            {
+                artifact_path = Some(PathBuf::from(&artifact.filenames[0]));
+            }
+        }
+    }
+
+    // This file is under `target_dir` and is already under `OUT_DIR`.
+    let artifact_path = artifact_path.expect("no dylib for proc-macro-test-impl found");
+
+    let info_path = out_dir.join("proc_macro_test_location.txt");
+    fs::write(info_path, artifact_path.to_str().unwrap()).unwrap();
+}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/.gitignore b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/.gitignore
new file mode 100644
index 00000000000..2c96eb1b651
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/.gitignore
@@ -0,0 +1,2 @@
+target/
+Cargo.lock
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/Cargo.toml
new file mode 100644
index 00000000000..fa189752b76
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "proc-macro-test-impl"
+version = "0.0.0"
+license = "MIT OR Apache-2.0"
+edition = "2021"
+publish = false
+
+[lib]
+doctest = false
+proc-macro = true
+
+[dependencies]
+# this crate should not have any dependencies, since it uses its own workspace,
+# and its own `Cargo.lock`
+
+[workspace]
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/build.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/build.rs
new file mode 100644
index 00000000000..07f914fece0
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/build.rs
@@ -0,0 +1,5 @@
+//! This teaches cargo about our cfg(rust_analyzer)
+
+fn main() {
+    println!("cargo:rustc-check-cfg=cfg(rust_analyzer)");
+}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs
new file mode 100644
index 00000000000..5f8530d08c4
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs
@@ -0,0 +1,139 @@
+//! Exports a few trivial procedural macros for testing.
+
+#![warn(rust_2018_idioms, unused_lifetimes)]
+#![feature(proc_macro_span, proc_macro_def_site)]
+#![allow(clippy::all)]
+
+use proc_macro::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree};
+
+#[proc_macro]
+pub fn fn_like_noop(args: TokenStream) -> TokenStream {
+    args
+}
+
+#[proc_macro]
+pub fn fn_like_panic(args: TokenStream) -> TokenStream {
+    panic!("fn_like_panic!({})", args);
+}
+
+#[proc_macro]
+pub fn fn_like_error(args: TokenStream) -> TokenStream {
+    format!("compile_error!(\"fn_like_error!({})\");", args).parse().unwrap()
+}
+
+#[proc_macro]
+pub fn fn_like_clone_tokens(args: TokenStream) -> TokenStream {
+    clone_stream(args)
+}
+
+#[proc_macro]
+pub fn fn_like_mk_literals(_args: TokenStream) -> TokenStream {
+    let trees: Vec<TokenTree> = vec![
+        TokenTree::from(Literal::byte_string(b"byte_string")),
+        TokenTree::from(Literal::character('c')),
+        TokenTree::from(Literal::string("string")),
+        // as of 2022-07-21, there's no method on `Literal` to build a raw
+        // string or a raw byte string
+        TokenTree::from(Literal::f64_suffixed(3.14)),
+        TokenTree::from(Literal::f64_unsuffixed(3.14)),
+        TokenTree::from(Literal::i64_suffixed(123)),
+        TokenTree::from(Literal::i64_unsuffixed(123)),
+    ];
+    TokenStream::from_iter(trees)
+}
+
+#[proc_macro]
+pub fn fn_like_mk_idents(_args: TokenStream) -> TokenStream {
+    let trees: Vec<TokenTree> = vec![
+        TokenTree::from(Ident::new("standard", Span::call_site())),
+        TokenTree::from(Ident::new_raw("raw", Span::call_site())),
+    ];
+    TokenStream::from_iter(trees)
+}
+
+#[proc_macro]
+pub fn fn_like_span_join(args: TokenStream) -> TokenStream {
+    let args = &mut args.into_iter();
+    let first = args.next().unwrap();
+    let second = args.next().unwrap();
+    TokenStream::from(TokenTree::from(Ident::new_raw(
+        "joined",
+        first.span().join(second.span()).unwrap(),
+    )))
+}
+
+#[proc_macro]
+pub fn fn_like_span_ops(args: TokenStream) -> TokenStream {
+    let args = &mut args.into_iter();
+    let mut first = args.next().unwrap();
+    first.set_span(Span::def_site());
+    let mut second = args.next().unwrap();
+    second.set_span(second.span().resolved_at(Span::def_site()));
+    let mut third = args.next().unwrap();
+    third.set_span(third.span().start());
+    TokenStream::from_iter(vec![first, second, third])
+}
+
+#[proc_macro_attribute]
+pub fn attr_noop(_args: TokenStream, item: TokenStream) -> TokenStream {
+    item
+}
+
+#[proc_macro_attribute]
+pub fn attr_panic(args: TokenStream, item: TokenStream) -> TokenStream {
+    panic!("#[attr_panic {}] {}", args, item);
+}
+
+#[proc_macro_attribute]
+pub fn attr_error(args: TokenStream, item: TokenStream) -> TokenStream {
+    format!("compile_error!(\"#[attr_error({})] {}\");", args, item).parse().unwrap()
+}
+
+#[proc_macro_derive(DeriveEmpty)]
+pub fn derive_empty(_item: TokenStream) -> TokenStream {
+    TokenStream::new()
+}
+
+#[proc_macro_derive(DerivePanic)]
+pub fn derive_panic(item: TokenStream) -> TokenStream {
+    panic!("#[derive(DerivePanic)] {}", item);
+}
+
+#[proc_macro_derive(DeriveError)]
+pub fn derive_error(item: TokenStream) -> TokenStream {
+    format!("compile_error!(\"#[derive(DeriveError)] {}\");", item).parse().unwrap()
+}
+
+fn clone_stream(ts: TokenStream) -> TokenStream {
+    ts.into_iter().map(clone_tree).collect()
+}
+
+fn clone_tree(t: TokenTree) -> TokenTree {
+    match t {
+        TokenTree::Group(orig) => {
+            let mut new = Group::new(orig.delimiter(), clone_stream(orig.stream()));
+            new.set_span(orig.span());
+            TokenTree::Group(new)
+        }
+        TokenTree::Ident(orig) => {
+            let s = orig.to_string();
+            if let Some(rest) = s.strip_prefix("r#") {
+                TokenTree::Ident(Ident::new_raw(rest, orig.span()))
+            } else {
+                TokenTree::Ident(Ident::new(&s, orig.span()))
+            }
+        }
+        TokenTree::Punct(orig) => {
+            let mut new = Punct::new(orig.as_char(), orig.spacing());
+            new.set_span(orig.span());
+            TokenTree::Punct(new)
+        }
+        TokenTree::Literal(orig) => {
+            // this goes through `literal_from_str` as of 2022-07-18, cf.
+            // https://github.com/rust-lang/rust/commit/b34c79f8f1ef4d0149ad4bf77e1759c07a9a01a8
+            let mut new: Literal = orig.to_string().parse().unwrap();
+            new.set_span(orig.span());
+            TokenTree::Literal(new)
+        }
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/src/lib.rs
new file mode 100644
index 00000000000..739c6ec6f44
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/src/lib.rs
@@ -0,0 +1,6 @@
+//! Exports a few trivial procedural macros for testing.
+
+#![warn(rust_2018_idioms, unused_lifetimes)]
+
+pub static PROC_MACRO_TEST_LOCATION: &str =
+    include_str!(concat!(env!("OUT_DIR"), "/proc_macro_test_location.txt"));