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

This commit is contained in:
flip1995 2021-01-15 10:02:28 +01:00
commit 9bd037d0b5
No known key found for this signature in database
GPG key ID: 1CA0DF2AF59D68A5
105 changed files with 2300 additions and 270 deletions

View file

@ -399,7 +399,7 @@ fn extract_clippy_lint(lint: &NestedMetaItem) -> Option<SymbolStr> {
if let Some(meta_item) = lint.meta_item();
if meta_item.path.segments.len() > 1;
if let tool_name = meta_item.path.segments[0].ident;
if tool_name.as_str() == "clippy";
if tool_name.name == sym::clippy;
let lint_name = meta_item.path.segments.last().unwrap().ident.name;
then {
return Some(lint_name.as_str());

View file

@ -23,9 +23,7 @@ use rustc_errors::Applicability;
declare_clippy_lint! {
/// **What it does:** Checks for nested `if` statements which can be collapsed
/// by `&&`-combining their conditions and for `else { if ... }` expressions
/// that
/// can be collapsed to `else if ...`.
/// by `&&`-combining their conditions.
///
/// **Why is this bad?** Each `if`-statement adds one level of nesting, which
/// makes code look more complex than it really is.
@ -40,7 +38,31 @@ declare_clippy_lint! {
/// }
/// }
///
/// // or
/// ```
///
/// Should be written:
///
/// ```rust.ignore
/// if x && y {
/// …
/// }
/// ```
pub COLLAPSIBLE_IF,
style,
"nested `if`s that can be collapsed (e.g., `if x { if y { ... } }`"
}
declare_clippy_lint! {
/// **What it does:** Checks for collapsible `else { if ... }` expressions
/// that can be collapsed to `else if ...`.
///
/// **Why is this bad?** Each `if`-statement adds one level of nesting, which
/// makes code look more complex than it really is.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust,ignore
///
/// if x {
/// …
@ -54,24 +76,18 @@ declare_clippy_lint! {
/// Should be written:
///
/// ```rust.ignore
/// if x && y {
/// …
/// }
///
/// // or
///
/// if x {
/// …
/// } else if y {
/// …
/// }
/// ```
pub COLLAPSIBLE_IF,
pub COLLAPSIBLE_ELSE_IF,
style,
"`if`s that can be collapsed (e.g., `if x { if y { ... } }` and `else { if x { ... } }`)"
"nested `else`-`if` expressions that can be collapsed (e.g., `else { if x { ... } }`)"
}
declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF]);
declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]);
impl EarlyLintPass for CollapsibleIf {
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
@ -112,7 +128,7 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) {
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
COLLAPSIBLE_IF,
COLLAPSIBLE_ELSE_IF,
block.span,
"this `else { if .. }` block can be collapsed",
"collapse nested if block",

View file

@ -13,7 +13,7 @@ declare_clippy_lint! {
/// repetitive
///
/// **Known problems:** The match statement may be slower due to the compiler
/// not inlining the call to cmp. See issue #5354
/// not inlining the call to cmp. See issue [#5354](https://github.com/rust-lang/rust-clippy/issues/5354)
///
/// **Example:**
/// ```rust,ignore

View file

@ -112,7 +112,8 @@ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste {
if let Some(&Expr {
kind: ExprKind::If(_, _, Some(ref else_expr)),
..
}) = get_parent_expr(cx, expr) {
}) = get_parent_expr(cx, expr)
{
if else_expr.hir_id == expr.hir_id {
return;
}

View file

@ -1,5 +1,5 @@
use crate::utils::{is_copy, match_path, paths, span_lint_and_note};
use rustc_hir::{Item, ItemKind, Impl};
use rustc_hir::{Impl, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

View file

@ -1,4 +1,6 @@
use crate::utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths, qpath_res, snippet};
use crate::utils::{
any_parent_is_automatically_derived, contains_name, match_def_path, paths, qpath_res, snippet_with_macro_callsite,
};
use crate::utils::{span_lint_and_note, span_lint_and_sugg};
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashSet;
@ -6,6 +8,7 @@ use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::{Ident, Symbol};
@ -118,6 +121,8 @@ impl LateLintPass<'_> for Default {
// only take `let ...` statements
if let StmtKind::Local(local) = stmt.kind;
if let Some(expr) = local.init;
if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
if !in_external_macro(cx.tcx.sess, expr.span);
// only take bindings to identifiers
if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind;
// only when assigning `... = Default::default()`
@ -187,7 +192,7 @@ impl LateLintPass<'_> for Default {
.into_iter()
.map(|(field, rhs)| {
// extract and store the assigned value for help message
let value_snippet = snippet(cx, rhs.span, "..");
let value_snippet = snippet_with_macro_callsite(cx, rhs.span, "..");
format!("{}: {}", field, value_snippet)
})
.collect::<Vec<String>>()

View file

@ -7,7 +7,7 @@ use if_chain::if_chain;
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, NestedVisitorMap, Visitor};
use rustc_hir::{
BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Item, ItemKind, Impl, TraitRef, UnsafeSource, Unsafety,
BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, TraitRef, UnsafeSource, Unsafety,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::map::Map;

View file

@ -8,8 +8,12 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// **What it does:** Checks for `enum`s with no variants.
///
/// As of this writing, the `never_type` is still a
/// nightly-only experimental API. Therefore, this lint is only triggered
/// if the `never_type` is enabled.
///
/// **Why is this bad?** If you want to introduce a type which
/// can't be instantiated, you should use `!` (the never type),
/// can't be instantiated, you should use `!` (the primitive type "never"),
/// or a wrapper around it, because `!` has more extensive
/// compiler support (type inference, etc...) and wrappers
/// around it are the conventional way to define an uninhabited type.
@ -40,6 +44,11 @@ declare_lint_pass!(EmptyEnum => [EMPTY_ENUM]);
impl<'tcx> LateLintPass<'tcx> for EmptyEnum {
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
// Only suggest the `never_type` if the feature is enabled
if !cx.tcx.features().never_type {
return;
}
let did = cx.tcx.hir().local_def_id(item.hir_id);
if let ItemKind::Enum(..) = item.kind {
let ty = cx.tcx.type_of(did);

View file

@ -1,15 +1,16 @@
use rustc_hir::intravisit;
use rustc_hir::{self, Body, FnDecl, HirId, HirIdSet, ItemKind, Impl, Node};
use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, Ty};
use rustc_middle::ty::{self, TraitRef, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span;
use rustc_span::symbol::kw;
use rustc_target::abi::LayoutOf;
use rustc_target::spec::abi::Abi;
use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
use crate::utils::span_lint;
use crate::utils::{contains_ty, span_lint};
#[derive(Copy, Clone)]
pub struct BoxedLocal {
@ -51,6 +52,7 @@ fn is_non_trait_box(ty: Ty<'_>) -> bool {
struct EscapeDelegate<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
set: HirIdSet,
trait_self_ty: Option<Ty<'a>>,
too_large_for_stack: u64,
}
@ -72,19 +74,34 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal {
}
}
// If the method is an impl for a trait, don't warn.
let parent_id = cx.tcx.hir().get_parent_item(hir_id);
let parent_node = cx.tcx.hir().find(parent_id);
let mut trait_self_ty = None;
if let Some(Node::Item(item)) = parent_node {
// If the method is an impl for a trait, don't warn.
if let ItemKind::Impl(Impl { of_trait: Some(_), .. }) = item.kind {
return;
}
// find `self` ty for this trait if relevant
if let ItemKind::Trait(_, _, _, _, items) = item.kind {
for trait_item in items {
if trait_item.id.hir_id == hir_id {
// be sure we have `self` parameter in this function
if let AssocItemKind::Fn { has_self: true } = trait_item.kind {
trait_self_ty =
Some(TraitRef::identity(cx.tcx, trait_item.id.hir_id.owner.to_def_id()).self_ty());
}
}
}
}
}
let mut v = EscapeDelegate {
cx,
set: HirIdSet::default(),
trait_self_ty,
too_large_for_stack: self.too_large_for_stack,
};
@ -153,10 +170,17 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
return;
}
// skip if there is a `self` parameter binding to a type
// that contains `Self` (i.e.: `self: Box<Self>`), see #4804
if let Some(trait_self_ty) = self.trait_self_ty {
if map.name(cmt.hir_id) == kw::SelfLower && contains_ty(cmt.place.ty(), trait_self_ty) {
return;
}
}
if is_non_trait_box(cmt.place.ty()) && !self.is_large_box(cmt.place.ty()) {
self.set.insert(cmt.hir_id);
}
return;
}
}
}

View file

@ -22,7 +22,7 @@ declare_clippy_lint! {
/// **Known problems:** If creating the closure inside the closure has a side-
/// effect then moving the closure creation out will change when that side-
/// effect runs.
/// See rust-lang/rust-clippy#1439 for more details.
/// See [#1439](https://github.com/rust-lang/rust-clippy/issues/1439) for more details.
///
/// **Example:**
/// ```rust,ignore
@ -45,8 +45,9 @@ declare_clippy_lint! {
///
/// **Why is this bad?** It's unnecessary to create the closure.
///
/// **Known problems:** rust-lang/rust-clippy#3071, rust-lang/rust-clippy#4002,
/// rust-lang/rust-clippy#3942
/// **Known problems:** [#3071](https://github.com/rust-lang/rust-clippy/issues/3071),
/// [#3942](https://github.com/rust-lang/rust-clippy/issues/3942),
/// [#4002](https://github.com/rust-lang/rust-clippy/issues/4002)
///
///
/// **Example:**

View file

@ -70,7 +70,7 @@ impl LateLintPass<'_> for FromOverInto {
span_lint_and_help(
cx,
FROM_OVER_INTO,
item.span,
cx.tcx.sess.source_map().guess_head_span(item.span),
"an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true",
None,
"consider to implement `From` instead",

View file

@ -92,13 +92,8 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
|db| {
cx.tcx.infer_ctxt().enter(|infcx| {
for FulfillmentError { obligation, .. } in send_errors {
infcx.maybe_note_obligation_cause_for_async_await(
db,
&obligation,
);
if let Trait(trait_pred, _) =
obligation.predicate.skip_binders()
{
infcx.maybe_note_obligation_cause_for_async_await(db, &obligation);
if let Trait(trait_pred, _) = obligation.predicate.skip_binders() {
db.note(&format!(
"`{}` doesn't implement `{}`",
trait_pred.self_ty(),

View file

@ -145,7 +145,7 @@ impl<'tcx, 'l> ArmVisitor<'tcx, 'l> {
fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
if_chain! {
if let ExprKind::MethodCall(path, _span, args, _) = &expr.kind;
if path.ident.to_string() == "lock";
if path.ident.as_str() == "lock";
let ty = cx.typeck_results().expr_ty(&args[0]);
if is_type_diagnostic_item(cx, ty, sym!(mutex_type));
then {

View file

@ -2,7 +2,7 @@
use crate::utils::{in_macro, span_lint_and_then};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::{def_id, Crate, Item, ItemKind, Impl};
use rustc_hir::{def_id, Crate, Impl, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span;

View file

@ -3,7 +3,7 @@ use rustc_ast::ast::LitKind;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
use rustc_hir::{AssocItemKind, BinOpKind, Expr, ExprKind, ImplItemRef, Item, ItemKind, Impl, TraitItemRef};
use rustc_hir::{AssocItemKind, BinOpKind, Expr, ExprKind, Impl, ImplItemRef, Item, ItemKind, TraitItemRef};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};

View file

@ -271,6 +271,7 @@ mod needless_borrow;
mod needless_borrowed_ref;
mod needless_continue;
mod needless_pass_by_value;
mod needless_question_mark;
mod needless_update;
mod neg_cmp_op_on_partial_ord;
mod neg_multiply;
@ -341,6 +342,7 @@ mod unwrap_in_result;
mod use_self;
mod useless_conversion;
mod vec;
mod vec_init_then_push;
mod vec_resize_to_zero;
mod verbose_file_reads;
mod wildcard_dependencies;
@ -524,6 +526,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&utils::internal_lints::OUTER_EXPN_EXPN_DATA,
#[cfg(feature = "internal-lints")]
&utils::internal_lints::PRODUCE_ICE,
#[cfg(feature = "internal-lints")]
&utils::internal_lints::UNNECESSARY_SYMBOL_STR,
&approx_const::APPROX_CONSTANT,
&arithmetic::FLOAT_ARITHMETIC,
&arithmetic::INTEGER_ARITHMETIC,
@ -556,6 +560,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&cargo_common_metadata::CARGO_COMMON_METADATA,
&checked_conversions::CHECKED_CONVERSIONS,
&cognitive_complexity::COGNITIVE_COMPLEXITY,
&collapsible_if::COLLAPSIBLE_ELSE_IF,
&collapsible_if::COLLAPSIBLE_IF,
&collapsible_match::COLLAPSIBLE_MATCH,
&comparison_chain::COMPARISON_CHAIN,
@ -799,6 +804,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE,
&needless_continue::NEEDLESS_CONTINUE,
&needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
&needless_question_mark::NEEDLESS_QUESTION_MARK,
&needless_update::NEEDLESS_UPDATE,
&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD,
&neg_multiply::NEG_MULTIPLY,
@ -908,6 +914,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&types::LET_UNIT_VALUE,
&types::LINKEDLIST,
&types::OPTION_OPTION,
&types::PTR_AS_PTR,
&types::RC_BUFFER,
&types::REDUNDANT_ALLOCATION,
&types::TYPE_COMPLEXITY,
@ -935,6 +942,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&use_self::USE_SELF,
&useless_conversion::USELESS_CONVERSION,
&vec::USELESS_VEC,
&vec_init_then_push::VEC_INIT_THEN_PUSH,
&vec_resize_to_zero::VEC_RESIZE_TO_ZERO,
&verbose_file_reads::VERBOSE_FILE_READS,
&wildcard_dependencies::WILDCARD_DEPENDENCIES,
@ -1019,6 +1027,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(move || box from_over_into::FromOverInto::new(msrv));
store.register_late_pass(move || box use_self::UseSelf::new(msrv));
store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv));
store.register_late_pass(move || box needless_question_mark::NeedlessQuestionMark::new(msrv));
store.register_late_pass(|| box size_of_in_element_count::SizeOfInElementCount);
store.register_late_pass(|| box map_clone::MapClone);
@ -1215,6 +1224,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| box strings::StrToString);
store.register_late_pass(|| box strings::StringToString);
store.register_late_pass(|| box zero_sized_map_values::ZeroSizedMapValues);
store.register_late_pass(|| box vec_init_then_push::VecInitThenPush::default());
store.register_late_pass(move || box types::PtrAsPtr::new(msrv));
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@ -1341,6 +1352,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&types::LET_UNIT_VALUE),
LintId::of(&types::LINKEDLIST),
LintId::of(&types::OPTION_OPTION),
LintId::of(&types::PTR_AS_PTR),
LintId::of(&unicode::NON_ASCII_LITERAL),
LintId::of(&unicode::UNICODE_NOT_NFC),
LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS),
@ -1362,6 +1374,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM),
LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA),
LintId::of(&utils::internal_lints::PRODUCE_ICE),
LintId::of(&utils::internal_lints::UNNECESSARY_SYMBOL_STR),
]);
store.register_group(true, "clippy::all", Some("clippy"), vec![
@ -1384,6 +1397,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&booleans::LOGIC_BUG),
LintId::of(&booleans::NONMINIMAL_BOOL),
LintId::of(&bytecount::NAIVE_BYTECOUNT),
LintId::of(&collapsible_if::COLLAPSIBLE_ELSE_IF),
LintId::of(&collapsible_if::COLLAPSIBLE_IF),
LintId::of(&collapsible_match::COLLAPSIBLE_MATCH),
LintId::of(&comparison_chain::COMPARISON_CHAIN),
@ -1545,6 +1559,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&needless_bool::BOOL_COMPARISON),
LintId::of(&needless_bool::NEEDLESS_BOOL),
LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
LintId::of(&needless_question_mark::NEEDLESS_QUESTION_MARK),
LintId::of(&needless_update::NEEDLESS_UPDATE),
LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
LintId::of(&neg_multiply::NEG_MULTIPLY),
@ -1636,6 +1651,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&unwrap::UNNECESSARY_UNWRAP),
LintId::of(&useless_conversion::USELESS_CONVERSION),
LintId::of(&vec::USELESS_VEC),
LintId::of(&vec_init_then_push::VEC_INIT_THEN_PUSH),
LintId::of(&vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
LintId::of(&write::PRINTLN_EMPTY_STRING),
LintId::of(&write::PRINT_LITERAL),
@ -1653,6 +1669,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS),
LintId::of(&blacklisted_name::BLACKLISTED_NAME),
LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
LintId::of(&collapsible_if::COLLAPSIBLE_ELSE_IF),
LintId::of(&collapsible_if::COLLAPSIBLE_IF),
LintId::of(&collapsible_match::COLLAPSIBLE_MATCH),
LintId::of(&comparison_chain::COMPARISON_CHAIN),
@ -1803,6 +1820,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&needless_bool::BOOL_COMPARISON),
LintId::of(&needless_bool::NEEDLESS_BOOL),
LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
LintId::of(&needless_question_mark::NEEDLESS_QUESTION_MARK),
LintId::of(&needless_update::NEEDLESS_UPDATE),
LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
LintId::of(&no_effect::NO_EFFECT),
@ -1935,6 +1953,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&types::BOX_VEC),
LintId::of(&types::REDUNDANT_ALLOCATION),
LintId::of(&vec::USELESS_VEC),
LintId::of(&vec_init_then_push::VEC_INIT_THEN_PUSH),
]);
store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![

View file

@ -218,7 +218,7 @@ declare_clippy_lint! {
/// **Why is this bad?** The `while let` loop is usually shorter and more
/// readable.
///
/// **Known problems:** Sometimes the wrong binding is displayed (#383).
/// **Known problems:** Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)).
///
/// **Example:**
/// ```rust,no_run

View file

@ -9,7 +9,7 @@ use rustc_hir::{
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Span;
use rustc_span::{sym, Span};
declare_clippy_lint! {
/// **What it does:** It checks for manual implementations of `async` functions.
@ -137,7 +137,7 @@ fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'t
if let Some(args) = segment.args;
if args.bindings.len() == 1;
let binding = &args.bindings[0];
if binding.ident.as_str() == "Output";
if binding.ident.name == sym::Output;
if let TypeBindingKind::Equality{ty: output} = binding.kind;
then {
return Some(output)

View file

@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone {
if_chain! {
if let hir::ExprKind::MethodCall(ref method, _, ref args, _) = e.kind;
if args.len() == 2;
if method.ident.as_str() == "map";
if method.ident.name == sym::map;
let ty = cx.typeck_results().expr_ty(&args[0]);
if is_type_diagnostic_item(cx, ty, sym::option_type) || match_trait_method(cx, e, &paths::ITERATOR);
if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].kind;

View file

@ -63,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for MapIdentity {
fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a [Expr<'a>]> {
if_chain! {
if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind;
if args.len() == 2 && method.ident.as_str() == "map";
if args.len() == 2 && method.ident.name == sym::map;
let caller_ty = cx.typeck_results().expr_ty(&args[0]);
if match_trait_method(cx, expr, &paths::ITERATOR)
|| is_type_diagnostic_item(cx, caller_ty, sym::result_type)

View file

@ -3096,7 +3096,7 @@ fn lint_flat_map_identity<'tcx>(
if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = body.value.kind;
if path.segments.len() == 1;
if path.segments[0].ident.as_str() == binding_ident.as_str();
if path.segments[0].ident.name == binding_ident.name;
then {
apply_lint("called `flat_map(|x| x)` on an `Iterator`");

View file

@ -89,9 +89,9 @@ fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Cons
if let [obj, _] = args;
if cx.typeck_results().expr_ty(obj).is_floating_point() || match_trait_method(cx, expr, &paths::ORD);
then {
if path.ident.as_str() == sym!(max).as_str() {
if path.ident.name == sym!(max) {
fetch_const(cx, args, MinMax::Max)
} else if path.ident.as_str() == sym!(min).as_str() {
} else if path.ident.name == sym!(min) {
fetch_const(cx, args, MinMax::Min)
} else {
None

View file

@ -63,7 +63,7 @@ impl MissingDoc {
if let Some(meta) = list.get(0);
if let Some(name) = meta.ident();
then {
name.as_str() == "include"
name.name == sym::include
} else {
false
}

View file

@ -90,10 +90,6 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> {
self.found = true;
return;
},
ExprKind::If(..) => {
self.found = true;
return;
},
ExprKind::Path(_) => {
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
if adj

View file

@ -3,9 +3,7 @@
//! This lint is **warn** by default
use crate::utils::sugg::Sugg;
use crate::utils::{
is_expn_of, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg,
};
use crate::utils::{is_expn_of, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg};
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};

View file

@ -2,7 +2,7 @@
//!
//! This lint is **warn** by default
use crate::utils::{snippet_opt, span_lint_and_then};
use crate::utils::{is_automatically_derived, snippet_opt, span_lint_and_then};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BindingAnnotation, BorrowKind, Expr, ExprKind, HirId, Item, Mutability, Pat, PatKind};
@ -10,7 +10,6 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::sym;
declare_clippy_lint! {
/// **What it does:** Checks for address of operations (`&`) that are going to
@ -116,7 +115,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow {
}
fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'_>) {
if item.attrs.iter().any(|a| a.has_name(sym::automatically_derived)) {
if is_automatically_derived(item.attrs) {
debug_assert!(self.derived_item.is_none());
self.derived_item = Some(item.hir_id);
}

View file

@ -8,11 +8,12 @@ use rustc_ast::ast::Attribute;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir::intravisit::FnKind;
use rustc_hir::{BindingAnnotation, Body, FnDecl, GenericArg, HirId, ItemKind, Impl, Node, PatKind, QPath, TyKind};
use rustc_hir::{BindingAnnotation, Body, FnDecl, GenericArg, HirId, Impl, ItemKind, Node, PatKind, QPath, TyKind};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, TypeFoldable};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::kw;
use rustc_span::{sym, Span};
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits;
@ -153,7 +154,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
// Ignore `self`s.
if idx == 0 {
if let PatKind::Binding(.., ident, _) = arg.pat.kind {
if ident.as_str() == "self" {
if ident.name == kw::SelfLower {
continue;
}
}

View file

@ -0,0 +1,232 @@
use rustc_errors::Applicability;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::DefIdTree;
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::sym;
use crate::utils;
use if_chain::if_chain;
declare_clippy_lint! {
/// **What it does:**
/// Suggests alternatives for useless applications of `?` in terminating expressions
///
/// **Why is this bad?** There's no reason to use `?` to short-circuit when execution of the body will end there anyway.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// struct TO {
/// magic: Option<usize>,
/// }
///
/// fn f(to: TO) -> Option<usize> {
/// Some(to.magic?)
/// }
///
/// struct TR {
/// magic: Result<usize, bool>,
/// }
///
/// fn g(tr: Result<TR, bool>) -> Result<usize, bool> {
/// tr.and_then(|t| Ok(t.magic?))
/// }
///
/// ```
/// Use instead:
/// ```rust
/// struct TO {
/// magic: Option<usize>,
/// }
///
/// fn f(to: TO) -> Option<usize> {
/// to.magic
/// }
///
/// struct TR {
/// magic: Result<usize, bool>,
/// }
///
/// fn g(tr: Result<TR, bool>) -> Result<usize, bool> {
/// tr.and_then(|t| t.magic)
/// }
/// ```
pub NEEDLESS_QUESTION_MARK,
complexity,
"Suggest `value.inner_option` instead of `Some(value.inner_option?)`. The same goes for `Result<T, E>`."
}
const NEEDLESS_QUESTION_MARK_RESULT_MSRV: RustcVersion = RustcVersion::new(1, 13, 0);
const NEEDLESS_QUESTION_MARK_OPTION_MSRV: RustcVersion = RustcVersion::new(1, 22, 0);
pub struct NeedlessQuestionMark {
msrv: Option<RustcVersion>,
}
impl NeedlessQuestionMark {
#[must_use]
pub fn new(msrv: Option<RustcVersion>) -> Self {
Self { msrv }
}
}
impl_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]);
#[derive(Debug)]
enum SomeOkCall<'a> {
SomeCall(&'a Expr<'a>, &'a Expr<'a>),
OkCall(&'a Expr<'a>, &'a Expr<'a>),
}
impl LateLintPass<'_> for NeedlessQuestionMark {
/*
* The question mark operator is compatible with both Result<T, E> and Option<T>,
* from Rust 1.13 and 1.22 respectively.
*/
/*
* What do we match:
* Expressions that look like this:
* Some(option?), Ok(result?)
*
* Where do we match:
* Last expression of a body
* Return statement
* A body's value (single line closure)
*
* What do we not match:
* Implicit calls to `from(..)` on the error value
*/
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
let e = match &expr.kind {
ExprKind::Ret(Some(e)) => e,
_ => return,
};
if let Some(ok_some_call) = is_some_or_ok_call(self, cx, e) {
emit_lint(cx, &ok_some_call);
}
}
fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
// Function / Closure block
let expr_opt = if let ExprKind::Block(block, _) = &body.value.kind {
block.expr
} else {
// Single line closure
Some(&body.value)
};
if_chain! {
if let Some(expr) = expr_opt;
if let Some(ok_some_call) = is_some_or_ok_call(self, cx, expr);
then {
emit_lint(cx, &ok_some_call);
}
};
}
extract_msrv_attr!(LateContext);
}
fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) {
let (entire_expr, inner_expr) = match expr {
SomeOkCall::OkCall(outer, inner) | SomeOkCall::SomeCall(outer, inner) => (outer, inner),
};
utils::span_lint_and_sugg(
cx,
NEEDLESS_QUESTION_MARK,
entire_expr.span,
"Question mark operator is useless here",
"try",
format!("{}", utils::snippet(cx, inner_expr.span, r#""...""#)),
Applicability::MachineApplicable,
);
}
fn is_some_or_ok_call<'a>(
nqml: &NeedlessQuestionMark,
cx: &'a LateContext<'_>,
expr: &'a Expr<'_>,
) -> Option<SomeOkCall<'a>> {
if_chain! {
// Check outer expression matches CALL_IDENT(ARGUMENT) format
if let ExprKind::Call(path, args) = &expr.kind;
if let ExprKind::Path(QPath::Resolved(None, path)) = &path.kind;
if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res);
// Extract inner expression from ARGUMENT
if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind;
if let ExprKind::Call(called, args) = &inner_expr_with_q.kind;
if args.len() == 1;
if let ExprKind::Path(QPath::LangItem(LangItem::TryIntoResult, _)) = &called.kind;
then {
// Extract inner expr type from match argument generated by
// question mark operator
let inner_expr = &args[0];
let inner_ty = cx.typeck_results().expr_ty(inner_expr);
let outer_ty = cx.typeck_results().expr_ty(expr);
// Check if outer and inner type are Option
let outer_is_some = utils::is_type_diagnostic_item(cx, outer_ty, sym::option_type);
let inner_is_some = utils::is_type_diagnostic_item(cx, inner_ty, sym::option_type);
// Check for Option MSRV
let meets_option_msrv = utils::meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_OPTION_MSRV);
if outer_is_some && inner_is_some && meets_option_msrv {
return Some(SomeOkCall::SomeCall(expr, inner_expr));
}
// Check if outer and inner type are Result
let outer_is_result = utils::is_type_diagnostic_item(cx, outer_ty, sym::result_type);
let inner_is_result = utils::is_type_diagnostic_item(cx, inner_ty, sym::result_type);
// Additional check: if the error type of the Result can be converted
// via the From trait, then don't match
let does_not_call_from = !has_implicit_error_from(cx, expr, inner_expr);
// Must meet Result MSRV
let meets_result_msrv = utils::meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_RESULT_MSRV);
if outer_is_result && inner_is_result && does_not_call_from && meets_result_msrv {
return Some(SomeOkCall::OkCall(expr, inner_expr));
}
}
}
None
}
fn has_implicit_error_from(cx: &LateContext<'_>, entire_expr: &Expr<'_>, inner_result_expr: &Expr<'_>) -> bool {
return cx.typeck_results().expr_ty(entire_expr) != cx.typeck_results().expr_ty(inner_result_expr);
}
fn is_ok_ctor(cx: &LateContext<'_>, res: Res) -> bool {
if let Some(ok_id) = cx.tcx.lang_items().result_ok_variant() {
if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
if let Some(variant_id) = cx.tcx.parent(id) {
return variant_id == ok_id;
}
}
}
false
}
fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool {
if let Some(some_id) = cx.tcx.lang_items().option_some_variant() {
if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
if let Some(variant_id) = cx.tcx.parent(id) {
return variant_id == some_id;
}
}
}
false
}

View file

@ -66,7 +66,7 @@ declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]);
/// Returns true iff the given expression is the result of calling `Result::ok`
fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
if let ExprKind::MethodCall(ref path, _, &[ref receiver], _) = &expr.kind {
path.ident.name.to_ident_string() == "ok"
path.ident.name.as_str() == "ok"
&& is_type_diagnostic_item(cx, &cx.typeck_results().expr_ty(&receiver), sym::result_type)
} else {
false
@ -110,7 +110,7 @@ fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> {
fn should_wrap_in_braces(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| {
let mut should_wrap = false;
if let Some(Expr {
kind:
ExprKind::Match(
@ -124,7 +124,11 @@ fn should_wrap_in_braces(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
}) = parent.expr
{
should_wrap = expr.hir_id == arms[1].body.hir_id;
} else if let Some(Expr { kind: ExprKind::If(_, _, Some(else_clause)), .. }) = parent.expr {
} else if let Some(Expr {
kind: ExprKind::If(_, _, Some(else_clause)),
..
}) = parent.expr
{
should_wrap = expr.hir_id == else_clause.hir_id;
}

View file

@ -1,6 +1,6 @@
use crate::utils::{is_automatically_derived, span_lint_hir};
use if_chain::if_chain;
use rustc_hir::{Item, ItemKind, Impl};
use rustc_hir::{Impl, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;

View file

@ -6,7 +6,7 @@ use rustc_ast::attr;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{BindingAnnotation, Body, FnDecl, HirId, ItemKind, MutTy, Mutability, Node, PatKind, Impl};
use rustc_hir::{BindingAnnotation, Body, FnDecl, HirId, Impl, ItemKind, MutTy, Mutability, Node, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass};
@ -63,7 +63,7 @@ declare_clippy_lint! {
///
/// **Why is this bad?** Arguments passed by value might result in an unnecessary
/// shallow copy, taking up more space in the stack and requiring a call to
/// `memcpy`, which which can be expensive.
/// `memcpy`, which can be expensive.
///
/// **Example:**
///

View file

@ -8,8 +8,8 @@ use crate::utils::{
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{
BinOpKind, BodyId, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Impl,
Lifetime, MutTy, Mutability, Node, PathSegment, QPath, TraitFn, TraitItem, TraitItemKind, Ty, TyKind,
BinOpKind, BodyId, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, HirId, Impl, ImplItem, ImplItemKind, Item,
ItemKind, Lifetime, MutTy, Mutability, Node, PathSegment, QPath, TraitFn, TraitItem, TraitItemKind, Ty, TyKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;

View file

@ -13,7 +13,7 @@ declare_clippy_lint! {
/// **Why is this bad?** Since `&` is Copy, it's useless to have a
/// reference on `Option<&T>`.
///
/// **Known problems:** It may be irrevelent to use this lint on
/// **Known problems:** It may be irrelevant to use this lint on
/// public API code as it will make a breaking change to apply it.
///
/// **Example:**

View file

@ -202,7 +202,7 @@ fn check_final_expr<'tcx>(
check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block);
}
},
| MatchSource::IfLetDesugar {
MatchSource::IfLetDesugar {
contains_else_clause: true,
} => {
if let ExprKind::Block(ref ifblock, _) = arms[0].body.kind {

View file

@ -1,5 +1,5 @@
use crate::utils::{get_trait_def_id, paths, span_lint};
use rustc_hir::{Item, ItemKind, Impl};
use rustc_hir::{Impl, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

View file

@ -396,5 +396,5 @@ fn is_self_shadow(name: Symbol, expr: &Expr<'_>) -> bool {
}
fn path_eq_name(name: Symbol, path: &Path<'_>) -> bool {
!path.is_global() && path.segments.len() == 1 && path.segments[0].ident.as_str() == name.as_str()
!path.is_global() && path.segments.len() == 1 && path.segments[0].ident.name == name
}

View file

@ -91,7 +91,7 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
if let ExprKind::Path(QPath::Resolved(None, ref rhs2)) = rhs2.kind;
if rhs2.segments.len() == 1;
if ident.as_str() == rhs2.segments[0].ident.as_str();
if ident.name == rhs2.segments[0].ident.name;
if eq_expr_value(cx, tmp_init, lhs1);
if eq_expr_value(cx, rhs1, lhs2);
then {

View file

@ -1,7 +1,7 @@
use crate::utils::{match_def_path, match_trait_method, paths, qpath_res, span_lint};
use if_chain::if_chain;
use rustc_hir::def::Res;
use rustc_hir::{Expr, ExprKind, HirId, ImplItem, ImplItemKind, Item, ItemKind, Impl};
use rustc_hir::{Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};

View file

@ -8,7 +8,6 @@ use if_chain::if_chain;
use rustc_ast::{FloatTy, IntTy, LitFloatType, LitIntType, LitKind, UintTy};
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor};
use rustc_hir::{
BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericBounds, GenericParamKind, HirId,
@ -19,7 +18,8 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::hir::map::Map;
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::TypeFoldable;
use rustc_middle::ty::{self, InferTy, Ty, TyCtxt, TyS, TypeckResults};
use rustc_middle::ty::{self, InferTy, Ty, TyCtxt, TyS, TypeAndMut, TypeckResults};
use rustc_semver::RustcVersion;
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::source_map::Span;
@ -30,11 +30,13 @@ use rustc_typeck::hir_ty_to_ty;
use crate::consts::{constant, Constant};
use crate::utils::paths;
use crate::utils::sugg::Sugg;
use crate::utils::{
clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_type_diagnostic_item,
last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral,
qpath_res, reindent_multiline, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite,
span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext,
clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_hir_ty_cfg_dependant,
is_type_diagnostic_item, last_path_segment, match_def_path, match_path, meets_msrv, method_chain_args,
multispan_sugg, numeric_literal::NumericLiteral, qpath_res, reindent_multiline, sext, snippet, snippet_opt,
snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg,
span_lint_and_then, unsext,
};
declare_clippy_lint! {
@ -73,7 +75,7 @@ declare_clippy_lint! {
/// **Why is this bad?** `Vec` already keeps its contents in a separate area on
/// the heap. So if you `Box` its contents, you just add another level of indirection.
///
/// **Known problems:** Vec<Box<T: Sized>> makes sense if T is a large type (see #3530,
/// **Known problems:** Vec<Box<T: Sized>> makes sense if T is a large type (see [#3530](https://github.com/rust-lang/rust-clippy/issues/3530),
/// 1st comment).
///
/// **Example:**
@ -1279,8 +1281,8 @@ declare_clippy_lint! {
}
declare_clippy_lint! {
/// **What it does:** Checks for casts from a less-strictly-aligned pointer to a
/// more-strictly-aligned pointer
/// **What it does:** Checks for casts, using `as` or `pointer::cast`,
/// from a less-strictly-aligned pointer to a more-strictly-aligned pointer
///
/// **Why is this bad?** Dereferencing the resulting pointer may be undefined
/// behavior.
@ -1293,6 +1295,9 @@ declare_clippy_lint! {
/// ```rust
/// let _ = (&1u8 as *const u8) as *const u16;
/// let _ = (&mut 1u8 as *mut u8) as *mut u16;
///
/// (&1u8 as *const u8).cast::<u16>();
/// (&mut 1u8 as *mut u8).cast::<u16>();
/// ```
pub CAST_PTR_ALIGNMENT,
pedantic,
@ -1634,12 +1639,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
return;
}
if let ExprKind::Cast(ref ex, cast_to) = expr.kind {
if let TyKind::Path(QPath::Resolved(_, path)) = cast_to.kind {
if let Res::Def(_, def_id) = path.res {
if cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr) {
return;
}
}
if is_hir_ty_cfg_dependant(cx, cast_to) {
return;
}
let (cast_from, cast_to) = (cx.typeck_results().expr_ty(ex), cx.typeck_results().expr_ty(expr));
lint_fn_to_numeric_cast(cx, expr, ex, cast_from, cast_to);
@ -1689,6 +1690,19 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
}
lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
} else if let ExprKind::MethodCall(method_path, _, args, _) = expr.kind {
if_chain! {
if method_path.ident.name == sym!(cast);
if let Some(generic_args) = method_path.args;
if let [GenericArg::Type(cast_to)] = generic_args.args;
// There probably is no obvious reason to do this, just to be consistent with `as` cases.
if !is_hir_ty_cfg_dependant(cx, cast_to);
then {
let (cast_from, cast_to) =
(cx.typeck_results().expr_ty(&args[0]), cx.typeck_results().expr_ty(expr));
lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
}
}
}
}
}
@ -2873,3 +2887,93 @@ impl<'tcx> LateLintPass<'tcx> for RefToMut {
}
}
}
const PTR_AS_PTR_MSRV: RustcVersion = RustcVersion::new(1, 38, 0);
declare_clippy_lint! {
/// **What it does:**
/// Checks for `as` casts between raw pointers without changing its mutability,
/// namely `*const T` to `*const U` and `*mut T` to `*mut U`.
///
/// **Why is this bad?**
/// Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because
/// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// let ptr: *const u32 = &42_u32;
/// let mut_ptr: *mut u32 = &mut 42_u32;
/// let _ = ptr as *const i32;
/// let _ = mut_ptr as *mut i32;
/// ```
/// Use instead:
/// ```rust
/// let ptr: *const u32 = &42_u32;
/// let mut_ptr: *mut u32 = &mut 42_u32;
/// let _ = ptr.cast::<i32>();
/// let _ = mut_ptr.cast::<i32>();
/// ```
pub PTR_AS_PTR,
pedantic,
"casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`"
}
pub struct PtrAsPtr {
msrv: Option<RustcVersion>,
}
impl PtrAsPtr {
#[must_use]
pub fn new(msrv: Option<RustcVersion>) -> Self {
Self { msrv }
}
}
impl_lint_pass!(PtrAsPtr => [PTR_AS_PTR]);
impl<'tcx> LateLintPass<'tcx> for PtrAsPtr {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if !meets_msrv(self.msrv.as_ref(), &PTR_AS_PTR_MSRV) {
return;
}
if expr.span.from_expansion() {
return;
}
if_chain! {
if let ExprKind::Cast(cast_expr, cast_to_hir_ty) = expr.kind;
let (cast_from, cast_to) = (cx.typeck_results().expr_ty(cast_expr), cx.typeck_results().expr_ty(expr));
if let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, .. }) = cast_from.kind();
if let ty::RawPtr(TypeAndMut { ty: to_pointee_ty, mutbl: to_mutbl }) = cast_to.kind();
if matches!((from_mutbl, to_mutbl),
(Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut));
// The `U` in `pointer::cast` have to be `Sized`
// as explained here: https://github.com/rust-lang/rust/issues/60602.
if to_pointee_ty.is_sized(cx.tcx.at(expr.span), cx.param_env);
then {
let mut applicability = Applicability::MachineApplicable;
let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut applicability);
let turbofish = match &cast_to_hir_ty.kind {
TyKind::Infer => Cow::Borrowed(""),
TyKind::Ptr(mut_ty) if matches!(mut_ty.ty.kind, TyKind::Infer) => Cow::Borrowed(""),
_ => Cow::Owned(format!("::<{}>", to_pointee_ty)),
};
span_lint_and_sugg(
cx,
PTR_AS_PTR,
expr.span,
"`as` casting between raw pointers without changing its mutability",
"try `pointer::cast`, a safer alternative",
format!("{}.cast{}()", cast_expr_sugg.maybe_par(), turbofish),
applicability,
);
}
}
}
extract_msrv_attr!(LateContext);
}

View file

@ -183,7 +183,7 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. }
] = &closure_body.params;
if let ExprKind::MethodCall(method_path, _, [ref left_expr, ref right_expr], _) = &closure_body.value.kind;
if method_path.ident.name.to_ident_string() == "cmp";
if method_path.ident.name == sym::cmp;
then {
let (closure_body, closure_arg, reverse) = if mirrored_exprs(
&cx,

View file

@ -5,7 +5,7 @@ use crate::utils::{
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, ExprKind, FnDecl, HirId, ItemKind, Impl, Node};
use rustc_hir::{Body, ExprKind, FnDecl, HirId, Impl, ItemKind, Node};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::subst::GenericArgKind;
use rustc_session::{declare_lint_pass, declare_tool_lint};

View file

@ -1,7 +1,7 @@
use if_chain::if_chain;
use rustc_hir::def::Res;
use rustc_hir::intravisit::{walk_path, NestedVisitorMap, Visitor};
use rustc_hir::{HirId, ImplItem, ImplItemKind, ItemKind, Impl, Path};
use rustc_hir::{HirId, Impl, ImplItem, ImplItemKind, ItemKind, Path};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::map::Map;
use rustc_session::{declare_lint_pass, declare_tool_lint};

View file

@ -1,6 +1,5 @@
use crate::utils::{
differing_macro_contexts, is_type_diagnostic_item, span_lint_and_then,
usage::is_potentially_mutated,
differing_macro_contexts, is_type_diagnostic_item, span_lint_and_then, usage::is_potentially_mutated,
};
use if_chain::if_chain;
use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor};

View file

@ -28,8 +28,8 @@ declare_clippy_lint! {
/// feels inconsistent.
///
/// **Known problems:**
/// - False positive when using associated types (#2843)
/// - False positives in some situations when using generics (#3410)
/// - False positive when using associated types ([#2843](https://github.com/rust-lang/rust-clippy/issues/2843))
/// - False positives in some situations when using generics ([#3410](https://github.com/rust-lang/rust-clippy/issues/3410))
///
/// **Example:**
/// ```rust

View file

@ -80,10 +80,10 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
);
}
}
if match_trait_method(cx, e, &paths::INTO_ITERATOR) && &*name.ident.as_str() == "into_iter" {
if match_trait_method(cx, e, &paths::INTO_ITERATOR) && name.ident.name == sym::into_iter {
if let Some(parent_expr) = get_parent_expr(cx, e) {
if let ExprKind::MethodCall(ref parent_name, ..) = parent_expr.kind {
if &*parent_name.ident.as_str() != "into_iter" {
if parent_name.ident.name != sym::into_iter {
return;
}
}

View file

@ -1,6 +1,7 @@
use rustc_ast::ast;
use rustc_errors::Applicability;
use rustc_session::Session;
use rustc_span::sym;
use std::str::FromStr;
/// Deprecation status of attributes known by Clippy.
@ -64,11 +65,11 @@ pub fn get_attr<'a>(
return false;
};
let attr_segments = &attr.path.segments;
if attr_segments.len() == 2 && attr_segments[0].ident.to_string() == "clippy" {
if attr_segments.len() == 2 && attr_segments[0].ident.name == sym::clippy {
BUILTIN_ATTRIBUTES
.iter()
.find_map(|(builtin_name, deprecation_status)| {
if *builtin_name == attr_segments[1].ident.to_string() {
.find_map(|&(builtin_name, ref deprecation_status)| {
if attr_segments[1].ident.name.as_str() == builtin_name {
Some(deprecation_status)
} else {
None
@ -99,7 +100,7 @@ pub fn get_attr<'a>(
},
DeprecationStatus::None => {
diag.cancel();
attr_segments[1].ident.to_string() == name
attr_segments[1].ident.name.as_str() == name
},
}
},

View file

@ -86,7 +86,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
lb == rb && l_mut == r_mut && self.eq_expr(le, re)
},
(&ExprKind::Continue(li), &ExprKind::Continue(ri)) => {
both(&li.label, &ri.label, |l, r| l.ident.as_str() == r.ident.as_str())
both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name)
},
(&ExprKind::Assign(ref ll, ref lr, _), &ExprKind::Assign(ref rl, ref rr, _)) => {
self.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
@ -102,7 +102,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
})
},
(&ExprKind::Break(li, ref le), &ExprKind::Break(ri, ref re)) => {
both(&li.label, &ri.label, |l, r| l.ident.as_str() == r.ident.as_str())
both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name)
&& both(le, re, |l, r| self.eq_expr(l, r))
},
(&ExprKind::Box(ref l), &ExprKind::Box(ref r)) => self.eq_expr(l, r),
@ -124,7 +124,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
},
(&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node,
(&ExprKind::Loop(ref lb, ref ll, ref lls), &ExprKind::Loop(ref rb, ref rl, ref rls)) => {
lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.as_str() == r.ident.as_str())
lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name)
},
(&ExprKind::Match(ref le, ref la, ref ls), &ExprKind::Match(ref re, ref ra, ref rs)) => {
ls == rs
@ -191,7 +191,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
pub fn eq_fieldpat(&mut self, left: &FieldPat<'_>, right: &FieldPat<'_>) -> bool {
let (FieldPat { ident: li, pat: lp, .. }, FieldPat { ident: ri, pat: rp, .. }) = (&left, &right);
li.name.as_str() == ri.name.as_str() && self.eq_pat(lp, rp)
li.name == ri.name && self.eq_pat(lp, rp)
}
/// Checks whether two patterns are the same.
@ -205,7 +205,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs
},
(&PatKind::Binding(ref lb, .., ref li, ref lp), &PatKind::Binding(ref rb, .., ref ri, ref rp)) => {
lb == rb && li.name.as_str() == ri.name.as_str() && both(lp, rp, |l, r| self.eq_pat(l, r))
lb == rb && li.name == ri.name && both(lp, rp, |l, r| self.eq_pat(l, r))
},
(&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r),
(&PatKind::Lit(ref l), &PatKind::Lit(ref r)) => self.eq_expr(l, r),
@ -266,8 +266,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
// The == of idents doesn't work with different contexts,
// we have to be explicit about hygiene
left.ident.as_str() == right.ident.as_str()
&& both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r))
left.ident.name == right.ident.name && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r))
}
pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {

View file

@ -10,9 +10,12 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::hir_id::CRATE_HIR_ID;
use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind};
use rustc_hir::{
BinOpKind, Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind, UnOp,
};
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
use rustc_middle::hir::map::Map;
use rustc_middle::mir::interpret::ConstValue;
@ -272,6 +275,28 @@ declare_clippy_lint! {
"interning a symbol that is pre-interned and defined as a constant"
}
declare_clippy_lint! {
/// **What it does:** Checks for unnecessary conversion from Symbol to a string.
///
/// **Why is this bad?** It's faster use symbols directly intead of strings.
///
/// **Known problems:** None.
///
/// **Example:**
/// Bad:
/// ```rust,ignore
/// symbol.as_str() == "clippy";
/// ```
///
/// Good:
/// ```rust,ignore
/// symbol == sym::clippy;
/// ```
pub UNNECESSARY_SYMBOL_STR,
internal,
"unnecessary conversion between Symbol and string"
}
declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
impl EarlyLintPass for ClippyLintsInternal {
@ -868,11 +893,11 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
#[derive(Default)]
pub struct InterningDefinedSymbol {
// Maps the symbol value to the constant name.
symbol_map: FxHashMap<u32, String>,
// Maps the symbol value to the constant DefId.
symbol_map: FxHashMap<u32, DefId>,
}
impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL]);
impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]);
impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
@ -880,16 +905,18 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
return;
}
if let Some(Res::Def(_, def_id)) = path_to_res(cx, &paths::SYM_MODULE) {
for item in cx.tcx.item_children(def_id).iter() {
if_chain! {
if let Res::Def(DefKind::Const, item_def_id) = item.res;
let ty = cx.tcx.type_of(item_def_id);
if match_type(cx, ty, &paths::SYMBOL);
if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id);
if let Ok(value) = value.to_u32();
then {
self.symbol_map.insert(value, item.ident.to_string());
for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] {
if let Some(Res::Def(_, def_id)) = path_to_res(cx, module) {
for item in cx.tcx.item_children(def_id).iter() {
if_chain! {
if let Res::Def(DefKind::Const, item_def_id) = item.res;
let ty = cx.tcx.type_of(item_def_id);
if match_type(cx, ty, &paths::SYMBOL);
if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id);
if let Ok(value) = value.to_u32();
then {
self.symbol_map.insert(value, item_def_id);
}
}
}
}
@ -903,7 +930,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN);
if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg);
let value = Symbol::intern(&arg).as_u32();
if let Some(symbol_const) = self.symbol_map.get(&value);
if let Some(&def_id) = self.symbol_map.get(&value);
then {
span_lint_and_sugg(
cx,
@ -911,10 +938,135 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
is_expn_of(expr.span, "sym").unwrap_or(expr.span),
"interning a defined symbol",
"try",
format!("rustc_span::symbol::sym::{}", symbol_const),
cx.tcx.def_path_str(def_id),
Applicability::MachineApplicable,
);
}
}
if let ExprKind::Binary(op, left, right) = expr.kind {
if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) {
let data = [
(left, self.symbol_str_expr(left, cx)),
(right, self.symbol_str_expr(right, cx)),
];
match data {
// both operands are a symbol string
[(_, Some(left)), (_, Some(right))] => {
span_lint_and_sugg(
cx,
UNNECESSARY_SYMBOL_STR,
expr.span,
"unnecessary `Symbol` to string conversion",
"try",
format!(
"{} {} {}",
left.as_symbol_snippet(cx),
op.node.as_str(),
right.as_symbol_snippet(cx),
),
Applicability::MachineApplicable,
);
},
// one of the operands is a symbol string
[(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => {
// creating an owned string for comparison
if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) {
span_lint_and_sugg(
cx,
UNNECESSARY_SYMBOL_STR,
expr.span,
"unnecessary string allocation",
"try",
format!("{}.as_str()", symbol.as_symbol_snippet(cx)),
Applicability::MachineApplicable,
);
}
},
// nothing found
[(_, None), (_, None)] => {},
}
}
}
}
}
impl InterningDefinedSymbol {
fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option<SymbolStrExpr<'tcx>> {
static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD];
static SYMBOL_STR_PATHS: &[&[&str]] = &[
&paths::SYMBOL_AS_STR,
&paths::SYMBOL_TO_IDENT_STRING,
&paths::TO_STRING_METHOD,
];
// SymbolStr might be de-referenced: `&*symbol.as_str()`
let call = if_chain! {
if let ExprKind::AddrOf(_, _, e) = expr.kind;
if let ExprKind::Unary(UnOp::UnDeref, e) = e.kind;
then { e } else { expr }
};
if_chain! {
// is a method call
if let ExprKind::MethodCall(_, _, [item], _) = call.kind;
if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id);
let ty = cx.typeck_results().expr_ty(item);
// ...on either an Ident or a Symbol
if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) {
Some(false)
} else if match_type(cx, ty, &paths::IDENT) {
Some(true)
} else {
None
};
// ...which converts it to a string
let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS };
if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path));
then {
let is_to_owned = path.last().unwrap().ends_with("string");
return Some(SymbolStrExpr::Expr {
item,
is_ident,
is_to_owned,
});
}
}
// is a string constant
if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) {
let value = Symbol::intern(&s).as_u32();
// ...which matches a symbol constant
if let Some(&def_id) = self.symbol_map.get(&value) {
return Some(SymbolStrExpr::Const(def_id));
}
}
None
}
}
enum SymbolStrExpr<'tcx> {
/// a string constant with a corresponding symbol constant
Const(DefId),
/// a "symbol to string" expression like `symbol.as_str()`
Expr {
/// part that evaluates to `Symbol` or `Ident`
item: &'tcx Expr<'tcx>,
is_ident: bool,
/// whether an owned `String` is created like `to_ident_string()`
is_to_owned: bool,
},
}
impl<'tcx> SymbolStrExpr<'tcx> {
/// Returns a snippet that evaluates to a `Symbol` and is const if possible
fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> {
match *self {
Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(),
Self::Expr { item, is_ident, .. } => {
let mut snip = snippet(cx, item.span.source_callsite(), "..");
if is_ident {
// get `Ident.name`
snip.to_mut().push_str(".name");
}
snip
},
}
}
}

