Skip to content

Commit 07563be

Browse files
committed
auto merge of #14373 : sfackler/rust/unused-attr, r=huonw
The compiler now tracks which attributes were actually looked at during the compilation process and warns for those that were unused. Some things of note: * The tracking is done via thread locals, as it made the implementation more straightforward. Note that this shouldn't hamper any future parallelization as each task can have its own thread local state which can be merged for the lint pass. If there are serious objections to this, I can restructure things to explicitly pass the state around. * There are a number of attributes that have to be special-cased and globally whitelisted. This happens for four reasons: * The `doc` and `automatically_derived` attributes are used by rustdoc, but not by the compiler. * The crate-level attributes `license`, `desc` and `comment` aren't currently used by anything. * Stability attributes as well as `must_use` are checked only when the tagged item is used, so we can't guarantee that the compiler's looked at them. * 12 attributes are used only in trans, which happens after the lint pass. #14300 is adding infrastructure to track lint state through trans, which this lint should also be able to use to handle the last case. For the other attributes, the right solution would probably involve a specific pass to mark uses that occur in the correct context. For example, a `doc` attribute attached to a match arm should generate a warning, but will not currently. RFC: 0002-attribute-usage
2 parents 6304a27 + 3347993 commit 07563be

33 files changed

+277
-132
lines changed

src/doc/rust.md

+1
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,7 @@ Attributes on the anonymous crate module define important metadata that influenc
661661
the behavior of the compiler.
662662

663663
~~~~ {.rust}
664+
# #![allow(unused_attribute)]
664665
// Crate ID
665666
#![crate_id = "projx#2.5"]
666667

src/doc/rustdoc.md

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Documenting Rust APIs is quite simple. To document a given item, we have "doc
1111
comments":
1212

1313
~~~
14+
# #![allow(unused_attribute)]
1415
// the "link" crate attribute is currently required for rustdoc, but normally
1516
// isn't needed.
1617
#![crate_id = "universe"]

src/doc/tutorial.md

+5
Original file line numberDiff line numberDiff line change
@@ -3166,6 +3166,7 @@ without conflict.
31663166
Therefore, if you plan to compile your crate as a library, you should annotate it with that information:
31673167

31683168
~~~~
3169+
# #![allow(unused_attribute)]
31693170
// `lib.rs`
31703171
31713172
# #![crate_type = "lib"]
@@ -3189,6 +3190,7 @@ Other crate settings and metadata include things like enabling/disabling certain
31893190
or setting the crate type (library or executable) explicitly:
31903191

31913192
~~~~
3193+
# #![allow(unused_attribute)]
31923194
// `lib.rs`
31933195
// ...
31943196
@@ -3208,6 +3210,7 @@ Now for something that you can actually compile yourself.
32083210
We define two crates, and use one of them as a library in the other.
32093211

