Skip to content

Commit f48ecb6

Browse files
committed
Try to support pre and post-change metavars
1 parent 8753ca5 commit f48ecb6

21 files changed

+255
-110
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ nohash-hasher = "0.2.0"
110110
rayon = "1.8.0"
111111
rust-analyzer-salsa = "0.17.0-pre.4"
112112
rustc-hash = "1.1.0"
113+
semver = "1.0.14"
113114
serde = { version = "1.0.192", features = ["derive"] }
114115
serde_json = "1.0.108"
115116
smallvec = { version = "1.10.0", features = [

crates/base-db/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ la-arena.workspace = true
1616
rust-analyzer-salsa.workspace = true
1717
rustc-hash.workspace = true
1818
triomphe.workspace = true
19+
semver.workspace = true
1920

2021
# local deps
2122
cfg.workspace = true

crates/base-db/src/input.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use std::{fmt, mem, ops, str::FromStr};
1111
use cfg::CfgOptions;
1212
use la_arena::{Arena, Idx};
1313
use rustc_hash::{FxHashMap, FxHashSet};
14+
use semver::Version;
1415
use syntax::SmolStr;
1516
use triomphe::Arc;
1617
use vfs::{file_set::FileSet, AbsPathBuf, AnchoredPath, FileId, VfsPath};
@@ -258,7 +259,7 @@ impl ReleaseChannel {
258259

259260
pub fn from_str(str: &str) -> Option<Self> {
260261
Some(match str {
261-
"" => ReleaseChannel::Stable,
262+
"" | "stable" => ReleaseChannel::Stable,
262263
"nightly" => ReleaseChannel::Nightly,
263264
_ if str.starts_with("beta") => ReleaseChannel::Beta,
264265
_ => return None,
@@ -289,7 +290,7 @@ pub struct CrateData {
289290
// things. This info does need to be somewhat present though as to prevent deduplication from
290291
// happening across different workspaces with different layouts.
291292
pub target_layout: TargetLayoutLoadResult,
292-
pub channel: Option<ReleaseChannel>,
293+
pub toolchain: Option<Version>,
293294
}
294295

295296
impl CrateData {
@@ -346,6 +347,10 @@ impl CrateData {
346347

347348
slf_deps.eq(other_deps)
348349
}
350+
351+
pub fn channel(&self) -> Option<ReleaseChannel> {
352+
self.toolchain.as_ref().and_then(|v| ReleaseChannel::from_str(&v.pre))
353+
}
349354
}
350355

351356
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -427,7 +432,7 @@ impl CrateGraph {
427432
is_proc_macro: bool,
428433
origin: CrateOrigin,
429434
target_layout: Result<Arc<str>, Arc<str>>,
430-
channel: Option<ReleaseChannel>,
435+
toolchain: Option<Version>,
431436
) -> CrateId {
432437
let data = CrateData {
433438
root_file_id,
@@ -441,7 +446,7 @@ impl CrateGraph {
441446
origin,
442447
target_layout,
443448
is_proc_macro,
444-
channel,
449+
toolchain,
445450
};
446451
self.arena.alloc(data)
447452
}

crates/base-db/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ pub use salsa::{self, Cancelled};
2323
pub use span::{FilePosition, FileRange};
2424
pub use vfs::{file_set::FileSet, AnchoredPath, AnchoredPathBuf, FileId, VfsPath};
2525

26+
pub use semver::{BuildMetadata, Prerelease, Version, VersionReq};
27+
2628
#[macro_export]
2729
macro_rules! impl_intern_key {
2830
($name:ident) => {

crates/hir-expand/src/db.rs

+59-8
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
//! Defines database & queries for macro expansion.
22
3+
use std::sync::OnceLock;
4+
35
use base_db::{
46
salsa::{self, debug::DebugQueryTable},
5-
CrateId, Edition, FileId, SourceDatabase,
7+
CrateId, Edition, FileId, SourceDatabase, VersionReq,
68
};
79
use either::Either;
810
use limit::Limit;
@@ -45,32 +47,68 @@ pub struct DeclarativeMacroExpander {
4547
pub transparency: Transparency,
4648
}
4749

50+
// FIXME: Remove this once we drop support for 1.76
51+
static REQUIREMENT: OnceLock<VersionReq> = OnceLock::new();
52+
4853
impl DeclarativeMacroExpander {
4954
pub fn expand(
5055
&self,
5156
db: &dyn ExpandDatabase,
5257
tt: tt::Subtree,
5358
call_id: MacroCallId,
5459
) -> ExpandResult<tt::Subtree> {
60+
let toolchain = &db.crate_graph()[db.lookup_intern_macro_call(call_id).def.krate].toolchain;
61+
let new_meta_vars = toolchain.as_ref().map_or(false, |version| {
62+
REQUIREMENT.get_or_init(|| VersionReq::parse(">=1.76").unwrap()).matches(
63+
&base_db::Version {
64+
pre: base_db::Prerelease::EMPTY,
65+
build: base_db::BuildMetadata::EMPTY,
66+
major: version.major,
67+
minor: version.minor,
68+
patch: version.patch,
69+
},
70+
)
71+
});
5572
match self.mac.err() {
5673
Some(e) => ExpandResult::new(
5774
tt::Subtree::empty(tt::DelimSpan::DUMMY),
5875
ExpandError::other(format!("invalid macro definition: {e}")),
5976
),
6077
None => self
6178
.mac
62-
.expand(&tt, |s| s.ctx = apply_mark(db, s.ctx, call_id, self.transparency))
79+
.expand(
80+
&tt,
81+
|s| s.ctx = apply_mark(db, s.ctx, call_id, self.transparency),
82+
new_meta_vars,
83+
)
6384
.map_err(Into::into),
6485
}
6586
}
6687

67-
pub fn expand_unhygienic(&self, tt: tt::Subtree) -> ExpandResult<tt::Subtree> {
88+
pub fn expand_unhygienic(
89+
&self,
90+
db: &dyn ExpandDatabase,
91+
tt: tt::Subtree,
92+
krate: CrateId,
93+
) -> ExpandResult<tt::Subtree> {
94+
let toolchain = &db.crate_graph()[krate].toolchain;
95+
let new_meta_vars = toolchain.as_ref().map_or(false, |version| {
96+
REQUIREMENT.get_or_init(|| VersionReq::parse(">=1.76").unwrap()).matches(
97+
&base_db::Version {
98+
pre: base_db::Prerelease::EMPTY,
99+
build: base_db::BuildMetadata::EMPTY,
100+
major: version.major,
101+
minor: version.minor,
102+
patch: version.patch,
103+
},
104+
)
105+
});
68106
match self.mac.err() {
69107
Some(e) => ExpandResult::new(
70108
tt::Subtree::empty(tt::DelimSpan::DUMMY),
71109
ExpandError::other(format!("invalid macro definition: {e}")),
72110
),
73-
None => self.mac.expand(&tt, |_| ()).map_err(Into::into),
111+
None => self.mac.expand(&tt, |_| (), new_meta_vars).map_err(Into::into),
74112
}
75113
}
76114
}
@@ -278,7 +316,7 @@ pub fn expand_speculative(
278316
expander.expand(db, actual_macro_call, &adt, span_map)
279317
}
280318
MacroDefKind::Declarative(it) => {
281-
db.decl_macro_expander(loc.krate, it).expand_unhygienic(tt)
319+
db.decl_macro_expander(loc.krate, it).expand_unhygienic(db, tt, loc.def.krate)
282320
}
283321
MacroDefKind::BuiltIn(it, _) => it.expand(db, actual_macro_call, &tt).map_err(Into::into),
284322
MacroDefKind::BuiltInEager(it, _) => {
@@ -525,7 +563,8 @@ fn decl_macro_expander(
525563
def_crate: CrateId,
526564
id: AstId<ast::Macro>,
527565
) -> Arc<DeclarativeMacroExpander> {
528-
let is_2021 = db.crate_graph()[def_crate].edition >= Edition::Edition2021;
566+
let crate_data = &db.crate_graph()[def_crate];
567+
let is_2021 = crate_data.edition >= Edition::Edition2021;
529568
let (root, map) = parse_with_map(db, id.file_id);
530569
let root = root.syntax_node();
531570

@@ -549,13 +588,25 @@ fn decl_macro_expander(
549588
_ => None,
550589
}
551590
};
591+
let toolchain = crate_data.toolchain.as_ref();
592+
let new_meta_vars = toolchain.as_ref().map_or(false, |version| {
593+
REQUIREMENT.get_or_init(|| VersionReq::parse(">=1.76").unwrap()).matches(
594+
&base_db::Version {
595+
pre: base_db::Prerelease::EMPTY,
596+
build: base_db::BuildMetadata::EMPTY,
597+
major: version.major,
598+
minor: version.minor,
599+
patch: version.patch,
600+
},
601+
)
602+
});
552603

553604
let (mac, transparency) = match id.to_ptr(db).to_node(&root) {
554605
ast::Macro::MacroRules(macro_rules) => (
555606
match macro_rules.token_tree() {
556607
Some(arg) => {
557608
let tt = mbe::syntax_node_to_token_tree(arg.syntax(), map.as_ref());
558-
let mac = mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021);
609+
let mac = mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021, new_meta_vars);
559610
mac
560611
}
561612
None => mbe::DeclarativeMacro::from_err(
@@ -569,7 +620,7 @@ fn decl_macro_expander(
569620
match macro_def.body() {
570621
Some(arg) => {
571622
let tt = mbe::syntax_node_to_token_tree(arg.syntax(), map.as_ref());
572-
let mac = mbe::DeclarativeMacro::parse_macro2(&tt, is_2021);
623+
let mac = mbe::DeclarativeMacro::parse_macro2(&tt, is_2021, new_meta_vars);
573624
mac
574625
}
575626
None => mbe::DeclarativeMacro::from_err(

crates/ide-completion/src/context.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -693,7 +693,7 @@ impl<'a> CompletionContext<'a> {
693693
let krate = scope.krate();
694694
let module = scope.module();
695695

696-
let toolchain = db.crate_graph()[krate.into()].channel;
696+
let toolchain = db.crate_graph()[krate.into()].channel();
697697
// `toolchain == None` means we're in some detached files. Since we have no information on
698698
// the toolchain being used, let's just allow unstable items to be listed.
699699
let is_nightly = matches!(toolchain, Some(base_db::ReleaseChannel::Nightly) | None);

crates/ide/src/doc_links.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ fn get_doc_base_urls(
492492
let Some(krate) = def.krate(db) else { return Default::default() };
493493
let Some(display_name) = krate.display_name(db) else { return Default::default() };
494494
let crate_data = &db.crate_graph()[krate.into()];
495-
let channel = crate_data.channel.map_or("nightly", ReleaseChannel::as_str);
495+
let channel = crate_data.channel().unwrap_or(ReleaseChannel::Nightly).as_str();
496496

497497
let (web_base, local_base) = match &crate_data.origin {
498498
// std and co do not specify `html_root_url` any longer so we gotta handwrite this ourself.

crates/ide/src/shuffle_crate_graph.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) {
4040
data.is_proc_macro,
4141
data.origin.clone(),
4242
data.target_layout.clone(),
43-
data.channel,
43+
data.toolchain.clone(),
4444
);
4545
new_proc_macros.insert(new_id, proc_macros[&old_id].clone());
4646
map.insert(old_id, new_id);

crates/mbe/src/benchmark.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ fn benchmark_parse_macro_rules() {
2020
let rules = macro_rules_fixtures_tt();
2121
let hash: usize = {
2222
let _pt = bench("mbe parse macro rules");
23-
rules.values().map(|it| DeclarativeMacro::parse_macro_rules(it, true).rules.len()).sum()
23+
rules
24+
.values()
25+
.map(|it| DeclarativeMacro::parse_macro_rules(it, true, true).rules.len())
26+
.sum()
2427
};
2528
assert_eq!(hash, 1144);
2629
}
@@ -38,7 +41,7 @@ fn benchmark_expand_macro_rules() {
3841
invocations
3942
.into_iter()
4043
.map(|(id, tt)| {
41-
let res = rules[&id].expand(&tt, |_| ());
44+
let res = rules[&id].expand(&tt, |_| (), true);
4245
assert!(res.err.is_none());
4346
res.value.token_trees.len()
4447
})
@@ -50,7 +53,7 @@ fn benchmark_expand_macro_rules() {
5053
fn macro_rules_fixtures() -> FxHashMap<String, DeclarativeMacro<DummyTestSpanData>> {
5154
macro_rules_fixtures_tt()
5255
.into_iter()
53-
.map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt, true)))
56+
.map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt, true, true)))
5457
.collect()
5558
}
5659

@@ -105,7 +108,7 @@ fn invocation_fixtures(
105108
for op in rule.lhs.iter() {
106109
collect_from_op(op, &mut subtree, &mut seed);
107110
}
108-
if it.expand(&subtree, |_| ()).err.is_none() {
111+
if it.expand(&subtree, |_| (), true).err.is_none() {
109112
res.push((name.clone(), subtree));
110113
break;
111114
}

crates/mbe/src/expander.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub(crate) fn expand_rules<S: Span>(
1616
input: &tt::Subtree<S>,
1717
marker: impl Fn(&mut S) + Copy,
1818
is_2021: bool,
19+
new_meta_vars: bool,
1920
) -> ExpandResult<tt::Subtree<S>> {
2021
let mut match_: Option<(matcher::Match<S>, &crate::Rule<S>)> = None;
2122
for rule in rules {
@@ -26,7 +27,7 @@ pub(crate) fn expand_rules<S: Span>(
2627
// Unconditionally returning the transcription here makes the
2728
// `test_repeat_bad_var` test fail.
2829
let ExpandResult { value, err: transcribe_err } =
29-
transcriber::transcribe(&rule.rhs, &new_match.bindings, marker);
30+
transcriber::transcribe(&rule.rhs, &new_match.bindings, marker, new_meta_vars);
3031
if transcribe_err.is_none() {
3132
return ExpandResult::ok(value);
3233
}
@@ -45,7 +46,7 @@ pub(crate) fn expand_rules<S: Span>(
4546
if let Some((match_, rule)) = match_ {
4647
// if we got here, there was no match without errors
4748
let ExpandResult { value, err: transcribe_err } =
48-
transcriber::transcribe(&rule.rhs, &match_.bindings, marker);
49+
transcriber::transcribe(&rule.rhs, &match_.bindings, marker, new_meta_vars);
4950
ExpandResult { value, err: match_.err.or(transcribe_err) }
5051
} else {
5152
ExpandResult::new(

crates/mbe/src/expander/transcriber.rs

+39-2
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,9 @@ pub(super) fn transcribe<S: Span>(
131131
template: &MetaTemplate<S>,
132132
bindings: &Bindings<S>,
133133
marker: impl Fn(&mut S) + Copy,
134+
new_meta_vars: bool,
134135
) -> ExpandResult<tt::Subtree<S>> {
135-
let mut ctx = ExpandCtx { bindings, nesting: Vec::new() };
136+
let mut ctx = ExpandCtx { bindings, nesting: Vec::new(), new_meta_vars };
136137
let mut arena: Vec<tt::TokenTree<S>> = Vec::new();
137138
expand_subtree(&mut ctx, template, None, &mut arena, marker)
138139
}
@@ -152,6 +153,7 @@ struct NestingState {
152153
struct ExpandCtx<'a, S> {
153154
bindings: &'a Bindings<S>,
154155
nesting: Vec<NestingState>,
156+
new_meta_vars: bool,
155157
}
156158

157159
fn expand_subtree<S: Span>(
@@ -284,7 +286,13 @@ fn expand_subtree<S: Span>(
284286
}
285287
}
286288

287-
let c = match count(ctx, binding, 0, *depth) {
289+
let res = if ctx.new_meta_vars {
290+
count(ctx, binding, 0, depth.unwrap_or(0))
291+
} else {
292+
count_old(ctx, binding, 0, *depth)
293+
};
294+
295+
let c = match res {
288296
Ok(c) => c,
289297
Err(e) => {
290298
// XXX: It *might* make sense to emit a dummy integer value like `0` here.
@@ -548,3 +556,32 @@ fn count<S>(
548556
Binding::Fragment(_) | Binding::Missing(_) => Ok(1),
549557
}
550558
}
559+
560+
fn count_old<S>(
561+
ctx: &ExpandCtx<'_, S>,
562+
binding: &Binding<S>,
563+
our_depth: usize,
564+
count_depth: Option<usize>,
565+
) -> Result<usize, CountError> {
566+
match binding {
567+
Binding::Nested(bs) => match count_depth {
568+
None => bs.iter().map(|b| count_old(ctx, b, our_depth + 1, None)).sum(),
569+
Some(0) => Ok(bs.len()),
570+
Some(d) => bs.iter().map(|b| count_old(ctx, b, our_depth + 1, Some(d - 1))).sum(),
571+
},
572+
Binding::Empty => Ok(0),
573+
Binding::Fragment(_) | Binding::Missing(_) => {
574+
if our_depth == 0 {
575+
// `${count(t)}` is placed inside the innermost repetition. This includes cases
576+
// where `t` is not a repeated fragment.
577+
Err(CountError::Misplaced)
578+
} else if count_depth.is_none() {
579+
Ok(1)
580+
} else {
581+
// We've reached at the innermost repeated fragment, but the user wants us to go
582+
// further!
583+
Err(CountError::OutOfBounds)
584+
}
585+
}
586+
}
587+
}

0 commit comments

Comments
 (0)