Merge remote-tracking branch 'upstream/master' into rustup

This commit is contained in:
Eduardo Broto 2020-10-05 22:13:44 +02:00
commit 5f492490a3
83 changed files with 1028 additions and 414 deletions

View File

@ -1559,6 +1559,7 @@ Released 2018-09-13
[`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof
[`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord
[`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method
[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
@ -1634,6 +1635,8 @@ Released 2018-09-13
[`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string
[`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display
[`inline_always`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_always
[`inline_asm_x86_att_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_att_syntax
[`inline_asm_x86_intel_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_intel_syntax
[`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body
[`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one
[`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic
@ -1644,6 +1647,7 @@ Released 2018-09-13
[`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref
[`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
[`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
[`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect
[`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop
@ -1919,6 +1923,5 @@ Released 2018-09-13
[`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero
[`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal
[`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr
[`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space
[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
<!-- end autogenerated links to lint list -->

View File

@ -5,7 +5,7 @@
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
[There are over 350 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
[There are over 400 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:

View File

@ -0,0 +1,125 @@
use std::fmt;
use crate::utils::span_lint_and_help;
use rustc_ast::ast::{Expr, ExprKind, InlineAsmOptions};
use rustc_lint::{EarlyContext, EarlyLintPass, Lint};
use rustc_session::{declare_lint_pass, declare_tool_lint};
#[derive(Clone, Copy, PartialEq, Eq)]
enum AsmStyle {
Intel,
Att,
}
impl fmt::Display for AsmStyle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AsmStyle::Intel => f.write_str("Intel"),
AsmStyle::Att => f.write_str("AT&T"),
}
}
}
impl std::ops::Not for AsmStyle {
type Output = AsmStyle;
fn not(self) -> AsmStyle {
match self {
AsmStyle::Intel => AsmStyle::Att,
AsmStyle::Att => AsmStyle::Intel,
}
}
}
fn check_expr_asm_syntax(lint: &'static Lint, cx: &EarlyContext<'_>, expr: &Expr, check_for: AsmStyle) {
if let ExprKind::InlineAsm(ref inline_asm) = expr.kind {
let style = if inline_asm.options.contains(InlineAsmOptions::ATT_SYNTAX) {
AsmStyle::Att
} else {
AsmStyle::Intel
};
if style == check_for {
span_lint_and_help(
cx,
lint,
expr.span,
&format!("{} x86 assembly syntax used", style),
None,
&format!("use {} x86 assembly syntax", !style),
);
}
}
}
declare_clippy_lint! {
/// **What it does:** Checks for usage of Intel x86 assembly syntax.
///
/// **Why is this bad?** The lint has been enabled to indicate a preference
/// for AT&T x86 assembly syntax.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust,no_run
/// # #![feature(asm)]
/// # unsafe { let ptr = "".as_ptr();
/// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr);
/// # }
/// ```
/// Use instead:
/// ```rust,no_run
/// # #![feature(asm)]
/// # unsafe { let ptr = "".as_ptr();
/// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax));
/// # }
/// ```
pub INLINE_ASM_X86_INTEL_SYNTAX,
restriction,
"prefer AT&T x86 assembly syntax"
}
declare_lint_pass!(InlineAsmX86IntelSyntax => [INLINE_ASM_X86_INTEL_SYNTAX]);
impl EarlyLintPass for InlineAsmX86IntelSyntax {
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
check_expr_asm_syntax(Self::get_lints()[0], cx, expr, AsmStyle::Intel);
}
}
declare_clippy_lint! {
/// **What it does:** Checks for usage of AT&T x86 assembly syntax.
///
/// **Why is this bad?** The lint has been enabled to indicate a preference
/// for Intel x86 assembly syntax.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust,no_run
/// # #![feature(asm)]
/// # unsafe { let ptr = "".as_ptr();
/// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax));
/// # }
/// ```
/// Use instead:
/// ```rust,no_run
/// # #![feature(asm)]
/// # unsafe { let ptr = "".as_ptr();
/// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr);
/// # }
/// ```
pub INLINE_ASM_X86_ATT_SYNTAX,
restriction,
"prefer Intel x86 assembly syntax"
}
declare_lint_pass!(InlineAsmX86AttSyntax => [INLINE_ASM_X86_ATT_SYNTAX]);
impl EarlyLintPass for InlineAsmX86AttSyntax {
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
check_expr_asm_syntax(Self::get_lints()[0], cx, expr, AsmStyle::Att);
}
}

View File

@ -0,0 +1,73 @@
use crate::utils::span_lint;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Symbol;
declare_clippy_lint! {
/// **What it does:** Lints for specific trait methods defined in clippy.toml
///
/// **Why is this bad?** Some methods are undesirable in certain contexts,
/// and it would be beneficial to lint for them as needed.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust,ignore
/// // example code where clippy issues a warning
/// foo.bad_method(); // Foo::bad_method is disallowed in the configuration
/// ```
/// Use instead:
/// ```rust,ignore
/// // example code which does not raise clippy warning
/// goodStruct.bad_method(); // GoodStruct::bad_method is not disallowed
/// ```
pub DISALLOWED_METHOD,
nursery,
"use of a disallowed method call"
}
#[derive(Clone, Debug)]
pub struct DisallowedMethod {
disallowed: FxHashSet<Vec<Symbol>>,
}
impl DisallowedMethod {
pub fn new(disallowed: &FxHashSet<String>) -> Self {
Self {
disallowed: disallowed
.iter()
.map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::<Vec<_>>())
.collect(),
}
}
}
impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]);
impl<'tcx> LateLintPass<'tcx> for DisallowedMethod {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::MethodCall(_path, _, _args, _) = &expr.kind {
let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
let method_call = cx.get_def_path(def_id);
if self.disallowed.contains(&method_call) {
let method = method_call
.iter()
.map(|s| s.to_ident_string())
.collect::<Vec<_>>()
.join("::");
span_lint(
cx,
DISALLOWED_METHOD,
expr.span,
&format!("use of a disallowed method `{}`", method),
);
}
}
}
}

View File

@ -153,6 +153,7 @@ mod utils;
mod approx_const;
mod arithmetic;
mod as_conversions;
mod asm_syntax;
mod assertions_on_constants;
mod assign_ops;
mod async_yields_async;
@ -176,6 +177,7 @@ mod dbg_macro;
mod default_trait_access;
mod dereference;
mod derive;
mod disallowed_method;
mod doc;
mod double_comparison;
mod double_parens;
@ -489,6 +491,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&arithmetic::FLOAT_ARITHMETIC,
&arithmetic::INTEGER_ARITHMETIC,
&as_conversions::AS_CONVERSIONS,
&asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
&asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
&assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
&assign_ops::ASSIGN_OP_PATTERN,
&assign_ops::MISREFACTORED_ASSIGN_OP,
@ -529,6 +533,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&derive::DERIVE_ORD_XOR_PARTIAL_ORD,
&derive::EXPL_IMPL_CLONE_ON_COPY,
&derive::UNSAFE_DERIVE_DESERIALIZE,
&disallowed_method::DISALLOWED_METHOD,
&doc::DOC_MARKDOWN,
&doc::MISSING_ERRORS_DOC,
&doc::MISSING_SAFETY_DOC,
@ -851,9 +856,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&types::UNIT_CMP,
&types::UNNECESSARY_CAST,
&types::VEC_BOX,
&unicode::INVISIBLE_CHARACTERS,
&unicode::NON_ASCII_LITERAL,
&unicode::UNICODE_NOT_NFC,
&unicode::ZERO_WIDTH_SPACE,
&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD,
&unnamed_address::FN_ADDRESS_COMPARISONS,
&unnamed_address::VTABLE_ADDRESS_COMPARISONS,
@ -1120,11 +1125,18 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync);
store.register_late_pass(|| box manual_strip::ManualStrip);
store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem);
let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::<FxHashSet<_>>();
store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods));
store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax);
store.register_early_pass(|| box asm_syntax::InlineAsmX86IntelSyntax);
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
LintId::of(&arithmetic::INTEGER_ARITHMETIC),
LintId::of(&as_conversions::AS_CONVERSIONS),
LintId::of(&asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
LintId::of(&asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
LintId::of(&create_dir::CREATE_DIR),
LintId::of(&dbg_macro::DBG_MACRO),
LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE),
@ -1499,7 +1511,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&types::UNIT_CMP),
LintId::of(&types::UNNECESSARY_CAST),
LintId::of(&types::VEC_BOX),
LintId::of(&unicode::ZERO_WIDTH_SPACE),
LintId::of(&unicode::INVISIBLE_CHARACTERS),
LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS),
LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS),
@ -1592,6 +1604,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED),
LintId::of(&neg_multiply::NEG_MULTIPLY),
LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT),
LintId::of(&non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES),
LintId::of(&panic_unimplemented::PANIC_PARAMS),
@ -1747,8 +1761,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&misc::FLOAT_CMP),
LintId::of(&misc::MODULO_ONE),
LintId::of(&mut_key::MUTABLE_KEY_TYPE),
LintId::of(&non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS),
LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP),
LintId::of(&ptr::MUT_FROM_REF),
@ -1766,7 +1778,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&types::ABSURD_EXTREME_COMPARISONS),
LintId::of(&types::CAST_REF_TO_MUT),
LintId::of(&types::UNIT_CMP),
LintId::of(&unicode::ZERO_WIDTH_SPACE),
LintId::of(&unicode::INVISIBLE_CHARACTERS),
LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS),
LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS),
@ -1807,6 +1819,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
LintId::of(&attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
LintId::of(&cognitive_complexity::COGNITIVE_COMPLEXITY),
LintId::of(&disallowed_method::DISALLOWED_METHOD),
LintId::of(&fallible_impl_from::FALLIBLE_IMPL_FROM),
LintId::of(&floating_point_arithmetic::IMPRECISE_FLOPS),
LintId::of(&floating_point_arithmetic::SUBOPTIMAL_FLOPS),
@ -1896,6 +1909,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles");
ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles");
ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion");
ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters");
}
// only exists to let the dogfood integration test works.

