mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-03 15:15:24 +00:00
Merge #8048
8048: Fix missing unresolved macro diagnostic in function body r=edwin0cheng a=brandondong This was an issue I found while working on https://github.com/rust-analyzer/rust-analyzer/pull/7970. **Reproduction:** 1. Call a non-existent macro in a function body. ``` fn main() { foo!(); } ``` 2. No diagnostics are raised. An unresolved-macro-call diagnostic is expected. 3. If the macro call is instead outside of the function body, this works as expected. I believe this worked previously and regressed in https://github.com/rust-analyzer/rust-analyzer/pull/7805. **Behavior prior to https://github.com/rust-analyzer/rust-analyzer/pull/7805:** - The unresolved-macro-call diagnostic did not exist. Instead, a macro-error diagnostic would be raised with the text "could not resolve macro [path]". - This was implemented by adding an error to the error sink (https://github.com/rust-analyzer/rust-analyzer/pull/7805/files#diff-50a326c5ae465bd9b31ee4310186380aa06e4fa1f6b41dbc0aed5bcc656a3cb8L657). - The error was propagated through1a82af3527/crates/hir_def/src/body.rs (L123)
eventually reaching1a82af3527/crates/hir_def/src/body/lower.rs (L569)
. **Behavior after:** - Instead of writing to the error sink, an UnresolvedMacro error is now returned (https://github.com/rust-analyzer/rust-analyzer/pull/7805/files#diff-50a326c5ae465bd9b31ee4310186380aa06e4fa1f6b41dbc0aed5bcc656a3cb8R631). - The parent caller throws away the error as its function signature is `Option<MacroCallId>` (https://github.com/rust-analyzer/rust-analyzer/pull/7805/files#diff-50a326c5ae465bd9b31ee4310186380aa06e4fa1f6b41dbc0aed5bcc656a3cb8R604). - We instead now reach the warn condition (1a82af3527/crates/hir_def/src/body.rs (L124)
) and no diagnostics are created in1a82af3527/crates/hir_def/src/body/lower.rs (L575)
. **Fix:** - Make sure to propagate the UnresolvedMacro error. Report the error using the new unresolved-macro-call diagnostic. Co-authored-by: Brandon <brandondong604@hotmail.com>
This commit is contained in:
commit
6fcb5d772f
8 changed files with 78 additions and 45 deletions
|
@ -32,6 +32,7 @@ use crate::{
|
||||||
path::{ModPath, Path},
|
path::{ModPath, Path},
|
||||||
src::HasSource,
|
src::HasSource,
|
||||||
AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, ModuleId,
|
AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, ModuleId,
|
||||||
|
UnresolvedMacro,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A subset of Expander that only deals with cfg attributes. We only need it to
|
/// A subset of Expander that only deals with cfg attributes. We only need it to
|
||||||
|
@ -101,10 +102,12 @@ impl Expander {
|
||||||
&mut self,
|
&mut self,
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
macro_call: ast::MacroCall,
|
macro_call: ast::MacroCall,
|
||||||
) -> ExpandResult<Option<(Mark, T)>> {
|
) -> Result<ExpandResult<Option<(Mark, T)>>, UnresolvedMacro> {
|
||||||
if self.recursion_limit + 1 > EXPANSION_RECURSION_LIMIT {
|
if self.recursion_limit + 1 > EXPANSION_RECURSION_LIMIT {
|
||||||
cov_mark::hit!(your_stack_belongs_to_me);
|
cov_mark::hit!(your_stack_belongs_to_me);
|
||||||
return ExpandResult::str_err("reached recursion limit during macro expansion".into());
|
return Ok(ExpandResult::str_err(
|
||||||
|
"reached recursion limit during macro expansion".into(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let macro_call = InFile::new(self.current_file_id, ¯o_call);
|
let macro_call = InFile::new(self.current_file_id, ¯o_call);
|
||||||
|
@ -116,14 +119,11 @@ impl Expander {
|
||||||
let call_id =
|
let call_id =
|
||||||
macro_call.as_call_id_with_errors(db, self.def_map.krate(), resolver, &mut |e| {
|
macro_call.as_call_id_with_errors(db, self.def_map.krate(), resolver, &mut |e| {
|
||||||
err.get_or_insert(e);
|
err.get_or_insert(e);
|
||||||
});
|
})?;
|
||||||
let call_id = match call_id {
|
let call_id = match call_id {
|
||||||
Some(it) => it,
|
Ok(it) => it,
|
||||||
None => {
|
Err(_) => {
|
||||||
if err.is_none() {
|
return Ok(ExpandResult { value: None, err });
|
||||||
log::warn!("no error despite `as_call_id_with_errors` returning `None`");
|
|
||||||
}
|
|
||||||
return ExpandResult { value: None, err };
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -141,9 +141,9 @@ impl Expander {
|
||||||
log::warn!("no error despite `parse_or_expand` failing");
|
log::warn!("no error despite `parse_or_expand` failing");
|
||||||
}
|
}
|
||||||
|
|
||||||
return ExpandResult::only_err(err.unwrap_or_else(|| {
|
return Ok(ExpandResult::only_err(err.unwrap_or_else(|| {
|
||||||
mbe::ExpandError::Other("failed to parse macro invocation".into())
|
mbe::ExpandError::Other("failed to parse macro invocation".into())
|
||||||
}));
|
})));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ impl Expander {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => {
|
None => {
|
||||||
// This can happen without being an error, so only forward previous errors.
|
// This can happen without being an error, so only forward previous errors.
|
||||||
return ExpandResult { value: None, err };
|
return Ok(ExpandResult { value: None, err });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -167,7 +167,7 @@ impl Expander {
|
||||||
self.current_file_id = file_id;
|
self.current_file_id = file_id;
|
||||||
self.ast_id_map = db.ast_id_map(file_id);
|
self.ast_id_map = db.ast_id_map(file_id);
|
||||||
|
|
||||||
ExpandResult { value: Some((mark, node)), err }
|
Ok(ExpandResult { value: Some((mark, node)), err })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
|
pub(crate) fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
|
||||||
|
|
|
@ -2,13 +2,14 @@
|
||||||
|
|
||||||
use hir_expand::diagnostics::DiagnosticSink;
|
use hir_expand::diagnostics::DiagnosticSink;
|
||||||
|
|
||||||
use crate::diagnostics::{InactiveCode, MacroError, UnresolvedProcMacro};
|
use crate::diagnostics::{InactiveCode, MacroError, UnresolvedMacroCall, UnresolvedProcMacro};
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub(crate) enum BodyDiagnostic {
|
pub(crate) enum BodyDiagnostic {
|
||||||
InactiveCode(InactiveCode),
|
InactiveCode(InactiveCode),
|
||||||
MacroError(MacroError),
|
MacroError(MacroError),
|
||||||
UnresolvedProcMacro(UnresolvedProcMacro),
|
UnresolvedProcMacro(UnresolvedProcMacro),
|
||||||
|
UnresolvedMacroCall(UnresolvedMacroCall),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BodyDiagnostic {
|
impl BodyDiagnostic {
|
||||||
|
@ -23,6 +24,9 @@ impl BodyDiagnostic {
|
||||||
BodyDiagnostic::UnresolvedProcMacro(diag) => {
|
BodyDiagnostic::UnresolvedProcMacro(diag) => {
|
||||||
sink.push(diag.clone());
|
sink.push(diag.clone());
|
||||||
}
|
}
|
||||||
|
BodyDiagnostic::UnresolvedMacroCall(diag) => {
|
||||||
|
sink.push(diag.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ use crate::{
|
||||||
body::{Body, BodySourceMap, Expander, LabelSource, PatPtr, SyntheticSyntax},
|
body::{Body, BodySourceMap, Expander, LabelSource, PatPtr, SyntheticSyntax},
|
||||||
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
|
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
diagnostics::{InactiveCode, MacroError, UnresolvedProcMacro},
|
diagnostics::{InactiveCode, MacroError, UnresolvedMacroCall, UnresolvedProcMacro},
|
||||||
expr::{
|
expr::{
|
||||||
dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Label,
|
dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Label,
|
||||||
LabelId, Literal, LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField,
|
LabelId, Literal, LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField,
|
||||||
|
@ -33,7 +33,7 @@ use crate::{
|
||||||
item_scope::BuiltinShadowMode,
|
item_scope::BuiltinShadowMode,
|
||||||
path::{GenericArgs, Path},
|
path::{GenericArgs, Path},
|
||||||
type_ref::{Mutability, Rawness, TypeRef},
|
type_ref::{Mutability, Rawness, TypeRef},
|
||||||
AdtId, BlockLoc, ModuleDefId,
|
AdtId, BlockLoc, ModuleDefId, UnresolvedMacro,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource};
|
use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource};
|
||||||
|
@ -554,6 +554,17 @@ impl ExprCollector<'_> {
|
||||||
let macro_call = self.expander.to_source(AstPtr::new(&e));
|
let macro_call = self.expander.to_source(AstPtr::new(&e));
|
||||||
let res = self.expander.enter_expand(self.db, e);
|
let res = self.expander.enter_expand(self.db, e);
|
||||||
|
|
||||||
|
let res = match res {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(UnresolvedMacro) => {
|
||||||
|
self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedMacroCall(
|
||||||
|
UnresolvedMacroCall { file: outer_file, node: syntax_ptr.cast().unwrap() },
|
||||||
|
));
|
||||||
|
collector(self, None);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
match &res.err {
|
match &res.err {
|
||||||
Some(ExpandError::UnresolvedProcMacro) => {
|
Some(ExpandError::UnresolvedProcMacro) => {
|
||||||
self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedProcMacro(
|
self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedProcMacro(
|
||||||
|
|
|
@ -174,6 +174,18 @@ fn f() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unresolved_macro_diag() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
fn f() {
|
||||||
|
m!();
|
||||||
|
//^^^^ unresolved macro call
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dollar_crate_in_builtin_macro() {
|
fn dollar_crate_in_builtin_macro() {
|
||||||
check_diagnostics(
|
check_diagnostics(
|
||||||
|
|
|
@ -267,23 +267,26 @@ fn collect_items(
|
||||||
let ast_id_map = db.ast_id_map(file_id);
|
let ast_id_map = db.ast_id_map(file_id);
|
||||||
let root = db.parse_or_expand(file_id).unwrap();
|
let root = db.parse_or_expand(file_id).unwrap();
|
||||||
let call = ast_id_map.get(call.ast_id).to_node(&root);
|
let call = ast_id_map.get(call.ast_id).to_node(&root);
|
||||||
|
let res = expander.enter_expand(db, call);
|
||||||
|
|
||||||
if let Some((mark, mac)) = expander.enter_expand(db, call).value {
|
if let Ok(res) = res {
|
||||||
let src: InFile<ast::MacroItems> = expander.to_source(mac);
|
if let Some((mark, mac)) = res.value {
|
||||||
let item_tree = db.item_tree(src.file_id);
|
let src: InFile<ast::MacroItems> = expander.to_source(mac);
|
||||||
let iter =
|
let item_tree = db.item_tree(src.file_id);
|
||||||
item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item);
|
let iter =
|
||||||
items.extend(collect_items(
|
item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item);
|
||||||
db,
|
items.extend(collect_items(
|
||||||
module,
|
db,
|
||||||
expander,
|
module,
|
||||||
iter,
|
expander,
|
||||||
src.file_id,
|
iter,
|
||||||
container,
|
src.file_id,
|
||||||
limit - 1,
|
container,
|
||||||
));
|
limit - 1,
|
||||||
|
));
|
||||||
|
|
||||||
expander.exit(db, mark);
|
expander.exit(db, mark);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,7 @@ impl Diagnostic for UnresolvedImport {
|
||||||
//
|
//
|
||||||
// This diagnostic is triggered if rust-analyzer is unable to resolve the path to a
|
// This diagnostic is triggered if rust-analyzer is unable to resolve the path to a
|
||||||
// macro in a macro invocation.
|
// macro in a macro invocation.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct UnresolvedMacroCall {
|
pub struct UnresolvedMacroCall {
|
||||||
pub file: HirFileId,
|
pub file: HirFileId,
|
||||||
pub node: AstPtr<ast::MacroCall>,
|
pub node: AstPtr<ast::MacroCall>,
|
||||||
|
|
|
@ -58,7 +58,7 @@ use std::{
|
||||||
use base_db::{impl_intern_key, salsa, CrateId};
|
use base_db::{impl_intern_key, salsa, CrateId};
|
||||||
use hir_expand::{
|
use hir_expand::{
|
||||||
ast_id_map::FileAstId,
|
ast_id_map::FileAstId,
|
||||||
eager::{expand_eager_macro, ErrorEmitted},
|
eager::{expand_eager_macro, ErrorEmitted, ErrorSink},
|
||||||
hygiene::Hygiene,
|
hygiene::Hygiene,
|
||||||
AstId, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
|
AstId, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
|
||||||
};
|
};
|
||||||
|
@ -583,7 +583,7 @@ pub trait AsMacroCall {
|
||||||
krate: CrateId,
|
krate: CrateId,
|
||||||
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
||||||
) -> Option<MacroCallId> {
|
) -> Option<MacroCallId> {
|
||||||
self.as_call_id_with_errors(db, krate, resolver, &mut |_| ())
|
self.as_call_id_with_errors(db, krate, resolver, &mut |_| ()).ok()?.ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_call_id_with_errors(
|
fn as_call_id_with_errors(
|
||||||
|
@ -592,7 +592,7 @@ pub trait AsMacroCall {
|
||||||
krate: CrateId,
|
krate: CrateId,
|
||||||
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
||||||
error_sink: &mut dyn FnMut(mbe::ExpandError),
|
error_sink: &mut dyn FnMut(mbe::ExpandError),
|
||||||
) -> Option<MacroCallId>;
|
) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsMacroCall for InFile<&ast::MacroCall> {
|
impl AsMacroCall for InFile<&ast::MacroCall> {
|
||||||
|
@ -601,25 +601,28 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
|
||||||
db: &dyn db::DefDatabase,
|
db: &dyn db::DefDatabase,
|
||||||
krate: CrateId,
|
krate: CrateId,
|
||||||
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
||||||
error_sink: &mut dyn FnMut(mbe::ExpandError),
|
mut error_sink: &mut dyn FnMut(mbe::ExpandError),
|
||||||
) -> Option<MacroCallId> {
|
) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
|
||||||
let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
|
let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
|
||||||
let h = Hygiene::new(db.upcast(), self.file_id);
|
let h = Hygiene::new(db.upcast(), self.file_id);
|
||||||
let path = self.value.path().and_then(|path| path::ModPath::from_src(path, &h));
|
let path = self.value.path().and_then(|path| path::ModPath::from_src(path, &h));
|
||||||
|
|
||||||
if path.is_none() {
|
let path = match error_sink
|
||||||
error_sink(mbe::ExpandError::Other("malformed macro invocation".into()));
|
.option(path, || mbe::ExpandError::Other("malformed macro invocation".into()))
|
||||||
}
|
{
|
||||||
|
Ok(path) => path,
|
||||||
|
Err(error) => {
|
||||||
|
return Ok(Err(error));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
macro_call_as_call_id(
|
macro_call_as_call_id(
|
||||||
&AstIdWithPath::new(ast_id.file_id, ast_id.value, path?),
|
&AstIdWithPath::new(ast_id.file_id, ast_id.value, path),
|
||||||
db,
|
db,
|
||||||
krate,
|
krate,
|
||||||
resolver,
|
resolver,
|
||||||
error_sink,
|
error_sink,
|
||||||
)
|
)
|
||||||
.ok()?
|
|
||||||
.ok()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -636,7 +639,7 @@ impl<T: ast::AstNode> AstIdWithPath<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UnresolvedMacro;
|
pub struct UnresolvedMacro;
|
||||||
|
|
||||||
fn macro_call_as_call_id(
|
fn macro_call_as_call_id(
|
||||||
call: &AstIdWithPath<ast::MacroCall>,
|
call: &AstIdWithPath<ast::MacroCall>,
|
||||||
|
|
|
@ -35,7 +35,7 @@ pub struct ErrorEmitted {
|
||||||
_private: (),
|
_private: (),
|
||||||
}
|
}
|
||||||
|
|
||||||
trait ErrorSink {
|
pub trait ErrorSink {
|
||||||
fn emit(&mut self, err: mbe::ExpandError);
|
fn emit(&mut self, err: mbe::ExpandError);
|
||||||
|
|
||||||
fn option<T>(
|
fn option<T>(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue