Merge branch 'main' into redsun82/rust-derive-macro-expansion

This commit is contained in:
Arthur Baars
2025-06-20 19:27:15 +02:00
31 changed files with 558 additions and 170 deletions

View File

@@ -62,6 +62,7 @@ pub struct Config {
pub qltest: bool,
pub qltest_cargo_check: bool,
pub qltest_dependencies: Vec<String>,
pub qltest_use_nightly: bool,
pub sysroot: Option<PathBuf>,
pub sysroot_src: Option<PathBuf>,
pub rustc_src: Option<PathBuf>,

View File

@@ -103,6 +103,7 @@ impl<'a> Extractor<'a> {
}
}
translator.emit_source_file(&ast);
translator.emit_truncated_diagnostics_message();
translator.trap.commit().unwrap_or_else(|err| {
error!(
"Failed to write trap file for: {}: {}",

View File

@@ -8,6 +8,9 @@ use std::path::Path;
use std::process::Command;
use tracing::info;
const EDITION: &str = "2021";
const NIGHTLY: &str = "nightly-2025-06-01";
fn dump_lib() -> anyhow::Result<()> {
let path_iterator = glob("*.rs").context("globbing test sources")?;
let paths = path_iterator
@@ -29,8 +32,11 @@ enum TestCargoManifest<'a> {
uses_proc_macro: bool,
uses_main: bool,
dependencies: &'a [String],
edition: &'a str,
},
Macro {
edition: &'a str,
},
Macro {},
}
impl TestCargoManifest<'_> {
@@ -56,16 +62,26 @@ fn dump_cargo_manifest(dependencies: &[String]) -> anyhow::Result<()> {
uses_proc_macro,
uses_main: fs::exists("main.rs").context("checking existence of main.rs")?,
dependencies,
edition: EDITION,
};
if uses_proc_macro {
TestCargoManifest::Workspace {}.dump("")?;
lib_manifest.dump(".lib")?;
TestCargoManifest::Macro {}.dump(".proc_macro")
TestCargoManifest::Macro { edition: EDITION }.dump(".proc_macro")
} else {
lib_manifest.dump("")
}
}
fn dump_nightly_toolchain() -> anyhow::Result<()> {
fs::write(
"rust-toolchain.toml",
format!("[toolchain]\nchannel = \"{NIGHTLY}\"\n"),
)
.context("writing rust-toolchain.toml")?;
Ok(())
}
fn set_sources(config: &mut Config) -> anyhow::Result<()> {
let path_iterator = glob("**/*.rs").context("globbing test sources")?;
config.inputs = path_iterator
@@ -79,6 +95,9 @@ pub(crate) fn prepare(config: &mut Config) -> anyhow::Result<()> {
dump_lib()?;
set_sources(config)?;
dump_cargo_manifest(&config.qltest_dependencies)?;
if config.qltest_use_nightly {
dump_nightly_toolchain()?;
}
if config.qltest_cargo_check {
let status = Command::new("cargo")
.env("RUSTFLAGS", "-Awarnings")

View File

@@ -11,7 +11,7 @@ members = [".lib", ".proc_macro"]
[package]
name = "test"
version = "0.0.1"
edition = "2021"
edition = "{{ edition }}"
[lib]
path = "{{#uses_proc_macro}}../{{/uses_proc_macro}}lib.rs"
{{#uses_main}}
@@ -32,7 +32,7 @@ proc_macro = { path = "../.proc_macro" }
[package]
name = "proc_macro"
version = "0.0.1"
edition = "2021"
edition = "{{ edition }}"
[lib]
path = "../proc_macro.rs"
proc_macro = true

View File

@@ -26,12 +26,38 @@ use ra_ap_syntax::{
macro_rules! pre_emit {
(Item, $self:ident, $node:ident) => {
if let Some(label) = $self.prepare_item_expansion($node) {
return Some(label);
return Some(label.into());
}
};
(AssocItem, $self:ident, $node:ident) => {
if let Some(label) = $self.prepare_item_expansion(&$node.clone().into()) {
return Some(label.into());
}
};
(ExternItem, $self:ident, $node:ident) => {
if let Some(label) = $self.prepare_item_expansion(&$node.clone().into()) {
return Some(label.into());
}
};
($($_:tt)*) => {};
}
// TODO: remove the mannually written Label conversions. These can be auto-generated by
// changing the base class of AssocItem from AstNode to Item
impl From<crate::trap::Label<generated::AssocItem>> for crate::trap::Label<generated::Item> {
fn from(value: crate::trap::Label<generated::AssocItem>) -> Self {
// SAFETY: this is safe because every concrete instance of `@assoc_item` is also an instance of `@item`
unsafe { Self::from_untyped(value.as_untyped()) }
}
}
// TODO: remove the mannually written Label conversions. These can be auto-generated by
// changing the base class of ExternItem from AstNode to Item
impl From<crate::trap::Label<generated::ExternItem>> for crate::trap::Label<generated::Item> {
fn from(value: crate::trap::Label<generated::ExternItem>) -> Self {
// SAFETY: this is safe because every concrete instance of `@extern_item` is also an instance of `@item`
unsafe { Self::from_untyped(value.as_untyped()) }
}
}
#[macro_export]
macro_rules! post_emit {
(MacroCall, $self:ident, $node:ident, $label:ident) => {
@@ -65,6 +91,18 @@ macro_rules! post_emit {
(Item, $self:ident, $node:ident, $label:ident) => {
$self.emit_item_expansion($node, $label);
};
(AssocItem, $self:ident, $node:ident, $label:ident) => {
$self.emit_item_expansion(
&$node.clone().into(),
From::<Label<generated::AssocItem>>::from($label),
);
};
(ExternItem, $self:ident, $node:ident, $label:ident) => {
$self.emit_item_expansion(
&$node.clone().into(),
From::<Label<generated::ExternItem>>::from($label),
);
};
// TODO canonical origin of other items
(PathExpr, $self:ident, $node:ident, $label:ident) => {
$self.extract_path_canonical_destination($node, $label.into());
@@ -126,11 +164,14 @@ pub struct Translator<'a> {
resolve_paths: bool,
source_kind: SourceKind,
macro_context_depth: usize,
diagnostic_count: usize,
}
const UNKNOWN_LOCATION: (LineCol, LineCol) =
(LineCol { line: 0, col: 0 }, LineCol { line: 0, col: 0 });
const DIAGNOSTIC_LIMIT_PER_FILE: usize = 100;
impl<'a> Translator<'a> {
pub fn new(
trap: TrapFile,
@@ -151,6 +192,7 @@ impl<'a> Translator<'a> {
resolve_paths: resolve_paths == ResolvePaths::Yes,
source_kind,
macro_context_depth: 0,
diagnostic_count: 0,
}
}
fn location(&self, range: TextRange) -> Option<(LineCol, LineCol)> {
@@ -237,6 +279,36 @@ impl<'a> Translator<'a> {
} else {
severity
};
if severity > DiagnosticSeverity::Debug {
self.diagnostic_count += 1;
if self.diagnostic_count > DIAGNOSTIC_LIMIT_PER_FILE {
return;
}
}
self.emit_diagnostic_unchecked(severity, tag, message, full_message, location);
}
pub fn emit_truncated_diagnostics_message(&mut self) {
if self.diagnostic_count > DIAGNOSTIC_LIMIT_PER_FILE {
let count = self.diagnostic_count - DIAGNOSTIC_LIMIT_PER_FILE;
self.emit_diagnostic_unchecked(
DiagnosticSeverity::Warning,
"diagnostics".to_owned(),
"Too many diagnostic messages".to_owned(),
format!(
"Too many diagnostic messages, {count} diagnostic messages were suppressed"
),
UNKNOWN_LOCATION,
);
}
}
fn emit_diagnostic_unchecked(
&mut self,
severity: DiagnosticSeverity,
tag: String,
message: String,
full_message: String,
location: (LineCol, LineCol),
) {
let (start, end) = location;
dispatch_to_tracing!(
severity,
@@ -716,10 +788,21 @@ impl<'a> Translator<'a> {
}
}
fn is_attribute_macro_target(&self, node: &ast::Item) -> bool {
// rust-analyzer considers as an `attr_macro_call` also a plain macro call, but we want to
// process that differently (in `extract_macro_call_expanded`)
!matches!(node, ast::Item::MacroCall(_))
&& self.semantics.is_some_and(|semantics| {
let file = semantics.hir_file_for(node.syntax());
let node = InFile::new(file, node);
semantics.is_attr_macro_call(node)
})
}
pub(crate) fn prepare_item_expansion(
&mut self,
node: &ast::Item,
) -> Option<Label<generated::Item>> {
) -> Option<Label<generated::MacroCall>> {
if self.source_kind == SourceKind::Library {
// if the item expands via an attribute macro, we want to only emit the expansion
if let Some(expanded) = self.emit_attribute_macro_expansion(node) {
@@ -736,13 +819,10 @@ impl<'a> Translator<'a> {
expanded.into(),
&mut self.trap.writer,
);
return Some(label.into());
return Some(label);
}
}
let semantics = self.semantics.as_ref()?;
let file = semantics.hir_file_for(node.syntax());
let node = InFile::new(file, node);
if semantics.is_attr_macro_call(node) {
if self.is_attribute_macro_target(node) {
self.macro_context_depth += 1;
}
None
@@ -785,10 +865,7 @@ impl<'a> Translator<'a> {
&mut self,
node: &ast::Item,
) -> Option<Label<generated::MacroItems>> {
let semantics = self.semantics?;
let file = semantics.hir_file_for(node.syntax());
let infile_node = InFile::new(file, node);
if !semantics.is_attr_macro_call(infile_node) {
if !self.is_attribute_macro_target(node) {
return None;
}
self.macro_context_depth -= 1;
@@ -796,7 +873,7 @@ impl<'a> Translator<'a> {
// only expand the outermost attribute macro
return None;
}
let expansion = semantics.expand_attr_macro(node)?;
let expansion = self.semantics?.expand_attr_macro(node)?;
self.process_item_macro_expansion(node, expansion)
}