Skip to content

Commit 1463d6f

Browse files
committed
Error an unknown or deprecated Clippy attribute
1 parent f69ec96 commit 1463d6f

File tree

4 files changed

+140
-84
lines changed

4 files changed

+140
-84
lines changed

clippy_lints/src/utils/attrs.rs

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
use rustc::session::Session;
2+
use rustc_errors::Applicability;
3+
use std::str::FromStr;
4+
use syntax::ast;
5+
6+
/// Deprecation status of attributes known by Clippy.
7+
#[allow(dead_code)]
8+
pub enum DeprecationStatus {
9+
/// Attribute is deprecated
10+
Deprecated,
11+
/// Attribute is deprecated and was replaced by the named attribute
12+
Replaced(&'static str),
13+
None,
14+
}
15+
16+
pub const BUILTIN_ATTRIBUTES: &[(&str, DeprecationStatus)] = &[
17+
("author", DeprecationStatus::None),
18+
("cyclomatic_complexity", DeprecationStatus::None),
19+
("dump", DeprecationStatus::None),
20+
];
21+
22+
pub struct LimitStack {
23+
stack: Vec<u64>,
24+
}
25+
26+
impl Drop for LimitStack {
27+
fn drop(&mut self) {
28+
assert_eq!(self.stack.len(), 1);
29+
}
30+
}
31+
32+
impl LimitStack {
33+
pub fn new(limit: u64) -> Self {
34+
Self { stack: vec![limit] }
35+
}
36+
pub fn limit(&self) -> u64 {
37+
*self.stack.last().expect("there should always be a value in the stack")
38+
}
39+
pub fn push_attrs(&mut self, sess: &Session, attrs: &[ast::Attribute], name: &'static str) {
40+
let stack = &mut self.stack;
41+
parse_attrs(sess, attrs, name, |val| stack.push(val));
42+
}
43+
pub fn pop_attrs(&mut self, sess: &Session, attrs: &[ast::Attribute], name: &'static str) {
44+
let stack = &mut self.stack;
45+
parse_attrs(sess, attrs, name, |val| assert_eq!(stack.pop(), Some(val)));
46+
}
47+
}
48+
49+
pub fn get_attr<'a>(
50+
sess: &'a Session,
51+
attrs: &'a [ast::Attribute],
52+
name: &'static str,
53+
) -> impl Iterator<Item = &'a ast::Attribute> {
54+
attrs.iter().filter(move |attr| {
55+
let attr_segments = &attr.path.segments;
56+
if attr_segments.len() == 2 && attr_segments[0].ident.to_string() == "clippy" {
57+
if let Some(deprecation_status) = BUILTIN_ATTRIBUTES
58+
.iter()
59+
.find(|(builtin_name, _)| *builtin_name == attr_segments[1].ident.to_string())
60+
.map(|(_, deprecation_status)| deprecation_status)
61+
{
62+
let mut db = sess.struct_span_err(attr_segments[1].ident.span, "Usage of deprecated attribute");
63+
match deprecation_status {
64+
DeprecationStatus::Deprecated => {
65+
db.emit();
66+
false
67+
},
68+
DeprecationStatus::Replaced(new_name) => {
69+
db.span_suggestion(
70+
attr_segments[1].ident.span,
71+
"consider using",
72+
new_name.to_string(),
73+
Applicability::MachineApplicable,
74+
);
75+
db.emit();
76+
false
77+
},
78+
DeprecationStatus::None => {
79+
db.cancel();
80+
attr_segments[1].ident.to_string() == name
81+
},
82+
}
83+
} else {
84+
sess.span_err(attr_segments[1].ident.span, "Usage of unknown attribute");
85+
false
86+
}
87+
} else {
88+
false
89+
}
90+
})
91+
}
92+
93+
fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'static str, mut f: F) {
94+
for attr in get_attr(sess, attrs, name) {
95+
if let Some(ref value) = attr.value_str() {
96+
if let Ok(value) = FromStr::from_str(&value.as_str()) {
97+
f(value)
98+
} else {
99+
sess.span_err(attr.span, "not a number");
100+
}
101+
} else {
102+
sess.span_err(attr.span, "bad clippy attribute");
103+
}
104+
}
105+
}

