Skip to content

Commit f732632

Browse files
committed
Create diagnostic::on_unimplemented attr which can roundtrip through ast lowering
1 parent 6132a8f commit f732632

14 files changed

Lines changed: 199 additions & 17 deletions

File tree

compiler/rustc_ast/src/ast.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ use rustc_span::{
3636
use thin_vec::{ThinVec, thin_vec};
3737

3838
use crate::attr::data_structures::CfgEntry;
39+
use crate::attr::diagnostic::Directive;
3940
pub use crate::format::*;
4041
use crate::token::{self, CommentKind, Delimiter};
4142
use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream};
@@ -3795,6 +3796,8 @@ pub struct Trait {
37953796
pub bounds: GenericBounds,
37963797
#[visitable(extra = AssocCtxt::Trait)]
37973798
pub items: ThinVec<Box<AssocItem>>,
3799+
#[visitable(ignore)]
3800+
pub on_unimplemented: Option<Directive>,
37983801
}
37993802

38003803
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]

compiler/rustc_ast_lowering/src/item.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
225225
.lower_eii_decl(id, *name, target)
226226
.map(|decl| vec![hir::Attribute::Parsed(AttributeKind::EiiDeclaration(decl))])
227227
.unwrap_or_default(),
228-
228+
ItemKind::Trait(Trait { on_unimplemented: Some(ou), .. }) => {
229+
// FIXME Lower here
230+
vec![hir::Attribute::Parsed(AttributeKind::OnUnimplemented {
231+
directive: Some(Box::new(ou.clone())),
232+
})]
233+
}
229234
ItemKind::ExternCrate(..)
230235
| ItemKind::Use(..)
231236
| ItemKind::Const(..)
@@ -546,6 +551,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
546551
generics,
547552
bounds,
548553
items,
554+
on_unimplemented: _,
549555
}) => {
550556
let constness = self.lower_constness(*constness);
551557
let impl_restriction = self.lower_impl_restriction(impl_restriction);

compiler/rustc_ast_pretty/src/pprust/state/item.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,7 @@ impl<'a> State<'a> {
379379
generics,
380380
bounds,
381381
items,
382+
on_unimplemented,
382383
}) => {
383384
let (cb, ib) = self.head("");
384385
self.print_visibility(&item.vis);
@@ -397,6 +398,10 @@ impl<'a> State<'a> {
397398
self.word(" ");
398399
self.bopen(ib);
399400
self.print_inner_attributes(&item.attrs);
401+
if on_unimplemented.is_some() {
402+
// FIXME make this nicer
403+
self.word("#[diagnostic::rustc_on_unimplemented(...)]")
404+
}
400405
for trait_item in items {
401406
self.print_assoc_item(trait_item);
402407
}

compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,15 @@ impl Mode {
112112
}
113113
}
114114

115+
pub fn parse_rustc_on_unimplemented(
116+
cx: &mut AcceptContext<'_, '_>,
117+
args: &ArgParser,
118+
) -> Option<Directive> {
119+
let items = parse_list(cx, args, Mode::RustcOnUnimplemented)?;
120+
121+
parse_directive_items(cx, Mode::RustcOnUnimplemented, items.mixed(), true)
122+
}
123+
115124
fn merge_directives(
116125
cx: &mut AcceptContext<'_, '_>,
117126
first: &mut Option<(Span, Directive)>,

compiler/rustc_attr_parsing/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ pub use attributes::cfg::{
118118
CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg, parse_cfg_attr, parse_cfg_entry,
119119
};
120120
pub use attributes::cfg_select::*;
121+
pub use attributes::diagnostic::parse_rustc_on_unimplemented;
121122
pub use attributes::util::{is_builtin_attr, parse_version};
122123
pub use context::{OmitDoc, ShouldEmit};
123124
pub use interface::{AttributeParser, EmitAttribute};
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#![allow(warnings)]
2+
3+
use std::convert::identity;
4+
5+
use rustc_ast::{AttrArgs, AttrStyle, Item, ItemKind, Trait, ast};
6+
use rustc_attr_parsing::parser::{AllowExprMetavar, ArgParser};
7+
use rustc_attr_parsing::{AttributeParser, AttributeSafety, ParsedDescription, ShouldEmit};
8+
use rustc_expand::base::{Annotatable, ExtCtxt};
9+
use rustc_feature::template;
10+
use rustc_hir::{AttrPath, Target};
11+
use rustc_parse::parser::Recovery;
12+
use rustc_span::{Span, sym};
13+
14+
pub(crate) mod rustc_on_unimplemented {
15+
use super::*;
16+
pub(crate) fn expand(ecx: &mut ExtCtxt<'_>, attr: &ast::Attribute, item: &mut Annotatable) {
17+
let attr = attr.get_normal_item();
18+
let span = attr.span();
19+
let Some(args) = ArgParser::from_attr_args(
20+
&attr.args.unparsed_ref().unwrap(),
21+
&[sym::diagnostic, sym::rustc_on_unimplemented],
22+
&ecx.sess.psess,
23+
ShouldEmit::ErrorsAndLints { recovery: Recovery::Forbidden },
24+
AllowExprMetavar::No,
25+
) else {
26+
// Lints/errors are/will be emitted by ArgParser.
27+
return;
28+
};
29+
30+
match item {
31+
Annotatable::Item(box Item {
32+
span: target_span,
33+
kind: ItemKind::Trait(box Trait { on_unimplemented, .. }),
34+
..
35+
}) => {
36+
if on_unimplemented.is_some() {
37+
// FIXME(mejrs) coalescing multiple rustc_on_unimplemented attrs isn't
38+
// (and was never) supported - might be nice to have at some point
39+
ecx.dcx().span_err(
40+
span,
41+
"using multiple `#[diagnostic::rustc_on_unimplemented]` is not supported",
42+
);
43+
}
44+
*on_unimplemented = AttributeParser::parse_single_args(
45+
ecx.sess,
46+
span,
47+
span,
48+
AttrStyle::Inner,
49+
AttrPath::from_ast(&attr.path, identity),
50+
None,
51+
AttributeSafety::Normal,
52+
ParsedDescription::Macro,
53+
*target_span,
54+
ecx.current_expansion.lint_node_id,
55+
Target::Trait,
56+
Some(ecx.ecfg.features),
57+
ShouldEmit::ErrorsAndLints { recovery: Recovery::Forbidden },
58+
&args,
59+
rustc_attr_parsing::parse_rustc_on_unimplemented,
60+
&template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]),
61+
);
62+
}
63+
_ => {
64+
ecx.dcx()
65+
.span_err(item.span(), "`#[diagnostic::rustc_on_unimplemented]` is only supported on trait definitions");
66+
}
67+
}
68+
}
69+
}

