Skip to content

Commit 6c1138e

Browse files
authored
Merge pull request #18674 from Veykril/push-lrxotqknvxvr
Show expansion errors in expand_macro feature
2 parents 7718173 + 3a83938 commit 6c1138e

File tree

7 files changed

+76
-37
lines changed

7 files changed

+76
-37
lines changed

src/tools/rust-analyzer/crates/hir-expand/src/lib.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use rustc_hash::FxHashMap;
2828
use stdx::TupleExt;
2929
use triomphe::Arc;
3030

31+
use core::fmt;
3132
use std::hash::Hash;
3233

3334
use base_db::{ra_salsa::InternValueTrivial, CrateId};
@@ -147,6 +148,10 @@ impl ExpandError {
147148
pub fn span(&self) -> Span {
148149
self.inner.1
149150
}
151+
152+
pub fn render_to_string(&self, db: &dyn ExpandDatabase) -> RenderedExpandError {
153+
self.inner.0.render_to_string(db)
154+
}
150155
}
151156

152157
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
@@ -164,18 +169,18 @@ pub enum ExpandErrorKind {
164169
ProcMacroPanic(Box<str>),
165170
}
166171

167-
impl ExpandError {
168-
pub fn render_to_string(&self, db: &dyn ExpandDatabase) -> RenderedExpandError {
169-
self.inner.0.render_to_string(db)
170-
}
171-
}
172-
173172
pub struct RenderedExpandError {
174173
pub message: String,
175174
pub error: bool,
176175
pub kind: &'static str,
177176
}
178177

178+
impl fmt::Display for RenderedExpandError {
179+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
180+
write!(f, "{}", self.message)
181+
}
182+
}
183+
179184
impl RenderedExpandError {
180185
const GENERAL_KIND: &str = "macro-error";
181186
}

src/tools/rust-analyzer/crates/hir/src/semantics.rs

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use hir_expand::{
2828
hygiene::SyntaxContextExt as _,
2929
inert_attr_macro::find_builtin_attr_idx,
3030
name::AsName,
31-
FileRange, InMacroFile, MacroCallId, MacroFileId, MacroFileIdExt,
31+
ExpandResult, FileRange, InMacroFile, MacroCallId, MacroFileId, MacroFileIdExt,
3232
};
3333
use intern::Symbol;
3434
use itertools::Itertools;
@@ -381,7 +381,13 @@ impl<'db> SemanticsImpl<'db> {
381381
node
382382
}
383383