View file

@ -1,5 +1,5 @@
#[macro_use]
pub mod sym;
pub mod sym_helper;
#[allow(clippy::module_name_repetitions)]
pub mod ast_utils;
@ -56,8 +56,8 @@ use rustc_semver::RustcVersion;
use rustc_session::Session;
use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::source_map::original_sp;
use rustc_span::sym as rustc_sym;
use rustc_span::symbol::{self, kw, Symbol};
use rustc_span::sym;
use rustc_span::symbol::{kw, Symbol};
use rustc_span::{BytePos, Pos, Span, DUMMY_SP};
use rustc_target::abi::Integer;
use rustc_trait_selection::traits::query::normalize::AtExt;
@ -1121,7 +1121,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
/// Checks for the `#[automatically_derived]` attribute all `#[derive]`d
/// implementations have.
pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool {
attrs.iter().any(|attr| attr.has_name(rustc_sym::automatically_derived))
attrs.iter().any(|attr| attr.has_name(sym::automatically_derived))
}
/// Remove blocks around an expression.
@ -1434,12 +1434,13 @@ pub fn parent_node_is_if_expr(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool {
let map = cx.tcx.hir();
let parent_id = map.get_parent_node(expr.hir_id);
let parent_node = map.get(parent_id);
if let Node::Expr(Expr { kind: ExprKind::If(_, _, _), .. }) = parent_node {
true
}
else {
false
}
matches!(
parent_node,
Node::Expr(Expr {
kind: ExprKind::If(_, _, _),
..
})
)
}
// Finds the attribute with the given name, if any
@ -1514,7 +1515,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
pub fn is_no_std_crate(krate: &Crate<'_>) -> bool {
krate.item.attrs.iter().any(|attr| {
if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
attr.path == symbol::sym::no_std
attr.path == sym::no_std
} else {
false
}
@ -1686,6 +1687,18 @@ macro_rules! unwrap_cargo_metadata {
}};
}
pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
if_chain! {
if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
if let Res::Def(_, def_id) = path.res;
then {
cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr)
} else {
false
}
}
}
#[cfg(test)]
mod test {
use super::{reindent_multiline, without_block_comments};

View file

@ -54,6 +54,10 @@ pub const HASH: [&str; 3] = ["core", "hash", "Hash"];
pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"];
pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"];
#[cfg(feature = "internal-lints")]
pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
#[cfg(feature = "internal-lints")]
pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"];
pub const INDEX: [&str; 3] = ["core", "ops", "Index"];
pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"];
pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"];
@ -65,6 +69,8 @@ pub const IPADDR_V4: [&str; 4] = ["std", "net", "IpAddr", "V4"];
pub const IPADDR_V6: [&str; 4] = ["std", "net", "IpAddr", "V6"];
pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"];
#[cfg(feature = "internal-lints")]
pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
#[cfg(feature = "internal-lints")]
pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"];
#[cfg(feature = "internal-lints")]
@ -148,8 +154,12 @@ pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_wit
#[cfg(feature = "internal-lints")]
pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"];
#[cfg(feature = "internal-lints")]
pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"];
#[cfg(feature = "internal-lints")]
pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"];
#[cfg(feature = "internal-lints")]
pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"];
#[cfg(feature = "internal-lints")]
pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"];
#[cfg(feature = "internal-lints")]
pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];