compiler/rustc_builtin_macros/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ mod concat_bytes;
3333
mod define_opaque;
3434
mod derive;
3535
mod deriving;
36+
mod diagnostic;
3637
mod edition_panic;
3738
mod eii;
3839
mod env;
@@ -143,4 +144,9 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
143144
register(sym::contracts_requires, requires);
144145
let ensures = SyntaxExtensionKind::Attr(Arc::new(contracts::ExpandEnsures));
145146
register(sym::contracts_ensures, ensures);
147+
148+
resolver.insert_inert_attr(
149+
&[sym::diagnostic, sym::rustc_on_unimplemented],
150+
SyntaxExtensionKind::InertAttr(Arc::new(diagnostic::rustc_on_unimplemented::expand)),
151+
);
146152
}

compiler/rustc_expand/src/base.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,19 @@ pub trait GlobDelegationExpander {
409409
fn expand(&self, ecx: &mut ExtCtxt<'_>) -> ExpandResult<Vec<(Ident, Option<Ident>)>, ()>;
410410
}
411411

412+
pub trait AstAnnotate {
413+
fn annotate(&self, ecx: &mut ExtCtxt<'_>, attr: &ast::Attribute, item: &mut Annotatable);
414+
}
415+
416+
impl<F> AstAnnotate for F
417+
where
418+
F: Fn(&mut ExtCtxt<'_>, &ast::Attribute, &mut Annotatable),
419+
{
420+
fn annotate(&self, ecx: &mut ExtCtxt<'_>, attr: &ast::Attribute, item: &mut Annotatable) {
421+
self(ecx, attr, item)
422+
}
423+
}
424+
412425
fn make_stmts_default(expr: Option<Box<ast::Expr>>) -> Option<SmallVec<[ast::Stmt; 1]>> {
413426
expr.map(|e| {
414427
smallvec![ast::Stmt { id: ast::DUMMY_NODE_ID, span: e.span, kind: ast::StmtKind::Expr(e) }]
@@ -787,6 +800,16 @@ pub enum SyntaxExtensionKind {
787800
///
788801
/// This is for delegated function implementations, and has nothing to do with glob imports.
789802
GlobDelegation(Arc<dyn GlobDelegationExpander + sync::DynSync + sync::DynSend>),
803+
804+
/// An AST-based "inert" attribute.
805+
///
806+
/// These represent otherwise inert attributes. Instead of creating or modifying items like
807+
/// a macro would, they instead attach state to these items which is carried through
808+
/// ast/hir lowering and then emitted like an actual inert attribute.
809+
InertAttr(
810+
/// An expander with signature (&mut AST).
811+
Arc<dyn AstAnnotate + sync::DynSync + sync::DynSend>,
812+
),
790813
}
791814

792815
impl SyntaxExtensionKind {
@@ -857,7 +880,8 @@ impl SyntaxExtension {
857880
| SyntaxExtensionKind::GlobDelegation(..) => MacroKinds::BANG,
858881
SyntaxExtensionKind::Attr(..)
859882
| SyntaxExtensionKind::LegacyAttr(..)
860-
| SyntaxExtensionKind::NonMacroAttr => MacroKinds::ATTR,
883+
| SyntaxExtensionKind::NonMacroAttr
884+
| SyntaxExtensionKind::InertAttr(..) => MacroKinds::ATTR,
861885
SyntaxExtensionKind::Derive(..) | SyntaxExtensionKind::LegacyDerive(..) => {
862886
MacroKinds::DERIVE
863887
}
@@ -1180,6 +1204,8 @@ pub trait ResolverExpand {
11801204
/// Mark the scope as having a compile error so that error for lookup in this scope
11811205
/// should be suppressed
11821206
fn mark_scope_with_compile_error(&mut self, parent_node: NodeId);
1207+
1208+
fn insert_inert_attr(&mut self, path: &'static [Symbol], kind: SyntaxExtensionKind);
11831209
}
11841210

11851211
pub trait LintStoreExpand {

compiler/rustc_expand/src/expand.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -897,6 +897,10 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
897897
self.cx.expanded_inert_attrs.mark(&attr);
898898
item.visit_attrs(|attrs| attrs.insert(pos, attr));
899899
fragment_kind.expect_from_annotatables(iter::once(item))
900+
} else if let SyntaxExtensionKind::InertAttr(expander) = ext {
901+
let mut item = item;
902+
expander.annotate(self.cx, &attr, &mut item);
903+
fragment_kind.expect_from_annotatables([item])
900904
} else {
901905
unreachable!();
902906
}

compiler/rustc_parse/src/parser/item.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,6 +1178,7 @@ impl<'a> Parser<'a> {
11781178
generics,
11791179
bounds,
11801180
items,
1181+
on_unimplemented: None,
11811182
})))
11821183
}
11831184
}

0 commit comments

Comments
 (0)