about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml1
-rw-r--r--README.md3
-rw-r--r--src/attrs.rs45
-rw-r--r--src/lib.rs4
-rw-r--r--tests/compile-fail/attrs.rs12
5 files changed, 60 insertions, 5 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 2b203e8b796..792326d6c1d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,6 +18,7 @@ plugin = true
 
 [dependencies]
 unicode-normalization = "0.1"
+semver = "0.2.1"
 
 [dev-dependencies]
 compiletest_rs = "0.0.11"
diff --git a/README.md b/README.md
index f1c28888636..13105087706 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ A collection of lints to catch common mistakes and improve your Rust code.
 [Jump to usage instructions](#usage)
 
 ##Lints
-There are 91 lints included in this crate:
+There are 92 lints included in this crate:
 
 name                                                                                                           | default | meaning
 ---------------------------------------------------------------------------------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -24,6 +24,7 @@ name
 [cmp_owned](https://github.com/Manishearth/rust-clippy/wiki#cmp_owned)                                         | warn    | creating owned instances for comparing with others, e.g. `x == "foo".to_string()`
 [collapsible_if](https://github.com/Manishearth/rust-clippy/wiki#collapsible_if)                               | warn    | two nested `if`-expressions can be collapsed into one, e.g. `if x { if y { foo() } }` can be written as `if x && y { foo() }`
 [cyclomatic_complexity](https://github.com/Manishearth/rust-clippy/wiki#cyclomatic_complexity)                 | warn    | finds functions that should be split up into multiple functions
+[deprecated_semver](https://github.com/Manishearth/rust-clippy/wiki#deprecated_semver)                         | warn    | `Warn` on `#[deprecated(since = "x")]` where x is not semver
 [duplicate_underscore_argument](https://github.com/Manishearth/rust-clippy/wiki#duplicate_underscore_argument) | warn    | Function arguments having names which only differ by an underscore
 [empty_loop](https://github.com/Manishearth/rust-clippy/wiki#empty_loop)                                       | warn    | empty `loop {}` detected
 [eq_op](https://github.com/Manishearth/rust-clippy/wiki#eq_op)                                                 | warn    | equal operands on both sides of a comparison or bitwise combination (e.g. `x == x`)
diff --git a/src/attrs.rs b/src/attrs.rs
index ec2cfcb0efc..853e2ab5910 100644
--- a/src/attrs.rs
+++ b/src/attrs.rs
@@ -3,9 +3,10 @@
 use rustc::lint::*;
 use rustc_front::hir::*;
 use reexport::*;
+use semver::Version;
 use syntax::codemap::Span;
 use syntax::attr::*;
-use syntax::ast::{Attribute, MetaList, MetaWord};
+use syntax::ast::{Attribute, Lit, Lit_, MetaList, MetaWord, MetaNameValue};
 use utils::{in_macro, match_path, span_lint, BEGIN_UNWIND};
 
 /// **What it does:** This lint `Warn`s on items annotated with `#[inline(always)]`, unless the annotated function is empty or simply panics.
@@ -24,17 +25,45 @@ use utils::{in_macro, match_path, span_lint, BEGIN_UNWIND};
 declare_lint! { pub INLINE_ALWAYS, Warn,
     "`#[inline(always)]` is a bad idea in most cases" }
 
+/// **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" }
 
 #[derive(Copy,Clone)]
 pub struct AttrPass;
 
 impl LintPass for AttrPass {
     fn get_lints(&self) -> LintArray {
-        lint_array!(INLINE_ALWAYS)
+        lint_array!(INLINE_ALWAYS, DEPRECATED_SEMVER)
     }
 }
 
 impl LateLintPass for AttrPass {
+    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);
+                    }
+                }
+            } 
+        }
+    }
+    
     fn check_item(&mut self, cx: &LateContext, item: &Item) {
         if is_relevant_item(item) {
             check_attrs(cx, item.span, &item.name, &item.attrs)
@@ -128,3 +157,15 @@ fn check_attrs(cx: &LateContext, span: Span, name: &Name, attrs: &[Attribute]) {
         }
     }
 }
+
+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");
+}
diff --git a/src/lib.rs b/src/lib.rs
index 45f47fe3d63..f8db15605ff 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -24,6 +24,9 @@ extern crate collections;
 // for unicode nfc normalization
 extern crate unicode_normalization;
 
+// for semver check in attrs.rs
+extern crate semver;
+
 extern crate rustc_plugin;
 
 use rustc_plugin::Registry;
@@ -156,6 +159,7 @@ pub fn plugin_registrar(reg: &mut Registry) {
     reg.register_lint_group("clippy", vec![
         approx_const::APPROX_CONSTANT,
         array_indexing::OUT_OF_BOUNDS_INDEXING,
+        attrs::DEPRECATED_SEMVER,
         attrs::INLINE_ALWAYS,
         bit_mask::BAD_BIT_MASK,
         bit_mask::INEFFECTIVE_BIT_MASK,
diff --git a/tests/compile-fail/attrs.rs b/tests/compile-fail/attrs.rs
index ca7a0d5c07b..c7a4af60982 100644
--- a/tests/compile-fail/attrs.rs
+++ b/tests/compile-fail/attrs.rs
@@ -1,7 +1,7 @@
-#![feature(plugin)]
+#![feature(plugin, deprecated)]
 #![plugin(clippy)]
 
-#![deny(inline_always)]
+#![deny(inline_always, deprecated_semver)]
 
 #[inline(always)] //~ERROR you have declared `#[inline(always)]` on `test_attr_lint`.
 fn test_attr_lint() {
@@ -24,6 +24,14 @@ fn empty_and_false_positive_stmt() {
     unreachable!();
 }
 
+#[deprecated(since = "forever")] //~ERROR the since field must contain a semver-compliant version
+pub const SOME_CONST : u8 = 42;
+
+#[deprecated(since = "1")] //~ERROR the since field must contain a semver-compliant version
+pub const ANOTHER_CONST : u8 = 23;
+
+#[deprecated(since = "0.1.1")]
+pub const YET_ANOTHER_CONST : u8 = 0;
 
 fn main() {
     test_attr_lint();