Skip to content

Commit 82aed0b

Browse files
committed
Auto merge of #39752 - keeperofdakeys:macro-error, r=keeperofdakeys
Refactor macro resolution errors + add derive macro suggestions Move legacy macro resolution error reporting to `finalize_current_module_macro_resolutions`, and provide suggestions for derive macros. Fixes #39323 cc #30197 r? @jseyfried
2 parents ccd96c9 + 2d91e7a commit 82aed0b

27 files changed

+300
-123
lines changed

src/librustc_driver/driver.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,8 @@ pub fn phase_2_configure_and_expand<F>(sess: &Session,
726726
});
727727
}
728728

729+
after_expand(&krate)?;
730+
729731
if sess.opts.debugging_opts.input_stats {
730732
println!("Post-expansion node count: {}", count_nodes(&krate));
731733
}
@@ -751,14 +753,14 @@ pub fn phase_2_configure_and_expand<F>(sess: &Session,
751753
|| ast_validation::check_crate(sess, &krate));
752754

753755
time(time_passes, "name resolution", || -> CompileResult {
754-
// Since import resolution will eventually happen in expansion,
755-
// don't perform `after_expand` until after import resolution.
756-
after_expand(&krate)?;
757-
758756
resolver.resolve_crate(&krate);
759757
Ok(())
760758
})?;
761759

