Skip to content

Commit

Permalink
Add lint and test for malformed but unused #[on_unimplemented] attrib…
Browse files Browse the repository at this point in the history
…utes
  • Loading branch information
Manishearth committed Jan 11, 2015
1 parent 4d17fba commit dc0de42
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 0 deletions.
63 changes: 63 additions & 0 deletions src/librustc/lint/builtin.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -1921,3 +1924,63 @@ impl LintPass for UnstableFeatures {
}
}
}

/// 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
}
}
}
1 change: 1 addition & 0 deletions src/librustc/lint/context.rs
Expand Up @@ -211,6 +211,7 @@ impl LintStore {
UnusedAllocation,
MissingCopyImplementations,
UnstableFeatures,
BadOnUnimplemented,
);

add_builtin_with_new!(sess,
Expand Down
32 changes: 32 additions & 0 deletions src/test/compile-fail/on-unimplemented-bad-anno.rs
@@ -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() {
}

0 comments on commit dc0de42

Please sign in to comment.