Skip to content

Commit 76b1ffa

Browse files
committed
syntax_ext: Reuse built-in attribute template checking for macro attributes
1 parent 4330241 commit 76b1ffa

File tree

9 files changed

+132
-113
lines changed

9 files changed

+132
-113
lines changed

src/librustc_lint/builtin.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ use syntax::tokenstream::{TokenTree, TokenStream};
3636
use syntax::ast;
3737
use syntax::ptr::P;
3838
use syntax::ast::Expr;
39-
use syntax::attr::{self, HasAttrs};
39+
use syntax::attr::{self, HasAttrs, AttributeTemplate};
4040
use syntax::source_map::Spanned;
4141
use syntax::edition::Edition;
42-
use syntax::feature_gate::{AttributeGate, AttributeTemplate, AttributeType};
42+
use syntax::feature_gate::{AttributeGate, AttributeType};
4343
use syntax::feature_gate::{Stability, deprecated_attributes};
4444
use syntax_pos::{BytePos, Span, SyntaxContext};
4545
use syntax::symbol::{Symbol, kw, sym};

src/libsyntax/attr/builtin.rs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
//! Parsing and validation of builtin attributes
22
33
use crate::ast::{self, Attribute, MetaItem, NestedMetaItem};
4+
use crate::early_buffered_lints::BufferedEarlyLintId;
5+
use crate::ext::base::ExtCtxt;
6+
use crate::ext::build::AstBuilder;
47
use crate::feature_gate::{Features, GatedCfg};
58
use crate::parse::ParseSess;
69