760+
if resolver.found_unresolved_macro {
761+
sess.parse_sess.span_diagnostic.abort_if_errors();
762+
}
763+
762764
// Needs to go *after* expansion to be able to check the results of macro expansion.
763765
time(time_passes, "complete gated feature checking", || {
764766
sess.track_errors(|| {

src/librustc_resolve/lib.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ use syntax::ext::hygiene::{Mark, SyntaxContext};
5151
use syntax::ast::{self, Name, NodeId, Ident, SpannedIdent, FloatTy, IntTy, UintTy};
5252
use syntax::ext::base::SyntaxExtension;
5353
use syntax::ext::base::Determinacy::{Determined, Undetermined};
54+
use syntax::ext::base::MacroKind;
5455
use syntax::symbol::{Symbol, keywords};
5556
use syntax::util::lev_distance::find_best_match_for_name;
5657

@@ -785,7 +786,7 @@ pub struct ModuleData<'a> {
785786
normal_ancestor_id: DefId,
786787

787788
resolutions: RefCell<FxHashMap<(Ident, Namespace), &'a RefCell<NameResolution<'a>>>>,
788-
legacy_macro_resolutions: RefCell<Vec<(Mark, Ident, Span)>>,
789+
legacy_macro_resolutions: RefCell<Vec<(Mark, Ident, Span, MacroKind)>>,
789790
macro_resolutions: RefCell<Vec<(Box<[Ident]>, Span)>>,
790791

791792
// Macro invocations that can expand into items in this module.
@@ -1117,6 +1118,7 @@ pub struct Resolver<'a> {
11171118
macro_map: FxHashMap<DefId, Rc<SyntaxExtension>>,
11181119
macro_exports: Vec<Export>,
11191120
pub whitelisted_legacy_custom_derives: Vec<Name>,
1121+
pub found_unresolved_macro: bool,
11201122

11211123
// Maps the `Mark` of an expansion to its containing module or block.
11221124
invocations: FxHashMap<Mark, &'a InvocationData<'a>>,
@@ -1315,6 +1317,7 @@ impl<'a> Resolver<'a> {
13151317
warned_proc_macros: FxHashSet(),
13161318
potentially_unused_imports: Vec::new(),
13171319
struct_constructors: DefIdMap(),
1320+
found_unresolved_macro: false,
13181321
}
13191322
}
13201323

src/librustc_resolve/macros.rs

+73-52
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use syntax::attr;
2424
use syntax::errors::DiagnosticBuilder;
2525
use syntax::ext::base::{self, Determinacy, MultiModifier, MultiDecorator};
2626
use syntax::ext::base::{NormalTT, Resolver as SyntaxResolver, SyntaxExtension};
27+
use syntax::ext::base::MacroKind;
2728
use syntax::ext::expand::{Expansion, mark_tts};
2829
use syntax::ext::hygiene::Mark;
2930
use syntax::ext::tt::macro_rules;
@@ -236,8 +237,8 @@ impl<'a> base::Resolver for Resolver<'a> {
236237
None
237238
}
238239

239-
fn resolve_macro(&mut self, scope: Mark, path: &ast::Path, force: bool)
240-
-> Result<Rc<SyntaxExtension>, Determinacy> {
240+
fn resolve_macro(&mut self, scope: Mark, path: &ast::Path, kind: MacroKind,
241+
force: bool) -> Result<Rc<SyntaxExtension>, Determinacy> {
241242
let ast::Path { ref segments, span } = *path;
242243
if segments.iter().any(|segment| segment.parameters.is_some()) {
243244
let kind =
@@ -256,6 +257,7 @@ impl<'a> base::Resolver for Resolver<'a> {
256257
let msg = "non-ident macro paths are experimental";
257258
let feature = "use_extern_macros";
258259
emit_feature_err(&self.session.parse_sess, feature, span, GateIssue::Language, msg);
260+
self.found_unresolved_macro = true;
259261
return Err(Determinacy::Determined);
260262
}
261263

@@ -266,7 +268,10 @@ impl<'a> base::Resolver for Resolver<'a> {
266268
},
267269
PathResult::Module(..) => unreachable!(),
268270
PathResult::Indeterminate if !force => return Err(Determinacy::Undetermined),
269-
_ => Err(Determinacy::Determined),
271+
_ => {
272+
self.found_unresolved_macro = true;
273+
Err(Determinacy::Determined)
274+
},
270275
};
271276
self.current_module.macro_resolutions.borrow_mut()
272277
.push((path.into_boxed_slice(), span));
@@ -279,40 +284,19 @@ impl<'a> base::Resolver for Resolver<'a> {
279284
Some(MacroBinding::Modern(binding)) => Ok(binding.get_macro(self)),
280285
None => match self.resolve_lexical_macro_path_segment(path[0], MacroNS, None) {
281286
Ok(binding) => Ok(binding.get_macro(self)),
282-
Err(Determinacy::Undetermined) if !force => return Err(Determinacy::Undetermined),
283-
_ => {
284-
let msg = format!("macro undefined: `{}`", name);
285-
let mut err = self.session.struct_span_err(span, &msg);
286-
self.suggest_macro_name(&name.as_str(), &mut err);
287-
err.emit();
288-
return Err(Determinacy::Determined);
289-
},
287+
Err(Determinacy::Undetermined) if !force =>
288+
return Err(Determinacy::Undetermined),
289+
Err(_) => {
290+
self.found_unresolved_macro = true;
291+
Err(Determinacy::Determined)
292+
}
290293
},
291294
};
292295

293-
if self.use_extern_macros {
294-
self.current_module.legacy_macro_resolutions.borrow_mut().push((scope, path[0], span));
295-
}
296-
result
297-
}
296+
self.current_module.legacy_macro_resolutions.borrow_mut()
297+
.push((scope, path[0], span, kind));
298298

299-
fn resolve_derive_macro(&mut self, scope: Mark, path: &ast::Path, force: bool)
300-
-> Result<Rc<SyntaxExtension>, Determinacy> {
301-
let ast::Path { span, .. } = *path;
302-
match self.resolve_macro(scope, path, false) {
303-
Ok(ext) => match *ext {
304-
SyntaxExtension::BuiltinDerive(..) |
305-
SyntaxExtension::ProcMacroDerive(..) => Ok(ext),
306-
_ => Err(Determinacy::Determined),
307-
},
308-
Err(Determinacy::Undetermined) if force => {
309-
let msg = format!("cannot find derive macro `{}` in this scope", path);
310-
let mut err = self.session.struct_span_err(span, &msg);
311-
err.emit();
312-
Err(Determinacy::Determined)
313-
},
314-
Err(err) => Err(err),
315-
}
299+
result
316300
}
317301
}
318302

@@ -438,37 +422,74 @@ impl<'a> Resolver<'a> {
438422
}
439423
}
440424

441-
for &(mark, ident, span) in module.legacy_macro_resolutions.borrow().iter() {
425+
for &(mark, ident, span, kind) in module.legacy_macro_resolutions.borrow().iter() {
442426
let legacy_scope = &self.invocations[&mark].legacy_scope;
443427
let legacy_resolution = self.resolve_legacy_scope(legacy_scope, ident.name, true);
444428
let resolution = self.resolve_lexical_macro_path_segment(ident, MacroNS, Some(span));
445-
let (legacy_resolution, resolution) = match (legacy_resolution, resolution) {
446-
(Some(legacy_resolution), Ok(resolution)) => (legacy_resolution, resolution),
429+
match (legacy_resolution, resolution) {
430+
(Some(legacy_resolution), Ok(resolution)) => {
431+
let (legacy_span, participle) = match legacy_resolution {
432+
MacroBinding::Modern(binding)
433+
if binding.def() == resolution.def() => continue,
434+
MacroBinding::Modern(binding) => (binding.span, "imported"),
435+
MacroBinding::Legacy(binding) => (binding.span, "defined"),
436+
};
437+
let msg1 = format!("`{}` could refer to the macro {} here", ident, participle);
438+
let msg2 = format!("`{}` could also refer to the macro imported here", ident);
439+
self.session.struct_span_err(span, &format!("`{}` is ambiguous", ident))
440+
.span_note(legacy_span, &msg1)
441+
.span_note(resolution.span, &msg2)
442+
.emit();
443+
},
447444
(Some(MacroBinding::Modern(binding)), Err(_)) => {
448445
self.record_use(ident, MacroNS, binding, span);
449446
self.err_if_macro_use_proc_macro(ident.name, span, binding);
450-
continue
451447
},
452-
_ => continue,
453-
};
454-
let (legacy_span, participle) = match legacy_resolution {
455-
MacroBinding::Modern(binding) if binding.def() == resolution.def() => continue,
456-
MacroBinding::Modern(binding) => (binding.span, "imported"),
457-
MacroBinding::Legacy(binding) => (binding.span, "defined"),
448+
(None, Err(_)) => {
449+
let msg = match kind {
450+
MacroKind::Bang =>
451+
format!("cannot find macro `{}!` in this scope", ident),
452+
MacroKind::Attr =>
453+
format!("cannot find attribute macro `{}` in this scope", ident),
454+
MacroKind::Derive =>
455+
format!("cannot find derive macro `{}` in this scope", ident),
456+
};
457+
let mut err = self.session.struct_span_err(span, &msg);
458+
self.suggest_macro_name(&ident.name.as_str(), kind, &mut err);
459+
err.emit();
460+
},
461+
_ => {},
458462
};
459-
let msg1 = format!("`{}` could refer to the macro {} here", ident, participle);
460-
let msg2 = format!("`{}` could also refer to the macro imported here", ident);
461-
self.session.struct_span_err(span, &format!("`{}` is ambiguous", ident))
462-
.span_note(legacy_span, &msg1)
463-
.span_note(resolution.span, &msg2)
464-
.emit();
465463
}
466464
}
467465

468-
fn suggest_macro_name(&mut self, name: &str, err: &mut DiagnosticBuilder<'a>) {
469-
if let Some(suggestion) = find_best_match_for_name(self.macro_names.iter(), name, None) {
466+
fn suggest_macro_name(&mut self, name: &str, kind: MacroKind,
467+
err: &mut DiagnosticBuilder<'a>) {
468+
let suggestion = match kind {
469+
MacroKind::Bang =>
470+
find_best_match_for_name(self.macro_names.iter(), name, None),
471+
MacroKind::Attr |
472+
MacroKind::Derive => {
473+
// Find a suggestion from the legacy namespace.
474+
// FIXME: get_macro needs an &mut Resolver, can we do it without cloning?
475+
let builtin_macros = self.builtin_macros.clone();
476+
let names = builtin_macros.iter().filter_map(|(name, binding)| {
477+
if binding.get_macro(self).kind() == kind {
478+
Some(name)
479+
} else {
480+
None
481+
}
482+
});
483+
find_best_match_for_name(names, name, None)
484+
}
485+
};
486+
if let Some(suggestion) = suggestion {
470487
if suggestion != name {
471-
err.help(&format!("did you mean `{}!`?", suggestion));
488+
if let MacroKind::Bang = kind {
489+
err.help(&format!("did you mean `{}!`?", suggestion));
490+
} else {
491+
err.help(&format!("did you mean `{}`?", suggestion));
492+
}
472493
} else {
473494
err.help(&format!("have you added the `#[macro_use]` on the module/import?"));
474495
}

src/libsyntax/ext/base.rs

+34-10
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,17 @@ impl MacResult for DummyResult {
474474
pub type BuiltinDeriveFn =
475475
for<'cx> fn(&'cx mut ExtCtxt, Span, &MetaItem, &Annotatable, &mut FnMut(Annotatable));
476476

477+
/// Represents different kinds of macro invocations that can be resolved.
478+
#[derive(Clone, Copy, PartialEq, Eq)]
479+
pub enum MacroKind {
480+
/// A bang macro - foo!()
481+
Bang,
482+
/// An attribute macro - #[foo]
483+
Attr,
484+
/// A derive attribute macro - #[derive(Foo)]
485+
Derive,
486+
}
487+
477488
/// An enum representing the different kinds of syntax extensions.
478489
pub enum SyntaxExtension {
479490
/// A syntax extension that is attached to an item and creates new items
@@ -520,6 +531,25 @@ pub enum SyntaxExtension {
520531
BuiltinDerive(BuiltinDeriveFn),
521532
}
522533

534+
impl SyntaxExtension {
535+
/// Return which kind of macro calls this syntax extension.
536+
pub fn kind(&self) -> MacroKind {
537+
match *self {
538+
SyntaxExtension::NormalTT(..) |
539+
SyntaxExtension::IdentTT(..) |
540+
SyntaxExtension::ProcMacro(..) =>
541+
MacroKind::Bang,
542+
SyntaxExtension::MultiDecorator(..) |
543+
SyntaxExtension::MultiModifier(..) |
544+
SyntaxExtension::AttrProcMacro(..) =>
545+
MacroKind::Attr,
546+
SyntaxExtension::ProcMacroDerive(..) |
547+
SyntaxExtension::BuiltinDerive(..) =>
548+
MacroKind::Derive,
549+
}
550+
}
551+
}
552+
523553
pub type NamedSyntaxExtension = (Name, SyntaxExtension);
524554

525555
pub trait Resolver {
@@ -535,10 +565,8 @@ pub trait Resolver {
535565
fn resolve_imports(&mut self);
536566
// Resolves attribute and derive legacy macros from `#![plugin(..)]`.
537567
fn find_legacy_attr_invoc(&mut self, attrs: &mut Vec<Attribute>) -> Option<Attribute>;
538-
fn resolve_macro(&mut self, scope: Mark, path: &ast::Path, force: bool)
539-
-> Result<Rc<SyntaxExtension>, Determinacy>;
540-
fn resolve_derive_macro(&mut self, scope: Mark, path: &ast::Path, force: bool)
541-
-> Result<Rc<SyntaxExtension>, Determinacy>;
568+
fn resolve_macro(&mut self, scope: Mark, path: &ast::Path, kind: MacroKind,
569+
force: bool) -> Result<Rc<SyntaxExtension>, Determinacy>;
542570
}
543571

544572
#[derive(Copy, Clone, Debug)]
@@ -561,12 +589,8 @@ impl Resolver for DummyResolver {
561589

562590
fn resolve_imports(&mut self) {}
563591
fn find_legacy_attr_invoc(&mut self, _attrs: &mut Vec<Attribute>) -> Option<Attribute> { None }
564-
fn resolve_macro(&mut self, _scope: Mark, _path: &ast::Path, _force: bool)
565-
-> Result<Rc<SyntaxExtension>, Determinacy> {
566-
Err(Determinacy::Determined)
567-
}
568-
fn resolve_derive_macro(&mut self, _scope: Mark, _path: &ast::Path, _force: bool)
569-
-> Result<Rc<SyntaxExtension>, Determinacy> {
592+
fn resolve_macro(&mut self, _scope: Mark, _path: &ast::Path, _kind: MacroKind,
593+
_force: bool) -> Result<Rc<SyntaxExtension>, Determinacy> {
570594
Err(Determinacy::Determined)
571595
}
572596
}

src/libsyntax/ext/expand.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -282,8 +282,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
282282
let mark = Mark::fresh();
283283
derives.push(mark);
284284
let path = ast::Path::from_ident(span, Ident::with_empty_ctxt(name));
285-
let item = match self.cx.resolver
286-
.resolve_macro(Mark::root(), &path, false) {
285+
let item = match self.cx.resolver.resolve_macro(
286+
Mark::root(), &path, MacroKind::Derive, false) {
287287
Ok(ext) => match *ext {
288288
SyntaxExtension::BuiltinDerive(..) => item_with_markers.clone(),
289289
_ => item.clone(),
@@ -369,12 +369,14 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
369369
-> Result<Option<Rc<SyntaxExtension>>, Determinacy> {
370370
let (attr, traits, item) = match invoc.kind {
371371
InvocationKind::Bang { ref mac, .. } => {
372-
return self.cx.resolver.resolve_macro(scope, &mac.node.path, force).map(Some);
372+
return self.cx.resolver.resolve_macro(scope, &mac.node.path,
373+
MacroKind::Bang, force).map(Some);
373374
}
374375
InvocationKind::Attr { attr: None, .. } => return Ok(None),
375376
InvocationKind::Derive { name, span, .. } => {
376377
let path = ast::Path::from_ident(span, Ident::with_empty_ctxt(name));
377-
return self.cx.resolver.resolve_derive_macro(scope, &path, force).map(Some);
378+
return self.cx.resolver.resolve_macro(scope, &path,
379+
MacroKind::Derive, force).map(Some)
378380
}
379381
InvocationKind::Attr { ref mut attr, ref traits, ref mut item } => (attr, traits, item),
380382
};
@@ -385,7 +387,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
385387
};
386388

387389
let mut determined = true;
388-
match self.cx.resolver.resolve_macro(scope, &path, force) {
390+
match self.cx.resolver.resolve_macro(scope, &path, MacroKind::Attr, force) {
389391
Ok(ext) => return Ok(Some(ext)),
390392
Err(Determinacy::Undetermined) => determined = false,
391393
Err(Determinacy::Determined) if force => return Err(Determinacy::Determined),
@@ -394,7 +396,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
394396

395397
for &(name, span) in traits {
396398
let path = ast::Path::from_ident(span, Ident::with_empty_ctxt(name));
397-
match self.cx.resolver.resolve_macro(scope, &path, force) {
399+
match self.cx.resolver.resolve_macro(scope, &path, MacroKind::Derive, force) {
398400
Ok(ext) => if let SyntaxExtension::ProcMacroDerive(_, ref inert_attrs) = *ext {
399401
if inert_attrs.contains(&attr_name) {
400402
// FIXME(jseyfried) Avoid `mem::replace` here.

0 commit comments

Comments
 (0)