32103212
~~~~
3213+
# #![allow(unused_attribute)]
32113214
// `world.rs`
32123215
#![crate_id = "world#0.42"]
32133216
@@ -3282,11 +3285,13 @@ fn main() {
32823285
Both auto-insertions can be disabled with an attribute if necessary:
32833286

32843287
~~~
3288+
# #![allow(unused_attribute)]
32853289
// In the crate root:
32863290
#![no_std]
32873291
~~~
32883292

32893293
~~~
3294+
# #![allow(unused_attribute)]
32903295
// In any module:
32913296
#![no_implicit_prelude]
32923297
~~~

src/librustc/back/svh.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,12 @@ impl Svh {
9191
// types and then use hash_content. But, since all crate
9292
// attributes should appear near beginning of the file, it is
9393
// not such a big deal to be sensitive to their spans for now.
94-
krate.attrs.hash(&mut state);
94+
//
95+
// We hash only the MetaItems instead of the entire Attribute
96+
// to avoid hashing the AttrId
97+
for attr in krate.attrs.iter() {
98+
attr.node.value.hash(&mut state);
99+
}
95100

96101
let hash = state.result();
97102
return Svh {

src/librustc/driver/driver.rs

+40-38
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,45 @@ fn print_flowgraph<W:io::Writer>(analysis: CrateAnalysis,
716716

717717
pub fn collect_crate_types(session: &Session,
718718
attrs: &[ast::Attribute]) -> Vec<config::CrateType> {
719+
// Unconditionally collect crate types from attributes to make them used
720+
let attr_types: Vec<config::CrateType> = attrs.iter().filter_map(|a| {
721+
if a.check_name("crate_type") {
722+
match a.value_str() {
723+
Some(ref n) if n.equiv(&("rlib")) => {
724+
Some(config::CrateTypeRlib)
725+
}
726+
Some(ref n) if n.equiv(&("dylib")) => {
727+
Some(config::CrateTypeDylib)
728+
}
729+
Some(ref n) if n.equiv(&("lib")) => {
730+
Some(config::default_lib_output())
731+
}
732+
Some(ref n) if n.equiv(&("staticlib")) => {
733+
Some(config::CrateTypeStaticlib)
734+
}
735+
Some(ref n) if n.equiv(&("bin")) => Some(config::CrateTypeExecutable),
736+
Some(_) => {
737+
session.add_lint(lint::UnknownCrateType,
738+
ast::CRATE_NODE_ID,
739+
a.span,
740+
"invalid `crate_type` \
741+
value".to_strbuf());
742+
None
743+
}
744+
_ => {
745+
session.add_lint(lint::UnknownCrateType,
746+
ast::CRATE_NODE_ID,
747+
a.span,
748+
"`crate_type` requires a \
749+
value".to_strbuf());
750+
None
751+
}
752+
}
753+
} else {
754+
None
755+
}
756+
}).collect();
757+
719758
// If we're generating a test executable, then ignore all other output
720759
// styles at all other locations
721760
if session.opts.test {
@@ -729,44 +768,7 @@ pub fn collect_crate_types(session: &Session,
729768
if base.len() > 0 {
730769
return base
731770
} else {
732-
let iter = attrs.iter().filter_map(|a| {
733-
if a.name().equiv(&("crate_type")) {
734-
match a.value_str() {
735-
Some(ref n) if n.equiv(&("rlib")) => {
736-
Some(config::CrateTypeRlib)
737-
}
738-
Some(ref n) if n.equiv(&("dylib")) => {
739-
Some(config::CrateTypeDylib)
740-
}
741-
Some(ref n) if n.equiv(&("lib")) => {
742-
Some(config::default_lib_output())
743-
}
744-
Some(ref n) if n.equiv(&("staticlib")) => {
745-
Some(config::CrateTypeStaticlib)
746-
}
747-
Some(ref n) if n.equiv(&("bin")) => Some(config::CrateTypeExecutable),
748-
Some(_) => {
749-
session.add_lint(lint::UnknownCrateType,
750-
ast::CRATE_NODE_ID,
751-
a.span,
752-
"invalid `crate_type` \
753-
value".to_strbuf());
754-
None
755-
}
756-
_ => {
757-
session.add_lint(lint::UnknownCrateType,
758-
ast::CRATE_NODE_ID,
759-
a.span,
760-
"`crate_type` requires a \
761-
value".to_strbuf());
762-
None
763-
}
764-
}
765-
} else {
766-
None
767-
}
768-
});
769-
base.extend(iter);
771+
base.extend(attr_types.move_iter());
770772
if base.len() == 0 {
771773
base.push(config::CrateTypeExecutable);
772774
}

src/librustc/front/feature_gate.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ pub fn check_crate(sess: &Session, krate: &ast::Crate) {
327327
};
328328

329329
for attr in krate.attrs.iter() {
330-
if !attr.name().equiv(&("feature")) {
330+
if !attr.check_name("feature") {
331331
continue
332332
}
333333

src/librustc/front/std_inject.rs

+13-4
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ impl<'a> fold::Folder for StandardLibraryInjector<'a> {
7878
with_version("std"),
7979
ast::DUMMY_NODE_ID),
8080
attrs: vec!(
81-
attr::mk_attr_outer(attr::mk_list_item(
81+
attr::mk_attr_outer(attr::mk_attr_id(), attr::mk_list_item(
8282
InternedString::new("phase"),
8383
vec!(
8484
attr::mk_word_item(InternedString::new("syntax")),
@@ -110,10 +110,13 @@ impl<'a> fold::Folder for StandardLibraryInjector<'a> {
110110
// Add it during the prelude injection instead.
111111

112112
// Add #![feature(phase)] here, because we use #[phase] on extern crate std.
113-
let feat_phase_attr = attr::mk_attr_inner(attr::mk_list_item(
113+
let feat_phase_attr = attr::mk_attr_inner(attr::mk_attr_id(),
114+
attr::mk_list_item(
114115
InternedString::new("feature"),
115116
vec![attr::mk_word_item(InternedString::new("phase"))],
116117
));
118+
// std_inject runs after feature checking so manually mark this attr
119+
attr::mark_used(&feat_phase_attr);
117120
krate.attrs.push(feat_phase_attr);
118121

119122
krate
@@ -138,19 +141,25 @@ impl<'a> fold::Folder for PreludeInjector<'a> {
138141
// This must happen here and not in StandardLibraryInjector because this
139142
// fold happens second.
140143

141-
let no_std_attr = attr::mk_attr_inner(attr::mk_word_item(InternedString::new("no_std")));
144+
let no_std_attr = attr::mk_attr_inner(attr::mk_attr_id(),
145+
attr::mk_word_item(InternedString::new("no_std")));
146+
// std_inject runs after feature checking so manually mark this attr
147+
attr::mark_used(&no_std_attr);
142148
krate.attrs.push(no_std_attr);
143149

144150
if !no_prelude(krate.attrs.as_slice()) {
145151
// only add `use std::prelude::*;` if there wasn't a
146152
// `#![no_implicit_prelude]` at the crate level.
147153

148154
// fold_mod() will insert glob path.
149-
let globs_attr = attr::mk_attr_inner(attr::mk_list_item(
155+
let globs_attr = attr::mk_attr_inner(attr::mk_attr_id(),
156+
attr::mk_list_item(
150157
InternedString::new("feature"),
151158
vec!(
152159
attr::mk_word_item(InternedString::new("globs")),
153160
)));
161+
// std_inject runs after feature checking so manually mark this attr
162+
attr::mark_used(&globs_attr);
154163
krate.attrs.push(globs_attr);
155164

156165
krate.module = self.fold_mod(&krate.module);

src/librustc/front/test.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ fn is_bench_fn(cx: &TestCtxt, i: @ast::Item) -> bool {
253253
fn is_ignored(cx: &TestCtxt, i: @ast::Item) -> bool {
254254
i.attrs.iter().any(|attr| {
255255
// check ignore(cfg(foo, bar))
256-
attr.name().equiv(&("ignore")) && match attr.meta_item_list() {
256+
attr.check_name("ignore") && match attr.meta_item_list() {
257257
Some(ref cfgs) => {
258258
attr::test_cfg(cx.config.as_slice(), cfgs.iter().map(|x| *x))
259259
}
@@ -341,7 +341,8 @@ fn mk_test_module(cx: &TestCtxt) -> @ast::Item {
341341
// This attribute tells resolve to let us call unexported functions
342342
let resolve_unexported_str = InternedString::new("!resolve_unexported");
343343
let resolve_unexported_attr =
344-
attr::mk_attr_inner(attr::mk_word_item(resolve_unexported_str));
344+
attr::mk_attr_inner(attr::mk_attr_id(),
345+
attr::mk_word_item(resolve_unexported_str));
345346

346347
let item = ast::Item {
347348
ident: token::str_to_ident("__test"),

src/librustc/metadata/csearch.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ pub fn get_static_methods_if_impl(cstore: &cstore::CStore,
176176

177177
pub fn get_item_attrs(cstore: &cstore::CStore,
178178
def_id: ast::DefId,
179-
f: |Vec<@ast::MetaItem> |) {
179+
f: |Vec<ast::Attribute> |) {
180180
let cdata = cstore.get_crate_data(def_id.krate);
181181
decoder::get_item_attrs(&*cdata, def_id.node, f)
182182
}

src/librustc/metadata/decoder.rs

+3-8
Original file line numberDiff line numberDiff line change
@@ -953,20 +953,14 @@ pub fn get_tuple_struct_definition_if_ctor(cdata: Cmd,
953953

954954
pub fn get_item_attrs(cdata: Cmd,
955955
orig_node_id: ast::NodeId,
956-
f: |Vec<@ast::MetaItem> |) {
956+
f: |Vec<ast::Attribute>|) {
957957
// The attributes for a tuple struct are attached to the definition, not the ctor;
958958
// we assume that someone passing in a tuple struct ctor is actually wanting to
959959
// look at the definition
960960
let node_id = get_tuple_struct_definition_if_ctor(cdata, orig_node_id);
961961
let node_id = node_id.map(|x| x.node).unwrap_or(orig_node_id);
962962
let item = lookup_item(node_id, cdata.data());
963-
reader::tagged_docs(item, tag_attributes, |attributes| {
964-
reader::tagged_docs(attributes, tag_attribute, |attribute| {
965-
f(get_meta_items(attribute));
966-
true
967-
});
968-
true
969-
});
963+
f(get_attributes(item));
970964
}
971965

972966
fn struct_field_family_to_visibility(family: Family) -> ast::Visibility {
@@ -1056,6 +1050,7 @@ fn get_attributes(md: ebml::Doc) -> Vec<ast::Attribute> {
10561050
attrs.push(
10571051
codemap::Spanned {
10581052
node: ast::Attribute_ {
1053+
id: attr::mk_attr_id(),
10591054
style: ast::AttrOuter,
10601055
value: meta_item,
10611056
is_sugared_doc: false,

src/librustc/metadata/encoder.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1436,7 +1436,7 @@ fn synthesize_crate_attrs(ecx: &EncodeContext,
14361436
fn synthesize_crateid_attr(ecx: &EncodeContext) -> Attribute {
14371437
assert!(!ecx.link_meta.crateid.name.is_empty());
14381438

1439-
attr::mk_attr_inner(
1439+
attr::mk_attr_inner(attr::mk_attr_id(),
14401440
attr::mk_name_value_item_str(
14411441
InternedString::new("crate_id"),
14421442
token::intern_and_get_ident(ecx.link_meta
@@ -1447,7 +1447,7 @@ fn synthesize_crate_attrs(ecx: &EncodeContext,
14471447

14481448
let mut attrs = Vec::new();
14491449
for attr in krate.attrs.iter() {
1450-
if !attr.name().equiv(&("crate_id")) {
1450+
if !attr.check_name("crate_id") {
14511451
attrs.push(*attr);
14521452
}
14531453
}

0 commit comments

Comments
 (0)