Skip to content

Commit f60d96a

Browse files
committed
Support custom attributes when macro modularization is enabled
1 parent c84562e commit f60d96a

11 files changed

+108
-105
lines changed

src/librustc_resolve/build_reduced_graph.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,9 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
630630
pub fn get_macro(&mut self, def: Def) -> Lrc<SyntaxExtension> {
631631
let def_id = match def {
632632
Def::Macro(def_id, ..) => def_id,
633-
Def::NonMacroAttr(..) => return Lrc::new(SyntaxExtension::NonMacroAttr),
633+
Def::NonMacroAttr(attr_kind) => return Lrc::new(SyntaxExtension::NonMacroAttr {
634+
mark_used: attr_kind == NonMacroAttrKind::Tool,
635+
}),
634636
_ => panic!("expected `Def::Macro` or `Def::NonMacroAttr`"),
635637
};
636638
if let Some(ext) = self.macro_map.get(&def_id) {

src/librustc_resolve/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3485,8 +3485,9 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
34853485
let binding = if let Some(module) = module {
34863486
self.resolve_ident_in_module(module, ident, ns, record_used, path_span)
34873487
} else if opt_ns == Some(MacroNS) {
3488-
self.resolve_lexical_macro_path_segment(ident, ns, record_used, path_span)
3489-
.map(MacroBinding::binding)
3488+
assert!(ns == TypeNS);
3489+
self.resolve_lexical_macro_path_segment(ident, ns, record_used, record_used,
3490+
false, path_span).map(MacroBinding::binding)
34903491
} else {
34913492
let record_used_id =
34923493
if record_used { crate_lint.node_id().or(Some(CRATE_NODE_ID)) } else { None };
@@ -4549,6 +4550,8 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
45494550
let result = self.resolve_lexical_macro_path_segment(ident,
45504551
MacroNS,
45514552
false,
4553+
false,
4554+
true,
45524555
attr.path.span);
45534556
if let Ok(binding) = result {
45544557
if let SyntaxExtension::AttrProcMacro(..) = *binding.binding().get_macro(self) {

src/librustc_resolve/macros.rs

Lines changed: 57 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -331,18 +331,29 @@ impl<'a, 'crateloader: 'a> base::Resolver for Resolver<'a, 'crateloader> {
331331
} else if let Def::NonMacroAttr(attr_kind) = def {
332332
let is_attr_invoc =
333333
if let InvocationKind::Attr { .. } = invoc.kind { true } else { false };
334+
let path = invoc.path().expect("no path for non-macro attr");
334335
match attr_kind {
335-
NonMacroAttrKind::Tool | NonMacroAttrKind::DeriveHelper if is_attr_invoc => {
336+
NonMacroAttrKind::Tool | NonMacroAttrKind::DeriveHelper |
337+
NonMacroAttrKind::Custom if is_attr_invoc => {
336338
if attr_kind == NonMacroAttrKind::Tool &&
337339
!self.session.features_untracked().tool_attributes {
338340
feature_err(&self.session.parse_sess, "tool_attributes",
339341
invoc.span(), GateIssue::Language,
340342
"tool attributes are unstable").emit();
341343
}
342-
return Ok(Some(Lrc::new(SyntaxExtension::NonMacroAttr)));
344+
if attr_kind == NonMacroAttrKind::Custom &&
345+
!self.session.features_untracked().custom_attribute {
346+
let msg = format!("The attribute `{}` is currently unknown to the compiler \
347+
and may have meaning added to it in the future", path);
348+
feature_err(&self.session.parse_sess, "custom_attribute", invoc.span(),
349+
GateIssue::Language, &msg).emit();
350+
}
351+
return Ok(Some(Lrc::new(SyntaxExtension::NonMacroAttr {
352+
mark_used: attr_kind == NonMacroAttrKind::Tool,
353+
})));
343354
}
344355
_ => {
345-
self.report_non_macro_attr(invoc.path_span(), def);
356+
self.report_non_macro_attr(path.span, def);
346357
return Err(Determinacy::Determined);
347358
}
348359
}
@@ -418,43 +429,42 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
418429
};
419430

420431
let path = attr.as_ref().unwrap().path.clone();
421-
let mut determinacy = Determinacy::Determined;
422-
match self.resolve_macro_to_def(scope, &path, MacroKind::Attr, force) {
423-
Ok(def) => return Ok(def),
424-
Err(Determinacy::Undetermined) => determinacy = Determinacy::Undetermined,
425-
Err(Determinacy::Determined) if force => return Err(Determinacy::Determined),
426-
Err(Determinacy::Determined) => {}
432+
let def = self.resolve_macro_to_def(scope, &path, MacroKind::Attr, force);
433+
if let Ok(Def::NonMacroAttr(NonMacroAttrKind::Custom)) = def {} else {
434+
return def;
427435
}
428436

429-
// Ok at this point we've determined that the `attr` above doesn't
430-
// actually resolve at this time, so we may want to report an error.
431-
// It could be the case, though, that `attr` won't ever resolve! If
432-
// there's a custom derive that could be used it might declare `attr` as
433-
// a custom attribute accepted by the derive. In this case we don't want
434-
// to report this particular invocation as unresolved, but rather we'd
435-
// want to move on to the next invocation.
437+
// At this point we've found that the `attr` is determinately unresolved and thus can be
438+
// interpreted as a custom attribute. Normally custom attributes are feature gated, but
439+
// it may be a custom attribute whitelisted by a derive macro and they do not require
440+
// a feature gate.
436441
//
437-
// This loop here looks through all of the derive annotations in scope
438-
// and tries to resolve them. If they themselves successfully resolve
439-
// *and* the resolve mentions that this attribute's name is a registered
440-
// custom attribute then we return that custom attribute as the resolution result.
441-
let attr_name = match path.segments.len() {
442-
1 => path.segments[0].ident.name,
443-
_ => return Err(determinacy),
444-
};
442+
// So here we look through all of the derive annotations in scope and try to resolve them.
443+
// If they themselves successfully resolve *and* one of the resolved derive macros
444+
// whitelists this attribute's name, then this is a registered attribute and we can convert
445+
// it from a "generic custom attrite" into a "known derive helper attribute".
446+
enum ConvertToDeriveHelper { Yes, No, DontKnow }
447+
let mut convert_to_derive_helper = ConvertToDeriveHelper::No;
448+
let attr_name = path.segments[0].ident.name;
445449
for path in traits {
446450
match self.resolve_macro(scope, path, MacroKind::Derive, force) {
447451
Ok(ext) => if let SyntaxExtension::ProcMacroDerive(_, ref inert_attrs, _) = *ext {
448452
if inert_attrs.contains(&attr_name) {
449-
return Ok(Def::NonMacroAttr(NonMacroAttrKind::DeriveHelper));
453+
convert_to_derive_helper = ConvertToDeriveHelper::Yes;
454+
break
450455
}
451456
},
452-
Err(Determinacy::Undetermined) => determinacy = Determinacy::Undetermined,
457+
Err(Determinacy::Undetermined) =>
458+
convert_to_derive_helper = ConvertToDeriveHelper::DontKnow,
453459
Err(Determinacy::Determined) => {}
454460
}
455461
}
456462

457-
Err(determinacy)
463+
match convert_to_derive_helper {
464+
ConvertToDeriveHelper::Yes => Ok(Def::NonMacroAttr(NonMacroAttrKind::DeriveHelper)),
465+
ConvertToDeriveHelper::No => def,
466+
ConvertToDeriveHelper::DontKnow => Err(Determinacy::determined(force)),
467+
}
458468
}
459469

460470
fn resolve_macro_to_def(&mut self, scope: Mark, path: &ast::Path, kind: MacroKind, force: bool)
@@ -537,10 +547,11 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
537547
let result = if let Some(MacroBinding::Legacy(binding)) = legacy_resolution {
538548
Ok(Def::Macro(binding.def_id, MacroKind::Bang))
539549
} else {
540-
match self.resolve_lexical_macro_path_segment(path[0], MacroNS, false, span) {
550+
match self.resolve_lexical_macro_path_segment(path[0], MacroNS, false, force,
551+
kind == MacroKind::Attr, span) {
541552
Ok(binding) => Ok(binding.binding().def_ignoring_ambiguity()),
542-
Err(Determinacy::Undetermined) if !force => return Err(Determinacy::Undetermined),
543-
Err(_) => {
553+
Err(Determinacy::Undetermined) => return Err(Determinacy::Undetermined),
554+
Err(Determinacy::Determined) => {
544555
self.found_unresolved_macro = true;
545556
Err(Determinacy::Determined)
546557
}
@@ -561,6 +572,8 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
561572
mut ident: Ident,
562573
ns: Namespace,
563574
record_used: bool,
575+
force: bool,
576+
is_attr: bool,
564577
path_span: Span)
565578
-> Result<MacroBinding<'a>, Determinacy> {
566579
// General principles:
@@ -591,6 +604,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
591604
// 3. Builtin attributes (closed, controlled).
592605

593606
assert!(ns == TypeNS || ns == MacroNS);
607+
let force = force || record_used;
594608
ident = ident.modern();
595609

596610
// Names from inner scope that can't shadow names from outer scopes, e.g.
@@ -764,7 +778,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
764778
Err(Determinacy::Determined) => {
765779
continue_search!();
766780
}
767-
Err(Determinacy::Undetermined) => return Err(Determinacy::Undetermined),
781+
Err(Determinacy::Undetermined) => return Err(Determinacy::determined(force)),
768782
}
769783
}
770784

@@ -773,7 +787,16 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
773787
return Ok(previous_result);
774788
}
775789

776-
if record_used { Err(Determinacy::Determined) } else { Err(Determinacy::Undetermined) }
790+
let determinacy = Determinacy::determined(force);
791+
if determinacy == Determinacy::Determined && is_attr {
792+
// For attributes interpret determinate "no solution" as a custom attribute.
793+
let binding = (Def::NonMacroAttr(NonMacroAttrKind::Custom),
794+
ty::Visibility::Public, ident.span, Mark::root())
795+
.to_name_binding(self.arenas);
796+
Ok(MacroBinding::Global(binding))
797+
} else {
798+
Err(determinacy)
799+
}
777800
}
778801

779802
pub fn resolve_legacy_scope(&mut self,
@@ -857,7 +880,8 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
857880
let span = ident.span;
858881
let legacy_scope = &self.invocations[&mark].legacy_scope;
859882
let legacy_resolution = self.resolve_legacy_scope(legacy_scope, ident, true);
860-
let resolution = self.resolve_lexical_macro_path_segment(ident, MacroNS, true, span);
883+
let resolution = self.resolve_lexical_macro_path_segment(ident, MacroNS, true, true,
884+
kind == MacroKind::Attr, span);
861885

862886
let check_consistency = |this: &Self, binding: MacroBinding| {
863887
if let Some(def) = def {

src/libsyntax/ext/base.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,7 @@ impl MacroKind {
589589
/// An enum representing the different kinds of syntax extensions.
590590
pub enum SyntaxExtension {
591591
/// A trivial "extension" that does nothing, only keeps the attribute and marks it as known.
592-
NonMacroAttr,
592+
NonMacroAttr { mark_used: bool },
593593

594594
/// A syntax extension that is attached to an item and creates new items
595595
/// based upon it.
@@ -670,7 +670,7 @@ impl SyntaxExtension {
670670
SyntaxExtension::IdentTT(..) |
671671
SyntaxExtension::ProcMacro { .. } =>
672672
MacroKind::Bang,
673-
SyntaxExtension::NonMacroAttr |
673+
SyntaxExtension::NonMacroAttr { .. } |
674674
SyntaxExtension::MultiDecorator(..) |
675675
SyntaxExtension::MultiModifier(..) |
676676
SyntaxExtension::AttrProcMacro(..) =>
@@ -700,7 +700,7 @@ impl SyntaxExtension {
700700
SyntaxExtension::AttrProcMacro(.., edition) |
701701
SyntaxExtension::ProcMacroDerive(.., edition) => edition,
702702
// Unstable legacy stuff
703-
SyntaxExtension::NonMacroAttr |
703+
SyntaxExtension::NonMacroAttr { .. } |
704704
SyntaxExtension::IdentTT(..) |
705705
SyntaxExtension::MultiDecorator(..) |
706706
SyntaxExtension::MultiModifier(..) |
@@ -739,6 +739,12 @@ pub enum Determinacy {
739739
Undetermined,
740740
}
741741

742+
impl Determinacy {
743+
pub fn determined(determined: bool) -> Determinacy {
744+
if determined { Determinacy::Determined } else { Determinacy::Undetermined }
745+
}
746+
}
747+
742748
pub struct DummyResolver;
743749

744750
impl Resolver for DummyResolver {

src/libsyntax/ext/expand.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -244,12 +244,12 @@ impl Invocation {
244244
}
245245
}
246246

247-
pub fn path_span(&self) -> Span {
247+
pub fn path(&self) -> Option<&Path> {
248248
match self.kind {
249-
InvocationKind::Bang { ref mac, .. } => mac.node.path.span,
250-
InvocationKind::Attr { attr: Some(ref attr), .. } => attr.path.span,
251-
InvocationKind::Attr { attr: None, .. } => DUMMY_SP,
252-
InvocationKind::Derive { ref path, .. } => path.span,
249+
InvocationKind::Bang { ref mac, .. } => Some(&mac.node.path),
250+
InvocationKind::Attr { attr: Some(ref attr), .. } => Some(&attr.path),
251+
InvocationKind::Attr { attr: None, .. } => None,
252+
InvocationKind::Derive { ref path, .. } => Some(path),
253253
}
254254
}
255255
}
@@ -548,7 +548,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
548548
_ => unreachable!(),
549549
};
550550

551-
attr::mark_used(&attr);
551+
if let NonMacroAttr { mark_used: false } = *ext {} else {
552+
attr::mark_used(&attr);
553+
}
552554
invoc.expansion_data.mark.set_expn_info(ExpnInfo {
553555
call_site: attr.span,
554556
def_site: None,
@@ -560,7 +562,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
560562
});
561563

562564
match *ext {
563-
NonMacroAttr => {
565+
NonMacroAttr { .. } => {
564566
attr::mark_known(&attr);
565567
let item = item.map_attrs(|mut attrs| { attrs.push(attr); attrs });
566568
Some(invoc.fragment_kind.expect_from_annotatables(iter::once(item)))
@@ -810,7 +812,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
810812
}
811813

812814
MultiDecorator(..) | MultiModifier(..) |
813-
AttrProcMacro(..) | SyntaxExtension::NonMacroAttr => {
815+
AttrProcMacro(..) | SyntaxExtension::NonMacroAttr { .. } => {
814816
self.cx.span_err(path.span,
815817
&format!("`{}` can only be used in attributes", path));
816818
self.cx.trace_macros_diag();
@@ -1487,7 +1489,8 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
14871489
};
14881490

14891491
if attr.is_some() || !traits.is_empty() {
1490-
if !self.cx.ecfg.macros_in_extern_enabled() {
1492+
if !self.cx.ecfg.macros_in_extern_enabled() &&
1493+
!self.cx.ecfg.custom_attribute_enabled() {
14911494
if let Some(ref attr) = attr {
14921495
emit_feature_err(&self.cx.parse_sess, "macros_in_extern", attr.span,
14931496
GateIssue::Language, explain);
@@ -1668,6 +1671,7 @@ impl<'feat> ExpansionConfig<'feat> {
16681671
fn enable_custom_derive = custom_derive,
16691672
fn enable_format_args_nl = format_args_nl,
16701673
fn macros_in_extern_enabled = macros_in_extern,
1674+
fn custom_attribute_enabled = custom_attribute,
16711675
fn proc_macro_mod = proc_macro_mod,
16721676
fn proc_macro_gen = proc_macro_gen,
16731677
fn proc_macro_expr = proc_macro_expr,

src/libsyntax/feature_gate.rs

Lines changed: 5 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use attr;
3232
use codemap::Spanned;
3333
use edition::{ALL_EDITIONS, Edition};
3434
use syntax_pos::{Span, DUMMY_SP};
35-
use errors::{DiagnosticBuilder, Handler, FatalError};
35+
use errors::{DiagnosticBuilder, Handler};
3636
use visit::{self, FnKind, Visitor};
3737
use parse::ParseSess;
3838
use symbol::{keywords, Symbol};
@@ -83,8 +83,10 @@ macro_rules! declare_features {
8383
}
8484

8585
pub fn use_extern_macros(&self) -> bool {
86-
// The `decl_macro` and `tool_attributes` features imply `use_extern_macros`.
87-
self.use_extern_macros || self.decl_macro || self.tool_attributes
86+
// The `decl_macro`, `tool_attributes` and `custom_attributes`
87+
// features imply `use_extern_macros`.
88+
self.use_extern_macros || self.decl_macro ||
89+
self.tool_attributes || self.custom_attribute
8890
}
8991
}
9092
};
@@ -1898,9 +1900,6 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute],
18981900
}
18991901

19001902
let mut features = Features::new();
1901-
1902-
let mut feature_checker = FeatureChecker::default();
1903-
19041903
let mut edition_enabled_features = FxHashMap();
19051904

19061905
for &(name, .., f_edition, set) in ACTIVE_FEATURES.iter() {
@@ -1989,45 +1988,9 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute],
19891988
}
19901989
}
19911990

1992-
feature_checker.check(span_handler);
1993-
19941991
features
19951992
}
19961993

1997-
/// A collector for mutually exclusive and interdependent features and their flag spans.
1998-
#[derive(Default)]
1999-
struct FeatureChecker {
2000-
use_extern_macros: Option<Span>,
2001-
custom_attribute: Option<Span>,
2002-
}
2003-
2004-
impl FeatureChecker {
2005-
// If this method turns out to be a hotspot due to branching,
2006-
// the branching can be eliminated by modifying `set!()` to set these spans
2007-
// only for the features that need to be checked for mutual exclusion.
2008-
fn collect(&mut self, features: &Features, span: Span) {
2009-
if features.use_extern_macros() {
2010-
// If self.use_extern_macros is None, set to Some(span)
2011-
self.use_extern_macros = self.use_extern_macros.or(Some(span));
2012-
}
2013-
2014-
if features.custom_attribute {
2015-
self.custom_attribute = self.custom_attribute.or(Some(span));
2016-
}
2017-
}
2018-
2019-
fn check(self, handler: &Handler) {
2020-
if let (Some(pm_span), Some(ca_span)) = (self.use_extern_macros, self.custom_attribute) {
2021-
handler.struct_span_err(pm_span, "Cannot use `#![feature(use_extern_macros)]` and \
2022-
`#![feature(custom_attribute)] at the same time")
2023-
.span_note(ca_span, "`#![feature(custom_attribute)]` declared here")
2024-
.emit();
2025-
2026-
FatalError.raise();
2027-
}
2028-
}
2029-
}
2030-
20311994
pub fn check_crate(krate: &ast::Crate,
20321995
sess: &ParseSess,
20331996
features: &Features,

src/test/compile-fail/stmt_expr_attrs_no_feature.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ fn main() {
2020
#[attr]
2121
fn a() {}
2222

23-
#[attr]
23+
#[attr] //~ ERROR attributes on expressions are experimental
2424
{
2525

2626
}

0 commit comments

Comments
 (0)