path::{ModPath, Path},
src::HasSource,
AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, ModuleId,
+ UnresolvedMacro,
};
/// A subset of Expander that only deals with cfg attributes. We only need it to
&mut self,
db: &dyn DefDatabase,
macro_call: ast::MacroCall,
- ) -> ExpandResult<Option<(Mark, T)>> {
+ ) -> Result<ExpandResult<Option<(Mark, T)>>, UnresolvedMacro> {
if self.recursion_limit + 1 > EXPANSION_RECURSION_LIMIT {
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 call_id =
macro_call.as_call_id_with_errors(db, self.def_map.krate(), resolver, &mut |e| {
err.get_or_insert(e);
- });
+ })?;
let call_id = match call_id {
- Some(it) => it,
- None => {
- if err.is_none() {
- log::warn!("no error despite `as_call_id_with_errors` returning `None`");
- }
- return ExpandResult { value: None, err };
+ Ok(it) => it,
+ Err(_) => {
+ return Ok(ExpandResult { value: None, err });
}
};
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())
- }));
+ })));
}
};
Some(it) => it,
None => {
// This can happen without being an error, so only forward previous errors.
- return ExpandResult { value: None, err };
+ return Ok(ExpandResult { value: None, err });
}
};
self.current_file_id = 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) {
use hir_expand::diagnostics::DiagnosticSink;
-use crate::diagnostics::{InactiveCode, MacroError, UnresolvedProcMacro};
+use crate::diagnostics::{InactiveCode, MacroError, UnresolvedMacroCall, UnresolvedProcMacro};
#[derive(Debug, Eq, PartialEq)]
pub(crate) enum BodyDiagnostic {
InactiveCode(InactiveCode),
MacroError(MacroError),
UnresolvedProcMacro(UnresolvedProcMacro),
+ UnresolvedMacroCall(UnresolvedMacroCall),
}
impl BodyDiagnostic {
BodyDiagnostic::UnresolvedProcMacro(diag) => {
sink.push(diag.clone());
}
+ BodyDiagnostic::UnresolvedMacroCall(diag) => {
+ sink.push(diag.clone());
+ }
}
}
}
body::{Body, BodySourceMap, Expander, LabelSource, PatPtr, SyntheticSyntax},
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
db::DefDatabase,
- diagnostics::{InactiveCode, MacroError, UnresolvedProcMacro},
+ diagnostics::{InactiveCode, MacroError, UnresolvedMacroCall, UnresolvedProcMacro},
expr::{
dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Label,
LabelId, Literal, LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField,
item_scope::BuiltinShadowMode,
path::{GenericArgs, Path},
type_ref::{Mutability, Rawness, TypeRef},
- AdtId, BlockLoc, ModuleDefId,
+ AdtId, BlockLoc, ModuleDefId, UnresolvedMacro,
};
use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource};
let macro_call = self.expander.to_source(AstPtr::new(&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 {
Some(ExpandError::UnresolvedProcMacro) => {
self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedProcMacro(
);
}
+#[test]
+fn unresolved_macro_diag() {
+ check_diagnostics(
+ r#"
+fn f() {
+ m!();
+ //^^^^ unresolved macro call
+}
+ "#,
+ );
+}
+
#[test]
fn dollar_crate_in_builtin_macro() {
check_diagnostics(
let ast_id_map = db.ast_id_map(file_id);
let root = db.parse_or_expand(file_id).unwrap();
let call = ast_id_map.get(call.ast_id).to_node(&root);
-
- if let Some((mark, mac)) = expander.enter_expand(db, call).value {
- let src: InFile<ast::MacroItems> = expander.to_source(mac);
- let item_tree = db.item_tree(src.file_id);
- let iter =
- item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item);
- items.extend(collect_items(
- db,
- module,
- expander,
- iter,
- src.file_id,
- container,
- limit - 1,
- ));
-
- expander.exit(db, mark);
+ let res = expander.enter_expand(db, call);
+
+ if let Ok(res) = res {
+ if let Some((mark, mac)) = res.value {
+ let src: InFile<ast::MacroItems> = expander.to_source(mac);
+ let item_tree = db.item_tree(src.file_id);
+ let iter =
+ item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item);
+ items.extend(collect_items(
+ db,
+ module,
+ expander,
+ iter,
+ src.file_id,
+ container,
+ limit - 1,
+ ));
+
+ expander.exit(db, mark);
+ }
}
}
}
//
// This diagnostic is triggered if rust-analyzer is unable to resolve the path to a
// macro in a macro invocation.
-#[derive(Debug)]
+#[derive(Debug, Clone, Eq, PartialEq)]
pub struct UnresolvedMacroCall {
pub file: HirFileId,
pub node: AstPtr<ast::MacroCall>,
use base_db::{impl_intern_key, salsa, CrateId};
use hir_expand::{
ast_id_map::FileAstId,
- eager::{expand_eager_macro, ErrorEmitted},
+ eager::{expand_eager_macro, ErrorEmitted, ErrorSink},
hygiene::Hygiene,
AstId, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
};
krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
) -> 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(
krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
error_sink: &mut dyn FnMut(mbe::ExpandError),
- ) -> Option<MacroCallId>;
+ ) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro>;
}
impl AsMacroCall for InFile<&ast::MacroCall> {
db: &dyn db::DefDatabase,
krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
- error_sink: &mut dyn FnMut(mbe::ExpandError),
- ) -> Option<MacroCallId> {
+ mut error_sink: &mut dyn FnMut(mbe::ExpandError),
+ ) -> 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 h = Hygiene::new(db.upcast(), self.file_id);
let path = self.value.path().and_then(|path| path::ModPath::from_src(path, &h));
- if path.is_none() {
- error_sink(mbe::ExpandError::Other("malformed macro invocation".into()));
- }
+ let path = match error_sink
+ .option(path, || mbe::ExpandError::Other("malformed macro invocation".into()))
+ {
+ Ok(path) => path,
+ Err(error) => {
+ return Ok(Err(error));
+ }
+ };
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,
krate,
resolver,
error_sink,
)
- .ok()?
- .ok()
}
}
}
}
-struct UnresolvedMacro;
+pub struct UnresolvedMacro;
fn macro_call_as_call_id(
call: &AstIdWithPath<ast::MacroCall>,
_private: (),
}
-trait ErrorSink {
+pub trait ErrorSink {
fn emit(&mut self, err: mbe::ExpandError);
fn option<T>(