View file

@ -1,4 +1,5 @@
#[macro_export]
/// Convenience wrapper around rustc's `Symbol::intern`
macro_rules! sym {
($tt:tt) => {
rustc_span::symbol::Symbol::intern(stringify!($tt))

View file

@ -107,7 +107,7 @@ where
if let Some(el) = else_opt {
self.visit_expr(el);
}
}
},
hir::ExprKind::Match(cond, arms, _) => {
self.inside_stmt(true).visit_expr(cond);
for arm in arms {

View file

@ -0,0 +1,187 @@
use crate::utils::{is_type_diagnostic_item, match_def_path, paths, snippet, span_lint_and_sugg};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, Local, PatKind, QPath, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{symbol::sym, Span, Symbol};
use std::convert::TryInto;
declare_clippy_lint! {
/// **What it does:** Checks for calls to `push` immediately after creating a new `Vec`.
///
/// **Why is this bad?** The `vec![]` macro is both more performant and easier to read than
/// multiple `push` calls.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// let mut v = Vec::new();
/// v.push(0);
/// ```
/// Use instead:
/// ```rust
/// let v = vec![0];
/// ```
pub VEC_INIT_THEN_PUSH,
perf,
"`push` immediately after `Vec` creation"
}
impl_lint_pass!(VecInitThenPush => [VEC_INIT_THEN_PUSH]);
#[derive(Default)]
pub struct VecInitThenPush {
searcher: Option<VecPushSearcher>,
}
#[derive(Clone, Copy)]
enum VecInitKind {
New,
WithCapacity(u64),
}
struct VecPushSearcher {
init: VecInitKind,
name: Symbol,
lhs_is_local: bool,
lhs_span: Span,
err_span: Span,
found: u64,
}
impl VecPushSearcher {
fn display_err(&self, cx: &LateContext<'_>) {
match self.init {
_ if self.found == 0 => return,
VecInitKind::WithCapacity(x) if x > self.found => return,
_ => (),
};
let mut s = if self.lhs_is_local {
String::from("let ")
} else {
String::new()
};
s.push_str(&snippet(cx, self.lhs_span, ".."));
s.push_str(" = vec![..];");
span_lint_and_sugg(
cx,
VEC_INIT_THEN_PUSH,
self.err_span,
"calls to `push` immediately after creation",
"consider using the `vec![]` macro",
s,
Applicability::HasPlaceholders,
);
}
}
impl LateLintPass<'_> for VecInitThenPush {
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
self.searcher = None;
if_chain! {
if !in_external_macro(cx.sess(), local.span);
if let Some(init) = local.init;
if let PatKind::Binding(BindingAnnotation::Mutable, _, ident, None) = local.pat.kind;
if let Some(init_kind) = get_vec_init_kind(cx, init);
then {
self.searcher = Some(VecPushSearcher {
init: init_kind,
name: ident.name,
lhs_is_local: true,
lhs_span: local.ty.map_or(local.pat.span, |t| local.pat.span.to(t.span)),
err_span: local.span,
found: 0,
});
}
}
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if self.searcher.is_none() {
if_chain! {
if !in_external_macro(cx.sess(), expr.span);
if let ExprKind::Assign(left, right, _) = expr.kind;
if let ExprKind::Path(QPath::Resolved(_, path)) = left.kind;
if let Some(name) = path.segments.get(0);
if let Some(init_kind) = get_vec_init_kind(cx, right);
then {
self.searcher = Some(VecPushSearcher {
init: init_kind,
name: name.ident.name,
lhs_is_local: false,
lhs_span: left.span,
err_span: expr.span,
found: 0,
});
}
}
}
}
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
if let Some(searcher) = self.searcher.take() {
if_chain! {
if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind;
if let ExprKind::MethodCall(path, _, [self_arg, _], _) = expr.kind;
if path.ident.name.as_str() == "push";
if let ExprKind::Path(QPath::Resolved(_, self_path)) = self_arg.kind;
if let [self_name] = self_path.segments;
if self_name.ident.name == searcher.name;
then {
self.searcher = Some(VecPushSearcher {
found: searcher.found + 1,
err_span: searcher.err_span.to(stmt.span),
.. searcher
});
} else {
searcher.display_err(cx);
}
}
}
}
fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx Block<'tcx>) {
if let Some(searcher) = self.searcher.take() {
searcher.display_err(cx);
}
}
}
fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<VecInitKind> {
if let ExprKind::Call(func, args) = expr.kind {
match func.kind {
ExprKind::Path(QPath::TypeRelative(ty, name))
if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::vec_type) =>
{
if name.ident.name == sym::new {
return Some(VecInitKind::New);
} else if name.ident.name.as_str() == "with_capacity" {
return args.get(0).and_then(|arg| {
if_chain! {
if let ExprKind::Lit(lit) = &arg.kind;
if let LitKind::Int(num, _) = lit.node;
then {
Some(VecInitKind::WithCapacity(num.try_into().ok()?))
} else {
None
}
}
});
}
}
ExprKind::Path(QPath::Resolved(_, path))
if match_def_path(cx, path.res.opt_def_id()?, &paths::DEFAULT_TRAIT_METHOD)
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::vec_type) =>
{
return Some(VecInitKind::New);
}
_ => (),
}
}
None
}

