Skip to content

Commit d999b87

Browse files
committed
Don't use ExpnKind::descr to get the name of a bang macro.
1 parent 2670627 commit d999b87

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1161
-687
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1227,6 +1227,7 @@ Released 2018-09-13
12271227
[`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect
12281228
[`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref
12291229
[`option_and_then_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_and_then_some
1230+
[`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref
12301231
[`option_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_expect_used
12311232
[`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none
12321233
[`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
88

9-
[There are 348 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
9+
[There are 349 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
1010

1111
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
1212

clippy_lints/src/empty_enum.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,29 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
88
declare_clippy_lint! {
99
/// **What it does:** Checks for `enum`s with no variants.
1010
///
11-
/// **Why is this bad?** Enum's with no variants should be replaced with `!`,
12-
/// the uninhabited type,
13-
/// or a wrapper around it.
11+
/// **Why is this bad?** If you want to introduce a type which
12+
/// can't be instantiated, you should use `!` (the never type),
13+
/// or a wrapper around it, because `!` has more extensive
14+
/// compiler support (type inference, etc...) and wrappers
15+
/// around it are the conventional way to define an uninhabited type.
16+
/// For further information visit [never type documentation](https://doc.rust-lang.org/std/primitive.never.html)
17+
///
1418
///
1519
/// **Known problems:** None.
1620
///
1721
/// **Example:**
22+
///
23+
/// Bad:
1824
/// ```rust
1925
/// enum Test {}
2026
/// ```
27+
///
28+
/// Good:
29+
/// ```rust
30+
/// #![feature(never_type)]
31+
///
32+
/// struct Test(!);
33+
/// ```
2134
pub EMPTY_ENUM,
2235
pedantic,
2336
"enum with no variants"
@@ -35,7 +48,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EmptyEnum {
3548
span_lint_and_then(cx, EMPTY_ENUM, item.span, "enum with no variants", |db| {
3649
db.span_help(
3750
item.span,
38-
"consider using the uninhabited type `!` or a wrapper around it",
51+
"consider using the uninhabited type `!` (never type) or a wrapper \
52+
around it to introduce a type which can't be instantiated",
3953
);
4054
});
4155
}

clippy_lints/src/eq_op.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use crate::utils::{implements_trait, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then, SpanlessEq};
1+
use crate::utils::{
2+
implements_trait, in_macro, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then, SpanlessEq,
3+
};
24
use rustc_errors::Applicability;
35
use rustc_hir::*;
46
use rustc_lint::{LateContext, LateLintPass};
@@ -53,6 +55,16 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EqOp {
5355
if e.span.from_expansion() {
5456
return;
5557
}
58+
let macro_with_not_op = |expr_kind: &ExprKind<'_>| {
59+
if let ExprKind::Unary(_, ref expr) = *expr_kind {
60+
in_macro(expr.span)
61+
} else {
62+
false
63+
}
64+
};
65+
if macro_with_not_op(&left.kind) || macro_with_not_op(&right.kind) {
66+
return;
67+
}
5668
if is_valid_operator(op) && SpanlessEq::new(cx).ignore_fn().eq_expr(left, right) {
5769
span_lint(
5870
cx,

clippy_lints/src/formatting.rs

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use if_chain::if_chain;
33
use rustc::lint::in_external_macro;
44
use rustc_lint::{EarlyContext, EarlyLintPass};
55
use rustc_session::{declare_lint_pass, declare_tool_lint};
6+
use rustc_span::source_map::Span;
67
use syntax::ast::*;
78

89
declare_clippy_lint! {
@@ -242,26 +243,31 @@ fn has_unary_equivalent(bin_op: BinOpKind) -> bool {
242243
bin_op == BinOpKind::And || bin_op == BinOpKind::Mul || bin_op == BinOpKind::Sub
243244
}
244245

246+
fn indentation(cx: &EarlyContext<'_>, span: Span) -> usize {
247+
cx.sess.source_map().lookup_char_pos(span.lo()).col.0
248+
}
249+
245250
/// Implementation of the `POSSIBLE_MISSING_COMMA` lint for array
246251
fn check_array(cx: &EarlyContext<'_>, expr: &Expr) {
247252
if let ExprKind::Array(ref array) = expr.kind {
248253
for element in array {
249-
if let ExprKind::Binary(ref op, ref lhs, _) = element.kind {
250-
if has_unary_equivalent(op.node) && !differing_macro_contexts(lhs.span, op.span) {
251-
let space_span = lhs.span.between(op.span);
252-
if let Some(space_snippet) = snippet_opt(cx, space_span) {
253-
let lint_span = lhs.span.with_lo(lhs.span.hi());
254-
if space_snippet.contains('\n') {
255-
span_note_and_lint(
256-
cx,
257-
POSSIBLE_MISSING_COMMA,
258-
lint_span,
259-
"possibly missing a comma here",
260-
lint_span,
261-
"to remove this lint, add a comma or write the expr in a single line",
262-
);
263-
}
264-
}
254+
if_chain! {
255+
if let ExprKind::Binary(ref op, ref lhs, _) = element.kind;
256+
if has_unary_equivalent(op.node) && !differing_macro_contexts(lhs.span, op.span);
257+
let space_span = lhs.span.between(op.span);
258+
if let Some(space_snippet) = snippet_opt(cx, space_span);
259+
let lint_span = lhs.span.with_lo(lhs.span.hi());
260+
if space_snippet.contains('\n');
261+
if indentation(cx, op.span) <= indentation(cx, lhs.span);
262+
then {
263+
span_note_and_lint(
264+
cx,
265+
POSSIBLE_MISSING_COMMA,
266+
lint_span,
267+
"possibly missing a comma here",
268+
lint_span,
269+
"to remove this lint, add a comma or write the expr in a single line",
270+
);
265271
}
266272
}
267273
}

clippy_lints/src/let_underscore.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use if_chain::if_chain;
2+
use rustc::lint::in_external_macro;
23
use rustc_hir::*;
34
use rustc_lint::{LateContext, LateLintPass};
45
use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -33,6 +34,10 @@ declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE]);
3334

3435
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetUnderscore {
3536
fn check_stmt(&mut self, cx: &LateContext<'_, '_>, stmt: &Stmt<'_>) {
37+
if in_external_macro(cx.tcx.sess, stmt.span) {
38+
return;
39+
}
40+
3641
if_chain! {
3742
if let StmtKind::Local(ref local) = stmt.kind;
3843
if let PatKind::Wild = local.pat.kind;

clippy_lints/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
633633
&methods::NEW_RET_NO_SELF,
634634
&methods::OK_EXPECT,
635635
&methods::OPTION_AND_THEN_SOME,
636+
&methods::OPTION_AS_REF_DEREF,
636637
&methods::OPTION_EXPECT_USED,
637638
&methods::OPTION_MAP_OR_NONE,
638639
&methods::OPTION_MAP_UNWRAP_OR,
@@ -818,7 +819,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
818819
store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass);
819820
store.register_late_pass(|| box utils::inspector::DeepCodeInspector);
820821
store.register_late_pass(|| box utils::author::Author);
821-
store.register_late_pass(|| box types::Types);
822+
let vec_box_size_threshold = conf.vec_box_size_threshold;
823+
store.register_late_pass(move || box types::Types::new(vec_box_size_threshold));
822824
store.register_late_pass(|| box booleans::NonminimalBool);
823825
store.register_late_pass(|| box eq_op::EqOp);
824826
store.register_late_pass(|| box enum_glob_use::EnumGlobUse);
@@ -1219,6 +1221,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
12191221
LintId::of(&methods::NEW_RET_NO_SELF),
12201222
LintId::of(&methods::OK_EXPECT),
12211223
LintId::of(&methods::OPTION_AND_THEN_SOME),
1224+
LintId::of(&methods::OPTION_AS_REF_DEREF),
12221225
LintId::of(&methods::OPTION_MAP_OR_NONE),
12231226
LintId::of(&methods::OR_FUN_CALL),
12241227
LintId::of(&methods::SEARCH_IS_SOME),
@@ -1476,6 +1479,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
14761479
LintId::of(&methods::FILTER_NEXT),
14771480
LintId::of(&methods::FLAT_MAP_IDENTITY),
14781481
LintId::of(&methods::OPTION_AND_THEN_SOME),
1482+
LintId::of(&methods::OPTION_AS_REF_DEREF),
14791483
LintId::of(&methods::SEARCH_IS_SOME),
14801484
LintId::of(&methods::SKIP_WHILE_NEXT),
14811485
LintId::of(&methods::SUSPICIOUS_MAP),

clippy_lints/src/loops.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ use crate::utils::paths;
44
use crate::utils::usage::{is_unused, mutated_variables};
55
use crate::utils::{
66
get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait,
7-
is_integer_const, is_refutable, last_path_segment, match_trait_method, match_type, match_var, multispan_sugg,
8-
snippet, snippet_opt, snippet_with_applicability, span_help_and_lint, span_lint, span_lint_and_sugg,
9-
span_lint_and_then, SpanlessEq,
7+
is_integer_const, is_no_std_crate, is_refutable, last_path_segment, match_trait_method, match_type, match_var,
8+
multispan_sugg, snippet, snippet_opt, snippet_with_applicability, span_help_and_lint, span_lint,
9+
span_lint_and_sugg, span_lint_and_then, SpanlessEq,
1010
};
1111
use crate::utils::{is_type_diagnostic_item, qpath_res, same_tys, sext, sugg};
1212
use if_chain::if_chain;
@@ -502,7 +502,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Loops {
502502
// (even if the "match" or "if let" is used for declaration)
503503
if let ExprKind::Loop(ref block, _, LoopSource::Loop) = expr.kind {
504504
// also check for empty `loop {}` statements
505-
if block.stmts.is_empty() && block.expr.is_none() {
505+
if block.stmts.is_empty() && block.expr.is_none() && !is_no_std_crate(cx.tcx.hir().krate()) {
506506
span_lint(
507507
cx,
508508
EMPTY_LOOP,

clippy_lints/src/main_recursion.rs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
use rustc_hir::{Crate, Expr, ExprKind, QPath};
22
use rustc_lint::{LateContext, LateLintPass};
33
use rustc_session::{declare_tool_lint, impl_lint_pass};
4-
use rustc_span::symbol::sym;
5-
use syntax::ast::AttrKind;
64

7-
use crate::utils::{is_entrypoint_fn, snippet, span_help_and_lint};
5+
use crate::utils::{is_entrypoint_fn, is_no_std_crate, snippet, span_help_and_lint};
86
use if_chain::if_chain;
97

108
declare_clippy_lint! {
@@ -35,13 +33,7 @@ impl_lint_pass!(MainRecursion => [MAIN_RECURSION]);
3533

3634
impl LateLintPass<'_, '_> for MainRecursion {
3735
fn check_crate(&mut self, _: &LateContext<'_, '_>, krate: &Crate<'_>) {
38-
self.has_no_std_attr = krate.attrs.iter().any(|attr| {
39-
if let AttrKind::Normal(ref attr) = attr.kind {
40-
attr.path == sym::no_std
41-
} else {
42-
false
43-
}
44-
});
36+
self.has_no_std_attr = is_no_std_crate(krate);
4537
}
4638

4739
fn check_expr_post(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) {

clippy_lints/src/methods/mod.rs

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1189,6 +1189,27 @@ declare_clippy_lint! {
11891189
"`FileType::is_file` is not recommended to test for readable file type"
11901190
}
11911191

1192+
declare_clippy_lint! {
1193+
/// **What it does:** Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str).
1194+
///
1195+
/// **Why is this bad?** Readability, this can be written more concisely as a
1196+
/// single method call.
1197+
///
1198+
/// **Known problems:** None.
1199+
///
1200+
/// **Example:**
1201+
/// ```rust,ignore
1202+
/// opt.as_ref().map(String::as_str)
1203+
/// ```
1204+
/// Can be written as
1205+
/// ```rust,ignore
1206+
/// opt.as_deref()
1207+
/// ```
1208+
pub OPTION_AS_REF_DEREF,
1209+
complexity,
1210+
"using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`"
1211+
}
1212+
11921213
declare_lint_pass!(Methods => [
11931214
OPTION_UNWRAP_USED,
11941215
RESULT_UNWRAP_USED,
@@ -1238,10 +1259,11 @@ declare_lint_pass!(Methods => [
12381259
MANUAL_SATURATING_ARITHMETIC,
12391260
ZST_OFFSET,
12401261
FILETYPE_IS_FILE,
1262+
OPTION_AS_REF_DEREF,
12411263
]);
12421264

12431265
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
1244-
#[allow(clippy::cognitive_complexity)]
1266+
#[allow(clippy::cognitive_complexity, clippy::too_many_lines)]
12451267
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
12461268
if in_macro(expr.span) {
12471269
return;
@@ -1303,6 +1325,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
13031325
check_pointer_offset(cx, expr, arg_lists[0])
13041326
},
13051327
["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]),
1328+
["map", "as_ref"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false),
1329+
["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true),
13061330
_ => {},
13071331
}
13081332

@@ -3062,6 +3086,83 @@ fn lint_suspicious_map(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) {
30623086
);
30633087
}
30643088

3089+
/// lint use of `_.as_ref().map(Deref::deref)` for `Option`s
3090+
fn lint_option_as_ref_deref<'a, 'tcx>(
3091+
cx: &LateContext<'a, 'tcx>,
3092+
expr: &hir::Expr<'_>,
3093+
as_ref_args: &[hir::Expr<'_>],
3094+
map_args: &[hir::Expr<'_>],
3095+
is_mut: bool,
3096+
) {
3097+
let option_ty = cx.tables.expr_ty(&as_ref_args[0]);
3098+
if !match_type(cx, option_ty, &paths::OPTION) {
3099+
return;
3100+
}
3101+
3102+
let deref_aliases: [&[&str]; 9] = [
3103+
&paths::DEREF_TRAIT_METHOD,
3104+
&paths::DEREF_MUT_TRAIT_METHOD,
3105+
&paths::CSTRING_AS_C_STR,
3106+
&paths::OS_STRING_AS_OS_STR,
3107+
&paths::PATH_BUF_AS_PATH,
3108+
&paths::STRING_AS_STR,
3109+
&paths::STRING_AS_MUT_STR,
3110+
&paths::VEC_AS_SLICE,
3111+
&paths::VEC_AS_MUT_SLICE,
3112+
];
3113+
3114+
let is_deref = match map_args[1].kind {
3115+
hir::ExprKind::Path(ref expr_qpath) => deref_aliases.iter().any(|path| match_qpath(expr_qpath, path)),
3116+
hir::ExprKind::Closure(_, _, body_id, _, _) => {
3117+
let closure_body = cx.tcx.hir().body(body_id);
3118+
let closure_expr = remove_blocks(&closure_body.value);
3119+
if_chain! {
3120+
if let hir::ExprKind::MethodCall(_, _, args) = &closure_expr.kind;
3121+
if args.len() == 1;
3122+
if let hir::ExprKind::Path(qpath) = &args[0].kind;
3123+
if let hir::def::Res::Local(local_id) = cx.tables.qpath_res(qpath, args[0].hir_id);
3124+
if closure_body.params[0].pat.hir_id == local_id;
3125+
let adj = cx.tables.expr_adjustments(&args[0]).iter().map(|x| &x.kind).collect::<Box<[_]>>();
3126+
if let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj;
3127+
then {
3128+
let method_did = cx.tables.type_dependent_def_id(closure_expr.hir_id).unwrap();
3129+
deref_aliases.iter().any(|path| match_def_path(cx, method_did, path))
3130+
} else {
3131+
false
3132+
}
3133+
}
3134+
},
3135+
3136+
_ => false,
3137+
};
3138+
3139+
if is_deref {
3140+
let current_method = if is_mut {
3141+
".as_mut().map(DerefMut::deref_mut)"
3142+
} else {
3143+
".as_ref().map(Deref::deref)"
3144+
};
3145+
let method_hint = if is_mut { "as_deref_mut" } else { "as_deref" };
3146+
let hint = format!("{}.{}()", snippet(cx, as_ref_args[0].span, ".."), method_hint);
3147+
let suggestion = format!("try using {} instead", method_hint);
3148+
3149+
let msg = format!(
3150+
"called `{0}` (or with one of deref aliases) on an Option value. \
3151+
This can be done more directly by calling `{1}` instead",
3152+
current_method, hint
3153+
);
3154+
span_lint_and_sugg(
3155+
cx,
3156+
OPTION_AS_REF_DEREF,
3157+
expr.span,
3158+
&msg,
3159+
&suggestion,
3160+
hint,
3161+
Applicability::MachineApplicable,
3162+
);
3163+
}
3164+
}
3165+
30653166
/// Given a `Result<T, E>` type, return its error type (`E`).
30663167
fn get_error_type<'a>(cx: &LateContext<'_, '_>, ty: Ty<'a>) -> Option<Ty<'a>> {
30673168
match ty.kind {

0 commit comments

Comments
 (0)