2015-08-16 06:54:43 +00:00
//! checks for attributes
2015-05-30 13:10:19 +00:00
use rustc ::lint ::* ;
2015-09-03 14:42:17 +00:00
use rustc_front ::hir ::* ;
use reexport ::* ;
2016-01-09 01:05:43 +00:00
use semver ::Version ;
2015-09-06 08:53:55 +00:00
use syntax ::codemap ::Span ;
2015-09-17 00:01:41 +00:00
use syntax ::attr ::* ;
2016-01-09 01:05:43 +00:00
use syntax ::ast ::{ Attribute , Lit , Lit_ , MetaList , MetaWord , MetaNameValue } ;
2015-12-23 21:37:52 +00:00
use utils ::{ in_macro , match_path , span_lint , BEGIN_UNWIND } ;
2015-05-30 13:10:19 +00:00
2016-01-01 16:48:19 +00:00
/// **What it does:** This lint `Warn`s on items annotated with `#[inline(always)]`, unless the annotated function is empty or simply panics.
2015-12-11 00:22:27 +00:00
///
/// **Why is this bad?** While there are valid uses of this annotation (and once you know when to use it, by all means `allow` this lint), it's a common newbie-mistake to pepper one's code with it.
///
/// As a rule of thumb, before slapping `#[inline(always)]` on a function, measure if that additional function call really affects your runtime profile sufficiently to make up for the increase in compile time.
///
/// **Known problems:** False positives, big time. This lint is meant to be deactivated by everyone doing serious performance work. This means having done the measurement.
///
/// **Example:**
/// ```
/// #[inline(always)]
/// fn not_quite_hot_code(..) { ... }
/// ```
2015-05-30 13:10:19 +00:00
declare_lint! { pub INLINE_ALWAYS , Warn ,
2015-08-13 08:32:35 +00:00
" `#[inline(always)]` is a bad idea in most cases " }
2015-05-30 13:10:19 +00:00
2016-01-09 01:05:43 +00:00
/// **What it does:** This lint `Warn`s on `#[deprecated]` annotations with a `since` field that is not a valid semantic version..
///
/// **Why is this bad?** For checking the version of the deprecation, it must be valid semver. Failing that, the contained information is useless.
///
/// **Known problems:** None
///
/// **Example:**
/// ```
/// #[deprecated(since = "forever")]
/// fn something_else(..) { ... }
/// ```
declare_lint! { pub DEPRECATED_SEMVER , Warn ,
" `Warn` on `#[deprecated(since = \" x \" )]` where x is not semver " }
2015-05-30 13:10:19 +00:00
#[ derive(Copy,Clone) ]
pub struct AttrPass ;
impl LintPass for AttrPass {
fn get_lints ( & self ) -> LintArray {
2016-01-09 01:05:43 +00:00
lint_array! ( INLINE_ALWAYS , DEPRECATED_SEMVER )
2015-05-30 13:10:19 +00:00
}
2015-09-19 02:53:04 +00:00
}
2015-08-11 18:22:20 +00:00
2015-09-19 02:53:04 +00:00
impl LateLintPass for AttrPass {
2016-01-09 01:05:43 +00:00
fn check_attribute ( & mut self , cx : & LateContext , attr : & Attribute ) {
if let MetaList ( ref name , ref items ) = attr . node . value . node {
if items . is_empty ( ) | | name ! = & " deprecated " {
return ;
}
for ref item in items {
if let MetaNameValue ( ref name , ref lit ) = item . node {
if name = = & " since " {
check_semver ( cx , item . span , lit ) ;
}
}
}
}
}
2015-09-19 02:53:04 +00:00
fn check_item ( & mut self , cx : & LateContext , item : & Item ) {
2015-08-11 18:22:20 +00:00
if is_relevant_item ( item ) {
2015-09-24 00:30:39 +00:00
check_attrs ( cx , item . span , & item . name , & item . attrs )
2015-08-11 18:22:20 +00:00
}
}
2015-09-19 02:53:04 +00:00
fn check_impl_item ( & mut self , cx : & LateContext , item : & ImplItem ) {
2015-08-11 18:22:20 +00:00
if is_relevant_impl ( item ) {
2015-09-24 00:30:39 +00:00
check_attrs ( cx , item . span , & item . name , & item . attrs )
2015-08-11 18:22:20 +00:00
}
}
2015-09-19 02:53:04 +00:00
fn check_trait_item ( & mut self , cx : & LateContext , item : & TraitItem ) {
2015-08-11 18:22:20 +00:00
if is_relevant_trait ( item ) {
2015-09-24 00:30:39 +00:00
check_attrs ( cx , item . span , & item . name , & item . attrs )
2015-08-11 18:22:20 +00:00
}
}
2015-06-07 10:03:56 +00:00
}
fn is_relevant_item ( item : & Item ) -> bool {
2015-11-24 17:44:40 +00:00
if let ItemFn ( _ , _ , _ , _ , _ , ref block ) = item . node {
2015-08-11 18:22:20 +00:00
is_relevant_block ( block )
2016-01-04 04:26:12 +00:00
} else {
false
}
2015-06-07 10:03:56 +00:00
}
fn is_relevant_impl ( item : & ImplItem ) -> bool {
2015-08-11 18:22:20 +00:00
match item . node {
2015-11-19 14:51:30 +00:00
ImplItemKind ::Method ( _ , ref block ) = > is_relevant_block ( block ) ,
2016-01-04 04:26:12 +00:00
_ = > false ,
2015-08-11 18:22:20 +00:00
}
2015-06-07 10:03:56 +00:00
}
fn is_relevant_trait ( item : & TraitItem ) -> bool {
2015-08-11 18:22:20 +00:00
match item . node {
MethodTraitItem ( _ , None ) = > true ,
MethodTraitItem ( _ , Some ( ref block ) ) = > is_relevant_block ( block ) ,
2016-01-04 04:26:12 +00:00
_ = > false ,
2015-08-11 18:22:20 +00:00
}
2015-06-07 10:03:56 +00:00
}
fn is_relevant_block ( block : & Block ) -> bool {
2015-08-13 13:36:31 +00:00
for stmt in & block . stmts {
2015-08-11 18:22:20 +00:00
match stmt . node {
StmtDecl ( _ , _ ) = > return true ,
StmtExpr ( ref expr , _ ) | StmtSemi ( ref expr , _ ) = > {
return is_relevant_expr ( expr ) ;
}
}
}
2015-08-25 12:41:35 +00:00
block . expr . as_ref ( ) . map_or ( false , | e | is_relevant_expr ( e ) )
2015-06-07 10:03:56 +00:00
}
fn is_relevant_expr ( expr : & Expr ) -> bool {
2015-08-11 18:22:20 +00:00
match expr . node {
ExprBlock ( ref block ) = > is_relevant_block ( block ) ,
2015-09-19 02:53:04 +00:00
ExprRet ( Some ( ref e ) ) = > is_relevant_expr ( e ) ,
2015-09-03 14:42:17 +00:00
ExprRet ( None ) | ExprBreak ( _ ) = > false ,
2015-08-11 18:22:20 +00:00
ExprCall ( ref path_expr , _ ) = > {
if let ExprPath ( _ , ref path ) = path_expr . node {
2015-12-23 21:37:52 +00:00
! match_path ( path , & BEGIN_UNWIND )
2016-01-04 04:26:12 +00:00
} else {
true
}
2015-08-11 18:22:20 +00:00
}
2016-01-04 04:26:12 +00:00
_ = > true ,
2015-08-11 18:22:20 +00:00
}
2015-05-30 13:10:19 +00:00
}
2016-01-04 04:26:12 +00:00
fn check_attrs ( cx : & LateContext , span : Span , name : & Name , attrs : & [ Attribute ] ) {
if in_macro ( cx , span ) {
return ;
}
2015-08-11 18:22:20 +00:00
for attr in attrs {
if let MetaList ( ref inline , ref values ) = attr . node . value . node {
2016-01-04 04:26:12 +00:00
if values . len ( ) ! = 1 | | inline ! = & " inline " {
continue ;
}
2015-08-11 18:22:20 +00:00
if let MetaWord ( ref always ) = values [ 0 ] . node {
2016-01-04 04:26:12 +00:00
if always ! = & " always " {
continue ;
}
span_lint ( cx ,
INLINE_ALWAYS ,
attr . span ,
& format! ( " you have declared `#[inline(always)]` on ` {} `. This is usually a bad idea " ,
name ) ) ;
2015-08-11 18:22:20 +00:00
}
}
}
2015-05-30 13:10:19 +00:00
}
2016-01-09 01:05:43 +00:00
fn check_semver ( cx : & LateContext , span : Span , lit : & Lit ) {
if let Lit_ ::LitStr ( ref is , _ ) = lit . node {
if Version ::parse ( & * is ) . is_ok ( ) {
return ;
}
}
span_lint ( cx ,
DEPRECATED_SEMVER ,
span ,
" the since field must contain a semver-compliant version " ) ;
}