clippy_lints/src/utils/author.rs

+22-21
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use crate::utils::get_attr;
55
use rustc::hir;
66
use rustc::hir::intravisit::{NestedVisitorMap, Visitor};
77
use rustc::hir::{BindingAnnotation, Expr, ExprKind, Pat, PatKind, QPath, Stmt, StmtKind, TyKind};
8-
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
8+
use rustc::lint::{LateContext, LateLintPass, LintArray, LintContext, LintPass};
9+
use rustc::session::Session;
910
use rustc::{declare_tool_lint, lint_array};
1011
use rustc_data_structures::fx::FxHashMap;
1112
use syntax::ast::{Attribute, LitKind};
@@ -71,80 +72,80 @@ fn done() {
7172
}
7273

7374
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
74-
fn check_item(&mut self, _cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item) {
75-
if !has_attr(&item.attrs) {
75+
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item) {
76+
if !has_attr(cx.sess(), &item.attrs) {
7677
return;
7778
}
7879
prelude();
7980
PrintVisitor::new("item").visit_item(item);
8081
done();
8182
}
8283

83-
fn check_impl_item(&mut self, _cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ImplItem) {
84-
if !has_attr(&item.attrs) {
84+
fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ImplItem) {
85+
if !has_attr(cx.sess(), &item.attrs) {
8586
return;
8687
}
8788
prelude();
8889
PrintVisitor::new("item").visit_impl_item(item);
8990
done();
9091
}
9192

92-
fn check_trait_item(&mut self, _cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem) {
93-
if !has_attr(&item.attrs) {
93+
fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem) {
94+
if !has_attr(cx.sess(), &item.attrs) {
9495
return;
9596
}
9697
prelude();
9798
PrintVisitor::new("item").visit_trait_item(item);
9899
done();
99100
}
100101

101-
fn check_variant(&mut self, _cx: &LateContext<'a, 'tcx>, var: &'tcx hir::Variant, generics: &hir::Generics) {
102-
if !has_attr(&var.node.attrs) {
102+
fn check_variant(&mut self, cx: &LateContext<'a, 'tcx>, var: &'tcx hir::Variant, generics: &hir::Generics) {
103+
if !has_attr(cx.sess(), &var.node.attrs) {
103104
return;
104105
}
105106
prelude();
106107
PrintVisitor::new("var").visit_variant(var, generics, hir::DUMMY_HIR_ID);
107108
done();
108109
}
109110

110-
fn check_struct_field(&mut self, _cx: &LateContext<'a, 'tcx>, field: &'tcx hir::StructField) {
111-
if !has_attr(&field.attrs) {
111+
fn check_struct_field(&mut self, cx: &LateContext<'a, 'tcx>, field: &'tcx hir::StructField) {
112+
if !has_attr(cx.sess(), &field.attrs) {
112113
return;
113114
}
114115
prelude();
115116
PrintVisitor::new("field").visit_struct_field(field);
116117
done();
117118
}
118119

119-
fn check_expr(&mut self, _cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
120-
if !has_attr(&expr.attrs) {
120+
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
121+
if !has_attr(cx.sess(), &expr.attrs) {
121122
return;
122123
}
123124
prelude();
124125
PrintVisitor::new("expr").visit_expr(expr);
125126
done();
126127
}
127128

128-
fn check_arm(&mut self, _cx: &LateContext<'a, 'tcx>, arm: &'tcx hir::Arm) {
129-
if !has_attr(&arm.attrs) {
129+
fn check_arm(&mut self, cx: &LateContext<'a, 'tcx>, arm: &'tcx hir::Arm) {
130+
if !has_attr(cx.sess(), &arm.attrs) {
130131
return;
131132
}
132133
prelude();
133134
PrintVisitor::new("arm").visit_arm(arm);
134135
done();
135136
}
136137

137-
fn check_stmt(&mut self, _cx: &LateContext<'a, 'tcx>, stmt: &'tcx hir::Stmt) {
138-
if !has_attr(stmt.node.attrs()) {
138+
fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx hir::Stmt) {
139+
if !has_attr(cx.sess(), stmt.node.attrs()) {
139140
return;
140141
}
141142
prelude();
142143
PrintVisitor::new("stmt").visit_stmt(stmt);
143144
done();
144145
}
145146

146-
fn check_foreign_item(&mut self, _cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ForeignItem) {
147-
if !has_attr(&item.attrs) {
147+
fn check_foreign_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ForeignItem) {
148+
if !has_attr(cx.sess(), &item.attrs) {
148149
return;
149150
}
150151
prelude();
@@ -673,8 +674,8 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor {
673674
}
674675
}
675676

676-
fn has_attr(attrs: &[Attribute]) -> bool {
677-
get_attr(attrs, "author").count() > 0
677+
fn has_attr(sess: &Session, attrs: &[Attribute]) -> bool {
678+
get_attr(sess, attrs, "author").count() > 0
678679
}
679680

680681
fn desugaring_name(des: hir::MatchSource) -> String {

clippy_lints/src/utils/inspector.rs

+9-8
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
use crate::utils::get_attr;
44
use rustc::hir;
55
use rustc::hir::print;
6-
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
6+
use rustc::lint::{LateContext, LateLintPass, LintArray, LintContext, LintPass};
7+
use rustc::session::Session;
78
use rustc::{declare_tool_lint, lint_array};
89
use syntax::ast::Attribute;
910

@@ -43,14 +44,14 @@ impl LintPass for Pass {
4344

4445
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
4546
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item) {
46-
if !has_attr(&item.attrs) {
47+
if !has_attr(cx.sess(), &item.attrs) {
4748
return;
4849
}
4950
print_item(cx, item);
5051
}
5152

5253
fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ImplItem) {
53-
if !has_attr(&item.attrs) {
54+
if !has_attr(cx.sess(), &item.attrs) {
5455
return;
5556
}
5657
println!("impl item `{}`", item.ident.name);
@@ -100,14 +101,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
100101
//
101102

102103
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
103-
if !has_attr(&expr.attrs) {
104+
if !has_attr(cx.sess(), &expr.attrs) {
104105
return;
105106
}
106107
print_expr(cx, expr, 0);
107108
}
108109

109110
fn check_arm(&mut self, cx: &LateContext<'a, 'tcx>, arm: &'tcx hir::Arm) {
110-
if !has_attr(&arm.attrs) {
111+
if !has_attr(cx.sess(), &arm.attrs) {
111112
return;
112113
}
113114
for pat in &arm.pats {
@@ -122,7 +123,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
122123
}
123124

124125
fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx hir::Stmt) {
125-
if !has_attr(stmt.node.attrs()) {
126+
if !has_attr(cx.sess(), stmt.node.attrs()) {
126127
return;
127128
}
128129
match stmt.node {
@@ -148,8 +149,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
148149
//
149150
}
150151

151-
fn has_attr(attrs: &[Attribute]) -> bool {
152-
get_attr(attrs, "dump").count() > 0
152+
fn has_attr(sess: &Session, attrs: &[Attribute]) -> bool {
153+
get_attr(sess, attrs, "dump").count() > 0
153154
}
154155

155156
#[allow(clippy::similar_names)]

clippy_lints/src/utils/mod.rs

+4-55
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use rustc::hir::intravisit::{NestedVisitorMap, Visitor};
88
use rustc::hir::Node;
99
use rustc::hir::*;
1010
use rustc::lint::{LateContext, Level, Lint, LintContext};
11-
use rustc::session::Session;
1211
use rustc::traits;
1312
use rustc::ty::{
1413
self,
@@ -20,32 +19,31 @@ use rustc_data_structures::sync::Lrc;
2019
use rustc_errors::Applicability;
2120
use std::borrow::Cow;
2221
use std::mem;
23-
use std::str::FromStr;
2422
use syntax::ast::{self, LitKind};
2523
use syntax::attr;
2624
use syntax::source_map::{Span, DUMMY_SP};
2725
use syntax::symbol;
2826
use syntax::symbol::{keywords, Symbol};
2927

30-
pub mod camel_case;
31-
28+
pub mod attrs;
3229
pub mod author;
30+
pub mod camel_case;
3331
pub mod comparisons;
3432
pub mod conf;
3533
pub mod constants;
3634
mod diagnostics;
35+
pub mod higher;
3736
mod hir_utils;
3837
pub mod inspector;
3938
pub mod internal_lints;
4039
pub mod paths;
4140
pub mod ptr;
4241
pub mod sugg;
4342
pub mod usage;
43+
pub use self::attrs::*;
4444
pub use self::diagnostics::*;
4545
pub use self::hir_utils::{SpanlessEq, SpanlessHash};
4646

47-
pub mod higher;
48-
4947
/// Returns true if the two spans come from differing expansions (i.e. one is
5048
/// from a macro and one
5149
/// isn't).
@@ -662,55 +660,6 @@ pub fn is_adjusted(cx: &LateContext<'_, '_>, e: &Expr) -> bool {
662660
cx.tables.adjustments().get(e.hir_id).is_some()
663661
}
664662

665-
pub struct LimitStack {
666-
stack: Vec<u64>,
667-
}
668-
669-
impl Drop for LimitStack {
670-
fn drop(&mut self) {
671-
assert_eq!(self.stack.len(), 1);
672-
}
673-
}
674-
675-
impl LimitStack {
676-
pub fn new(limit: u64) -> Self {
677-
Self { stack: vec![limit] }
678-
}
679-
pub fn limit(&self) -> u64 {
680-
*self.stack.last().expect("there should always be a value in the stack")
681-
}
682-
pub fn push_attrs(&mut self, sess: &Session, attrs: &[ast::Attribute], name: &'static str) {
683-
let stack = &mut self.stack;
684-
parse_attrs(sess, attrs, name, |val| stack.push(val));
685-
}
686-
pub fn pop_attrs(&mut self, sess: &Session, attrs: &[ast::Attribute], name: &'static str) {
687-
let stack = &mut self.stack;
688-
parse_attrs(sess, attrs, name, |val| assert_eq!(stack.pop(), Some(val)));
689-
}
690-
}
691-
692-
pub fn get_attr<'a>(attrs: &'a [ast::Attribute], name: &'static str) -> impl Iterator<Item = &'a ast::Attribute> {
693-
attrs.iter().filter(move |attr| {
694-
attr.path.segments.len() == 2
695-
&& attr.path.segments[0].ident.to_string() == "clippy"
696-
&& attr.path.segments[1].ident.to_string() == name
697-
})
698-
}
699-
700-
fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'static str, mut f: F) {
701-
for attr in get_attr(attrs, name) {
702-
if let Some(ref value) = attr.value_str() {
703-
if let Ok(value) = FromStr::from_str(&value.as_str()) {
704-
f(value)
705-
} else {
706-
sess.span_err(attr.span, "not a number");
707-
}
708-
} else {
709-
sess.span_err(attr.span, "bad clippy attribute");
710-
}
711-
}
712-
}
713-
714663
/// Return the pre-expansion span if is this comes from an expansion of the
715664
/// macro `name`.
716665
/// See also `is_direct_expn_of`.

0 commit comments

Comments
 (0)