Unnamed repository; edit this file 'description' to name the repository.
Auto merge of #12668 - Veykril:mac-source-map, r=Veykril
fix: Simplify macro statement expansion handling I only meant to fix https://github.com/rust-lang/rust-analyzer/issues/12644 but that somehow turned into a rewrite of the statement handling ... at least this fixes a few more issues in the IDE layer now
bors 2022-07-01
parent 8489cd7 · parent e5e5a09 · commit ed44fe5
-rw-r--r--crates/hir-def/src/body.rs11
-rw-r--r--crates/hir-def/src/body/lower.rs154
-rw-r--r--crates/hir-def/src/body/scope.rs22
-rw-r--r--crates/hir-def/src/expr.rs6
-rw-r--r--crates/hir-ty/src/infer/expr.rs4
-rw-r--r--crates/hir-ty/src/tests/macros.rs46
-rw-r--r--crates/hir-ty/src/tests/regression.rs1
-rw-r--r--crates/hir/src/lib.rs1
-rw-r--r--crates/hir/src/source_analyzer.rs34
-rw-r--r--crates/ide-completion/src/tests/special.rs30
-rw-r--r--crates/ide/src/highlight_related.rs2
-rw-r--r--crates/ide/src/hover/tests.rs31
-rw-r--r--crates/syntax/src/ptr.rs7
13 files changed, 221 insertions, 128 deletions
diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs
index c7566b06d0..94210ab33f 100644
--- a/crates/hir-def/src/body.rs
+++ b/crates/hir-def/src/body.rs
@@ -19,7 +19,6 @@ use la_arena::{Arena, ArenaMap};
use limit::Limit;
use profile::Count;
use rustc_hash::FxHashMap;
-use smallvec::SmallVec;
use syntax::{ast, AstNode, AstPtr, SyntaxNodePtr};
use crate::{
@@ -294,10 +293,6 @@ pub struct BodySourceMap {
field_map: FxHashMap<InFile<AstPtr<ast::RecordExprField>>, ExprId>,
field_map_back: FxHashMap<ExprId, InFile<AstPtr<ast::RecordExprField>>>,
- /// Maps a macro call to its lowered expressions, a single one if it expands to an expression,
- /// or multiple if it expands to MacroStmts.
- macro_call_to_exprs: FxHashMap<InFile<AstPtr<ast::MacroCall>>, SmallVec<[ExprId; 1]>>,
-
expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>,
/// Diagnostics accumulated during body lowering. These contain `AstPtr`s and so are stored in
@@ -466,9 +461,9 @@ impl BodySourceMap {
self.field_map.get(&src).cloned()
}
- pub fn macro_expansion_expr(&self, node: InFile<&ast::MacroCall>) -> Option<&[ExprId]> {
- let src = node.map(AstPtr::new);
- self.macro_call_to_exprs.get(&src).map(|it| &**it)
+ pub fn macro_expansion_expr(&self, node: InFile<&ast::MacroExpr>) -> Option<ExprId> {
+ let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::MacroExpr>).map(AstPtr::upcast);
+ self.expr_map.get(&src).copied()
}
/// Get a reference to the body source map's diagnostics.
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index dff3449098..049afa8227 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -13,7 +13,6 @@ use hir_expand::{
use la_arena::Arena;
use profile::Count;
use rustc_hash::FxHashMap;
-use smallvec::smallvec;
use syntax::{
ast::{
self, ArrayExprKind, AstChildren, HasArgList, HasLoopBody, HasName, LiteralKind,
@@ -97,7 +96,6 @@ pub(super) fn lower(
or_pats: Default::default(),
},
expander,
- statements_in_scope: Vec::new(),
name_to_pat_grouping: Default::default(),
is_lowering_inside_or_pat: false,
}
@@ -109,7 +107,6 @@ struct ExprCollector<'a> {
expander: Expander,
body: Body,
source_map: BodySourceMap,
- statements_in_scope: Vec<Statement>,
// a poor-mans union-find?
name_to_pat_grouping: FxHashMap<Name, Vec<PatId>>,
is_lowering_inside_or_pat: bool,
@@ -514,27 +511,25 @@ impl ExprCollector<'_> {
ast::Expr::MacroExpr(e) => {
let e = e.macro_call()?;
let macro_ptr = AstPtr::new(&e);
- let id = self.collect_macro_call(e, macro_ptr.clone(), true, |this, expansion| {
+ let id = self.collect_macro_call(e, macro_ptr, true, |this, expansion| {
expansion.map(|it| this.collect_expr(it))
});
match id {
Some(id) => {
- self.source_map
- .macro_call_to_exprs
- .insert(self.expander.to_source(macro_ptr), smallvec![id]);
+ // Make the macro-call point to its expanded expression so we can query
+ // semantics on syntax pointers to the macro
+ let src = self.expander.to_source(syntax_ptr);
+ self.source_map.expr_map.insert(src, id);
id
}
- None => self.alloc_expr(Expr::Missing, syntax_ptr.clone()),
+ None => self.alloc_expr(Expr::Missing, syntax_ptr),
}
}
ast::Expr::MacroStmts(e) => {
- e.statements().for_each(|s| self.collect_stmt(s));
- let tail = e
- .expr()
- .map(|e| self.collect_expr(e))
- .unwrap_or_else(|| self.alloc_expr(Expr::Missing, syntax_ptr.clone()));
+ let statements = e.statements().filter_map(|s| self.collect_stmt(s)).collect();
+ let tail = e.expr().map(|e| self.collect_expr(e));
- self.alloc_expr(Expr::MacroStmts { tail }, syntax_ptr)
+ self.alloc_expr(Expr::MacroStmts { tail, statements }, syntax_ptr)
}
ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr),
})
@@ -607,11 +602,11 @@ impl ExprCollector<'_> {
}
}
- fn collect_stmt(&mut self, s: ast::Stmt) {
+ fn collect_stmt(&mut self, s: ast::Stmt) -> Option<Statement> {
match s {
ast::Stmt::LetStmt(stmt) => {
if self.check_cfg(&stmt).is_none() {
- return;
+ return None;
}
let pat = self.collect_pat_opt(stmt.pat());
let type_ref =
@@ -621,70 +616,61 @@ impl ExprCollector<'_> {
.let_else()
.and_then(|let_else| let_else.block_expr())
.map(|block| self.collect_block(block));
- self.statements_in_scope.push(Statement::Let {
- pat,
- type_ref,
- initializer,
- else_branch,
- });
+ Some(Statement::Let { pat, type_ref, initializer, else_branch })
}
ast::Stmt::ExprStmt(stmt) => {
- if let Some(expr) = stmt.expr() {
- if self.check_cfg(&expr).is_none() {
- return;
+ let expr = stmt.expr();
+ if let Some(expr) = &expr {
+ if self.check_cfg(expr).is_none() {
+ return None;
}
}
let has_semi = stmt.semicolon_token().is_some();
- // Note that macro could be expended to multiple statements
- if let Some(ast::Expr::MacroExpr(e)) = stmt.expr() {
- let m = match e.macro_call() {
- Some(it) => it,
- None => return,
- };
- let macro_ptr = AstPtr::new(&m);
- let syntax_ptr = AstPtr::new(&stmt.expr().unwrap());
-
- let prev_stmt = self.statements_in_scope.len();
- self.collect_macro_call(m, macro_ptr.clone(), false, |this, expansion| {
- match expansion {
+ // Note that macro could be expanded to multiple statements
+ if let Some(expr @ ast::Expr::MacroExpr(mac)) = &expr {
+ let mac_call = mac.macro_call()?;
+ let syntax_ptr = AstPtr::new(expr);
+ let macro_ptr = AstPtr::new(&mac_call);
+ let stmt = self.collect_macro_call(
+ mac_call,
+ macro_ptr,
+ false,
+ |this, expansion: Option<ast::MacroStmts>| match expansion {
Some(expansion) => {
- let statements: ast::MacroStmts = expansion;
-
- statements.statements().for_each(|stmt| this.collect_stmt(stmt));
- if let Some(expr) = statements.expr() {
- let expr = this.collect_expr(expr);
- this.statements_in_scope
- .push(Statement::Expr { expr, has_semi });
- }
+ let statements = expansion
+ .statements()
+ .filter_map(|stmt| this.collect_stmt(stmt))
+ .collect();
+ let tail = expansion.expr().map(|expr| this.collect_expr(expr));
+
+ let mac_stmts = this.alloc_expr(
+ Expr::MacroStmts { tail, statements },
+ AstPtr::new(&ast::Expr::MacroStmts(expansion)),
+ );
+
+ Some(mac_stmts)
}
- None => {
- let expr = this.alloc_expr(Expr::Missing, syntax_ptr.clone());
- this.statements_in_scope.push(Statement::Expr { expr, has_semi });
- }
- }
- });
+ None => None,
+ },
+ );
- let mut macro_exprs = smallvec![];
- for stmt in &self.statements_in_scope[prev_stmt..] {
- match *stmt {
- Statement::Let { initializer, else_branch, .. } => {
- macro_exprs.extend(initializer);
- macro_exprs.extend(else_branch);
- }
- Statement::Expr { expr, .. } => macro_exprs.push(expr),
+ let expr = match stmt {
+ Some(expr) => {
+ // Make the macro-call point to its expanded expression so we can query
+ // semantics on syntax pointers to the macro
+ let src = self.expander.to_source(syntax_ptr);
+ self.source_map.expr_map.insert(src, expr);
+ expr
}
- }
- if !macro_exprs.is_empty() {
- self.source_map
- .macro_call_to_exprs
- .insert(self.expander.to_source(macro_ptr), macro_exprs);
- }
+ None => self.alloc_expr(Expr::Missing, syntax_ptr),
+ };
+ Some(Statement::Expr { expr, has_semi })
} else {
- let expr = self.collect_expr_opt(stmt.expr());
- self.statements_in_scope.push(Statement::Expr { expr, has_semi });
+ let expr = self.collect_expr_opt(expr);
+ Some(Statement::Expr { expr, has_semi })
}
}
- ast::Stmt::Item(_item) => {}
+ ast::Stmt::Item(_item) => None,
}
}
@@ -703,25 +689,27 @@ impl ExprCollector<'_> {
};
let prev_def_map = mem::replace(&mut self.expander.def_map, def_map);
let prev_local_module = mem::replace(&mut self.expander.module, module);
- let prev_statements = std::mem::take(&mut self.statements_in_scope);
- block.statements().for_each(|s| self.collect_stmt(s));
- block.tail_expr().and_then(|e| {
- let expr = self.maybe_collect_expr(e)?;
- self.statements_in_scope.push(Statement::Expr { expr, has_semi: false });
- Some(())
+ let mut statements: Vec<_> =
+ block.statements().filter_map(|s| self.collect_stmt(s)).collect();
+ let tail = block.tail_expr().and_then(|e| self.maybe_collect_expr(e));
+ let tail = tail.or_else(|| {
+ let stmt = statements.pop()?;
+ if let Statement::Expr { expr, has_semi: false } = stmt {
+ return Some(expr);
+ }
+ statements.push(stmt);
+ None
});
- let mut tail = None;
- if let Some(Statement::Expr { expr, has_semi: false }) = self.statements_in_scope.last() {
- tail = Some(*expr);
- self.statements_in_scope.pop();
- }
- let tail = tail;
- let statements = std::mem::replace(&mut self.statements_in_scope, prev_statements).into();
let syntax_node_ptr = AstPtr::new(&block.into());
let expr_id = self.alloc_expr(
- Expr::Block { id: block_id, statements, tail, label: None },
+ Expr::Block {
+ id: block_id,
+ statements: statements.into_boxed_slice(),
+ tail,
+ label: None,
+ },
syntax_node_ptr,
);
@@ -903,10 +891,12 @@ impl ExprCollector<'_> {
ast::Pat::MacroPat(mac) => match mac.macro_call() {
Some(call) => {
let macro_ptr = AstPtr::new(&call);
+ let src = self.expander.to_source(Either::Left(AstPtr::new(&pat)));
let pat =
self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
this.collect_pat_opt_(expanded_pat)
});
+ self.source_map.pat_map.insert(src, pat);
return pat;
}
None => Pat::Missing,
diff --git a/crates/hir-def/src/body/scope.rs b/crates/hir-def/src/body/scope.rs
index c9ab19e59b..f4c390dce2 100644
--- a/crates/hir-def/src/body/scope.rs
+++ b/crates/hir-def/src/body/scope.rs
@@ -145,27 +145,28 @@ fn compute_block_scopes(
tail: Option<ExprId>,
body: &Body,
scopes: &mut ExprScopes,
- mut scope: ScopeId,
+ scope: &mut ScopeId,
) {
for stmt in statements {
match stmt {
Statement::Let { pat, initializer, else_branch, .. } => {
if let Some(expr) = initializer {
- compute_expr_scopes(*expr, body, scopes, &mut scope);
+ compute_expr_scopes(*expr, body, scopes, scope);
}
if let Some(expr) = else_branch {
- compute_expr_scopes(*expr, body, scopes, &mut scope);
+ compute_expr_scopes(*expr, body, scopes, scope);
}
- scope = scopes.new_scope(scope);
- scopes.add_bindings(body, scope, *pat);
+
+ *scope = scopes.new_scope(*scope);
+ scopes.add_bindings(body, *scope, *pat);
}
Statement::Expr { expr, .. } => {
- compute_expr_scopes(*expr, body, scopes, &mut scope);
+ compute_expr_scopes(*expr, body, scopes, scope);
}
}
}
if let Some(expr) = tail {
- compute_expr_scopes(expr, body, scopes, &mut scope);
+ compute_expr_scopes(expr, body, scopes, scope);
}
}
@@ -175,12 +176,15 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
scopes.set_scope(expr, *scope);
match &body[expr] {
+ Expr::MacroStmts { statements, tail } => {
+ compute_block_scopes(statements, *tail, body, scopes, scope);
+ }
Expr::Block { statements, tail, id, label } => {
- let scope = scopes.new_block_scope(*scope, *id, make_label(label));
+ let mut scope = scopes.new_block_scope(*scope, *id, make_label(label));
// Overwrite the old scope for the block expr, so that every block scope can be found
// via the block itself (important for blocks that only contain items, no expressions).
scopes.set_scope(expr, scope);
- compute_block_scopes(statements, *tail, body, scopes, scope);
+ compute_block_scopes(statements, *tail, body, scopes, &mut scope);
}
Expr::For { iterable, pat, body: body_expr, label } => {
compute_expr_scopes(*iterable, body, scopes, scope);
diff --git a/crates/hir-def/src/expr.rs b/crates/hir-def/src/expr.rs
index 49b7ef451e..4295bacc46 100644
--- a/crates/hir-def/src/expr.rs
+++ b/crates/hir-def/src/expr.rs
@@ -199,7 +199,8 @@ pub enum Expr {
body: ExprId,
},
MacroStmts {
- tail: ExprId,
+ statements: Box<[Statement]>,
+ tail: Option<ExprId>,
},
Array(Array),
Literal(Literal),
@@ -254,7 +255,7 @@ impl Expr {
Expr::Let { expr, .. } => {
f(*expr);
}
- Expr::Block { statements, tail, .. } => {
+ Expr::MacroStmts { tail, statements } | Expr::Block { statements, tail, .. } => {
for stmt in statements.iter() {
match stmt {
Statement::Let { initializer, .. } => {
@@ -344,7 +345,6 @@ impl Expr {
f(*repeat)
}
},
- Expr::MacroStmts { tail } => f(*tail),
Expr::Literal(_) => {}
Expr::Underscore => {}
}
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index a1a7b17f37..defceefc79 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -774,7 +774,9 @@ impl<'a> InferenceContext<'a> {
None => self.table.new_float_var(),
},
},
- Expr::MacroStmts { tail } => self.infer_expr_inner(*tail, expected),
+ Expr::MacroStmts { tail, statements } => {
+ self.infer_block(tgt_expr, statements, *tail, expected)
+ }
Expr::Underscore => {
// Underscore expressions may only appear in assignee expressions,
// which are handled by `infer_assignee_expr()`, so any underscore
diff --git a/crates/hir-ty/src/tests/macros.rs b/crates/hir-ty/src/tests/macros.rs
index a61175f273..a4299d9f05 100644
--- a/crates/hir-ty/src/tests/macros.rs
+++ b/crates/hir-ty/src/tests/macros.rs
@@ -1,6 +1,8 @@
use expect_test::expect;
use test_utils::{bench, bench_fixture, skip_slow_tests};
+use crate::tests::check_infer_with_mismatches;
+
use super::{check_infer, check_types};
#[test]
@@ -191,6 +193,8 @@ fn expr_macro_def_expanded_in_various_places() {
!0..6 '1isize': isize
!0..6 '1isize': isize
!0..6 '1isize': isize
+ !0..6 '1isize': isize
+ !0..6 '1isize': isize
39..442 '{ ...!(); }': ()
73..94 'spam!(...am!())': {unknown}
100..119 'for _ ...!() {}': ()
@@ -272,6 +276,8 @@ fn expr_macro_rules_expanded_in_various_places() {
!0..6 '1isize': isize
!0..6 '1isize': isize
!0..6 '1isize': isize
+ !0..6 '1isize': isize
+ !0..6 '1isize': isize
53..456 '{ ...!(); }': ()
87..108 'spam!(...am!())': {unknown}
114..133 'for _ ...!() {}': ()
@@ -307,7 +313,6 @@ fn expr_macro_expanded_in_stmts() {
"#,
expect![[r#"
!0..8 'leta=();': ()
- !0..8 'leta=();': ()
!3..4 'a': ()
!5..7 '()': ()
57..84 '{ ...); } }': ()
@@ -335,11 +340,11 @@ fn recurisve_macro_expanded_in_stmts() {
}
"#,
expect![[r#"
- !0..7 'leta=3;': {unknown}
- !0..7 'leta=3;': {unknown}
- !0..13 'ng!{[leta=3]}': {unknown}
- !0..13 'ng!{[leta=]3}': {unknown}
- !0..13 'ng!{[leta]=3}': {unknown}
+ !0..7 'leta=3;': ()
+ !0..13 'ng!{[leta=3]}': ()
+ !0..13 'ng!{[leta=]3}': ()
+ !0..13 'ng!{[leta]=3}': ()
+ !0..13 'ng!{[let]a=3}': ()
!3..4 'a': i32
!5..6 '3': i32
196..237 '{ ...= a; }': ()
@@ -364,8 +369,8 @@ fn recursive_inner_item_macro_rules() {
"#,
expect![[r#"
!0..1 '1': i32
- !0..26 'macro_...>{1};}': {unknown}
- !0..26 'macro_...>{1};}': {unknown}
+ !0..7 'mac!($)': ()
+ !0..26 'macro_...>{1};}': ()
107..143 '{ ...!(); }': ()
129..130 'a': i32
"#]],
@@ -1244,3 +1249,28 @@ fn infinitely_recursive_macro_type() {
"#]],
);
}
+
+#[test]
+fn cfg_tails() {
+ check_infer_with_mismatches(
+ r#"
+//- /lib.rs crate:foo cfg:feature=foo
+struct S {}
+
+impl S {
+ fn new2(bar: u32) -> Self {
+ #[cfg(feature = "foo")]
+ { Self { } }
+ #[cfg(not(feature = "foo"))]
+ { Self { } }
+ }
+}
+"#,
+ expect![[r#"
+ 34..37 'bar': u32
+ 52..170 '{ ... }': S
+ 62..106 '#[cfg(... { } }': S
+ 96..104 'Self { }': S
+ "#]],
+ );
+}
diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs
index 93f765f703..ee0a631a1b 100644
--- a/crates/hir-ty/src/tests/regression.rs
+++ b/crates/hir-ty/src/tests/regression.rs
@@ -573,6 +573,7 @@ fn issue_6811() {
}
"#,
expect![[r#"
+ !0..16 'let_a=...t_b=1;': ()
!3..5 '_a': i32
!6..7 '1': i32
!11..13 '_b': i32
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 2264bbbfd7..ef17f2a75e 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -2223,6 +2223,7 @@ impl Local {
let src = source_map.pat_syntax(self.pat_id).unwrap(); // Hmm...
let root = src.file_syntax(db.upcast());
src.map(|ast| match ast {
+ // Suspicious unwrap
Either::Left(it) => Either::Left(it.cast().unwrap().to_node(&root)),
Either::Right(it) => Either::Right(it.to_node(&root)),
})
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 9bdde5e6e0..2298a75d57 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -539,24 +539,26 @@ impl SourceAnalyzer {
_ => (),
}
}
+ let macro_expr = match macro_call
+ .map(|it| it.syntax().parent().and_then(ast::MacroExpr::cast))
+ .transpose()
+ {
+ Some(it) => it,
+ None => return false,
+ };
+
if let (Some((def, body, sm)), Some(infer)) = (&self.def, &self.infer) {
- if let Some(expr_ids) = sm.macro_expansion_expr(macro_call) {
+ if let Some(expanded_expr) = sm.macro_expansion_expr(macro_expr.as_ref()) {
let mut is_unsafe = false;
- for &expr_id in expr_ids {
- unsafe_expressions(
- db,
- infer,
- *def,
- body,
- expr_id,
- &mut |UnsafeExpr { inside_unsafe_block, .. }| {
- is_unsafe |= !inside_unsafe_block
- },
- );
- if is_unsafe {
- return true;
- }
- }
+ unsafe_expressions(
+ db,
+ infer,
+ *def,
+ body,
+ expanded_expr,
+ &mut |UnsafeExpr { inside_unsafe_block, .. }| is_unsafe |= !inside_unsafe_block,
+ );
+ return is_unsafe;
}
}
false
diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs
index 39cb41485b..96d0219fef 100644
--- a/crates/ide-completion/src/tests/special.rs
+++ b/crates/ide-completion/src/tests/special.rs
@@ -810,3 +810,33 @@ fn main() {
"#]],
)
}
+
+#[test]
+fn regression_12644() {
+ check(
+ r#"
+macro_rules! __rust_force_expr {
+ ($e:expr) => {
+ $e
+ };
+}
+macro_rules! vec {
+ ($elem:expr) => {
+ __rust_force_expr!($elem)
+ };
+}
+
+struct Struct;
+impl Struct {
+ fn foo(self) {}
+}
+
+fn f() {
+ vec![Struct].$0;
+}
+"#,
+ expect![[r#"
+ me foo() fn(self)
+ "#]],
+ );
+}
diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs
index eae5fc8d17..7c0a7fa1e7 100644
--- a/crates/ide/src/highlight_related.rs
+++ b/crates/ide/src/highlight_related.rs
@@ -689,7 +689,7 @@ fn foo() ->$0 u32 {
never();
// ^^^^^^^
never!();
- // FIXME sema doesn't give us types for macrocalls
+ // ^^^^^^^^
Never.never();
// ^^^^^^^^^^^^^
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index af502e4106..a1484fa19f 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -4920,3 +4920,34 @@ impl T for () {
"#]],
);
}
+
+#[test]
+fn hover_ranged_macro_call() {
+ check_hover_range(
+ r#"
+macro_rules! __rust_force_expr {
+ ($e:expr) => {
+ $e
+ };
+}
+macro_rules! vec {
+ ($elem:expr) => {
+ __rust_force_expr!($elem)
+ };
+}
+
+struct Struct;
+impl Struct {
+ fn foo(self) {}
+}
+
+fn f() {
+ $0vec![Struct]$0;
+}
+"#,
+ expect![[r#"
+ ```rust
+ Struct
+ ```"#]],
+ );
+}
diff --git a/crates/syntax/src/ptr.rs b/crates/syntax/src/ptr.rs
index 4a54bed0d4..bd96e35cc6 100644
--- a/crates/syntax/src/ptr.rs
+++ b/crates/syntax/src/ptr.rs
@@ -67,6 +67,13 @@ impl<N: AstNode> AstPtr<N> {
Some(AstPtr { raw: self.raw, _ty: PhantomData })
}
+ pub fn upcast<M: AstNode>(self) -> AstPtr<M>
+ where
+ N: Into<M>,
+ {
+ AstPtr { raw: self.raw, _ty: PhantomData }
+ }
+
/// Like `SyntaxNodePtr::cast` but the trait bounds work out.
pub fn try_from_raw(raw: SyntaxNodePtr) -> Option<AstPtr<N>> {
N::can_cast(raw.kind()).then(|| AstPtr { raw, _ty: PhantomData })