From dabb820b002f738618371b66a4f3f4c6fee17e16 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 6 May 2018 22:52:58 +0100 Subject: [PATCH] Add trivial bounds lint --- src/librustc_lint/builtin.rs | 58 +++++++++++++++++++ src/librustc_lint/lib.rs | 1 + .../ui/feature-gate-trivial_bounds-lint.rs | 18 ++++++ src/test/ui/trivial-bounds-lint.rs | 50 ++++++++++++++++ src/test/ui/trivial-bounds-lint.stderr | 50 ++++++++++++++++ 5 files changed, 177 insertions(+) create mode 100644 src/test/ui/feature-gate-trivial_bounds-lint.rs create mode 100644 src/test/ui/trivial-bounds-lint.rs create mode 100644 src/test/ui/trivial-bounds-lint.stderr diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index c2dcbad8d245..912d5c37ca3c 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1591,3 +1591,61 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExternCrate { self.0 += 1; } } + +/// Lint for trait and lifetime bounds that don't depend on type parameters +/// which either do nothing, or stop the item from being used. +pub struct TrivialConstraints; + +declare_lint! { + TRIVIAL_BOUNDS, + Warn, + "these bounds don't depend on an type parameters" +} + +impl LintPass for TrivialConstraints { + fn get_lints(&self) -> LintArray { + lint_array!(TRIVIAL_BOUNDS) + } +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TrivialConstraints { + fn check_item( + &mut self, + cx: &LateContext<'a, 'tcx>, + item: &'tcx hir::Item, + ) { + use rustc::ty::fold::TypeFoldable; + use rustc::ty::Predicate::*; + + + if cx.tcx.features().trivial_bounds { + let def_id = cx.tcx.hir.local_def_id(item.id); + let predicates = cx.tcx.predicates_of(def_id); + for predicate in &predicates.predicates { + let predicate_kind_name = match *predicate { + Trait(..) => "Trait", + TypeOutlives(..) | + RegionOutlives(..) => "Lifetime", + + // Ignore projections, as they can only be global + // if the trait bound is global + Projection(..) | + // Ignore bounds that a user can't type + WellFormed(..) | + ObjectSafe(..) | + ClosureKind(..) | + Subtype(..) | + ConstEvaluatable(..) => continue, + }; + if !predicate.is_global() { + cx.span_lint( + TRIVIAL_BOUNDS, + item.span, + &format!("{} bound {} does not depend on any type \ + or lifetime parameters", predicate_kind_name, predicate), + ); + } + } + } + } +} diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 39f550a4b459..ae44ea6b65b9 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -137,6 +137,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { UnreachablePub, TypeAliasBounds, UnusedBrokenConst, + TrivialConstraints, ); add_builtin_with_new!(sess, diff --git a/src/test/ui/feature-gate-trivial_bounds-lint.rs b/src/test/ui/feature-gate-trivial_bounds-lint.rs new file mode 100644 index 000000000000..2d2d491bd4df --- /dev/null +++ b/src/test/ui/feature-gate-trivial_bounds-lint.rs @@ -0,0 +1,18 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// run-pass + +#![allow(unused)] +#![deny(trivial_bounds)] // Ignored without the trivial_bounds feature flag. + +struct A where i32: Copy; + +fn main() {} diff --git a/src/test/ui/trivial-bounds-lint.rs b/src/test/ui/trivial-bounds-lint.rs new file mode 100644 index 000000000000..e6988cb9f8bf --- /dev/null +++ b/src/test/ui/trivial-bounds-lint.rs @@ -0,0 +1,50 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(trivial_bounds)] +#![allow(unused)] +#![deny(trivial_bounds)] + +struct A where i32: Copy; //~ ERROR + +trait X {} + +trait Y: Copy {} + +trait Z { + type S: Copy; +} + +// Check only the bound the user writes trigger the lint +fn trivial_elaboration() where T: X + Z, i32: Y {} // OK + +fn global_param() where i32: X<()> {} //~ ERROR + +// Should only error on the trait bound, not the implicit +// projection bound ::S == i32. +fn global_projection() where i32: Z {} //~ ERROR + +impl A { + fn new() -> A { A } +} + +// Lifetime bounds should be linted as well +fn global_lifetimes() where i32: 'static, &'static str: 'static {} +//~^ ERROR +//~| ERROR + +fn local_lifetimes<'a>() where i32: 'a, &'a str: 'a {} // OK + +fn global_outlives() where 'static: 'static {} //~ ERROR + +// Check that each bound is checked individually +fn mixed_bounds() where i32: X + Copy {} //~ ERROR + +fn main() {} diff --git a/src/test/ui/trivial-bounds-lint.stderr b/src/test/ui/trivial-bounds-lint.stderr new file mode 100644 index 000000000000..6a3e1981025c --- /dev/null +++ b/src/test/ui/trivial-bounds-lint.stderr @@ -0,0 +1,50 @@ +error: Trait bound i32: std::marker::Copy does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-lint.rs:15:1 + | +LL | struct A where i32: Copy; //~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: lint level defined here + --> $DIR/trivial-bounds-lint.rs:13:9 + | +LL | #![deny(trivial_bounds)] + | ^^^^^^^^^^^^^^ + +error: Trait bound i32: X<()> does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-lint.rs:28:1 + | +LL | fn global_param() where i32: X<()> {} //~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: Trait bound i32: Z does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-lint.rs:32:1 + | +LL | fn global_projection() where i32: Z {} //~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: Lifetime bound i32 : 'static does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-lint.rs:39:1 + | +LL | fn global_lifetimes() where i32: 'static, &'static str: 'static {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: Lifetime bound &'static str : 'static does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-lint.rs:39:1 + | +LL | fn global_lifetimes() where i32: 'static, &'static str: 'static {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: Lifetime bound 'static : 'static does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-lint.rs:45:1 + | +LL | fn global_outlives() where 'static: 'static {} //~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: Trait bound i32: std::marker::Copy does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-lint.rs:48:1 + | +LL | fn mixed_bounds() where i32: X + Copy {} //~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors +