Skip to content

Commit cc56c20

Browse files
committed
Auto merge of #25168 - Manishearth:register_attr, r=eddyb
This lets plugin authors opt attributes out of the `custom_attribute` and `unused_attribute` checks. cc @thepowersgang
2 parents 0fc0476 + 22b720a commit cc56c20

File tree

7 files changed

+137
-17
lines changed

7 files changed

+137
-17
lines changed

src/librustc/plugin/registry.rs

+20
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use syntax::codemap::Span;
2020
use syntax::parse::token;
2121
use syntax::ptr::P;
2222
use syntax::ast;
23+
use syntax::feature_gate::AttributeType;
2324

2425
use std::collections::HashMap;
2526
use std::borrow::ToOwned;
@@ -54,6 +55,9 @@ pub struct Registry<'a> {
5455

5556
#[doc(hidden)]
5657
pub llvm_passes: Vec<String>,
58+
59+
#[doc(hidden)]
60+
pub attributes: Vec<(String, AttributeType)>,
5761
}
5862

5963
impl<'a> Registry<'a> {
@@ -67,6 +71,7 @@ impl<'a> Registry<'a> {
6771
lint_passes: vec!(),
6872
lint_groups: HashMap::new(),
6973
llvm_passes: vec!(),
74+
attributes: vec!(),
7075
}
7176
}
7277

@@ -132,4 +137,19 @@ impl<'a> Registry<'a> {
132137
pub fn register_llvm_pass(&mut self, name: &str) {
133138
self.llvm_passes.push(name.to_owned());
134139
}
140+
141+
142+
/// Register an attribute with an attribute type.
143+
///
144+
/// Registered attributes will bypass the `custom_attribute` feature gate.
145+
/// `Whitelisted` attributes will additionally not trigger the `unused_attribute`
146+
/// lint. `CrateLevel` attributes will not be allowed on anything other than a crate.
147+
pub fn register_attribute(&mut self, name: String, ty: AttributeType) {
148+
if let AttributeType::Gated(..) = ty {
149+
self.sess.span_err(self.krate_span, "plugin tried to register a gated \
150+
attribute. Only `Normal`, `Whitelisted`, \
151+
and `CrateLevel` attributes are allowed");
152+
}
153+
self.attributes.push((name, ty));
154+
}
135155
}

src/librustc/session/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use syntax::parse;
2323
use syntax::parse::token;
2424
use syntax::parse::ParseSess;
2525
use syntax::{ast, codemap};
26+
use syntax::feature_gate::AttributeType;
2627

2728
use rustc_back::target::Target;
2829

@@ -54,6 +55,7 @@ pub struct Session {
5455
pub lint_store: RefCell<lint::LintStore>,
5556
pub lints: RefCell<NodeMap<Vec<(lint::LintId, codemap::Span, String)>>>,
5657
pub plugin_llvm_passes: RefCell<Vec<String>>,
58+
pub plugin_attributes: RefCell<Vec<(String, AttributeType)>>,
5759
pub crate_types: RefCell<Vec<config::CrateType>>,
5860
pub crate_metadata: RefCell<Vec<String>>,
5961
pub features: RefCell<feature_gate::Features>,
@@ -425,6 +427,7 @@ pub fn build_session_(sopts: config::Options,
425427
lint_store: RefCell::new(lint::LintStore::new()),
426428
lints: RefCell::new(NodeMap()),
427429
plugin_llvm_passes: RefCell::new(Vec::new()),
430+
plugin_attributes: RefCell::new(Vec::new()),
428431
crate_types: RefCell::new(Vec::new()),
429432
crate_metadata: RefCell::new(Vec::new()),
430433
delayed_span_bug: RefCell::new(None),

src/librustc_driver/driver.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,8 @@ pub fn phase_2_configure_and_expand(sess: &Session,
444444
}
445445
});
446446

447-
let Registry { syntax_exts, lint_passes, lint_groups, llvm_passes, .. } = registry;
447+
let Registry { syntax_exts, lint_passes, lint_groups,
448+
llvm_passes, attributes, .. } = registry;
448449

449450
{
450451
let mut ls = sess.lint_store.borrow_mut();
@@ -457,6 +458,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
457458
}
458459

459460
*sess.plugin_llvm_passes.borrow_mut() = llvm_passes;
461+
*sess.plugin_attributes.borrow_mut() = attributes.clone();
460462
}
461463

462464
// Lint plugins are registered; now we can process command line flags.
@@ -511,7 +513,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
511513
let features =
512514
syntax::feature_gate::check_crate(sess.codemap(),
513515
&sess.parse_sess.span_diagnostic,
514-
&krate);
516+
&krate, &attributes);
515517
*sess.features.borrow_mut() = features;
516518
sess.abort_if_errors();
517519
});
@@ -541,7 +543,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
541543
let features =
542544
syntax::feature_gate::check_crate(sess.codemap(),
543545
&sess.parse_sess.span_diagnostic,
544-
&krate);
546+
&krate, &attributes);
545547
*sess.features.borrow_mut() = features;
546548
sess.abort_if_errors();
547549
});

src/librustc_lint/builtin.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -641,9 +641,26 @@ impl LintPass for UnusedAttributes {
641641
}
642642
}
643643

644+
let plugin_attributes = cx.sess().plugin_attributes.borrow_mut();
645+
for &(ref name, ty) in plugin_attributes.iter() {
646+
if ty == AttributeType::Whitelisted && attr.check_name(&*name) {
647+
break;
648+
}
649+
}
650+
644651
if !attr::is_used(attr) {
645652
cx.span_lint(UNUSED_ATTRIBUTES, attr.span, "unused attribute");
646-
if KNOWN_ATTRIBUTES.contains(&(&attr.name(), AttributeType::CrateLevel)) {
653+
// Is it a builtin attribute that must be used at the crate level?
654+
let known_crate = KNOWN_ATTRIBUTES.contains(&(&attr.name(),
655+
AttributeType::CrateLevel));
656+
// Has a plugin registered this attribute as one which must be used at
657+
// the crate level?
658+
let plugin_crate = plugin_attributes.iter()
659+
.find(|&&(ref x, t)| {
660+
&*attr.name() == &*x &&
661+
AttributeType::CrateLevel == t
662+
}).is_some();
663+
if known_crate || plugin_crate {
647664
let msg = match attr.node.style {
648665
ast::AttrOuter => "crate-level attribute should be an inner \
649666
attribute: add an exclamation mark: #![foo]",

src/libsyntax/feature_gate.rs

+31-13
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@ struct Context<'a> {
358358
features: Vec<&'static str>,
359359
span_handler: &'a SpanHandler,
360360
cm: &'a CodeMap,
361+
plugin_attributes: &'a [(String, AttributeType)],
361362
}
362363

363364
impl<'a> Context<'a> {
@@ -372,7 +373,7 @@ impl<'a> Context<'a> {
372373
self.features.iter().any(|&n| n == feature)
373374
}
374375

375-
fn check_attribute(&self, attr: &ast::Attribute) {
376+
fn check_attribute(&self, attr: &ast::Attribute, is_macro: bool) {
376377
debug!("check_attribute(attr = {:?})", attr);
377378
let name = &*attr.name();
378379
for &(n, ty) in KNOWN_ATTRIBUTES {
@@ -384,6 +385,15 @@ impl<'a> Context<'a> {
384385
return;
385386
}
386387
}
388+
for &(ref n, ref ty) in self.plugin_attributes.iter() {
389+
if &*n == name {
390+
// Plugins can't gate attributes, so we don't check for it
391+
// unlike the code above; we only use this loop to
392+
// short-circuit to avoid the checks below
393+
debug!("check_attribute: {:?} is registered by a plugin, {:?}", name, ty);
394+
return;
395+
}
396+
}
387397
if name.starts_with("rustc_") {
388398
self.gate_feature("rustc_attrs", attr.span,
389399
"unless otherwise specified, attributes \
@@ -394,12 +404,18 @@ impl<'a> Context<'a> {
394404
"attributes of the form `#[derive_*]` are reserved \
395405
for the compiler");
396406
} else {
397-
self.gate_feature("custom_attribute", attr.span,
398-
&format!("The attribute `{}` is currently \
399-
unknown to the compiler and \
400-
may have meaning \
401-
added to it in the future",
402-
name));
407+
// Only run the custom attribute lint during regular
408+
// feature gate checking. Macro gating runs
409+
// before the plugin attributes are registered
410+
// so we skip this then
411+
if !is_macro {
412+
self.gate_feature("custom_attribute", attr.span,
413+
&format!("The attribute `{}` is currently \
414+
unknown to the compiler and \
415+
may have meaning \
416+
added to it in the future",
417+
name));
418+
}
403419
}
404420
}
405421
}
@@ -478,7 +494,7 @@ impl<'a, 'v> Visitor<'v> for MacroVisitor<'a> {
478494
}
479495

480496
fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
481-
self.context.check_attribute(attr);
497+
self.context.check_attribute(attr, true);
482498
}
483499
}
484500

@@ -497,7 +513,7 @@ impl<'a> PostExpansionVisitor<'a> {
497513
impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
498514
fn visit_attribute(&mut self, attr: &ast::Attribute) {
499515
if !self.context.cm.span_allows_unstable(attr.span) {
500-
self.context.check_attribute(attr);
516+
self.context.check_attribute(attr, false);
501517
}
502518
}
503519

@@ -684,6 +700,7 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
684700

685701
fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
686702
krate: &ast::Crate,
703+
plugin_attributes: &[(String, AttributeType)],
687704
check: F)
688705
-> Features
689706
where F: FnOnce(&mut Context, &ast::Crate)
@@ -692,6 +709,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
692709
features: Vec::new(),
693710
span_handler: span_handler,
694711
cm: cm,
712+
plugin_attributes: plugin_attributes,
695713
};
696714

697715
let mut accepted_features = Vec::new();
@@ -764,14 +782,14 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
764782

765783
pub fn check_crate_macros(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate)
766784
-> Features {
767-
check_crate_inner(cm, span_handler, krate,
785+
check_crate_inner(cm, span_handler, krate, &[] as &'static [_],
768786
|ctx, krate| visit::walk_crate(&mut MacroVisitor { context: ctx }, krate))
769787
}
770788

771-
pub fn check_crate(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate)
772-
-> Features
789+
pub fn check_crate(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate,
790+
plugin_attributes: &[(String, AttributeType)]) -> Features
773791
{
774-
check_crate_inner(cm, span_handler, krate,
792+
check_crate_inner(cm, span_handler, krate, plugin_attributes,
775793
|ctx, krate| visit::walk_crate(&mut PostExpansionVisitor { context: ctx },
776794
krate))
777795
}
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// force-host
12+
13+
#![feature(plugin_registrar)]
14+
#![feature(rustc_private)]
15+
16+
extern crate syntax;
17+
18+
extern crate rustc;
19+
20+
use syntax::feature_gate::AttributeType;
21+
use rustc::plugin::Registry;
22+
23+
24+
25+
#[plugin_registrar]
26+
pub fn plugin_registrar(reg: &mut Registry) {
27+
reg.register_attribute("foo".to_owned(), AttributeType::Normal);
28+
reg.register_attribute("bar".to_owned(), AttributeType::CrateLevel);
29+
reg.register_attribute("baz".to_owned(), AttributeType::Whitelisted);
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// aux-build:attr_plugin_test.rs
12+
// ignore-stage1
13+
14+
#![feature(plugin)]
15+
#![plugin(attr_plugin_test)]
16+
#![deny(unused_attributes)]
17+
18+
#[baz]
19+
fn baz() { } // no error
20+
21+
#[foo]
22+
pub fn main() {
23+
//~^^ ERROR unused
24+
#[bar]
25+
fn inner() {}
26+
//~^^ ERROR crate
27+
//~^^^ ERROR unused
28+
baz();
29+
inner();
30+
}

0 commit comments

Comments
 (0)