@@ -19,6 +22,27 @@ enum AttrError {
1922
UnsupportedLiteral(&'static str, /* is_bytestr */ bool),
2023
}
2124

25+
/// A template that the attribute input must match.
26+
/// Only top-level shape (`#[attr]` vs `#[attr(...)]` vs `#[attr = ...]`) is considered now.
27+
#[derive(Clone, Copy)]
28+
pub struct AttributeTemplate {
29+
crate word: bool,
30+
crate list: Option<&'static str>,
31+
crate name_value_str: Option<&'static str>,
32+
}
33+
34+
impl AttributeTemplate {
35+
/// Checks that the given meta-item is compatible with this template.
36+
fn compatible(&self, meta_item_kind: &ast::MetaItemKind) -> bool {
37+
match meta_item_kind {
38+
ast::MetaItemKind::Word => self.word,
39+
ast::MetaItemKind::List(..) => self.list.is_some(),
40+
ast::MetaItemKind::NameValue(lit) if lit.node.is_str() => self.name_value_str.is_some(),
41+
ast::MetaItemKind::NameValue(..) => false,
42+
}
43+
}
44+
}
45+
2246
fn handle_errors(sess: &ParseSess, span: Span, error: AttrError) {
2347
let diag = &sess.span_diagnostic;
2448
match error {
@@ -901,3 +925,75 @@ pub fn find_transparency(
901925
let fallback = if is_legacy { Transparency::SemiTransparent } else { Transparency::Opaque };
902926
(transparency.map_or(fallback, |t| t.0), error)
903927
}
928+
929+
pub fn check_builtin_macro_attribute(ecx: &ExtCtxt<'_>, meta_item: &MetaItem, name: Symbol) {
930+
// All the built-in macro attributes are "words" at the moment.
931+
let template = AttributeTemplate { word: true, list: None, name_value_str: None };
932+
let attr = ecx.attribute(meta_item.span, meta_item.clone());
933+
check_builtin_attribute(ecx.parse_sess, &attr, name, template);
934+
}
935+
936+
crate fn check_builtin_attribute(
937+
sess: &ParseSess, attr: &ast::Attribute, name: Symbol, template: AttributeTemplate
938+
) {
939+
// Some special attributes like `cfg` must be checked
940+
// before the generic check, so we skip them here.
941+
let should_skip = |name| name == sym::cfg;
942+
// Some of previously accepted forms were used in practice,
943+
// report them as warnings for now.
944+
let should_warn = |name| name == sym::doc || name == sym::ignore ||
945+
name == sym::inline || name == sym::link;
946+
947+
match attr.parse_meta(sess) {
948+
Ok(meta) => if !should_skip(name) && !template.compatible(&meta.node) {
949+
let error_msg = format!("malformed `{}` attribute input", name);
950+
let mut msg = "attribute must be of the form ".to_owned();
951+
let mut suggestions = vec![];
952+
let mut first = true;
953+
if template.word {
954+
first = false;
955+
let code = format!("#[{}]", name);
956+
msg.push_str(&format!("`{}`", &code));
957+
suggestions.push(code);
958+
}
959+
if let Some(descr) = template.list {
960+
if !first {
961+
msg.push_str(" or ");
962+
}
963+
first = false;
964+
let code = format!("#[{}({})]", name, descr);
965+
msg.push_str(&format!("`{}`", &code));
966+
suggestions.push(code);
967+
}
968+
if let Some(descr) = template.name_value_str {
969+
if !first {
970+
msg.push_str(" or ");
971+
}
972+
let code = format!("#[{} = \"{}\"]", name, descr);
973+
msg.push_str(&format!("`{}`", &code));
974+
suggestions.push(code);
975+
}
976+
if should_warn(name) {
977+
sess.buffer_lint(
978+
BufferedEarlyLintId::IllFormedAttributeInput,
979+
meta.span,
980+
ast::CRATE_NODE_ID,
981+
&msg,
982+
);
983+
} else {
984+
sess.span_diagnostic.struct_span_err(meta.span, &error_msg)
985+
.span_suggestions(
986+
meta.span,
987+
if suggestions.len() == 1 {
988+
"must be of the form"
989+
} else {
990+
"the following are the possible correct uses"
991+
},
992+
suggestions.into_iter(),
993+
Applicability::HasPlaceholders,
994+
).emit();
995+
}
996+
}
997+
Err(mut err) => err.emit(),
998+
}
999+
}

src/libsyntax/feature_gate.rs

Lines changed: 2 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ use crate::ast::{
1919
self, AssocTyConstraint, AssocTyConstraintKind, NodeId, GenericParam, GenericParamKind,
2020
PatKind, RangeEnd,
2121
};
22-
use crate::attr;
23-
use crate::early_buffered_lints::BufferedEarlyLintId;
22+
use crate::attr::{self, check_builtin_attribute, AttributeTemplate};
2423
use crate::source_map::Spanned;
2524
use crate::edition::{ALL_EDITIONS, Edition};
2625
use crate::visit::{self, FnKind, Visitor};
@@ -906,27 +905,6 @@ pub enum AttributeGate {
906905
Ungated,
907906
}
908907

909-
/// A template that the attribute input must match.
910-
/// Only top-level shape (`#[attr]` vs `#[attr(...)]` vs `#[attr = ...]`) is considered now.
911-
#[derive(Clone, Copy)]
912-
pub struct AttributeTemplate {
913-
word: bool,
914-
list: Option<&'static str>,
915-
name_value_str: Option<&'static str>,
916-
}
917-
918-
impl AttributeTemplate {
919-
/// Checks that the given meta-item is compatible with this template.
920-
fn compatible(&self, meta_item_kind: &ast::MetaItemKind) -> bool {
921-
match meta_item_kind {
922-
ast::MetaItemKind::Word => self.word,
923-
ast::MetaItemKind::List(..) => self.list.is_some(),
924-
ast::MetaItemKind::NameValue(lit) if lit.node.is_str() => self.name_value_str.is_some(),
925-
ast::MetaItemKind::NameValue(..) => false,
926-
}
927-
}
928-
}
929-
930908
/// A convenience macro for constructing attribute templates.
931909
/// E.g., `template!(Word, List: "description")` means that the attribute
932910
/// supports forms `#[attr]` and `#[attr(description)]`.
@@ -1901,70 +1879,6 @@ impl<'a> PostExpansionVisitor<'a> {
19011879
Abi::System => {}
19021880
}
19031881
}
1904-
1905-
fn check_builtin_attribute(&mut self, attr: &ast::Attribute, name: Symbol,
1906-
template: AttributeTemplate) {
1907-
// Some special attributes like `cfg` must be checked
1908-
// before the generic check, so we skip them here.
1909-
let should_skip = |name| name == sym::cfg;
1910-
// Some of previously accepted forms were used in practice,
1911-
// report them as warnings for now.
1912-
let should_warn = |name| name == sym::doc || name == sym::ignore ||
1913-
name == sym::inline || name == sym::link;
1914-
1915-
match attr.parse_meta(self.context.parse_sess) {
1916-
Ok(meta) => if !should_skip(name) && !template.compatible(&meta.node) {
1917-
let error_msg = format!("malformed `{}` attribute input", name);
1918-
let mut msg = "attribute must be of the form ".to_owned();
1919-
let mut suggestions = vec![];
1920-
let mut first = true;
1921-
if template.word {
1922-
first = false;
1923-
let code = format!("#[{}]", name);
1924-
msg.push_str(&format!("`{}`", &code));
1925-
suggestions.push(code);
1926-
}
1927-
if let Some(descr) = template.list {
1928-
if !first {
1929-
msg.push_str(" or ");
1930-
}
1931-
first = false;
1932-
let code = format!("#[{}({})]", name, descr);
1933-
msg.push_str(&format!("`{}`", &code));
1934-
suggestions.push(code);
1935-
}
1936-
if let Some(descr) = template.name_value_str {
1937-
if !first {
1938-
msg.push_str(" or ");
1939-
}
1940-
let code = format!("#[{} = \"{}\"]", name, descr);
1941-
msg.push_str(&format!("`{}`", &code));
1942-
suggestions.push(code);
1943-
}
1944-
if should_warn(name) {
1945-
self.context.parse_sess.buffer_lint(
1946-
BufferedEarlyLintId::IllFormedAttributeInput,
1947-
meta.span,
1948-
ast::CRATE_NODE_ID,
1949-
&msg,
1950-
);
1951-
} else {
1952-
self.context.parse_sess.span_diagnostic.struct_span_err(meta.span, &error_msg)
1953-
.span_suggestions(
1954-
meta.span,
1955-
if suggestions.len() == 1 {
1956-
"must be of the form"
1957-
} else {
1958-
"the following are the possible correct uses"
1959-
},
1960-
suggestions.into_iter(),
1961-
Applicability::HasPlaceholders,
1962-
).emit();
1963-
}
1964-
}
1965-
Err(mut err) => err.emit(),
1966-
}
1967-
}
19681882
}
19691883

19701884
impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
@@ -2005,7 +1919,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
20051919
match attr_info {
20061920
// `rustc_dummy` doesn't have any restrictions specific to built-in attributes.
20071921
Some(&(name, _, template, _)) if name != sym::rustc_dummy =>
2008-
self.check_builtin_attribute(attr, name, template),
1922+
check_builtin_attribute(self.context.parse_sess, attr, name, template),
20091923
_ => if let Some(TokenTree::Token(token)) = attr.tokens.trees().next() {
20101924
if token == token::Eq {
20111925
// All key-value attributes are restricted to meta-item syntax.

src/libsyntax_ext/global_allocator.rs

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,22 @@
1-
use errors::Applicability;
2-
use syntax::ast::{self, Arg, Attribute, Expr, FnHeader, Generics, Ident, Item};
31
use syntax::ast::{ItemKind, Mutability, Ty, TyKind, Unsafety, VisibilityKind};
4-
use syntax::source_map::respan;
2+
use syntax::ast::{self, Arg, Attribute, Expr, FnHeader, Generics, Ident, Item};
3+
use syntax::attr::check_builtin_macro_attribute;
54
use syntax::ext::allocator::{AllocatorKind, AllocatorMethod, AllocatorTy, ALLOCATOR_METHODS};
65
use syntax::ext::base::{Annotatable, ExtCtxt};
76
use syntax::ext::build::AstBuilder;
87
use syntax::ext::hygiene::SyntaxContext;
98
use syntax::ptr::P;
9+
use syntax::source_map::respan;
1010
use syntax::symbol::{kw, sym};
1111
use syntax_pos::Span;
1212

1313
pub fn expand(
1414
ecx: &mut ExtCtxt<'_>,
15-
span: Span,
15+
_span: Span,
1616
meta_item: &ast::MetaItem,
1717
item: Annotatable,
1818
) -> Vec<Annotatable> {
19-
if !meta_item.is_word() {
20-
let msg = format!("malformed `{}` attribute input", meta_item.path);
21-
ecx.parse_sess.span_diagnostic.struct_span_err(span, &msg)
22-
.span_suggestion(
23-
span,
24-
"must be of the form",
25-
format!("`#[{}]`", meta_item.path),
26-
Applicability::MachineApplicable
27-
).emit();
28-
}
19+
check_builtin_macro_attribute(ecx, meta_item, sym::global_allocator);
2920

3021
let not_static = |item: Annotatable| {
3122
ecx.parse_sess.span_diagnostic.span_err(item.span(), "allocators must be statics");

src/libsyntax_ext/test.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,34 @@
11
/// The expansion from a test function to the appropriate test struct for libtest
22
/// Ideally, this code would be in libtest but for efficiency and error messages it lives here.
33
4+
use syntax::ast;
5+
use syntax::attr::{self, check_builtin_macro_attribute};
46
use syntax::ext::base::*;
57
use syntax::ext::build::AstBuilder;
68
use syntax::ext::hygiene::SyntaxContext;
7-
use syntax::attr;
8-
use syntax::ast;
99
use syntax::print::pprust;
1010
use syntax::symbol::{Symbol, sym};
1111
use syntax_pos::Span;
12+
1213
use std::iter;
1314

1415
pub fn expand_test(
1516
cx: &mut ExtCtxt<'_>,
1617
attr_sp: Span,
17-
_meta_item: &ast::MetaItem,
18+
meta_item: &ast::MetaItem,
1819
item: Annotatable,
1920
) -> Vec<Annotatable> {
21+
check_builtin_macro_attribute(cx, meta_item, sym::test);
2022
expand_test_or_bench(cx, attr_sp, item, false)
2123
}
2224

2325
pub fn expand_bench(
2426
cx: &mut ExtCtxt<'_>,
2527
attr_sp: Span,
26-
_meta_item: &ast::MetaItem,
28+
meta_item: &ast::MetaItem,
2729
item: Annotatable,
2830
) -> Vec<Annotatable> {
31+
check_builtin_macro_attribute(cx, meta_item, sym::bench);
2932
expand_test_or_bench(cx, attr_sp, item, true)
3033
}
3134

src/libsyntax_ext/test_case.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,23 @@
99
// We mark item with an inert attribute "rustc_test_marker" which the test generation
1010
// logic will pick up on.
1111

12+
use syntax::ast;
13+
use syntax::attr::check_builtin_macro_attribute;
1214
use syntax::ext::base::*;
1315
use syntax::ext::build::AstBuilder;
1416
use syntax::ext::hygiene::SyntaxContext;
15-
use syntax::ast;
1617
use syntax::source_map::respan;
1718
use syntax::symbol::sym;
1819
use syntax_pos::Span;
1920

2021
pub fn expand(
2122
ecx: &mut ExtCtxt<'_>,
2223
attr_sp: Span,
23-
_meta_item: &ast::MetaItem,
24+
meta_item: &ast::MetaItem,
2425
anno_item: Annotatable
2526
) -> Vec<Annotatable> {
27+
check_builtin_macro_attribute(ecx, meta_item, sym::test_case);
28+
2629
if !ecx.ecfg.should_test { return vec![]; }
2730

2831
let sp = attr_sp.with_ctxt(SyntaxContext::empty().apply_mark(ecx.current_expansion.id));

src/test/ui/allocator/allocator-args.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error: malformed `global_allocator` attribute input
22
--> $DIR/allocator-args.rs:10:1
33
|
44
LL | #[global_allocator(malloc)]
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: ``#[global_allocator]``
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[global_allocator]`
66

77
error: aborting due to previous error
88

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1+
error: malformed `bench` attribute input
2+
--> $DIR/issue-43106-gating-of-bench.rs:15:1
3+
|
4+
LL | #![bench = "4100"]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[bench]`
6+
17
error[E0601]: `main` function not found in crate `issue_43106_gating_of_bench`
28
|
39
= note: consider adding a `main` function to `$DIR/issue-43106-gating-of-bench.rs`
410

5-
error: aborting due to previous error
11+
error: aborting due to 2 previous errors
612

713
For more information about this error, try `rustc --explain E0601`.
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1+
error: malformed `test` attribute input
2+
--> $DIR/issue-43106-gating-of-test.rs:10:1
3+
|
4+
LL | #![test = "4200"]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[test]`
6+
17
error[E0601]: `main` function not found in crate `issue_43106_gating_of_test`
28
|
39
= note: consider adding a `main` function to `$DIR/issue-43106-gating-of-test.rs`
410

5-
error: aborting due to previous error
11+
error: aborting due to 2 previous errors
612

713
For more information about this error, try `rustc --explain E0601`.

0 commit comments

Comments
 (0)