384-
pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
384+
pub fn expand(&self, file_id: MacroFileId) -> ExpandResult<SyntaxNode> {
385+
let res = self.db.parse_macro_expansion(file_id).map(|it| it.0.syntax_node());
386+
self.cache(res.value.clone(), file_id.into());
387+
res
388+
}
389+
390+
pub fn expand_macro_call(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
385391
let sa = self.analyze_no_infer(macro_call.syntax())?;
386392

387393
let macro_call = InFile::new(sa.file_id, macro_call);
@@ -412,7 +418,10 @@ impl<'db> SemanticsImpl<'db> {
412418

413419
/// Expands the macro if it isn't one of the built-in ones that expand to custom syntax or dummy
414420
/// expansions.
415-
pub fn expand_allowed_builtins(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
421+
pub fn expand_allowed_builtins(
422+
&self,
423+
macro_call: &ast::MacroCall,
424+
) -> Option<ExpandResult<SyntaxNode>> {
416425
let sa = self.analyze_no_infer(macro_call.syntax())?;
417426

418427
let macro_call = InFile::new(sa.file_id, macro_call);
@@ -448,15 +457,15 @@ impl<'db> SemanticsImpl<'db> {
448457
return None;
449458
}
450459

451-
let node = self.parse_or_expand(file_id.into());
460+
let node = self.expand(file_id);
452461
Some(node)
453462
}
454463

455464
/// If `item` has an attribute macro attached to it, expands it.
456-
pub fn expand_attr_macro(&self, item: &ast::Item) -> Option<SyntaxNode> {
465+
pub fn expand_attr_macro(&self, item: &ast::Item) -> Option<ExpandResult<SyntaxNode>> {
457466
let src = self.wrap_node_infile(item.clone());
458467
let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(src.as_ref()))?;
459-
Some(self.parse_or_expand(macro_call_id.as_file()))
468+
Some(self.expand(macro_call_id.as_macro_file()))
460469
}
461470

462471
pub fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Attr) -> Option<SyntaxNode> {
@@ -480,15 +489,16 @@ impl<'db> SemanticsImpl<'db> {
480489
})
481490
}
482491

483-
pub fn expand_derive_macro(&self, attr: &ast::Attr) -> Option<Vec<SyntaxNode>> {
492+
pub fn expand_derive_macro(&self, attr: &ast::Attr) -> Option<Vec<ExpandResult<SyntaxNode>>> {
484493
let res: Vec<_> = self
485494
.derive_macro_calls(attr)?
486495
.into_iter()
487496
.flat_map(|call| {
488-
let file_id = call?.as_file();
489-
let node = self.db.parse_or_expand(file_id);
490-
self.cache(node.clone(), file_id);
491-
Some(node)
497+
let file_id = call?.as_macro_file();
498+
let ExpandResult { value, err } = self.db.parse_macro_expansion(file_id);
499+
let root_node = value.0.syntax_node();
500+
self.cache(root_node.clone(), file_id.into());
501+
Some(ExpandResult { value: root_node, err })
492502
})
493503
.collect();
494504
Some(res)
@@ -556,7 +566,7 @@ impl<'db> SemanticsImpl<'db> {
556566

557567
/// Expand the macro call with a different token tree, mapping the `token_to_map` down into the
558568
/// expansion. `token_to_map` should be a token from the `speculative args` node.
559-
pub fn speculative_expand(
569+
pub fn speculative_expand_macro_call(
560570
&self,
561571
actual_macro_call: &ast::MacroCall,
562572
speculative_args: &ast::TokenTree,

src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Module responsible for analyzing the code surrounding the cursor for completion.
22
use std::iter;
33

4-
use hir::{Semantics, Type, TypeInfo, Variant};
4+
use hir::{ExpandResult, Semantics, Type, TypeInfo, Variant};
55
use ide_db::{active_parameter::ActiveParameter, RootDatabase};
66
use itertools::Either;
77
use syntax::{
@@ -104,7 +104,10 @@ fn expand(
104104
// maybe parent items have attributes, so continue walking the ancestors
105105
(None, None) => continue 'ancestors,
106106
// successful expansions
107-
(Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) => {
107+
(
108+
Some(ExpandResult { value: actual_expansion, err: _ }),
109+
Some((fake_expansion, fake_mapped_token)),
110+
) => {
108111
let new_offset = fake_mapped_token.text_range().start();
109112
if new_offset + relative_offset > actual_expansion.text_range().end() {
110113
// offset outside of bounds from the original expansion,
@@ -239,8 +242,8 @@ fn expand(
239242
};
240243

241244
match (
242-
sema.expand(&actual_macro_call),
243-
sema.speculative_expand(
245+
sema.expand_macro_call(&actual_macro_call),
246+
sema.speculative_expand_macro_call(
244247
&actual_macro_call,
245248
&speculative_args,
246249
fake_ident_token.clone(),

src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ impl<'db> MatchFinder<'db> {
286286
});
287287
}
288288
} else if let Some(macro_call) = ast::MacroCall::cast(node.clone()) {
289-
if let Some(expanded) = self.sema.expand(&macro_call) {
289+
if let Some(expanded) = self.sema.expand_macro_call(&macro_call) {
290290
if let Some(tt) = macro_call.token_tree() {
291291
self.output_debug_for_nodes_at_range(
292292
&expanded,

src/tools/rust-analyzer/crates/ide-ssr/src/search.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ impl MatchFinder<'_> {
189189
// If we've got a macro call, we already tried matching it pre-expansion, which is the only
190190
// way to match the whole macro, now try expanding it and matching the expansion.
191191
if let Some(macro_call) = ast::MacroCall::cast(code.clone()) {
192-
if let Some(expanded) = self.sema.expand(&macro_call) {
192+
if let Some(expanded) = self.sema.expand_macro_call(&macro_call) {
193193
if let Some(tt) = macro_call.token_tree() {
194194
// When matching within a macro expansion, we only want to allow matches of
195195
// nodes that originated entirely from within the token tree of the macro call.

src/tools/rust-analyzer/crates/ide/src/expand_macro.rs

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use hir::db::ExpandDatabase;
2-
use hir::{InFile, MacroFileIdExt, Semantics};
2+
use hir::{ExpandResult, InFile, MacroFileIdExt, Semantics};
33
use ide_db::base_db::CrateId;
44
use ide_db::{
55
helpers::pick_best_token, syntax_helpers::prettify_macro_expansion, FileId, RootDatabase,
66
};
77
use span::{Edition, SpanMap, SyntaxContextId, TextRange, TextSize};
8+
use stdx::format_to;
89
use syntax::{ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T};
910

1011
use crate::FilePosition;
@@ -63,17 +64,23 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
6364
.take_while(|it| it != &token)
6465
.filter(|it| it.kind() == T![,])
6566
.count();
66-
let expansion = expansions.get(idx)?.clone();
67+
let ExpandResult { err, value: expansion } = expansions.get(idx)?.clone();
6768
let expansion_file_id = sema.hir_file_for(&expansion).macro_file()?;
6869
let expansion_span_map = db.expansion_span_map(expansion_file_id);
69-
let expansion = format(
70+
let mut expansion = format(
7071
db,
7172
SyntaxKind::MACRO_ITEMS,
7273
position.file_id,
7374
expansion,
7475
&expansion_span_map,
7576
krate,
7677
);
78+
if let Some(err) = err {
79+
expansion.insert_str(
80+
0,
81+
&format!("Expansion had errors: {}\n\n", err.render_to_string(sema.db)),
82+
);
83+
}
7784
Some(ExpandedMacro { name, expansion })
7885
});
7986

@@ -83,6 +90,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
8390

8491
let mut anc = tok.parent_ancestors();
8592
let mut span_map = SpanMap::empty();
93+
let mut error = String::new();
8694
let (name, expanded, kind) = loop {
8795
let node = anc.next()?;
8896

@@ -97,7 +105,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
97105
.unwrap_or(Edition::CURRENT),
98106
)
99107
.to_string(),
100-
expand_macro_recur(&sema, &item, &mut span_map, TextSize::new(0))?,
108+
expand_macro_recur(&sema, &item, &mut error, &mut span_map, TextSize::new(0))?,
101109
SyntaxKind::MACRO_ITEMS,
102110
);
103111
}
@@ -112,6 +120,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
112120
expand_macro_recur(
113121
&sema,
114122
&ast::Item::MacroCall(mac),
123+
&mut error,
115124
&mut span_map,
116125
TextSize::new(0),
117126
)?,
@@ -123,24 +132,31 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
123132
// FIXME:
124133
// macro expansion may lose all white space information
125134
// But we hope someday we can use ra_fmt for that
126-
let expansion = format(db, kind, position.file_id, expanded, &span_map, krate);
135+
let mut expansion = format(db, kind, position.file_id, expanded, &span_map, krate);
127136

137+
if !error.is_empty() {
138+
expansion.insert_str(0, &format!("Expansion had errors:{error}\n\n"));
139+
}
128140
Some(ExpandedMacro { name, expansion })
129141
}
130142

131143
fn expand_macro_recur(
132144
sema: &Semantics<'_, RootDatabase>,
133145
macro_call: &ast::Item,
146+
error: &mut String,
134147
result_span_map: &mut SpanMap<SyntaxContextId>,
135148
offset_in_original_node: TextSize,
136149
) -> Option<SyntaxNode> {
137-
let expanded = match macro_call {
138-
item @ ast::Item::MacroCall(macro_call) => sema
139-
.expand_attr_macro(item)
140-
.or_else(|| sema.expand_allowed_builtins(macro_call))?
141-
.clone_for_update(),
142-
item => sema.expand_attr_macro(item)?.clone_for_update(),
150+
let ExpandResult { value: expanded, err } = match macro_call {
151+
item @ ast::Item::MacroCall(macro_call) => {
152+
sema.expand_attr_macro(item).or_else(|| sema.expand_allowed_builtins(macro_call))?
153+
}
154+
item => sema.expand_attr_macro(item)?,
143155
};
156+
let expanded = expanded.clone_for_update();
157+
if let Some(err) = err {
158+
format_to!(error, "\n{}", err.render_to_string(sema.db));
159+
}
144160
let file_id =
145161
sema.hir_file_for(&expanded).macro_file().expect("expansion must produce a macro file");
146162
let expansion_span_map = sema.db.expansion_span_map(file_id);
@@ -149,12 +165,13 @@ fn expand_macro_recur(
149165
expanded.text_range().len(),
150166
&expansion_span_map,
151167
);
152-
Some(expand(sema, expanded, result_span_map, u32::from(offset_in_original_node) as i32))
168+
Some(expand(sema, expanded, error, result_span_map, u32::from(offset_in_original_node) as i32))
153169
}
154170

155171
fn expand(
156172
sema: &Semantics<'_, RootDatabase>,
157173
expanded: SyntaxNode,
174+
error: &mut String,
158175
result_span_map: &mut SpanMap<SyntaxContextId>,
159176
mut offset_in_original_node: i32,
160177
) -> SyntaxNode {
@@ -165,6 +182,7 @@ fn expand(
165182
if let Some(new_node) = expand_macro_recur(
166183
sema,
167184
&child,
185+
error,
168186
result_span_map,
169187
TextSize::new(
170188
(offset_in_original_node + (u32::from(child.syntax().text_range().start()) as i32))
@@ -495,6 +513,9 @@ fn main() {
495513
"#,
496514
expect![[r#"
497515
foo!
516+
Expansion had errors:
517+
expected ident: `BAD`
518+
498519
"#]],
499520
);
500521
}

src/tools/rust-analyzer/crates/ide/src/highlight_related.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,7 @@ impl<'a> WalkExpandedExprCtx<'a> {
608608

609609
if let ast::Expr::MacroExpr(expr) = expr {
610610
if let Some(expanded) =
611-
expr.macro_call().and_then(|call| self.sema.expand(&call))
611+
expr.macro_call().and_then(|call| self.sema.expand_macro_call(&call))
612612
{
613613
match_ast! {
614614
match expanded {

0 commit comments

Comments
 (0)