View file

@ -7,7 +7,8 @@ use rustc_hir::{
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::BytePos;
use rustc_span::symbol::kw;
use rustc_span::{sym, BytePos};
declare_clippy_lint! {
/// **What it does:** Checks for `use Enum::*`.
@ -198,12 +199,12 @@ impl WildcardImports {
// Allow "...prelude::..::*" imports.
// Many crates have a prelude, and it is imported as a glob by design.
fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool {
segments.iter().any(|ps| ps.ident.as_str() == "prelude")
segments.iter().any(|ps| ps.ident.name == sym::prelude)
}
// Allow "super::*" imports in tests.
fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool {
segments.len() == 1 && segments[0].ident.as_str() == "super"
segments.len() == 1 && segments[0].ident.name == kw::Super
}
fn is_test_module_or_function(item: &Item<'_>) -> bool {

View file

@ -10,7 +10,8 @@ use rustc_lexer::unescape::{self, EscapeError};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_parse::parser;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{sym, BytePos, Span, Symbol};
use rustc_span::symbol::kw;
use rustc_span::{sym, BytePos, Span};
declare_clippy_lint! {
/// **What it does:** This lint warns when you use `println!("")` to
@ -301,7 +302,7 @@ impl EarlyLintPass for Write {
}
} else if mac.path == sym!(writeln) {
if let (Some(fmt_str), expr) = self.check_tts(cx, mac.args.inner_tokens(), true) {
if fmt_str.symbol == Symbol::intern("") {
if fmt_str.symbol == kw::Empty {
let mut applicability = Applicability::MachineApplicable;
// FIXME: remove this `#[allow(...)]` once the issue #5822 gets fixed
#[allow(clippy::option_if_let_else)]
@ -484,7 +485,7 @@ impl Write {
fn lint_println_empty_string(&self, cx: &EarlyContext<'_>, mac: &MacCall) {
if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
if fmt_str.symbol == Symbol::intern("") {
if fmt_str.symbol == kw::Empty {
let name = mac.path.segments[0].ident.name;
span_lint_and_sugg(
cx,