mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-28 05:53:45 +00:00
Fix duplicate eager expansion errors
This commit is contained in:
parent
d1632c2727
commit
0f4ffaa5af
10 changed files with 70 additions and 50 deletions
|
@ -638,7 +638,6 @@ impl<'a> AssocItemCollector<'a> {
|
||||||
self.items.push((item.name.clone(), def.into()));
|
self.items.push((item.name.clone(), def.into()));
|
||||||
}
|
}
|
||||||
AssocItem::MacroCall(call) => {
|
AssocItem::MacroCall(call) => {
|
||||||
// TODO: These are the wrong errors to report, report in collect_macro_items instead
|
|
||||||
let file_id = self.expander.current_file_id();
|
let file_id = self.expander.current_file_id();
|
||||||
let root = self.db.parse_or_expand(file_id);
|
let root = self.db.parse_or_expand(file_id);
|
||||||
if let Some(root) = root {
|
if let Some(root) = root {
|
||||||
|
|
|
@ -101,6 +101,7 @@ pub struct ItemTree {
|
||||||
top_level: SmallVec<[ModItem; 1]>,
|
top_level: SmallVec<[ModItem; 1]>,
|
||||||
attrs: FxHashMap<AttrOwner, RawAttrs>,
|
attrs: FxHashMap<AttrOwner, RawAttrs>,
|
||||||
|
|
||||||
|
// FIXME: Remove this indirection, an item tree is almost always non-empty?
|
||||||
data: Option<Box<ItemTreeData>>,
|
data: Option<Box<ItemTreeData>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -823,7 +823,7 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
|
||||||
return Ok(ExpandResult::only_err(ExpandError::Other("malformed macro invocation".into())));
|
return Ok(ExpandResult::only_err(ExpandError::Other("malformed macro invocation".into())));
|
||||||
};
|
};
|
||||||
|
|
||||||
macro_call_as_call_id(
|
macro_call_as_call_id_(
|
||||||
db,
|
db,
|
||||||
&AstIdWithPath::new(ast_id.file_id, ast_id.value, path),
|
&AstIdWithPath::new(ast_id.file_id, ast_id.value, path),
|
||||||
expands_to,
|
expands_to,
|
||||||
|
@ -852,6 +852,16 @@ fn macro_call_as_call_id(
|
||||||
expand_to: ExpandTo,
|
expand_to: ExpandTo,
|
||||||
krate: CrateId,
|
krate: CrateId,
|
||||||
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
||||||
|
) -> Result<Option<MacroCallId>, UnresolvedMacro> {
|
||||||
|
macro_call_as_call_id_(db, call, expand_to, krate, resolver).map(|res| res.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn macro_call_as_call_id_(
|
||||||
|
db: &dyn db::DefDatabase,
|
||||||
|
call: &AstIdWithPath<ast::MacroCall>,
|
||||||
|
expand_to: ExpandTo,
|
||||||
|
krate: CrateId,
|
||||||
|
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
||||||
) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
|
) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
|
||||||
let def =
|
let def =
|
||||||
resolver(call.path.clone()).ok_or_else(|| UnresolvedMacro { path: call.path.clone() })?;
|
resolver(call.path.clone()).ok_or_else(|| UnresolvedMacro { path: call.path.clone() })?;
|
||||||
|
|
|
@ -1117,8 +1117,9 @@ impl DefCollector<'_> {
|
||||||
self.def_map.krate,
|
self.def_map.krate,
|
||||||
resolver_def_id,
|
resolver_def_id,
|
||||||
);
|
);
|
||||||
if let Ok(ExpandResult { value: Some(call_id), .. }) = call_id {
|
if let Ok(Some(call_id)) = call_id {
|
||||||
push_resolved(directive, call_id);
|
push_resolved(directive, call_id);
|
||||||
|
|
||||||
res = ReachedFixedPoint::No;
|
res = ReachedFixedPoint::No;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1354,26 +1355,30 @@ impl DefCollector<'_> {
|
||||||
let file_id = macro_call_id.as_file();
|
let file_id = macro_call_id.as_file();
|
||||||
|
|
||||||
// First, fetch the raw expansion result for purposes of error reporting. This goes through
|
// First, fetch the raw expansion result for purposes of error reporting. This goes through
|
||||||
// `macro_expand_error` to avoid depending on the full expansion result (to improve
|
// `parse_macro_expansion_error` to avoid depending on the full expansion result (to improve
|
||||||
// incrementality).
|
// incrementality).
|
||||||
let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id);
|
let ExpandResult { value, err } = self.db.parse_macro_expansion_error(macro_call_id);
|
||||||
let err = self.db.macro_expand_error(macro_call_id);
|
|
||||||
if let Some(err) = err {
|
if let Some(err) = err {
|
||||||
|
let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id);
|
||||||
let diag = match err {
|
let diag = match err {
|
||||||
|
// why is this reported here?
|
||||||
hir_expand::ExpandError::UnresolvedProcMacro(krate) => {
|
hir_expand::ExpandError::UnresolvedProcMacro(krate) => {
|
||||||
always!(krate == loc.def.krate);
|
always!(krate == loc.def.krate);
|
||||||
// Missing proc macros are non-fatal, so they are handled specially.
|
|
||||||
DefDiagnostic::unresolved_proc_macro(module_id, loc.kind.clone(), loc.def.krate)
|
DefDiagnostic::unresolved_proc_macro(module_id, loc.kind.clone(), loc.def.krate)
|
||||||
}
|
}
|
||||||
_ => DefDiagnostic::macro_error(module_id, loc.kind, err.to_string()),
|
_ => DefDiagnostic::macro_error(module_id, loc.kind.clone(), err.to_string()),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.def_map.diagnostics.push(diag);
|
self.def_map.diagnostics.push(diag);
|
||||||
}
|
}
|
||||||
|
if let Some(errors) = value {
|
||||||
|
let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id);
|
||||||
|
let diag = DefDiagnostic::macro_expansion_parse_error(module_id, loc.kind, &errors);
|
||||||
|
self.def_map.diagnostics.push(diag);
|
||||||
|
}
|
||||||
|
|
||||||
// Then, fetch and process the item tree. This will reuse the expansion result from above.
|
// Then, fetch and process the item tree. This will reuse the expansion result from above.
|
||||||
let item_tree = self.db.file_item_tree(file_id);
|
let item_tree = self.db.file_item_tree(file_id);
|
||||||
// FIXME: report parse errors for the macro expansion here
|
|
||||||
|
|
||||||
let mod_dir = self.mod_dirs[&module_id].clone();
|
let mod_dir = self.mod_dirs[&module_id].clone();
|
||||||
ModCollector {
|
ModCollector {
|
||||||
|
@ -1395,6 +1400,7 @@ impl DefCollector<'_> {
|
||||||
for directive in &self.unresolved_macros {
|
for directive in &self.unresolved_macros {
|
||||||
match &directive.kind {
|
match &directive.kind {
|
||||||
MacroDirectiveKind::FnLike { ast_id, expand_to } => {
|
MacroDirectiveKind::FnLike { ast_id, expand_to } => {
|
||||||
|
// FIXME: we shouldn't need to re-resolve the macro here just to get the unresolved error!
|
||||||
let macro_call_as_call_id = macro_call_as_call_id(
|
let macro_call_as_call_id = macro_call_as_call_id(
|
||||||
self.db,
|
self.db,
|
||||||
ast_id,
|
ast_id,
|
||||||
|
@ -2110,7 +2116,7 @@ impl ModCollector<'_, '_> {
|
||||||
let ast_id = AstIdWithPath::new(self.file_id(), mac.ast_id, ModPath::clone(&mac.path));
|
let ast_id = AstIdWithPath::new(self.file_id(), mac.ast_id, ModPath::clone(&mac.path));
|
||||||
|
|
||||||
// Case 1: try to resolve in legacy scope and expand macro_rules
|
// Case 1: try to resolve in legacy scope and expand macro_rules
|
||||||
match macro_call_as_call_id(
|
if let Ok(res) = macro_call_as_call_id(
|
||||||
self.def_collector.db,
|
self.def_collector.db,
|
||||||
&ast_id,
|
&ast_id,
|
||||||
mac.expand_to,
|
mac.expand_to,
|
||||||
|
@ -2131,29 +2137,18 @@ impl ModCollector<'_, '_> {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
Ok(res) => {
|
// Legacy macros need to be expanded immediately, so that any macros they produce
|
||||||
// Legacy macros need to be expanded immediately, so that any macros they produce
|
// are in scope.
|
||||||
// are in scope.
|
if let Some(val) = res {
|
||||||
if let Some(val) = res.value {
|
self.def_collector.collect_macro_expansion(
|
||||||
self.def_collector.collect_macro_expansion(
|
self.module_id,
|
||||||
self.module_id,
|
val,
|
||||||
val,
|
self.macro_depth + 1,
|
||||||
self.macro_depth + 1,
|
container,
|
||||||
container,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(err) = res.err {
|
|
||||||
self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error(
|
|
||||||
self.module_id,
|
|
||||||
MacroCallKind::FnLike { ast_id: ast_id.ast_id, expand_to: mac.expand_to },
|
|
||||||
err.to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
Err(UnresolvedMacro { .. }) => (),
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Case 2: resolve in module scope, expand during name resolution.
|
// Case 2: resolve in module scope, expand during name resolution.
|
||||||
|
|
|
@ -140,7 +140,7 @@ m!(Z);
|
||||||
let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
|
let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
|
||||||
assert_eq!(n_recalculated_item_trees, 6);
|
assert_eq!(n_recalculated_item_trees, 6);
|
||||||
let n_reparsed_macros =
|
let n_reparsed_macros =
|
||||||
events.iter().filter(|it| it.contains("parse_macro_expansion")).count();
|
events.iter().filter(|it| it.contains("parse_macro_expansion(")).count();
|
||||||
assert_eq!(n_reparsed_macros, 3);
|
assert_eq!(n_reparsed_macros, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ use mbe::syntax_node_to_token_tree;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, HasAttrs, HasDocComments},
|
ast::{self, HasAttrs, HasDocComments},
|
||||||
AstNode, GreenNode, Parse, SyntaxNode, SyntaxToken, T,
|
AstNode, GreenNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -132,15 +132,18 @@ pub trait ExpandDatabase: SourceDatabase {
|
||||||
/// just fetches procedural ones.
|
/// just fetches procedural ones.
|
||||||
fn macro_def(&self, id: MacroDefId) -> Result<Arc<TokenExpander>, mbe::ParseError>;
|
fn macro_def(&self, id: MacroDefId) -> Result<Arc<TokenExpander>, mbe::ParseError>;
|
||||||
|
|
||||||
/// Expand macro call to a token tree. This query is LRUed (we keep 128 or so results in memory)
|
/// Expand macro call to a token tree.
|
||||||
fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>;
|
fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>;
|
||||||
/// Special case of the previous query for procedural macros. We can't LRU
|
/// Special case of the previous query for procedural macros. We can't LRU
|
||||||
/// proc macros, since they are not deterministic in general, and
|
/// proc macros, since they are not deterministic in general, and
|
||||||
/// non-determinism breaks salsa in a very, very, very bad way. @edwin0cheng
|
/// non-determinism breaks salsa in a very, very, very bad way. @edwin0cheng
|
||||||
/// heroically debugged this once!
|
/// heroically debugged this once!
|
||||||
fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult<tt::Subtree>;
|
fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult<tt::Subtree>;
|
||||||
/// Firewall query that returns the error from the `macro_expand` query.
|
/// Firewall query that returns the errors from the `parse_macro_expansion` query.
|
||||||
fn macro_expand_error(&self, macro_call: MacroCallId) -> Option<ExpandError>;
|
fn parse_macro_expansion_error(
|
||||||
|
&self,
|
||||||
|
macro_call: MacroCallId,
|
||||||
|
) -> ExpandResult<Option<Box<[SyntaxError]>>>;
|
||||||
|
|
||||||
fn hygiene_frame(&self, file_id: HirFileId) -> Arc<HygieneFrame>;
|
fn hygiene_frame(&self, file_id: HirFileId) -> Arc<HygieneFrame>;
|
||||||
}
|
}
|
||||||
|
@ -448,6 +451,7 @@ fn macro_def(
|
||||||
fn macro_expand(
|
fn macro_expand(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn ExpandDatabase,
|
||||||
id: MacroCallId,
|
id: MacroCallId,
|
||||||
|
// FIXME: Remove the OPtion if possible
|
||||||
) -> ExpandResult<Option<Arc<tt::Subtree>>> {
|
) -> ExpandResult<Option<Arc<tt::Subtree>>> {
|
||||||
let _p = profile::span("macro_expand");
|
let _p = profile::span("macro_expand");
|
||||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(id);
|
let loc: MacroCallLoc = db.lookup_intern_macro_call(id);
|
||||||
|
@ -498,8 +502,12 @@ fn macro_expand(
|
||||||
ExpandResult { value: Some(Arc::new(tt)), err }
|
ExpandResult { value: Some(Arc::new(tt)), err }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn macro_expand_error(db: &dyn ExpandDatabase, macro_call: MacroCallId) -> Option<ExpandError> {
|
fn parse_macro_expansion_error(
|
||||||
db.macro_expand(macro_call).err
|
db: &dyn ExpandDatabase,
|
||||||
|
macro_call_id: MacroCallId,
|
||||||
|
) -> ExpandResult<Option<Box<[SyntaxError]>>> {
|
||||||
|
db.parse_macro_expansion(MacroFile { macro_call_id })
|
||||||
|
.map(|it| it.map(|(it, _)| it.errors().to_vec().into_boxed_slice()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<tt::Subtree> {
|
fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<tt::Subtree> {
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
pub use hir_def::db::*;
|
pub use hir_def::db::*;
|
||||||
pub use hir_expand::db::{
|
pub use hir_expand::db::{
|
||||||
AstIdMapQuery, ExpandDatabase, ExpandDatabaseStorage, ExpandProcMacroQuery, HygieneFrameQuery,
|
AstIdMapQuery, ExpandDatabase, ExpandDatabaseStorage, ExpandProcMacroQuery, HygieneFrameQuery,
|
||||||
InternMacroCallQuery, MacroArgTextQuery, MacroDefQuery, MacroExpandErrorQuery,
|
InternMacroCallQuery, MacroArgTextQuery, MacroDefQuery, MacroExpandQuery,
|
||||||
MacroExpandQuery, ParseMacroExpansionQuery,
|
ParseMacroExpansionQuery,
|
||||||
};
|
};
|
||||||
pub use hir_ty::db::*;
|
pub use hir_ty::db::*;
|
||||||
|
|
||||||
|
|
|
@ -80,7 +80,6 @@ impl RootDatabase {
|
||||||
hir::db::MacroDefQuery
|
hir::db::MacroDefQuery
|
||||||
hir::db::MacroExpandQuery
|
hir::db::MacroExpandQuery
|
||||||
hir::db::ExpandProcMacroQuery
|
hir::db::ExpandProcMacroQuery
|
||||||
hir::db::MacroExpandErrorQuery
|
|
||||||
hir::db::HygieneFrameQuery
|
hir::db::HygieneFrameQuery
|
||||||
|
|
||||||
// DefDatabase
|
// DefDatabase
|
||||||
|
|
|
@ -152,7 +152,6 @@ impl RootDatabase {
|
||||||
let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_LRU_CAP);
|
let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_LRU_CAP);
|
||||||
base_db::ParseQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
|
base_db::ParseQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
|
||||||
hir::db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
|
hir::db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
|
||||||
hir::db::MacroExpandQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_lru_capacities(&mut self, lru_capacities: &FxHashMap<Box<str>, usize>) {
|
pub fn update_lru_capacities(&mut self, lru_capacities: &FxHashMap<Box<str>, usize>) {
|
||||||
|
@ -167,12 +166,6 @@ impl RootDatabase {
|
||||||
.copied()
|
.copied()
|
||||||
.unwrap_or(base_db::DEFAULT_LRU_CAP),
|
.unwrap_or(base_db::DEFAULT_LRU_CAP),
|
||||||
);
|
);
|
||||||
hir_db::MacroExpandQuery.in_db_mut(self).set_lru_capacity(
|
|
||||||
lru_capacities
|
|
||||||
.get(stringify!(MacroExpandQuery))
|
|
||||||
.copied()
|
|
||||||
.unwrap_or(base_db::DEFAULT_LRU_CAP),
|
|
||||||
);
|
|
||||||
|
|
||||||
macro_rules! update_lru_capacity_per_query {
|
macro_rules! update_lru_capacity_per_query {
|
||||||
($( $module:ident :: $query:ident )*) => {$(
|
($( $module:ident :: $query:ident )*) => {$(
|
||||||
|
@ -201,7 +194,6 @@ impl RootDatabase {
|
||||||
hir_db::MacroDefQuery
|
hir_db::MacroDefQuery
|
||||||
// hir_db::MacroExpandQuery
|
// hir_db::MacroExpandQuery
|
||||||
hir_db::ExpandProcMacroQuery
|
hir_db::ExpandProcMacroQuery
|
||||||
hir_db::MacroExpandErrorQuery
|
|
||||||
hir_db::HygieneFrameQuery
|
hir_db::HygieneFrameQuery
|
||||||
|
|
||||||
// DefDatabase
|
// DefDatabase
|
||||||
|
|
|
@ -238,6 +238,22 @@ fn f() {
|
||||||
//^^^ error: invalid macro definition: expected subtree
|
//^^^ error: invalid macro definition: expected subtree
|
||||||
|
|
||||||
}
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expansion_syntax_diagnostic() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
macro_rules! foo {
|
||||||
|
() => { struct; };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
foo!();
|
||||||
|
//^^^ error: Syntax Error in Expansion: expected a name
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue