Skip to content

Commit 5c5b8af

Browse files
committed
Auto merge of #64694 - petrochenkov:reshelp, r=matthewjasper
Fully integrate derive helpers into name resolution ```rust #[derive(Foo)] #[foo_helper] // already goes through name resolution struct S { #[foo_helper] // goes through name resolution after this PR field: u8 } ``` How name resolution normally works: - We have an identifier we need to resolve, take its location (in some sense) and look what names are in scope in that location. How derive helper attributes are "resolved" (before this PR): - After resolving the derive `Foo` we visit the derive's input (`struct S { ... } `) as a piece of AST and mark attributes textually matching one of the derive's helper attributes (`foo_helper`) as "known", so they never go through name resolution. This PR changes the rules for derive helpers, so they are not proactively marked as known (which is a big hack ignoring any ambiguities or hygiene), but go through regular name resolution instead. This change was previously blocked by attributes not being resolved in some positions at all (fixed in #63468). "Where exactly are derive helpers in scope?" is an interesting question, and I need some feedback from proc macro authors to answer it, see the post below (#64694 (comment)).
2 parents 5f00849 + 8668c1a commit 5c5b8af

File tree

8 files changed

+195
-57
lines changed

8 files changed

+195
-57
lines changed

Diff for: src/librustc_resolve/diagnostics.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,16 @@ impl<'a> Resolver<'a> {
368368
let mut suggestions = Vec::new();
369369
self.visit_scopes(scope_set, parent_scope, ident, |this, scope, use_prelude, _| {
370370
match scope {
371-
Scope::DeriveHelpers => {
371+
Scope::DeriveHelpers(expn_id) => {
372+
let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper);
373+
if filter_fn(res) {
374+
suggestions.extend(this.helper_attrs.get(&expn_id)
375+
.into_iter().flatten().map(|ident| {
376+
TypoSuggestion::from_res(ident.name, res)
377+
}));
378+
}
379+
}
380+
Scope::DeriveHelpersCompat => {
372381
let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper);
373382
if filter_fn(res) {
374383
for derive in parent_scope.derives {

Diff for: src/librustc_resolve/lib.rs

+27-9
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ use syntax::symbol::{kw, sym};
4545
use syntax::source_map::Spanned;
4646
use syntax::visit::{self, Visitor};
4747
use syntax_expand::base::SyntaxExtension;
48-
use syntax_pos::hygiene::{MacroKind, ExpnId, Transparency, SyntaxContext};
48+
use syntax_pos::hygiene::{MacroKind, ExpnId, ExpnKind, Transparency, SyntaxContext};
4949
use syntax_pos::{Span, DUMMY_SP};
5050
use errors::{Applicability, DiagnosticBuilder};
5151

@@ -97,7 +97,8 @@ impl Determinacy {
9797
/// but not for late resolution yet.
9898
#[derive(Clone, Copy)]
9999
enum Scope<'a> {
100-
DeriveHelpers,
100+
DeriveHelpers(ExpnId),
101+
DeriveHelpersCompat,
101102
MacroRules(LegacyScope<'a>),
102103
CrateRoot,
103104
Module(Module<'a>),
@@ -942,6 +943,8 @@ pub struct Resolver<'a> {
942943
/// Legacy scopes *produced* by expanding the macro invocations,
943944
/// include all the `macro_rules` items and other invocations generated by them.
944945
output_legacy_scopes: FxHashMap<ExpnId, LegacyScope<'a>>,
946+
/// Helper attributes that are in scope for the given expansion.
947+
helper_attrs: FxHashMap<ExpnId, Vec<Ident>>,
945948

946949
/// Avoid duplicated errors for "name already defined".
947950
name_already_seen: FxHashMap<Name, Span>,
@@ -1219,6 +1222,7 @@ impl<'a> Resolver<'a> {
12191222
non_macro_attrs: [non_macro_attr(false), non_macro_attr(true)],
12201223
invocation_parent_scopes,
12211224
output_legacy_scopes: Default::default(),
1225+
helper_attrs: Default::default(),
12221226
macro_defs,
12231227
local_macro_def_scopes: FxHashMap::default(),
12241228
name_already_seen: FxHashMap::default(),
@@ -1467,24 +1471,27 @@ impl<'a> Resolver<'a> {
14671471
// in prelude, not sure where exactly (creates ambiguities with any other prelude names).
14681472

14691473
let rust_2015 = ident.span.rust_2015();
1470-
let (ns, is_absolute_path) = match scope_set {
1471-
ScopeSet::All(ns, _) => (ns, false),
1472-
ScopeSet::AbsolutePath(ns) => (ns, true),
1473-
ScopeSet::Macro(_) => (MacroNS, false),
1474+
let (ns, macro_kind, is_absolute_path) = match scope_set {
1475+
ScopeSet::All(ns, _) => (ns, None, false),
1476+
ScopeSet::AbsolutePath(ns) => (ns, None, true),
1477+
ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind), false),
14741478
};
14751479
// Jump out of trait or enum modules, they do not act as scopes.
14761480
let module = parent_scope.module.nearest_item_scope();
14771481
let mut scope = match ns {
14781482
_ if is_absolute_path => Scope::CrateRoot,
14791483
TypeNS | ValueNS => Scope::Module(module),
1480-
MacroNS => Scope::DeriveHelpers,
1484+
MacroNS => Scope::DeriveHelpers(parent_scope.expansion),
14811485
};
14821486
let mut ident = ident.modern();
14831487
let mut use_prelude = !module.no_implicit_prelude;
14841488

14851489
loop {
14861490
let visit = match scope {
1487-
Scope::DeriveHelpers => true,
1491+
// Derive helpers are not in scope when resolving derives in the same container.
1492+
Scope::DeriveHelpers(expn_id) =>
1493+
!(expn_id == parent_scope.expansion && macro_kind == Some(MacroKind::Derive)),
1494+
Scope::DeriveHelpersCompat => true,
14881495
Scope::MacroRules(..) => true,
14891496
Scope::CrateRoot => true,
14901497
Scope::Module(..) => true,
@@ -1505,7 +1512,18 @@ impl<'a> Resolver<'a> {
15051512
}
15061513

15071514
scope = match scope {
1508-
Scope::DeriveHelpers =>
1515+
Scope::DeriveHelpers(expn_id) if expn_id != ExpnId::root() => {
1516+
// Derive helpers are not visible to code generated by bang or derive macros.
1517+
let expn_data = expn_id.expn_data();
1518+
match expn_data.kind {
1519+
ExpnKind::Root |
1520+
ExpnKind::Macro(MacroKind::Bang, _) |
1521+
ExpnKind::Macro(MacroKind::Derive, _) => Scope::DeriveHelpersCompat,
1522+
_ => Scope::DeriveHelpers(expn_data.parent),
1523+
}
1524+
}
1525+
Scope::DeriveHelpers(..) => Scope::DeriveHelpersCompat,
1526+
Scope::DeriveHelpersCompat =>
15091527
Scope::MacroRules(parent_scope.legacy),
15101528
Scope::MacroRules(legacy_scope) => match legacy_scope {
15111529
LegacyScope::Binding(binding) => Scope::MacroRules(

Diff for: src/librustc_resolve/macros.rs

+25-2
Original file line numberDiff line numberDiff line change
@@ -237,15 +237,26 @@ impl<'a> base::Resolver for Resolver<'a> {
237237
// - Derives in the container need to know whether one of them is a built-in `Copy`.
238238
// FIXME: Try to avoid repeated resolutions for derives here and in expansion.
239239
let mut exts = Vec::new();
240+
let mut helper_attrs = Vec::new();
240241
for path in derives {
241242
exts.push(match self.resolve_macro_path(
242243
path, Some(MacroKind::Derive), &parent_scope, true, force
243244
) {
244-
Ok((Some(ext), _)) => ext,
245+
Ok((Some(ext), _)) => {
246+
let span = path.segments.last().unwrap().ident.span.modern();
247+
helper_attrs.extend(
248+
ext.helper_attrs.iter().map(|name| Ident::new(*name, span))
249+
);
250+
if ext.is_derive_copy {
251+
self.add_derive_copy(invoc_id);
252+
}
253+
ext
254+
}
245255
Ok(_) | Err(Determinacy::Determined) => self.dummy_ext(MacroKind::Derive),
246256
Err(Determinacy::Undetermined) => return Err(Indeterminate),
247257
})
248258
}
259+
self.helper_attrs.insert(invoc_id, helper_attrs);
249260
return Ok(InvocationRes::DeriveContainer(exts));
250261
}
251262
};
@@ -498,7 +509,19 @@ impl<'a> Resolver<'a> {
498509
Flags::empty(),
499510
));
500511
let result = match scope {
501-
Scope::DeriveHelpers => {
512+
Scope::DeriveHelpers(expn_id) => {
513+
if let Some(attr) = this.helper_attrs.get(&expn_id).and_then(|attrs| {
514+
attrs.iter().rfind(|i| ident == **i)
515+
}) {
516+
let binding = (Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper),
517+
ty::Visibility::Public, attr.span, expn_id)
518+
.to_name_binding(this.arenas);
519+
Ok((binding, Flags::empty()))
520+
} else {
521+
Err(Determinacy::Determined)
522+
}
523+
}
524+
Scope::DeriveHelpersCompat => {
502525
let mut result = Err(Determinacy::Determined);
503526
for derive in parent_scope.derives {
504527
let parent_scope = &ParentScope { derives: &[], ..*parent_scope };

Diff for: src/libsyntax_expand/expand.rs

+4-16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::base::*;
2-
use crate::proc_macro::{collect_derives, MarkAttrs};
2+
use crate::proc_macro::collect_derives;
33
use crate::hygiene::{ExpnId, SyntaxContext, ExpnData, ExpnKind};
44
use crate::mbe::macro_rules::annotate_err_with_kind;
55
use crate::placeholders::{placeholder, PlaceholderExpander};
@@ -394,7 +394,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
394394
let fragment = self.expand_invoc(invoc, &ext.kind);
395395
self.collect_invocations(fragment, &[])
396396
}
397-
InvocationRes::DeriveContainer(exts) => {
397+
InvocationRes::DeriveContainer(_exts) => {
398+
// FIXME: Consider using the derive resolutions (`_exts`) immediately,
399+
// instead of enqueuing the derives to be resolved again later.
398400
let (derives, item) = match invoc.kind {
399401
InvocationKind::DeriveContainer { derives, item } => (derives, item),
400402
_ => unreachable!(),
@@ -421,20 +423,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
421423

422424
let mut item = self.fully_configure(item);
423425
item.visit_attrs(|attrs| attrs.retain(|a| !a.has_name(sym::derive)));
424-
let mut helper_attrs = Vec::new();
425-
let mut has_copy = false;
426-
for ext in exts {
427-
helper_attrs.extend(&ext.helper_attrs);
428-
has_copy |= ext.is_derive_copy;
429-
}
430-
// Mark derive helpers inside this item as known and used.
431-
// FIXME: This is a hack, derive helpers should be integrated with regular name
432-
// resolution instead. For example, helpers introduced by a derive container
433-
// can be in scope for all code produced by that container's expansion.
434-
item.visit_with(&mut MarkAttrs(&helper_attrs));
435-
if has_copy {
436-
self.cx.resolver.add_derive_copy(invoc.expansion_data.id);
437-
}
438426

439427
let mut derive_placeholders = Vec::with_capacity(derives.len());
440428
invocations.reserve(derives.len());

Diff for: src/libsyntax_expand/proc_macro.rs

+1-18
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
use crate::base::{self, *};
22
use crate::proc_macro_server;
33

4-
use syntax::ast::{self, ItemKind, Attribute, Mac};
5-
use syntax::attr::{mark_used, mark_known};
4+
use syntax::ast::{self, ItemKind};
65
use syntax::errors::{Applicability, FatalError};
76
use syntax::symbol::sym;
87
use syntax::token;
98
use syntax::tokenstream::{self, TokenStream};
10-
use syntax::visit::Visitor;
119

1210
use rustc_data_structures::sync::Lrc;
1311
use syntax_pos::{Span, DUMMY_SP};
@@ -167,21 +165,6 @@ impl MultiItemModifier for ProcMacroDerive {
167165
}
168166
}
169167

170-
crate struct MarkAttrs<'a>(crate &'a [ast::Name]);
171-
172-
impl<'a> Visitor<'a> for MarkAttrs<'a> {
173-
fn visit_attribute(&mut self, attr: &Attribute) {
174-
if let Some(ident) = attr.ident() {
175-
if self.0.contains(&ident.name) {
176-
mark_used(attr);
177-
mark_known(attr);
178-
}
179-
}
180-
}
181-
182-
fn visit_mac(&mut self, _mac: &Mac) {}
183-
}
184-
185168
crate fn collect_derives(cx: &mut ExtCtxt<'_>, attrs: &mut Vec<ast::Attribute>) -> Vec<ast::Path> {
186169
let mut result = Vec::new();
187170
attrs.retain(|attr| {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// force-host
2+
// no-prefer-dynamic
3+
4+
#![crate_type = "proc-macro"]
5+
6+
extern crate proc_macro;
7+
use proc_macro::*;
8+
9+
#[proc_macro_derive(GenHelperUse)]
10+
pub fn derive_a(_: TokenStream) -> TokenStream {
11+
"
12+
#[empty_helper]
13+
struct Uwu;
14+
".parse().unwrap()
15+
}

Diff for: src/test/ui/proc-macro/derive-helper-shadowing.rs

+28-7
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,54 @@
1+
// edition:2018
12
// aux-build:test-macros.rs
3+
// aux-build:derive-helper-shadowing.rs
24

35
#[macro_use]
46
extern crate test_macros;
7+
#[macro_use]
8+
extern crate derive_helper_shadowing;
59

610
use test_macros::empty_attr as empty_helper;
711

12+
macro_rules! gen_helper_use {
13+
() => {
14+
#[empty_helper] //~ ERROR cannot find attribute `empty_helper` in this scope
15+
struct W;
16+
}
17+
}
18+
819
#[empty_helper] //~ ERROR `empty_helper` is ambiguous
920
#[derive(Empty)]
1021
struct S {
11-
// FIXME No ambiguity, attributes in non-macro positions are not resolved properly
12-
#[empty_helper]
22+
#[empty_helper] //~ ERROR `empty_helper` is ambiguous
1323
field: [u8; {
14-
// FIXME No ambiguity, derive helpers are not put into scope for non-attributes
15-
use empty_helper;
24+
use empty_helper; //~ ERROR `empty_helper` is ambiguous
1625

17-
// FIXME No ambiguity, derive helpers are not put into scope for inner items
18-
#[empty_helper]
26+
#[empty_helper] //~ ERROR `empty_helper` is ambiguous
1927
struct U;
2028

2129
mod inner {
22-
// FIXME No ambiguity, attributes in non-macro positions are not resolved properly
30+
// OK, no ambiguity, the non-helper attribute is not in scope here, only the helper.
2331
#[empty_helper]
2432
struct V;
33+
34+
gen_helper_use!();
35+
36+
#[derive(GenHelperUse)] //~ ERROR cannot find attribute `empty_helper` in this scope
37+
struct Owo;
38+
39+
use empty_helper as renamed;
40+
#[renamed] //~ ERROR cannot use a derive helper attribute through an import
41+
struct Wow;
2542
}
2643

2744
0
2845
}]
2946
}
3047

48+
// OK, no ambiguity, only the non-helper attribute is in scope.
49+
#[empty_helper]
50+
struct Z;
51+
3152
fn main() {
3253
let s = S { field: [] };
3354
}

0 commit comments

Comments
 (0)