View File

@ -1,21 +1,22 @@
use crate::utils::paths;
use crate::utils::{get_trait_def_id, in_macro, span_lint, trait_ref_of_method};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::{
walk_fn_decl, walk_generic_param, walk_generics, walk_param_bound, walk_ty, NestedVisitorMap, Visitor,
walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty,
NestedVisitorMap, Visitor,
};
use rustc_hir::FnRetTy::Return;
use rustc_hir::{
BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item,
ItemKind, Lifetime, LifetimeName, ParamName, QPath, TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, Ty,
TyKind, WhereClause, WherePredicate,
BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem,
ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier, TraitFn,
TraitItem, TraitItemKind, Ty, TyKind, WhereClause, WherePredicate,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::map::Map;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
use rustc_span::symbol::{kw, Symbol};
use crate::utils::{in_macro, last_path_segment, span_lint, trait_ref_of_method};
use std::iter::FromIterator;
declare_clippy_lint! {
/// **What it does:** Checks for lifetime annotations which can be removed by
@ -25,8 +26,11 @@ declare_clippy_lint! {
/// complicated, while there is nothing out of the ordinary going on. Removing
/// them leads to more readable code.
///
/// **Known problems:** Potential false negatives: we bail out if the function
/// has a `where` clause where lifetimes are mentioned.
/// **Known problems:**
/// - We bail out if the function has a `where` clause where lifetimes
/// are mentioned due to potenial false positives.
/// - Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the
/// placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`.
///
/// **Example:**
/// ```rust
@ -108,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
}
/// The lifetime of a &-reference.
#[derive(PartialEq, Eq, Hash, Debug)]
#[derive(PartialEq, Eq, Hash, Debug, Clone)]
enum RefLt {
Unnamed,
Static,
@ -127,7 +131,6 @@ fn check_fn_inner<'tcx>(
return;
}
let mut bounds_lts = Vec::new();
let types = generics
.params
.iter()
@ -156,13 +159,12 @@ fn check_fn_inner<'tcx>(
if bound.name != LifetimeName::Static && !bound.is_elided() {
return;
}
bounds_lts.push(bound);
}
}
}
}
}
if could_use_elision(cx, decl, body, &generics.params, bounds_lts) {
if could_use_elision(cx, decl, body, &generics.params) {
span_lint(
cx,
NEEDLESS_LIFETIMES,
@ -181,7 +183,6 @@ fn could_use_elision<'tcx>(
func: &'tcx FnDecl<'_>,
body: Option<BodyId>,
named_generics: &'tcx [GenericParam<'_>],
bounds_lts: Vec<&'tcx Lifetime>,
) -> bool {
// There are two scenarios where elision works:
// * no output references, all input references have different LT
@ -204,15 +205,31 @@ fn could_use_elision<'tcx>(
if let Return(ref ty) = func.output {
output_visitor.visit_ty(ty);
}
for lt in named_generics {
input_visitor.visit_generic_param(lt)
}
let input_lts = match input_visitor.into_vec() {
Some(lts) => lts_from_bounds(lts, bounds_lts.into_iter()),
None => return false,
};
let output_lts = match output_visitor.into_vec() {
Some(val) => val,
None => return false,
};
if input_visitor.abort() || output_visitor.abort() {
return false;
}
if allowed_lts
.intersection(&FxHashSet::from_iter(
input_visitor
.nested_elision_site_lts
.iter()
.chain(output_visitor.nested_elision_site_lts.iter())
.cloned()
.filter(|v| matches!(v, RefLt::Named(_))),
))
.next()
.is_some()
{
return false;
}
let input_lts = input_visitor.lts;
let output_lts = output_visitor.lts;
if let Some(body_id) = body {
let mut checker = BodyLifetimeChecker {
@ -277,27 +294,20 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<RefLt> {
allowed_lts
}
fn lts_from_bounds<'a, T: Iterator<Item = &'a Lifetime>>(mut vec: Vec<RefLt>, bounds_lts: T) -> Vec<RefLt> {
for lt in bounds_lts {
if lt.name != LifetimeName::Static {
vec.push(RefLt::Named(lt.name.ident().name));
}
}
vec
}
/// Number of unique lifetimes in the given vector.
#[must_use]
fn unique_lifetimes(lts: &[RefLt]) -> usize {
lts.iter().collect::<FxHashSet<_>>().len()
}
const CLOSURE_TRAIT_BOUNDS: [&[&str]; 3] = [&paths::FN, &paths::FN_MUT, &paths::FN_ONCE];
/// A visitor usable for `rustc_front::visit::walk_ty()`.
struct RefVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
lts: Vec<RefLt>,
abort: bool,
nested_elision_site_lts: Vec<RefLt>,
unelided_trait_object_lifetime: bool,
}
impl<'a, 'tcx> RefVisitor<'a, 'tcx> {
@ -305,7 +315,8 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> {
Self {
cx,
lts: Vec::new(),
abort: false,
nested_elision_site_lts: Vec::new(),
unelided_trait_object_lifetime: false,
}
}
@ -325,40 +336,16 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> {
}
}
fn into_vec(self) -> Option<Vec<RefLt>> {
if self.abort {
None
} else {
Some(self.lts)
}
fn all_lts(&self) -> Vec<RefLt> {
self.lts
.iter()
.chain(self.nested_elision_site_lts.iter())
.cloned()
.collect::<Vec<_>>()
}
fn collect_anonymous_lifetimes(&mut self, qpath: &QPath<'_>, ty: &Ty<'_>) {
if let Some(ref last_path_segment) = last_path_segment(qpath).args {
if !last_path_segment.parenthesized
&& !last_path_segment
.args
.iter()
.any(|arg| matches!(arg, GenericArg::Lifetime(_)))
{
let hir_id = ty.hir_id;
match self.cx.qpath_res(qpath, hir_id) {
Res::Def(DefKind::TyAlias | DefKind::Struct, def_id) => {
let generics = self.cx.tcx.generics_of(def_id);
for _ in generics.params.as_slice() {
self.record(&None);
}
},
Res::Def(DefKind::Trait, def_id) => {
let trait_def = self.cx.tcx.trait_def(def_id);
for _ in &self.cx.tcx.generics_of(trait_def.def_id).params {
self.record(&None);
}
},
_ => (),
}
}
}
fn abort(&self) -> bool {
self.unelided_trait_object_lifetime
}
}
@ -370,30 +357,37 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
self.record(&Some(*lifetime));
}
fn visit_poly_trait_ref(&mut self, poly_tref: &'tcx PolyTraitRef<'tcx>, tbm: TraitBoundModifier) {
let trait_ref = &poly_tref.trait_ref;
if CLOSURE_TRAIT_BOUNDS
.iter()
.any(|trait_path| trait_ref.trait_def_id() == get_trait_def_id(self.cx, trait_path))
{
let mut sub_visitor = RefVisitor::new(self.cx);
sub_visitor.visit_trait_ref(trait_ref);
self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
} else {
walk_poly_trait_ref(self, poly_tref, tbm);
}
}
fn visit_ty(&mut self, ty: &'tcx Ty<'_>) {
match ty.kind {
TyKind::Rptr(ref lt, _) if lt.is_elided() => {
self.record(&None);
},
TyKind::Path(ref path) => {
self.collect_anonymous_lifetimes(path, ty);
},
TyKind::OpaqueDef(item, _) => {
let map = self.cx.tcx.hir();
if let ItemKind::OpaqueTy(ref exist_ty) = map.expect_item(item.id).kind {
for bound in exist_ty.bounds {
if let GenericBound::Outlives(_) = *bound {
self.record(&None);
}
}
} else {
unreachable!()
}
let item = map.expect_item(item.id);
walk_item(self, item);
walk_ty(self, ty);
},
TyKind::BareFn(&BareFnTy { decl, .. }) => {
let mut sub_visitor = RefVisitor::new(self.cx);
sub_visitor.visit_fn_decl(decl);
self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
return;
},
TyKind::TraitObject(bounds, ref lt) => {
if !lt.is_elided() {
self.abort = true;
self.unelided_trait_object_lifetime = true;
}
for bound in bounds {
self.visit_poly_trait_ref(bound, TraitBoundModifier::None);
@ -430,16 +424,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl
walk_param_bound(&mut visitor, bound);
}
// and check that all lifetimes are allowed
match visitor.into_vec() {
None => return false,
Some(lts) => {
for lt in lts {
if !allowed_lts.contains(&lt) {
return true;
}
}
},
}
return visitor.all_lts().iter().any(|it| !allowed_lts.contains(it));
},
WherePredicate::EqPredicate(ref pred) => {
let mut visitor = RefVisitor::new(cx);

View File

@ -3,7 +3,7 @@ use crate::utils::paths;
use crate::utils::sugg::Sugg;
use crate::utils::usage::{is_unused, mutated_variables};
use crate::utils::{
get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait,
contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait,
is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method,
match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, snippet_with_applicability,
snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg,
@ -1276,6 +1276,8 @@ fn check_for_loop_range<'tcx>(
let skip = if starts_at_zero {
String::new()
} else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start) {
return;
} else {
format!(".skip({})", snippet(cx, start.span, ".."))
};
@ -1302,6 +1304,8 @@ fn check_for_loop_range<'tcx>(
if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) {
String::new()
} else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr) {
return;
} else {
match limits {
ast::RangeLimits::Closed => {
@ -2134,7 +2138,7 @@ enum VarState {
DontWarn,
}
/// Scan a for loop for variables that are incremented exactly once.
/// Scan a for loop for variables that are incremented exactly once and not used after that.
struct IncrementVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>, // context reference
states: FxHashMap<HirId, VarState>, // incremented variables
@ -2154,6 +2158,10 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> {
if let Some(def_id) = var_def_id(self.cx, expr) {
if let Some(parent) = get_parent_expr(self.cx, expr) {
let state = self.states.entry(def_id).or_insert(VarState::Initial);
if *state == VarState::IncrOnce {
*state = VarState::DontWarn;
return;
}
match parent.kind {
ExprKind::AssignOp(op, ref lhs, ref rhs) => {

View File

@ -1,10 +1,10 @@
use crate::utils::qualify_min_const_fn::is_min_const_fn;
use crate::utils::{fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, span_lint, trait_ref_of_method};
use rustc_hir as hir;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use crate::utils::qualify_min_const_fn::is_min_const_fn;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Span;
use rustc_typeck::hir_ty_to_ty;

View File

@ -1,4 +1,4 @@
use crate::utils::span_lint_and_sugg;
use crate::utils::{in_macro, span_lint_and_sugg};
use if_chain::if_chain;
use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind};
use rustc_errors::Applicability;
@ -69,11 +69,30 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod
if let [segment] = &path.segments[..];
if segment.ident.name == kw::SelfUpper;
then {
// In case we have a named lifetime, we check if the name comes from expansion.
// If it does, at this point we know the rest of the parameter was written by the user,
// so let them decide what the name of the lifetime should be.
// See #6089 for more details.
let mut applicability = Applicability::MachineApplicable;
let self_param = match (binding_mode, mutbl) {
(Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(),
(Mode::Ref(Some(lifetime)), Mutability::Mut) => format!("&{} mut self", &lifetime.ident.name),
(Mode::Ref(Some(lifetime)), Mutability::Mut) => {
if in_macro(lifetime.ident.span) {
applicability = Applicability::HasPlaceholders;
"&'_ mut self".to_string()
} else {
format!("&{} mut self", &lifetime.ident.name)
}
},
(Mode::Ref(None), Mutability::Not) => "&self".to_string(),
(Mode::Ref(Some(lifetime)), Mutability::Not) => format!("&{} self", &lifetime.ident.name),
(Mode::Ref(Some(lifetime)), Mutability::Not) => {
if in_macro(lifetime.ident.span) {
applicability = Applicability::HasPlaceholders;
"&'_ self".to_string()
} else {
format!("&{} self", &lifetime.ident.name)
}
},
(Mode::Value, Mutability::Mut) => "mut self".to_string(),
(Mode::Value, Mutability::Not) => "self".to_string(),
};
@ -85,7 +104,7 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod
"the type of the `self` parameter does not need to be arbitrary",
"consider to change this parameter to",
self_param,
Applicability::MachineApplicable,
applicability,
)
}
}
@ -93,7 +112,8 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod
impl EarlyLintPass for NeedlessArbitrarySelfType {
fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) {
if !p.is_self() {
// Bail out if the parameter it's not a receiver or was not written by the user
if !p.is_self() || in_macro(p.span) {
return;
}

View File

@ -1,6 +1,6 @@
//! Checks for uses of const which the type is not `Freeze` (`Cell`-free).
//!
//! This lint is **deny** by default.
//! This lint is **warn** by default.
use std::ptr;
@ -17,6 +17,8 @@ use rustc_typeck::hir_ty_to_ty;
use crate::utils::{in_constant, qpath_res, span_lint_and_then};
use if_chain::if_chain;
// FIXME: this is a correctness problem but there's no suitable
// warn-by-default category.
declare_clippy_lint! {
/// **What it does:** Checks for declaration of `const` items which is interior
/// mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.).
@ -34,6 +36,15 @@ declare_clippy_lint! {
/// `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit,
/// and this lint should be suppressed.
///
/// When an enum has variants with interior mutability, use of its non interior mutable
/// variants can generate false positives. See issue
/// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962)
///
/// Types that have underlying or potential interior mutability trigger the lint whether
/// the interior mutable field is used or not. See issues
/// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and
/// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825)
///
/// **Example:**
/// ```rust
/// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
@ -49,10 +60,12 @@ declare_clippy_lint! {
/// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance
/// ```
pub DECLARE_INTERIOR_MUTABLE_CONST,
correctness,
style,
"declaring `const` with interior mutability"
}
// FIXME: this is a correctness problem but there's no suitable
// warn-by-default category.
declare_clippy_lint! {
/// **What it does:** Checks if `const` items which is interior mutable (e.g.,
/// contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly.
@ -64,7 +77,14 @@ declare_clippy_lint! {
///
/// The `const` value should be stored inside a `static` item.
///
/// **Known problems:** None
/// **Known problems:** When an enum has variants with interior mutability, use of its non
/// interior mutable variants can generate false positives. See issue
/// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962)
///
/// Types that have underlying or potential interior mutability trigger the lint whether
/// the interior mutable field is used or not. See issues
/// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and
/// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825)
///
/// **Example:**
/// ```rust
@ -81,7 +101,7 @@ declare_clippy_lint! {
/// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance
/// ```
pub BORROW_INTERIOR_MUTABLE_CONST,
correctness,
style,
"referencing `const` with interior mutability"
}

View File

@ -216,18 +216,19 @@ declare_clippy_lint! {
}
declare_clippy_lint! {
/// **What it does:** Checks for Rc<T> and Arc<T> when T is a mutable buffer type such as String or Vec
/// **What it does:** Checks for `Rc<T>` and `Arc<T>` when `T` is a mutable buffer type such as `String` or `Vec`.
///
/// **Why is this bad?** Expressions such as Rc<String> have no advantage over Rc<str>, since
/// it is larger and involves an extra level of indirection, and doesn't implement Borrow<str>.
/// **Why is this bad?** Expressions such as `Rc<String>` usually have no advantage over `Rc<str>`, since
/// it is larger and involves an extra level of indirection, and doesn't implement `Borrow<str>`.
///
/// While mutating a buffer type would still be possible with Rc::get_mut(), it only
/// works if there are no additional references yet, which defeats the purpose of
/// While mutating a buffer type would still be possible with `Rc::get_mut()`, it only
/// works if there are no additional references yet, which usually defeats the purpose of
/// enclosing it in a shared ownership type. Instead, additionally wrapping the inner
/// type with an interior mutable container (such as RefCell or Mutex) would normally
/// type with an interior mutable container (such as `RefCell` or `Mutex`) would normally
/// be used.
///
/// **Known problems:** None.
/// **Known problems:** This pattern can be desirable to avoid the overhead of a `RefCell` or `Mutex` for
/// cases where mutation only happens before there are any additional references.
///
/// **Example:**
/// ```rust,ignore

View File

@ -8,18 +8,18 @@ use rustc_span::source_map::Span;
use unicode_normalization::UnicodeNormalization;
declare_clippy_lint! {
/// **What it does:** Checks for the Unicode zero-width space in the code.
/// **What it does:** Checks for invisible Unicode characters in the code.
///
/// **Why is this bad?** Having an invisible character in the code makes for all
/// sorts of April fools, but otherwise is very much frowned upon.
///
/// **Known problems:** None.
///
/// **Example:** You don't see it, but there may be a zero-width space
/// somewhere in this text.
pub ZERO_WIDTH_SPACE,
/// **Example:** You don't see it, but there may be a zero-width space or soft hyphen
/// some­where in this text.
pub INVISIBLE_CHARACTERS,
correctness,
"using a zero-width space in a string literal, which is confusing"
"using an invisible character in a string literal, which is confusing"
}
declare_clippy_lint! {
@ -63,7 +63,7 @@ declare_clippy_lint! {
"using a Unicode literal not in NFC normal form (see [Unicode tr15](http://www.unicode.org/reports/tr15/) for further information)"
}
declare_lint_pass!(Unicode => [ZERO_WIDTH_SPACE, NON_ASCII_LITERAL, UNICODE_NOT_NFC]);
declare_lint_pass!(Unicode => [INVISIBLE_CHARACTERS, NON_ASCII_LITERAL, UNICODE_NOT_NFC]);
impl LateLintPass<'_> for Unicode {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
@ -91,14 +91,17 @@ fn escape<T: Iterator<Item = char>>(s: T) -> String {
fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) {
let string = snippet(cx, span, "");
if string.contains('\u{200B}') {
if string.chars().any(|c| ['\u{200B}', '\u{ad}', '\u{2060}'].contains(&c)) {
span_lint_and_sugg(
cx,
ZERO_WIDTH_SPACE,
INVISIBLE_CHARACTERS,
span,
"zero-width space detected",
"invisible character detected",
"consider replacing the string with",
string.replace("\u{200B}", "\\u{200B}"),
string
.replace("\u{200B}", "\\u{200B}")
.replace("\u{ad}", "\\u{AD}")
.replace("\u{2060}", "\\u{2060}"),
Applicability::MachineApplicable,
);
}

View File

@ -164,6 +164,8 @@ define_Conf! {
(max_fn_params_bools, "max_fn_params_bools": u64, 3),
/// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests).
(warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false),
/// Lint: DISALLOWED_METHOD. The list of blacklisted methods to lint about. NB: `bar` is not here since it has legitimate uses
(disallowed_methods, "disallowed_methods": Vec<String>, Vec::<String>::new()),
}
impl Default for Conf {

View File

@ -18,9 +18,9 @@ pub mod internal_lints;
pub mod numeric_literal;
pub mod paths;
pub mod ptr;
pub mod qualify_min_const_fn;
pub mod sugg;
pub mod usage;
pub mod qualify_min_const_fn;
pub use self::attrs::*;
pub use self::diagnostics::*;
@ -47,7 +47,6 @@ use rustc_lint::{LateContext, Level, Lint, LintContext};
use rustc_middle::hir::map::Map;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable};
use rustc_mir::const_eval;
use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::source_map::original_sp;
use rustc_span::symbol::{self, kw, Symbol};
@ -884,19 +883,11 @@ pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
/// Checks if an expression is constructing a tuple-like enum variant or struct
pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
fn has_no_arguments(cx: &LateContext<'_>, def_id: DefId) -> bool {
cx.tcx.fn_sig(def_id).skip_binder().inputs().is_empty()
}
if let ExprKind::Call(ref fun, _) = expr.kind {
if let ExprKind::Path(ref qp) = fun.kind {
let res = cx.qpath_res(qp, fun.hir_id);
return match res {
def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
// FIXME: check the constness of the arguments, see https://github.com/rust-lang/rust-clippy/pull/5682#issuecomment-638681210
def::Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) if has_no_arguments(cx, def_id) => {
const_eval::is_const_fn(cx.tcx, def_id)
},
def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
_ => false,
};

View File

@ -36,8 +36,9 @@ pub struct NumericLiteral<'a> {
pub integer: &'a str,
/// The fraction part of the number.
pub fraction: Option<&'a str>,
/// The character used as exponent separator (b'e' or b'E') and the exponent part.
pub exponent: Option<(char, &'a str)>,
/// The exponent separator (b'e' or b'E') including preceding underscore if present
/// and the exponent part.
pub exponent: Option<(&'a str, &'a str)>,
/// The type suffix, including preceding underscore if present.
pub suffix: Option<&'a str>,
@ -100,7 +101,7 @@ impl<'a> NumericLiteral<'a> {
self.radix == Radix::Decimal
}
pub fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(char, &str)>) {
pub fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(&str, &str)>) {
let mut integer = digits;
let mut fraction = None;
let mut exponent = None;
@ -113,12 +114,14 @@ impl<'a> NumericLiteral<'a> {
fraction = Some(&digits[i + 1..]);
},
'e' | 'E' => {
if integer.len() > i {
integer = &digits[..i];
let exp_start = if digits[..i].ends_with('_') { i - 1 } else { i };
if integer.len() > exp_start {
integer = &digits[..exp_start];
} else {
fraction = Some(&digits[integer.len() + 1..i]);
fraction = Some(&digits[integer.len() + 1..exp_start]);
};
exponent = Some((c, &digits[i + 1..]));
exponent = Some((&digits[exp_start..=i], &digits[i + 1..]));
break;
},
_ => {},
@ -153,7 +156,7 @@ impl<'a> NumericLiteral<'a> {
}
if let Some((separator, exponent)) = self.exponent {
output.push(separator);
output.push_str(separator);
Self::group_digits(&mut output, exponent, group_size, true, false);
}

View File

@ -41,6 +41,9 @@ pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"];
pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"];
pub const FN: [&str; 3] = ["core", "ops", "Fn"];
pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"];
pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"];
pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"];
pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];

View File

@ -1,9 +1,12 @@
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::mir::*;
use rustc_middle::mir::{
Body, CastKind, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
TerminatorKind,
};
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
use rustc_span::symbol::{sym};
use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_target::spec::abi::Abi::RustIntrinsic;
use std::borrow::Cow;
@ -24,15 +27,9 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult {
| ty::PredicateAtom::ConstEvaluatable(..)
| ty::PredicateAtom::ConstEquate(..)
| ty::PredicateAtom::TypeWellFormedFromEnv(..) => continue,
ty::PredicateAtom::ObjectSafe(_) => {
panic!("object safe predicate on function: {:#?}", predicate)
}
ty::PredicateAtom::ClosureKind(..) => {
panic!("closure kind predicate on function: {:#?}", predicate)
}
ty::PredicateAtom::Subtype(_) => {
panic!("subtype predicate on function: {:#?}", predicate)
}
ty::PredicateAtom::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate),
ty::PredicateAtom::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate),
ty::PredicateAtom::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate),
ty::PredicateAtom::Trait(pred, _) => {
if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
continue;
@ -48,12 +45,12 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult {
on const fn parameters are unstable"
.into(),
));
}
},
// other kinds of bounds are either tautologies
// or cause errors in other passes
_ => continue,
}
}
},
}
}
match predicates.parent {
@ -93,24 +90,23 @@ fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
match ty.kind() {
ty::Ref(_, _, hir::Mutability::Mut) => {
return Err((span, "mutable references in const fn are unstable".into()));
}
return Err((span, "mutable references in const fn are unstable".into()));
},
ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
ty::FnPtr(..) => {
return Err((span, "function pointers in const fn are unstable".into()));
}
return Err((span, "function pointers in const fn are unstable".into()));
},
ty::Dynamic(preds, _) => {
for pred in preds.iter() {
match pred.skip_binder() {
ty::ExistentialPredicate::AutoTrait(_)
| ty::ExistentialPredicate::Projection(_) => {
ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => {
return Err((
span,
"trait bounds other than `Sized` \
on const fn parameters are unstable"
.into(),
));
}
},
ty::ExistentialPredicate::Trait(trait_ref) => {
if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
return Err((
@ -120,34 +116,23 @@ fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
.into(),
));
}
}
},
}
}
}
_ => {}
},
_ => {},
}
}
Ok(())
}
fn check_rvalue(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
def_id: DefId,
rvalue: &Rvalue<'tcx>,
span: Span,
) -> McfResult {
fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rvalue<'tcx>, span: Span) -> McfResult {
match rvalue {
Rvalue::ThreadLocalRef(_) => {
Err((span, "cannot access thread local storage in const fn".into()))
}
Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => {
check_operand(tcx, operand, span, body)
}
Rvalue::Len(place)
| Rvalue::Discriminant(place)
| Rvalue::Ref(_, _, place)
| Rvalue::AddressOf(_, place) => check_place(tcx, *place, span, body),
Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())),
Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => check_operand(tcx, operand, span, body),
Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
check_place(tcx, *place, span, body)
},
Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
use rustc_middle::ty::cast::CastTy;
let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast");
@ -155,20 +140,16 @@ fn check_rvalue(
match (cast_in, cast_out) {
(CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => {
Err((span, "casting pointers to ints is unstable in const fn".into()))
}
},
_ => check_operand(tcx, operand, span, body),
}
}
Rvalue::Cast(
CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer),
operand,
_,
) => check_operand(tcx, operand, span, body),
},
Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _) => {
check_operand(tcx, operand, span, body)
},
Rvalue::Cast(
CastKind::Pointer(
PointerCast::UnsafeFnPointer
| PointerCast::ClosureFnPointer(_)
| PointerCast::ReifyFnPointer,
PointerCast::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer,
),
_,
_,
@ -178,10 +159,7 @@ fn check_rvalue(
deref_ty.ty
} else {
// We cannot allow this for now.
return Err((
span,
"unsizing casts are only allowed for references right now".into(),
));
return Err((span, "unsizing casts are only allowed for references right now".into()));
};
let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id));
if let ty::Slice(_) | ty::Str = unsized_ty.kind() {
@ -192,7 +170,7 @@ fn check_rvalue(
// We just can't allow trait objects until we have figured out trait method calls.
Err((span, "unsizing casts are not allowed in const fn".into()))
}
}
},
// binops are fine on integers
Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
check_operand(tcx, lhs, span, body)?;
@ -201,13 +179,14 @@ fn check_rvalue(
if ty.is_integral() || ty.is_bool() || ty.is_char() {
Ok(())
} else {
Err((span, "only int, `bool` and `char` operations are stable in const fn".into()))
Err((
span,
"only int, `bool` and `char` operations are stable in const fn".into(),
))
}
}
},
Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()),
Rvalue::NullaryOp(NullOp::Box, _) => {
Err((span, "heap allocations are not allowed in const fn".into()))
}
Rvalue::NullaryOp(NullOp::Box, _) => Err((span, "heap allocations are not allowed in const fn".into())),
Rvalue::UnaryOp(_, operand) => {
let ty = operand.ty(body, tcx);
if ty.is_integral() || ty.is_bool() {
@ -215,39 +194,29 @@ fn check_rvalue(
} else {
Err((span, "only int and `bool` operations are stable in const fn".into()))
}
}
},
Rvalue::Aggregate(_, operands) => {
for operand in operands {
check_operand(tcx, operand, span, body)?;
}
Ok(())
}
},
}
}
fn check_statement(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
def_id: DefId,
statement: &Statement<'tcx>,
) -> McfResult {
fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statement: &Statement<'tcx>) -> McfResult {
let span = statement.source_info.span;
match &statement.kind {
StatementKind::Assign(box (place, rval)) => {
check_place(tcx, *place, span, body)?;
check_place(tcx, *place, span, body)?;
check_rvalue(tcx, body, def_id, rval, span)
}
StatementKind::FakeRead(_, place) => check_place(tcx, **place, span, body),
},
StatementKind::FakeRead(_, place) |
// just an assignment
StatementKind::SetDiscriminant { place, .. } => {
check_place(tcx, **place, span, body)
}
StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body),
StatementKind::LlvmInlineAsm { .. } => {
Err((span, "cannot use inline assembly in const fn".into()))
}
StatementKind::LlvmInlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
// These are all NOPs
StatementKind::StorageLive(_)
@ -259,12 +228,7 @@ fn check_statement(
}
}
fn check_operand(
tcx: TyCtxt<'tcx>,
operand: &Operand<'tcx>,
span: Span,
body: &Body<'tcx>,
) -> McfResult {
fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
match operand {
Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body),
Operand::Constant(c) => match c.check_static_ptr(tcx) {
@ -274,14 +238,9 @@ fn check_operand(
}
}
fn check_place(
tcx: TyCtxt<'tcx>,
place: Place<'tcx>,
span: Span,
body: &Body<'tcx>,
) -> McfResult {
fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
let mut cursor = place.projection.as_ref();
while let &[ref proj_base @ .., elem] = cursor {
while let [ref proj_base @ .., elem] = *cursor {
cursor = proj_base;
match elem {
ProjectionElem::Field(..) => {
@ -289,26 +248,22 @@ fn check_place(
if let Some(def) = base_ty.ty_adt_def() {
// No union field accesses in `const fn`
if def.is_union() {
return Err((span, "accessing union fields is unstable".into()));
return Err((span, "accessing union fields is unstable".into()));
}
}
}
},
ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Downcast(..)
| ProjectionElem::Subslice { .. }
| ProjectionElem::Deref
| ProjectionElem::Index(_) => {}
| ProjectionElem::Index(_) => {},
}
}
Ok(())
}
fn check_terminator(
tcx: TyCtxt<'tcx>,
body: &'a Body<'tcx>,
terminator: &Terminator<'tcx>,
) -> McfResult {
fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>) -> McfResult {
let span = terminator.source_info.span;
match &terminator.kind {
TerminatorKind::FalseEdge { .. }
@ -318,20 +273,23 @@ fn check_terminator(
| TerminatorKind::Resume
| TerminatorKind::Unreachable => Ok(()),
TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body),
TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body),
TerminatorKind::DropAndReplace { place, value, .. } => {
check_place(tcx, *place, span, body)?;
check_place(tcx, *place, span, body)?;
check_operand(tcx, value, span, body)
}
},
TerminatorKind::SwitchInt { discr, switch_ty: _, values: _, targets: _ } => {
check_operand(tcx, discr, span, body)
}
TerminatorKind::SwitchInt {
discr,
switch_ty: _,
values: _,
targets: _,
} => check_operand(tcx, discr, span, body),
TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())),
TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
Err((span, "const fn generators are unstable".into()))
}
},
TerminatorKind::Call {
func,
@ -343,8 +301,7 @@ fn check_terminator(
} => {
let fn_ty = func.ty(body, tcx);
if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id)
{
if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) {
return Err((
span,
format!(
@ -360,9 +317,7 @@ fn check_terminator(
// within const fns. `transmute` is allowed in all other const contexts.
// This won't really scale to more intrinsics or functions. Let's allow const
// transmutes in const fn before we add more hacks to this.
if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic
&& tcx.item_name(fn_def_id) == sym::transmute
{
if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic && tcx.item_name(fn_def_id) == sym::transmute {
return Err((
span,
"can only call `transmute` from const items, not `const fn`".into(),
@ -378,14 +333,16 @@ fn check_terminator(
} else {
Err((span, "can only call other const fns within const fn".into()))
}
}
},
TerminatorKind::Assert { cond, expected: _, msg: _, target: _, cleanup: _ } => {
check_operand(tcx, cond, span, body)
}
TerminatorKind::Assert {
cond,
expected: _,
msg: _,
target: _,
cleanup: _,
} => check_operand(tcx, cond, span, body),
TerminatorKind::InlineAsm { .. } => {
Err((span, "cannot use inline assembly in const fn".into()))
}
TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
}
}

View File

@ -235,8 +235,19 @@ impl EarlyLintPass for Write {
}
fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) {
fn is_build_script(cx: &EarlyContext<'_>) -> bool {
// Cargo sets the crate name for build scripts to `build_script_build`
cx.sess
.opts
.crate_name
.as_ref()
.map_or(false, |crate_name| crate_name == "build_script_build")
}
if mac.path == sym!(println) {
span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`");
if !is_build_script(cx) {
span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`");
}
if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
if fmt_str.symbol == Symbol::intern("") {
span_lint_and_sugg(
@ -251,7 +262,9 @@ impl EarlyLintPass for Write {
}
}
} else if mac.path == sym!(print) {
span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`");
if !is_build_script(cx) {
span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`");
}
if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
if check_newlines(&fmt_str) {
span_lint_and_then(

View File

@ -0,0 +1,7 @@
#![deny(clippy::print_stdout)]
fn main() {
// Test for #6041
println!("Hello");
print!("Hello");
}

View File

@ -110,7 +110,7 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
},
Lint {
name: "borrow_interior_mutable_const",
group: "correctness",
group: "style",
desc: "referencing `const` with interior mutability",
deprecation: None,
module: "non_copy_const",
@ -334,7 +334,7 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
},
Lint {
name: "declare_interior_mutable_const",
group: "correctness",
group: "style",
desc: "declaring `const` with interior mutability",
deprecation: None,
module: "non_copy_const",
@ -381,6 +381,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None,
module: "derive",
},
Lint {
name: "disallowed_method",
group: "nursery",
desc: "use of a disallowed method call",
deprecation: None,
module: "disallowed_method",
},
Lint {
name: "diverging_sub_expression",
group: "complexity",
@ -885,6 +892,20 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None,
module: "attrs",
},
Lint {
name: "inline_asm_x86_att_syntax",
group: "restriction",
desc: "prefer Intel x86 assembly syntax",
deprecation: None,
module: "asm_syntax",
},
Lint {
name: "inline_asm_x86_intel_syntax",
group: "restriction",
desc: "prefer AT&T x86 assembly syntax",
deprecation: None,
module: "asm_syntax",
},
Lint {
name: "inline_fn_without_body",
group: "correctness",
@ -941,6 +962,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None,
module: "types",
},
Lint {
name: "invisible_characters",
group: "correctness",
desc: "using an invisible character in a string literal, which is confusing",
deprecation: None,
module: "unicode",
},
Lint {
name: "items_after_statements",
group: "pedantic",
@ -2782,13 +2810,6 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None,
module: "misc",
},
Lint {
name: "zero_width_space",
group: "correctness",
desc: "using a zero-width space in a string literal, which is confusing",
deprecation: None,
module: "unicode",
},
Lint {
name: "zst_offset",
group: "correctness",

View File

@ -71,7 +71,7 @@ fn default_config() -> compiletest::Config {
}
config.target_rustcflags = Some(format!(
"-L {0} -L {1} -Dwarnings -Zui-testing {2}",
"--emit=metadata -L {0} -L {1} -Dwarnings -Zui-testing {2}",
host_lib().join("deps").display(),
cargo::TARGET_LIB.join("deps").display(),
third_party_crates(),

View File

@ -0,0 +1 @@
disallowed-methods = ["core::iter::traits::iterator::Iterator::sum", "regex::re_unicode::Regex::is_match"]

View File

@ -0,0 +1,13 @@
#![warn(clippy::disallowed_method)]
extern crate regex;
use regex::Regex;
fn main() {
let a = vec![1, 2, 3, 4];
let re = Regex::new(r"ab.*c").unwrap();
re.is_match("abc");
a.iter().sum::<i32>();
}

View File

@ -0,0 +1,16 @@
error: use of a disallowed method `regex::re_unicode::Regex::is_match`
--> $DIR/conf_disallowed_method.rs:10:5
|
LL | re.is_match("abc");
| ^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::disallowed-method` implied by `-D warnings`
error: use of a disallowed method `core::iter::traits::iterator::Iterator::sum`
--> $DIR/conf_disallowed_method.rs:12:5
|
LL | a.iter().sum::<i32>();
| ^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors

View File

@ -1,4 +1,4 @@
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `third-party` at line 5 column 1
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `third-party` at line 5 column 1
error: aborting due to previous error

31
tests/ui/asm_syntax.rs Normal file
View File

@ -0,0 +1,31 @@
#![feature(asm)]
// only-x86_64
#[warn(clippy::inline_asm_x86_intel_syntax)]
mod warn_intel {
pub(super) unsafe fn use_asm() {
asm!("");
asm!("", options());
asm!("", options(nostack));
asm!("", options(att_syntax));
asm!("", options(nostack, att_syntax));
}
}
#[warn(clippy::inline_asm_x86_att_syntax)]
mod warn_att {
pub(super) unsafe fn use_asm() {
asm!("");
asm!("", options());
asm!("", options(nostack));
asm!("", options(att_syntax));
asm!("", options(nostack, att_syntax));
}
}
fn main() {
unsafe {
warn_att::use_asm();
warn_intel::use_asm();
}
}

View File

@ -0,0 +1,44 @@
error: Intel x86 assembly syntax used
--> $DIR/asm_syntax.rs:7:9
|
LL | asm!("");
| ^^^^^^^^^
|
= note: `-D clippy::inline-asm-x86-intel-syntax` implied by `-D warnings`
= help: use AT&T x86 assembly syntax
error: Intel x86 assembly syntax used
--> $DIR/asm_syntax.rs:8:9
|
LL | asm!("", options());
| ^^^^^^^^^^^^^^^^^^^^
|
= help: use AT&T x86 assembly syntax
error: Intel x86 assembly syntax used
--> $DIR/asm_syntax.rs:9:9
|
LL | asm!("", options(nostack));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use AT&T x86 assembly syntax
error: AT&T x86 assembly syntax used
--> $DIR/asm_syntax.rs:21:9
|
LL | asm!("", options(att_syntax));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::inline-asm-x86-att-syntax` implied by `-D warnings`
= help: use Intel x86 assembly syntax
error: AT&T x86 assembly syntax used
--> $DIR/asm_syntax.rs:22:9
|
LL | asm!("", options(nostack, att_syntax));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use Intel x86 assembly syntax
error: aborting due to 5 previous errors

View File

@ -1,7 +1,8 @@
// compile-flags: --emit=link
// no-prefer-dynamic
#![crate_type = "proc-macro"]
#![feature(repr128, proc_macro_hygiene, proc_macro_quote)]
#![feature(repr128, proc_macro_hygiene, proc_macro_quote, box_patterns)]
#![allow(clippy::useless_conversion)]
extern crate proc_macro;
@ -11,7 +12,11 @@ extern crate syn;
use proc_macro::TokenStream;
use quote::{quote, quote_spanned};
use syn::parse_macro_input;
use syn::{parse_quote, ItemTrait, TraitItem};
use syn::spanned::Spanned;
use syn::token::Star;
use syn::{
parse_quote, FnArg, ImplItem, ItemImpl, ItemTrait, Lifetime, Pat, PatIdent, PatType, Signature, TraitItem, Type,
};
#[proc_macro_attribute]
pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream {
@ -35,3 +40,56 @@ pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream {
}
TokenStream::from(quote!(#item))
}
#[proc_macro_attribute]
pub fn rename_my_lifetimes(_args: TokenStream, input: TokenStream) -> TokenStream {
fn make_name(count: usize) -> String {
format!("'life{}", count)
}
fn mut_receiver_of(sig: &mut Signature) -> Option<&mut FnArg> {
let arg = sig.inputs.first_mut()?;
if let FnArg::Typed(PatType { pat, .. }) = arg {
if let Pat::Ident(PatIdent { ident, .. }) = &**pat {
if ident == "self" {
return Some(arg);
}
}
}
None
}
let mut elided = 0;
let mut item = parse_macro_input!(input as ItemImpl);
// Look for methods having arbitrary self type taken by &mut ref
for inner in &mut item.items {
if let ImplItem::Method(method) = inner {
if let Some(FnArg::Typed(pat_type)) = mut_receiver_of(&mut method.sig) {
if let box Type::Reference(reference) = &mut pat_type.ty {
// Target only unnamed lifetimes
let name = match &reference.lifetime {
Some(lt) if lt.ident == "_" => make_name(elided),
None => make_name(elided),
_ => continue,
};
elided += 1;
// HACK: Syn uses `Span` from the proc_macro2 crate, and does not seem to reexport it.
// In order to avoid adding the dependency, get a default span from a non-existent token.
// A default span is needed to mark the code as coming from expansion.
let span = Star::default().span();
// Replace old lifetime with the named one
let lifetime = Lifetime::new(&name, span);
reference.lifetime = Some(parse_quote!(#lifetime));
// Add lifetime to the generics of the method
method.sig.generics.params.push(parse_quote!(#lifetime));
}
}
}
}
TokenStream::from(quote!(#item))
}

View File

@ -1,3 +1,4 @@
// compile-flags: --emit=link
// no-prefer-dynamic
#![crate_type = "proc-macro"]

View File

@ -1,5 +1,3 @@
// run-pass
/// Test for https://github.com/rust-lang/rust-clippy/issues/1698
pub trait Trait {

View File

@ -1,3 +1,4 @@
// compile-flags: --emit=link
// no-prefer-dynamic
// ^ compiletest by default builds all aux files as dylibs, but we don't want that for proc-macro
// crates. If we don't set this, compiletest will override the `crate_type` attribute below and

View File

@ -1,5 +1,3 @@
// run-pass
#[allow(dead_code)]
/// Test for https://github.com/rust-lang/rust-clippy/issues/478

View File

@ -1,5 +1,3 @@
// run-pass
#![deny(clippy::all)]
#![allow(unused_imports)]

View File

@ -1,5 +1,3 @@
// run-pass
#![allow(clippy::all)]
/// Test for https://github.com/rust-lang/rust-clippy/issues/1588

View File

@ -1,5 +1,3 @@
// run-pass
#![allow(dead_code, unused_variables)]
/// Should not trigger an ICE in `SpanlessEq` / `consts::constant`

View File

@ -1,5 +1,3 @@
// run-pass
#![allow(clippy::all)]
/// Test for https://github.com/rust-lang/rust-clippy/issues/1969

View File

@ -1,5 +1,3 @@
// run-pass
#![allow(dead_code, clippy::char_lit_as_u8, clippy::needless_bool)]
/// Should not trigger an ICE in `SpanlessHash` / `consts::constant`

View File

@ -1,5 +1,3 @@
// run-pass
#![allow(dead_code, unused_variables)]
/// Should not trigger an ICE in `SpanlessHash` / `consts::constant`

View File

@ -1,5 +1,3 @@
// run-pass
/// Test for https://github.com/rust-lang/rust-clippy/issues/2727
pub fn f(new: fn()) {

View File

@ -1,5 +1,3 @@
// run-pass
#![allow(
unused_variables,
clippy::blacklisted_name,

View File

@ -1,5 +1,3 @@
// run-pass
use std::collections::HashSet;
// See rust-lang/rust-clippy#2774.

View File

@ -0,0 +1,10 @@
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/ice-2774.rs:15:1
|
LL | pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::needless-lifetimes` implied by `-D warnings`
error: aborting due to previous error

View File

@ -1,5 +1,3 @@
// run-pass
/// Test for https://github.com/rust-lang/rust-clippy/issues/2862
pub trait FooMap {

View File

@ -1,5 +1,3 @@
// run-pass
#[allow(dead_code)]
/// Test for https://github.com/rust-lang/rust-clippy/issues/2865

View File

@ -1,5 +1,3 @@
// run-pass
/// Test for https://github.com/rust-lang/rust-clippy/issues/2865
#[derive(Clone)]

View File

@ -1,5 +1,3 @@
// run-pass
#![warn(clippy::all)]
#![allow(clippy::blacklisted_name)]
#![allow(unused)]

View File

@ -1,5 +1,4 @@
// aux-build:proc_macro_crash.rs
// run-pass
#![warn(clippy::suspicious_else_formatting)]

View File

@ -1,5 +1,3 @@
// run-pass
/// Test for https://github.com/rust-lang/rust-clippy/issues/3747
macro_rules! a {

View File

@ -1,5 +1,3 @@
// run-pass
#![warn(clippy::use_self)]
#[path = "auxiliary/ice-4727-aux.rs"]

View File

@ -1,4 +1,3 @@
// run-pass
const COUNT: usize = 2;
struct Thing;
trait Dummy {}

View File

@ -1,5 +1,3 @@
// run-pass
#![deny(clippy::all)]
/// Test for https://github.com/rust-lang/rust-clippy/issues/700

View File

@ -1,5 +1,3 @@
// run-pass
#![deny(clippy::all)]
/// Test for https://github.com/rust-lang/rust-clippy/issues/1336

View File

@ -1,5 +1,3 @@
// run-pass
#![allow(clippy::comparison_chain)]
#![deny(clippy::if_same_then_else)]

View File

@ -1,5 +1,3 @@
// run-pass
#![allow(warnings)]
/// Test for https://github.com/rust-lang/rust-clippy/issues/825

View File

@ -1,5 +1,3 @@
// run-pass
#![allow(dead_code)]
/// Issue: https://github.com/rust-lang/rust-clippy/issues/2596

View File

@ -1,5 +1,3 @@
// run-pass
#![deny(clippy::match_same_arms)]
/// Test for https://github.com/rust-lang/rust-clippy/issues/2427

View File

@ -1,5 +1,3 @@
// run-pass
#![deny(clippy::mut_mut, clippy::zero_ptr, clippy::cmp_nan)]
#![allow(dead_code)]

View File

@ -1,5 +1,3 @@
// run-pass
#[deny(clippy::all)]
#[derive(Debug)]
pub enum Error {

View File

@ -1,5 +1,3 @@
// run-pass
#![deny(clippy::needless_lifetimes)]
#![allow(dead_code)]

View File

@ -0,0 +1,14 @@
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes_impl_trait.rs:15:5
|
LL | fn baz<'a>(&'a self) -> impl Foo + 'a {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/needless_lifetimes_impl_trait.rs:1:9
|
LL | #![deny(clippy::needless_lifetimes)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error

View File

@ -1,5 +1,3 @@
// run-pass
#[macro_use]
extern crate clippy_mini_macro_test;

View File

@ -1,5 +1,3 @@
// run-pass
#![allow(clippy::blacklisted_name)]
pub fn foo(bar: *const u8) {

View File

@ -1,5 +1,3 @@
// run-pass
/// Test for https://github.com/rust-lang/rust-clippy/issues/1346
#[deny(warnings)]

View File

@ -1,5 +1,3 @@
// run-pass
#![warn(clippy::single_match_else)]
//! Test for https://github.com/rust-lang/rust-clippy/issues/1588

View File

@ -1,5 +1,3 @@
// run-pass
#![feature(trivial_bounds)]
#![allow(unused, trivial_bounds)]

View File

@ -1,5 +1,3 @@
// run-pass
#![allow(clippy::useless_attribute)] //issue #2910
#[macro_use]

View File

@ -38,54 +38,54 @@ mod issue_1219 {
let text = "banana";
let mut count = 0;
for ch in text.chars() {
println!("{}", count);
if ch == 'a' {
continue;
}
count += 1;
println!("{}", count);
}
// should not trigger the lint because the count is conditional
let text = "banana";
let mut count = 0;
for ch in text.chars() {
println!("{}", count);
if ch == 'a' {
count += 1;
}
println!("{}", count);
}
// should trigger the lint because the count is not conditional
let text = "banana";
let mut count = 0;
for ch in text.chars() {
println!("{}", count);
count += 1;
if ch == 'a' {
continue;
}
println!("{}", count);
}
// should trigger the lint because the count is not conditional
let text = "banana";
let mut count = 0;
for ch in text.chars() {
println!("{}", count);
count += 1;
for i in 0..2 {
let _ = 123;
}
println!("{}", count);
}
// should not trigger the lint because the count is incremented multiple times
let text = "banana";
let mut count = 0;
for ch in text.chars() {
println!("{}", count);
count += 1;
for i in 0..2 {
count += 1;
}
println!("{}", count);
}
}
}
@ -96,30 +96,30 @@ mod issue_3308 {
let mut skips = 0;
let erasures = vec![];
for i in 0..10 {
println!("{}", skips);
while erasures.contains(&(i + skips)) {
skips += 1;
}
println!("{}", skips);
}
// should not trigger the lint because the count is incremented multiple times
let mut skips = 0;
for i in 0..10 {
println!("{}", skips);
let mut j = 0;
while j < 5 {
skips += 1;
j += 1;
}
println!("{}", skips);
}
// should not trigger the lint because the count is incremented multiple times
let mut skips = 0;
for i in 0..10 {
println!("{}", skips);
for j in 0..5 {
skips += 1;
}
println!("{}", skips);
}
}
}
@ -145,3 +145,16 @@ mod issue_4732 {
let _closure = || println!("index: {}", index);
}
}
mod issue_4677 {
pub fn test() {
let slice = &[1, 2, 3];
// should not trigger the lint because the count is used after incremented
let mut count = 0;
for _i in slice {
count += 1;
println!("{}", count);
}
}
}

View File

@ -40,4 +40,8 @@ fn main() {
// Ignore literals in macros
let _ = mac1!();
let _ = mac2!();
// Issue #6096
// Allow separating exponent with '_'
let _ = 1.025_011_10_E0;
}

View File

@ -40,4 +40,8 @@ fn main() {
// Ignore literals in macros
let _ = mac1!();
let _ = mac2!();
// Issue #6096
// Allow separating exponent with '_'
let _ = 1.025_011_10_E0;
}

View File

@ -0,0 +1,45 @@
// aux-build:proc_macro_attr.rs
#![warn(clippy::needless_arbitrary_self_type)]
#[macro_use]
extern crate proc_macro_attr;
mod issue_6089 {
// Check that we don't lint if the `self` parameter comes from expansion
macro_rules! test_from_expansion {
() => {
trait T1 {
fn test(self: &Self);
}
struct S1 {}
impl T1 for S1 {
fn test(self: &Self) {}
}
};
}
test_from_expansion!();
// If only the lifetime name comes from expansion we will lint, but the suggestion will have
// placeholders and will not be applied automatically, as we can't reliably know the original name.
// This specific case happened with async_trait.
trait T2 {
fn call_with_mut_self(&mut self);
}
struct S2 {}
// The method's signature will be expanded to:
// fn call_with_mut_self<'life0>(self: &'life0 mut Self) {}
#[rename_my_lifetimes]
impl T2 for S2 {
fn call_with_mut_self(self: &mut Self) {}
}
}
fn main() {}

View File

@ -0,0 +1,10 @@
error: the type of the `self` parameter does not need to be arbitrary
--> $DIR/needless_arbitrary_self_type_unfixable.rs:41:31
|
LL | fn call_with_mut_self(self: &mut Self) {}
| ^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'_ mut self`
|
= note: `-D clippy::needless-arbitrary-self-type` implied by `-D warnings`
error: aborting due to previous error

View File

@ -259,4 +259,102 @@ mod issue4291 {
}
}
mod issue2944 {
trait Foo {}
struct Bar {}
struct Baz<'a> {
bar: &'a Bar,
}
impl<'a> Foo for Baz<'a> {}
impl Bar {
fn baz<'a>(&'a self) -> impl Foo + 'a {
Baz { bar: self }
}
}
}
mod nested_elision_sites {
// issue #issue2944
// closure trait bounds subject to nested elision
// don't lint because they refer to outer lifetimes
fn trait_fn<'a>(i: &'a i32) -> impl Fn() -> &'a i32 {
move || i
}
fn trait_fn_mut<'a>(i: &'a i32) -> impl FnMut() -> &'a i32 {
move || i
}
fn trait_fn_once<'a>(i: &'a i32) -> impl FnOnce() -> &'a i32 {
move || i
}
// don't lint
fn impl_trait_in_input_position<'a>(f: impl Fn() -> &'a i32) -> &'a i32 {
f()
}
fn impl_trait_in_output_position<'a>(i: &'a i32) -> impl Fn() -> &'a i32 {
move || i
}
// lint
fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 {
f(i)
}
fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 {
f(i)
}
// don't lint
fn generics_not_elidable<'a, T: Fn() -> &'a i32>(f: T) -> &'a i32 {
f()
}
// lint
fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 {
f(i)
}
// don't lint
fn where_clause_not_elidable<'a, T>(f: T) -> &'a i32
where
T: Fn() -> &'a i32,
{
f()
}
// lint
fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32
where
T: Fn(&i32) -> &i32,
{
f(i)
}
// don't lint
fn pointer_fn_in_input_position<'a>(f: fn(&'a i32) -> &'a i32, i: &'a i32) -> &'a i32 {
f(i)
}
fn pointer_fn_in_output_position<'a>(_: &'a i32) -> fn(&'a i32) -> &'a i32 {
|i| i
}
// lint
fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 {
f(i)
}
// don't lint
fn nested_fn_pointer_1<'a>(_: &'a i32) -> fn(fn(&'a i32) -> &'a i32) -> i32 {
|f| 42
}
fn nested_fn_pointer_2<'a>(_: &'a i32) -> impl Fn(fn(&'a i32)) {
|f| ()
}
// lint
fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 {
|f| 42
}
fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) {
|f| ()
}
}
fn main() {}

View File

@ -102,5 +102,53 @@ error: explicit lifetimes given in parameter types where they could be elided (o
LL | fn needless_lt<'a>(_x: &'a u8) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 17 previous errors
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:271:9
|
LL | fn baz<'a>(&'a self) -> impl Foo + 'a {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:300:5
|
LL | fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:303:5
|
LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:312:5
|
LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:324:5
|
LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:339:5
|
LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:352:5
|
LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:355:5
|
LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 25 previous errors

