Skip to content

Commit 1eaaf44

Browse files
committed
Allow attributes in formal function parameters
1 parent 5c45343 commit 1eaaf44

28 files changed

+1115
-74
lines changed

src/librustc_data_structures/thin_vec.rs

+6
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,9 @@ impl<T: HashStable<CTX>, CTX> HashStable<CTX> for ThinVec<T> {
6666
(**self).hash_stable(hcx, hasher)
6767
}
6868
}
69+
70+
impl<T> Default for ThinVec<T> {
71+
fn default() -> Self {
72+
Self(None)
73+
}
74+
}

src/librustc_passes/ast_validation.rs

+43-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use rustc::session::Session;
1414
use rustc_data_structures::fx::FxHashMap;
1515
use syntax::ast::*;
1616
use syntax::attr;
17+
use syntax::feature_gate::is_builtin_attr;
1718
use syntax::source_map::Spanned;
1819
use syntax::symbol::{kw, sym};
1920
use syntax::ptr::P;
@@ -365,6 +366,29 @@ impl<'a> AstValidator<'a> {
365366
_ => None,
366367
}
367368
}
369+
370+
fn check_fn_decl(&self, fn_decl: &FnDecl) {
371+
fn_decl
372+
.inputs
373+
.iter()
374+
.flat_map(|i| i.attrs.as_ref())
375+
.filter(|attr| {
376+
let arr = [sym::allow, sym::cfg, sym::cfg_attr, sym::deny, sym::forbid, sym::warn];
377+
!arr.contains(&attr.name_or_empty()) && is_builtin_attr(attr)
378+
})
379+
.for_each(|attr| if attr.is_sugared_doc {
380+
let mut err = self.err_handler().struct_span_err(
381+
attr.span,
382+
"documentation comments cannot be applied to function parameters"
383+
);
384+
err.span_label(attr.span, "doc comments are not allowed here");
385+
err.emit();
386+
}
387+
else {
388+
self.err_handler().span_err(attr.span, "allow, cfg, cfg_attr, deny, \
389+
forbid, and warn are the only allowed built-in attributes in function parameters")
390+
});
391+
}
368392
}
369393

370394
enum GenericPosition {
@@ -470,6 +494,9 @@ fn validate_generics_order<'a>(
470494
impl<'a> Visitor<'a> for AstValidator<'a> {
471495
fn visit_expr(&mut self, expr: &'a Expr) {
472496
match expr.node {
497+
ExprKind::Closure(_, _, _, ref fn_decl, _, _) => {
498+
self.check_fn_decl(fn_decl);
499+
}
473500
ExprKind::IfLet(_, ref expr, _, _) | ExprKind::WhileLet(_, ref expr, _, _) =>
474501
self.while_if_let_ambiguity(&expr),
475502
ExprKind::InlineAsm(..) if !self.session.target.target.options.allow_asm => {
@@ -484,6 +511,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
484511
fn visit_ty(&mut self, ty: &'a Ty) {
485512
match ty.node {
486513
TyKind::BareFn(ref bfty) => {
514+
self.check_fn_decl(&bfty.decl);
487515
self.check_decl_no_pat(&bfty.decl, |span, _| {
488516
struct_span_err!(self.session, span, E0561,
489517
"patterns aren't allowed in function pointer types").emit();
@@ -601,10 +629,11 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
601629
.note("only trait implementations may be annotated with default").emit();
602630
}
603631
}
604-
ItemKind::Fn(_, ref header, ref generics, _) => {
632+
ItemKind::Fn(ref decl, ref header, ref generics, _) => {
633+
self.visit_fn_header(header);
634+
self.check_fn_decl(decl);
605635
// We currently do not permit const generics in `const fn`, as
606636
// this is tantamount to allowing compile-time dependent typing.
607-
self.visit_fn_header(header);
608637
if header.constness.node == Constness::Const {
609638
// Look for const generics and error if we find any.
610639
for param in &generics.params {
@@ -657,6 +686,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
657686
self.no_questions_in_bounds(bounds, "supertraits", true);
658687
for trait_item in trait_items {
659688
if let TraitItemKind::Method(ref sig, ref block) = trait_item.node {
689+
self.check_fn_decl(&sig.decl);
660690
self.check_trait_fn_not_async(trait_item.span, sig.header.asyncness.node);
661691
self.check_trait_fn_not_const(sig.header.constness);
662692
if block.is_none() {
@@ -711,6 +741,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
711741
fn visit_foreign_item(&mut self, fi: &'a ForeignItem) {
712742
match fi.node {
713743
ForeignItemKind::Fn(ref decl, _) => {
744+
self.check_fn_decl(decl);
714745
self.check_decl_no_pat(decl, |span, _| {
715746
struct_span_err!(self.session, span, E0130,
716747
"patterns aren't allowed in foreign function declarations")
@@ -864,6 +895,16 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
864895
"`async fn` is not permitted in the 2015 edition").emit();
865896
}
866897
}
898+
899+
fn visit_impl_item(&mut self, ii: &'a ImplItem) {
900+
match ii.node {
901+
ImplItemKind::Method(ref sig, _) => {
902+
self.check_fn_decl(&sig.decl);
903+
}
904+
_ => {}
905+
}
906+
visit::walk_impl_item(self, ii);
907+
}
867908
}
868909

869910
pub fn check_crate(session: &Session, krate: &Crate) -> (bool, bool) {

src/libsyntax/ast.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1770,6 +1770,7 @@ pub struct InlineAsm {
17701770
/// E.g., `bar: usize` as in `fn foo(bar: usize)`.
17711771
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
17721772
pub struct Arg {
1773+
pub attrs: ThinVec<Attribute>,
17731774
pub ty: P<Ty>,
17741775
pub pat: P<Pat>,
17751776
pub id: NodeId,
@@ -1817,14 +1818,15 @@ impl Arg {
18171818
}
18181819
}
18191820

1820-
pub fn from_self(eself: ExplicitSelf, eself_ident: Ident) -> Arg {
1821+
pub fn from_self(attrs: ThinVec<Attribute>, eself: ExplicitSelf, eself_ident: Ident) -> Arg {
18211822
let span = eself.span.to(eself_ident.span);
18221823
let infer_ty = P(Ty {
18231824
id: DUMMY_NODE_ID,
18241825
node: TyKind::ImplicitSelf,
18251826
span,
18261827
});
18271828
let arg = |mutbl, ty| Arg {
1829+
attrs,
18281830
pat: P(Pat {
18291831
id: DUMMY_NODE_ID,
18301832
node: PatKind::Ident(BindingMode::ByValue(mutbl), eself_ident, None),

src/libsyntax/attr/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -724,7 +724,7 @@ macro_rules! derive_has_attrs {
724724

725725
derive_has_attrs! {
726726
Item, Expr, Local, ast::ForeignItem, ast::StructField, ast::ImplItem, ast::TraitItem, ast::Arm,
727-
ast::Field, ast::FieldPat, ast::Variant_
727+
ast::Field, ast::FieldPat, ast::Variant_, ast::Arg
728728
}
729729

730730
pub fn inject(mut krate: ast::Crate, parse_sess: &ParseSess, attrs: &[String]) -> ast::Crate {

src/libsyntax/config.rs

+9
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,10 @@ impl<'a> StripUnconfigured<'a> {
298298
}
299299
}
300300

301+
pub fn configure_fn_decl(&mut self, fn_decl: &mut ast::FnDecl) {
302+
fn_decl.inputs.flat_map_in_place(|arg| self.configure(arg));
303+
}
304+
301305
/// Denies `#[cfg]` on generic parameters until we decide what to do with it.
302306
/// See issue #51279.
303307
pub fn disallow_cfg_on_generic_param(&mut self, param: &ast::GenericParam) {
@@ -364,6 +368,11 @@ impl<'a> MutVisitor for StripUnconfigured<'a> {
364368
self.configure_pat(pat);
365369
noop_visit_pat(pat, self)
366370
}
371+
372+
fn visit_fn_decl(&mut self, mut fn_decl: &mut P<ast::FnDecl>) {
373+
self.configure_fn_decl(&mut fn_decl);
374+
noop_visit_fn_decl(fn_decl, self);
375+
}
367376
}
368377

369378
fn is_cfg(attr: &ast::Attribute) -> bool {

src/libsyntax/ext/build.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -963,9 +963,10 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
963963
fn arg(&self, span: Span, ident: ast::Ident, ty: P<ast::Ty>) -> ast::Arg {
964964
let arg_pat = self.pat_ident(span, ident);
965965
ast::Arg {
966-
ty,
966+
attrs: ThinVec::default(),
967+
id: ast::DUMMY_NODE_ID,
967968
pat: arg_pat,
968-
id: ast::DUMMY_NODE_ID
969+
ty,
969970
}
970971
}
971972

src/libsyntax/ext/expand.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1616,6 +1616,11 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
16161616
*id = self.cx.resolver.next_node_id()
16171617
}
16181618
}
1619+
1620+
fn visit_fn_decl(&mut self, mut fn_decl: &mut P<ast::FnDecl>) {
1621+
self.cfg.configure_fn_decl(&mut fn_decl);
1622+
noop_visit_fn_decl(fn_decl, self);
1623+
}
16191624
}
16201625

16211626
pub struct ExpansionConfig<'feat> {

src/libsyntax/feature_gate.rs

+15
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,9 @@ declare_features! (
560560
// Allows the user of associated type bounds.
561561
(active, associated_type_bounds, "1.34.0", Some(52662), None),
562562

563+
// Attributes on formal function params
564+
(active, param_attrs, "1.36.0", Some(60406), None),
565+
563566
// Allows calling constructor functions in `const fn`
564567
// FIXME Create issue
565568
(active, const_constructor, "1.37.0", Some(61456), None),
@@ -2508,6 +2511,18 @@ pub fn check_crate(krate: &ast::Crate,
25082511
parse_sess: sess,
25092512
plugin_attributes,
25102513
};
2514+
2515+
sess
2516+
.param_attr_spans
2517+
.borrow()
2518+
.iter()
2519+
.for_each(|span| gate_feature!(
2520+
&ctx,
2521+
param_attrs,
2522+
*span,
2523+
"attributes on function parameters are unstable"
2524+
));
2525+
25112526
let visitor = &mut PostExpansionVisitor {
25122527
context: &ctx,
25132528
builtin_attributes: &*BUILTIN_ATTRIBUTE_MAP,

src/libsyntax/mut_visit.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -568,8 +568,9 @@ pub fn noop_visit_meta_item<T: MutVisitor>(mi: &mut MetaItem, vis: &mut T) {
568568
vis.visit_span(span);
569569
}
570570

571-
pub fn noop_visit_arg<T: MutVisitor>(Arg { id, pat, ty }: &mut Arg, vis: &mut T) {
571+
pub fn noop_visit_arg<T: MutVisitor>(Arg { attrs, id, pat, ty }: &mut Arg, vis: &mut T) {
572572
vis.visit_id(id);
573+
visit_thin_attrs(attrs, vis);
573574
vis.visit_pat(pat);
574575
vis.visit_ty(ty);
575576
}

src/libsyntax/parse/attr.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ const DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG: &str = "an inner attribute is not \
1818
permitted in this context";
1919

2020
impl<'a> Parser<'a> {
21+
crate fn parse_arg_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
22+
let attrs = self.parse_outer_attributes()?;
23+
attrs.iter().for_each(|a|
24+
self.sess.param_attr_spans.borrow_mut().push(a.span)
25+
);
26+
Ok(attrs)
27+
}
28+
2129
/// Parse attributes that appear before an item
2230
crate fn parse_outer_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
2331
let mut attrs: Vec<ast::Attribute> = Vec::new();
@@ -35,7 +43,8 @@ impl<'a> Parser<'a> {
3543
};
3644
let inner_parse_policy =
3745
InnerAttributeParsePolicy::NotPermitted { reason: inner_error_reason };
38-
attrs.push(self.parse_attribute_with_inner_parse_policy(inner_parse_policy)?);
46+
let attr = self.parse_attribute_with_inner_parse_policy(inner_parse_policy)?;
47+
attrs.push(attr);
3948
just_parsed_doc_comment = false;
4049
}
4150
token::DocComment(s) => {

src/libsyntax/parse/diagnostics.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ crate fn dummy_arg(ident: Ident) -> Arg {
2828
span: ident.span,
2929
id: ast::DUMMY_NODE_ID
3030
};
31-
Arg { ty: P(ty), pat: pat, id: ast::DUMMY_NODE_ID }
31+
Arg { attrs: ThinVec::default(), id: ast::DUMMY_NODE_ID, pat, ty: P(ty) }
3232
}
3333

3434
pub enum Error {
@@ -1074,11 +1074,11 @@ impl<'a> Parser<'a> {
10741074
Err(err)
10751075
}
10761076

1077-
crate fn eat_incorrect_doc_comment(&mut self, applied_to: &str) {
1077+
crate fn eat_incorrect_doc_comment_for_arg_type(&mut self) {
10781078
if let token::DocComment(_) = self.token.kind {
10791079
let mut err = self.diagnostic().struct_span_err(
10801080
self.token.span,
1081-
&format!("documentation comments cannot be applied to {}", applied_to),
1081+
"documentation comments cannot be applied to a function parameter's type",
10821082
);
10831083
err.span_label(self.token.span, "doc comments are not allowed here");
10841084
err.emit();
@@ -1095,7 +1095,7 @@ impl<'a> Parser<'a> {
10951095
self.bump();
10961096
let mut err = self.diagnostic().struct_span_err(
10971097
sp,
1098-
&format!("attributes cannot be applied to {}", applied_to),
1098+
"attributes cannot be applied to a function parameter's type",
10991099
);
11001100
err.span_label(sp, "attributes are not allowed here");
11011101
err.emit();

src/libsyntax/parse/lexer/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1532,6 +1532,7 @@ mod tests {
15321532
buffered_lints: Lock::new(vec![]),
15331533
edition: Edition::from_session(),
15341534
ambiguous_block_expr_parse: Lock::new(FxHashMap::default()),
1535+
param_attr_spans: Lock::new(Vec::new()),
15351536
}
15361537
}
15371538

src/libsyntax/parse/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ pub struct ParseSess {
5454
/// operation token that followed it, but that the parser cannot identify without further
5555
/// analysis.
5656
pub ambiguous_block_expr_parse: Lock<FxHashMap<Span, Span>>,
57+
pub param_attr_spans: Lock<Vec<Span>>
5758
}
5859

5960
impl ParseSess {
@@ -79,6 +80,7 @@ impl ParseSess {
7980
buffered_lints: Lock::new(vec![]),
8081
edition: Edition::from_session(),
8182
ambiguous_block_expr_parse: Lock::new(FxHashMap::default()),
83+
param_attr_spans: Lock::new(Vec::new()),
8284
}
8385
}
8486

0 commit comments

Comments
 (0)