]> git.lizzy.rs Git - rust.git/commitdiff
Add lint and test for malformed but unused #[on_unimplemented] attributes
authorManish Goregaokar <manishsmail@gmail.com>
Sun, 11 Jan 2015 04:19:39 +0000 (09:49 +0530)
committerManish Goregaokar <manishsmail@gmail.com>
Sun, 11 Jan 2015 04:21:09 +0000 (09:51 +0530)
src/librustc/lint/builtin.rs
src/librustc/lint/context.rs
src/test/compile-fail/on-unimplemented-bad-anno.rs [new file with mode: 0644]

index 300b9aaf10060cf02b68f92c3786cda435dd2ba1..29d37af30fc499c7fc2582547577bd90e4415d52 100644 (file)
@@ -26,6 +26,9 @@
 //! a `pub fn new()`.
 use self::MethodContext::*;
 
+
+use fmt_macros::{Parser, Piece, Position};
+
 use metadata::csearch;
 use middle::def::*;
 use middle::subst::Substs;
@@ -1921,3 +1924,63 @@ fn check_attribute(&mut self, ctx: &Context, attr: &ast::Attribute) {
         }
     }
 }
+
+/// Checks usage of `#[on_unimplemented]`
+#[derive(Copy)]
+pub struct BadOnUnimplemented;
+
+declare_lint!(BAD_ON_UNIMPLEMENTED, Deny,
+              "Checks usage of `#[on_unimplemented]`");
+
+impl LintPass for BadOnUnimplemented {
+    fn get_lints(&self) -> LintArray {
+        lint_array!(BAD_ON_UNIMPLEMENTED)
+    }
+    fn check_item(&mut self, ctx: &Context, item: &ast::Item) {
+        match item.node {
+            ast::ItemTrait(_, ref generics, _, _) => {
+                if let Some(ref attr) = item.attrs.iter().find(|&: a| {
+                    a.check_name("on_unimplemented")
+                }) {
+                    if let Some(ref istring) = attr.value_str() {
+                        let mut parser = Parser::new(istring.get());
+                        let types = generics.ty_params.as_slice();
+                        for token in parser {
+                            match token {
+                                Piece::String(_) => (), // Normal string, no need to check it
+                                Piece::NextArgument(a) => match a.position {
+                                    // `{Self}` is allowed
+                                    Position::ArgumentNamed(s) if s == "Self" => (),
+                                    // So is `{A}` if A is a type parameter
+                                    Position::ArgumentNamed(s) => match types.iter().find(|t| {
+                                        t.ident.as_str() == s
+                                    }) {
+                                        Some(_) => (),
+                                        None => {
+                                            ctx.span_lint(BAD_ON_UNIMPLEMENTED, attr.span,
+                                                         format!("there is no type parameter \
+                                                                    {} on trait {}",
+                                                                    s, item.ident.as_str())
+                                                           .as_slice());
+                                        }
+                                    },
+                                    // `{:1}` and `{}` are not to be used
+                                    Position::ArgumentIs(_) | Position::ArgumentNext => {
+                                        ctx.span_lint(BAD_ON_UNIMPLEMENTED, attr.span,
+                                                     "only named substitution \
+                                                     parameters are allowed");
+                                    }
+                                }
+                            }
+                        }
+                    } else {
+                        ctx.span_lint(BAD_ON_UNIMPLEMENTED, attr.span,
+                                     "this attribute must have a value, \
+                                      eg `#[on_unimplemented = \"foo\"]`")
+                    }
+                }
+            },
+            _ => () // Not a trait def, move along
+        }
+    }
+}
\ No newline at end of file
index 95e1e8d44bfc5044aec739ec98f5c89ec5aaeb7e..b50c505a3acedb1e64deb1e7495db9b674e82320 100644 (file)
@@ -211,6 +211,7 @@ macro_rules! add_lint_group {
                      UnusedAllocation,
                      MissingCopyImplementations,
                      UnstableFeatures,
+                     BadOnUnimplemented,
         );
 
         add_builtin_with_new!(sess,
diff --git a/src/test/compile-fail/on-unimplemented-bad-anno.rs b/src/test/compile-fail/on-unimplemented-bad-anno.rs
new file mode 100644 (file)
index 0000000..7ca228a
--- /dev/null
@@ -0,0 +1,32 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+// ignore-tidy-linelength
+
+#[allow(unused)]
+
+#[on_unimplemented = "test error `{Self}` with `{Bar}` `{Baz}` `{Quux}`"]
+trait Foo<Bar, Baz, Quux>{}
+
+#[on_unimplemented="a collection of type `{Self}` cannot be built from an iterator over elements of type `{A}`"]
+trait MyFromIterator<A> {
+    /// Build a container with elements from an external iterator.
+    fn my_from_iter<T: Iterator<Item=A>>(iterator: T) -> Self;
+}
+
+#[on_unimplemented] //~ ERROR this attribute must have a value
+trait BadAnnotation1 {}
+
+#[on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{C}>`"]
+//~^ ERROR there is no type parameter C on trait BadAnnotation2
+trait BadAnnotation2<A,B> {}
+
+
+pub fn main() {
+}