View File

@ -82,6 +82,20 @@ fn main() {
for i in 1..3 {
println!("{}", arr[i]);
}
// Fix #5945
let mut vec = vec![1, 2, 3, 4];
for i in 0..vec.len() - 1 {
vec[i] += 1;
}
let mut vec = vec![1, 2, 3, 4];
for i in vec.len() - 3..vec.len() {
vec[i] += 1;
}
let mut vec = vec![1, 2, 3, 4];
for i in vec.len() - 3..vec.len() - 1 {
vec[i] += 1;
}
}
mod issue2277 {

View File

@ -58,6 +58,12 @@ fn or_fun_call() {
let without_default = Some(Foo);
without_default.unwrap_or_else(Foo::new);
let mut map = HashMap::<u64, String>::new();
map.entry(42).or_insert_with(String::new);
let mut btree = BTreeMap::<u64, String>::new();
btree.entry(42).or_insert_with(String::new);
let stringy = Some(String::from(""));
let _ = stringy.unwrap_or_else(|| "".to_owned());
@ -110,23 +116,4 @@ fn f() -> Option<()> {
Some(())
}
// Issue 5886 - const fn (with no arguments)
pub fn skip_const_fn_with_no_args() {
const fn foo() -> Option<i32> {
Some(42)
}
let _ = None.or(foo());
// See issue #5693.
let mut map = std::collections::HashMap::new();
map.insert(1, vec![1]);
map.entry(1).or_insert(vec![]);
let mut map = HashMap::<u64, String>::new();
map.entry(42).or_insert(String::new());
let mut btree = BTreeMap::<u64, String>::new();
btree.entry(42).or_insert(String::new());
}
fn main() {}

View File

@ -58,6 +58,12 @@ fn or_fun_call() {
let without_default = Some(Foo);
without_default.unwrap_or(Foo::new());
let mut map = HashMap::<u64, String>::new();
map.entry(42).or_insert(String::new());
let mut btree = BTreeMap::<u64, String>::new();
btree.entry(42).or_insert(String::new());
let stringy = Some(String::from(""));
let _ = stringy.unwrap_or("".to_owned());
@ -110,23 +116,4 @@ fn f() -> Option<()> {
Some(())
}
// Issue 5886 - const fn (with no arguments)
pub fn skip_const_fn_with_no_args() {
const fn foo() -> Option<i32> {
Some(42)
}
let _ = None.or(foo());
// See issue #5693.
let mut map = std::collections::HashMap::new();
map.insert(1, vec![1]);
map.entry(1).or_insert(vec![]);
let mut map = HashMap::<u64, String>::new();
map.entry(42).or_insert(String::new());
let mut btree = BTreeMap::<u64, String>::new();
btree.entry(42).or_insert(String::new());
}
fn main() {}

View File

@ -60,23 +60,35 @@ error: use of `unwrap_or` followed by a function call
LL | without_default.unwrap_or(Foo::new());
| ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)`
error: use of `or_insert` followed by a function call
--> $DIR/or_fun_call.rs:62:19
|
LL | map.entry(42).or_insert(String::new());
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)`
error: use of `or_insert` followed by a function call
--> $DIR/or_fun_call.rs:65:21
|
LL | btree.entry(42).or_insert(String::new());
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)`
error: use of `unwrap_or` followed by a function call
--> $DIR/or_fun_call.rs:62:21
--> $DIR/or_fun_call.rs:68:21
|
LL | let _ = stringy.unwrap_or("".to_owned());
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())`
error: use of `or` followed by a function call
--> $DIR/or_fun_call.rs:87:35
--> $DIR/or_fun_call.rs:93:35
|
LL | let _ = Some("a".to_string()).or(Some("b".to_string()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))`
error: use of `or` followed by a function call
--> $DIR/or_fun_call.rs:91:10
--> $DIR/or_fun_call.rs:97:10
|
LL | .or(Some(Bar(b, Duration::from_secs(2))));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))`
error: aborting due to 13 previous errors
error: aborting due to 15 previous errors

View File

@ -0,0 +1,12 @@
// compile-flags: --crate-name=build_script_build
#![warn(clippy::print_stdout)]
fn main() {
// Fix #6041
//
// The `print_stdout` lint shouldn't emit in `build.rs`
// as these methods are used for the build script.
println!("Hello");
print!("Hello");
}

View File

@ -1,5 +1,4 @@
#![warn(clippy::temporary_assignment)]
#![allow(const_item_mutation)]
use std::ops::{Deref, DerefMut};

View File

@ -1,5 +1,5 @@
error: assignment to temporary
--> $DIR/temporary_assignment.rs:48:5
--> $DIR/temporary_assignment.rs:47:5
|
LL | Struct { field: 0 }.field = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -7,7 +7,7 @@ LL | Struct { field: 0 }.field = 1;
= note: `-D clippy::temporary-assignment` implied by `-D warnings`
error: assignment to temporary
--> $DIR/temporary_assignment.rs:49:5
--> $DIR/temporary_assignment.rs:48:5
|
LL | / MultiStruct {
LL | | structure: Struct { field: 0 },
@ -17,13 +17,13 @@ LL | | .field = 1;
| |______________^
error: assignment to temporary
--> $DIR/temporary_assignment.rs:54:5
--> $DIR/temporary_assignment.rs:53:5
|
LL | ArrayStruct { array: [0] }.array[0] = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: assignment to temporary
--> $DIR/temporary_assignment.rs:55:5
--> $DIR/temporary_assignment.rs:54:5
|
LL | (0, 0).0 = 1;
| ^^^^^^^^^^^^

View File

@ -1,7 +1,11 @@
#[warn(clippy::zero_width_space)]
#[warn(clippy::invisible_characters)]
fn zero() {
print!("Here >< is a ZWS, and another");
print!("This\u{200B}is\u{200B}fine");
print!("Here >­< is a SHY, and ­another");
print!("This\u{ad}is\u{ad}fine");
print!("Here >< is a WJ, and another");
print!("This\u{2060}is\u{2060}fine");
}
#[warn(clippy::unicode_not_nfc)]

View File

@ -1,13 +1,25 @@
error: zero-width space detected
error: invisible character detected
--> $DIR/unicode.rs:3:12
|
LL | print!("Here >< is a ZWS, and another");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{200B}< is a ZWS, and /u{200B}another"`
|
= note: `-D clippy::zero-width-space` implied by `-D warnings`
= note: `-D clippy::invisible-characters` implied by `-D warnings`
error: invisible character detected
--> $DIR/unicode.rs:5:12
|
LL | print!("Here >­< is a SHY, and ­another");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{AD}< is a SHY, and /u{AD}another"`
error: invisible character detected
--> $DIR/unicode.rs:7:12
|
LL | print!("Here >< is a WJ, and another");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{2060}< is a WJ, and /u{2060}another"`
error: non-NFC Unicode sequence detected
--> $DIR/unicode.rs:9:12
--> $DIR/unicode.rs:13:12
|
LL | print!("̀àh?");
| ^^^^^ help: consider replacing the string with: `"̀àh?"`
@ -15,12 +27,12 @@ LL | print!("̀àh?");
= note: `-D clippy::unicode-not-nfc` implied by `-D warnings`
error: literal non-ASCII character detected
--> $DIR/unicode.rs:15:12
--> $DIR/unicode.rs:19:12
|
LL | print!("Üben!");
| ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"`
|
= note: `-D clippy::non-ascii-literal` implied by `-D warnings`
error: aborting due to 3 previous errors
error: aborting due to 5 previous errors

View File

@ -1,7 +0,0 @@
#!/bin/sh
CARGO_TARGET_DIR=$(pwd)/target/
export CARGO_TARGET_DIR
echo 'Deprecated! `util/dev` usage is deprecated, please use `cargo dev` instead.'
cd clippy_dev && cargo run -- "$@"