Skip to content

Commit 575cd03

Browse files
authored
Merge branch 'master' into static_items_large_align
2 parents 4dcafd0 + baaddf2 commit 575cd03

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

+702
-167
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ document.
88

99
[57b3c4b...master](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...master)
1010

11-
## Rust 1.59 (beta)
11+
## Rust 1.60 (beta)
1212

1313
Current beta, release 2022-04-07
1414

@@ -3229,6 +3229,7 @@ Released 2018-09-13
32293229
[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
32303230
[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
32313231
[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
3232+
[`crate_in_macro_def`]: https://rust-lang.github.io/rust-clippy/master/index.html#crate_in_macro_def
32323233
[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir
32333234
[`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute
32343235
[`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ derive-new = "0.5"
4545
if_chain = "1.0"
4646
itertools = "0.10.1"
4747
quote = "1.0"
48-
serde = { version = "1.0", features = ["derive"] }
48+
serde = { version = "1.0.125", features = ["derive"] }
4949
syn = { version = "1.0", features = ["full"] }
5050
futures = "0.3"
5151
parking_lot = "0.11.2"
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use rustc_ast::ast::{AttrKind, Attribute, Item, ItemKind};
3+
use rustc_ast::token::{Token, TokenKind};
4+
use rustc_ast::tokenstream::{TokenStream, TokenTree};
5+
use rustc_errors::Applicability;
6+
use rustc_lint::{EarlyContext, EarlyLintPass};
7+
use rustc_session::{declare_lint_pass, declare_tool_lint};
8+
use rustc_span::{symbol::sym, Span};
9+
10+
declare_clippy_lint! {
11+
/// ### What it does
12+
/// Checks for use of `crate` as opposed to `$crate` in a macro definition.
13+
///
14+
/// ### Why is this bad?
15+
/// `crate` refers to the macro call's crate, whereas `$crate` refers to the macro definition's
16+
/// crate. Rarely is the former intended. See:
17+
/// https://doc.rust-lang.org/reference/macros-by-example.html#hygiene
18+
///
19+
/// ### Example
20+
/// ```rust
21+
/// #[macro_export]
22+
/// macro_rules! print_message {
23+
/// () => {
24+
/// println!("{}", crate::MESSAGE);
25+
/// };
26+
/// }
27+
/// pub const MESSAGE: &str = "Hello!";
28+
/// ```
29+
/// Use instead:
30+
/// ```rust
31+
/// #[macro_export]
32+
/// macro_rules! print_message {
33+
/// () => {
34+
/// println!("{}", $crate::MESSAGE);
35+
/// };
36+
/// }
37+
/// pub const MESSAGE: &str = "Hello!";
38+
/// ```
39+
///
40+
/// Note that if the use of `crate` is intentional, an `allow` attribute can be applied to the
41+
/// macro definition, e.g.:
42+
/// ```rust,ignore
43+
/// #[allow(clippy::crate_in_macro_def)]
44+
/// macro_rules! ok { ... crate::foo ... }
45+
/// ```
46+
#[clippy::version = "1.61.0"]
47+
pub CRATE_IN_MACRO_DEF,
48+
suspicious,
49+
"using `crate` in a macro definition"
50+
}
51+
declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]);
52+
53+
impl EarlyLintPass for CrateInMacroDef {
54+
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
55+
if_chain! {
56+
if item.attrs.iter().any(is_macro_export);
57+
if let ItemKind::MacroDef(macro_def) = &item.kind;
58+
let tts = macro_def.body.inner_tokens();
59+
if let Some(span) = contains_unhygienic_crate_reference(&tts);
60+
then {
61+
span_lint_and_sugg(
62+
cx,
63+
CRATE_IN_MACRO_DEF,
64+
span,
65+
"`crate` references the macro call's crate",
66+
"to reference the macro definition's crate, use",
67+
String::from("$crate"),
68+
Applicability::MachineApplicable,
69+
);
70+
}
71+
}
72+
}
73+
}
74+
75+
fn is_macro_export(attr: &Attribute) -> bool {
76+
if_chain! {
77+
if let AttrKind::Normal(attr_item, _) = &attr.kind;
78+
if let [segment] = attr_item.path.segments.as_slice();
79+
then {
80+
segment.ident.name == sym::macro_export
81+
} else {
82+
false
83+
}
84+
}
85+
}
86+
87+
fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option<Span> {
88+
let mut prev_is_dollar = false;
89+
let mut cursor = tts.trees();
90+
while let Some(curr) = cursor.next() {
91+
if_chain! {
92+
if !prev_is_dollar;
93+
if let Some(span) = is_crate_keyword(&curr);
94+
if let Some(next) = cursor.look_ahead(0);
95+
if is_token(next, &TokenKind::ModSep);
96+
then {
97+
return Some(span);
98+
}
99+
}
100+
if let TokenTree::Delimited(_, _, tts) = &curr {
101+
let span = contains_unhygienic_crate_reference(tts);
102+
if span.is_some() {
103+
return span;
104+
}
105+
}
106+
prev_is_dollar = is_token(&curr, &TokenKind::Dollar);
107+
}
108+
None
109+
}
110+
111+
fn is_crate_keyword(tt: &TokenTree) -> Option<Span> {
112+
if_chain! {
113+
if let TokenTree::Token(Token { kind: TokenKind::Ident(symbol, _), span }) = tt;
114+
if symbol.as_str() == "crate";
115+
then { Some(*span) } else { None }
116+
}
117+
}
118+
119+
fn is_token(tt: &TokenTree, kind: &TokenKind) -> bool {
120+
if let TokenTree::Token(Token { kind: other, .. }) = tt {
121+
kind == other
122+
} else {
123+
false
124+
}
125+
}

clippy_lints/src/identity_op.rs

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc_middle::ty;
55
use rustc_session::{declare_lint_pass, declare_tool_lint};
66
use rustc_span::source_map::Span;
77

8-
use clippy_utils::consts::{constant_simple, Constant};
8+
use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt};
99
use clippy_utils::diagnostics::span_lint;
1010
use clippy_utils::{clip, unsext};
1111

@@ -54,6 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for IdentityOp {
5454
check(cx, left, -1, e.span, right.span);
5555
check(cx, right, -1, e.span, left.span);
5656
},
57+
BinOpKind::Rem => check_remainder(cx, left, right, e.span, left.span),
5758
_ => (),
5859
}
5960
}
@@ -70,6 +71,18 @@ fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_
7071
&& constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1)))
7172
}
7273

74+
fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span: Span, arg: Span) {
75+
let lhs_const = constant_full_int(cx, cx.typeck_results(), left);
76+
let rhs_const = constant_full_int(cx, cx.typeck_results(), right);
77+
if match (lhs_const, rhs_const) {
78+
(Some(FullInt::S(lv)), Some(FullInt::S(rv))) => lv.abs() < rv.abs(),
79+
(Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv,
80+
_ => return,
81+
} {
82+
span_ineffective_operation(cx, span, arg);
83+
}
84+
}
85+
7386
fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) {
7487
if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) {
7588
let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() {
@@ -83,15 +96,19 @@ fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) {
8396
1 => v == 1,
8497
_ => unreachable!(),
8598
} {
86-
span_lint(
87-
cx,
88-
IDENTITY_OP,
89-
span,
90-
&format!(
91-
"the operation is ineffective. Consider reducing it to `{}`",
92-
snippet(cx, arg, "..")
93-
),
94-
);
99+
span_ineffective_operation(cx, span, arg);
95100
}
96101
}
97102
}
103+
104+
fn span_ineffective_operation(cx: &LateContext<'_>, span: Span, arg: Span) {
105+
span_lint(
106+
cx,
107+
IDENTITY_OP,
108+
span,
109+
&format!(
110+
"the operation is ineffective. Consider reducing it to `{}`",
111+
snippet(cx, arg, "..")
112+
),
113+
);
114+
}

clippy_lints/src/lib.register_all.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
3737
LintId::of(comparison_chain::COMPARISON_CHAIN),
3838
LintId::of(copies::IFS_SAME_COND),
3939
LintId::of(copies::IF_SAME_THEN_ELSE),
40+
LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
4041
LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
4142
LintId::of(dereference::NEEDLESS_BORROW),
4243
LintId::of(derivable_impls::DERIVABLE_IMPLS),

clippy_lints/src/lib.register_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ store.register_lints(&[
9797
copies::IF_SAME_THEN_ELSE,
9898
copies::SAME_FUNCTIONS_IN_IF_CONDITION,
9999
copy_iterator::COPY_ITERATOR,
100+
crate_in_macro_def::CRATE_IN_MACRO_DEF,
100101
create_dir::CREATE_DIR,
101102
dbg_macro::DBG_MACRO,
102103
default::DEFAULT_TRAIT_ACCESS,

clippy_lints/src/lib.register_suspicious.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
99
LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
1010
LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
1111
LintId::of(casts::CAST_ENUM_TRUNCATION),
12+
LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
1213
LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE),
1314
LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
1415
LintId::of(format_impl::PRINT_IN_FORMAT_IMPL),

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ mod collapsible_match;
190190
mod comparison_chain;
191191
mod copies;
192192
mod copy_iterator;
193+
mod crate_in_macro_def;
193194
mod create_dir;
194195
mod dbg_macro;
195196
mod default;
@@ -868,6 +869,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
868869
ignore_publish: cargo_ignore_publish,
869870
})
870871
});
872+
store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef));
871873
let page_size = conf.page_size;
872874
store.register_late_pass(move || Box::new(static_items_large_align::StaticItemsLargeAlign { page_size }));
873875
// add lints here, do not remove this comment, it's used in `new_lint`

clippy_lints/src/loops/single_element_loop.rs

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ use super::SINGLE_ELEMENT_LOOP;
22
use clippy_utils::diagnostics::span_lint_and_sugg;
33
use clippy_utils::source::{indent_of, snippet_with_applicability};
44
use if_chain::if_chain;
5+
use rustc_ast::util::parser::PREC_PREFIX;
6+
use rustc_ast::Mutability;
57
use rustc_errors::Applicability;
6-
use rustc_hir::{BorrowKind, Expr, ExprKind, Pat};
8+
use rustc_hir::{is_range_literal, BorrowKind, Expr, ExprKind, Pat};
79
use rustc_lint::LateContext;
10+
use rustc_span::edition::Edition;
811

912
pub(super) fn check<'tcx>(
1013
cx: &LateContext<'tcx>,
@@ -13,31 +16,84 @@ pub(super) fn check<'tcx>(
1316
body: &'tcx Expr<'_>,
1417
expr: &'tcx Expr<'_>,
1518
) {
16-
let arg_expr = match arg.kind {
17-
ExprKind::AddrOf(BorrowKind::Ref, _, ref_arg) => ref_arg,
18-
ExprKind::MethodCall(method, [arg], _) if method.ident.name == rustc_span::sym::iter => arg,
19+
let (arg_expression, prefix) = match arg.kind {
20+
ExprKind::AddrOf(
21+
BorrowKind::Ref,
22+
Mutability::Not,
23+
Expr {
24+
kind: ExprKind::Array([arg]),
25+
..
26+
},
27+
) => (arg, "&"),
28+
ExprKind::AddrOf(
29+
BorrowKind::Ref,
30+
Mutability::Mut,
31+
Expr {
32+
kind: ExprKind::Array([arg]),
33+
..
34+
},
35+
) => (arg, "&mut "),
36+
ExprKind::MethodCall(
37+
method,
38+
[
39+
Expr {
40+
kind: ExprKind::Array([arg]),
41+
..
42+
},
43+
],
44+
_,
45+
) if method.ident.name == rustc_span::sym::iter => (arg, "&"),
46+
ExprKind::MethodCall(
47+
method,
48+
[
49+
Expr {
50+
kind: ExprKind::Array([arg]),
51+
..
52+
},
53+
],
54+
_,
55+
) if method.ident.name.as_str() == "iter_mut" => (arg, "&mut "),
56+
ExprKind::MethodCall(
57+
method,
58+
[
59+
Expr {
60+
kind: ExprKind::Array([arg]),
61+
..
62+
},
63+
],
64+
_,
65+
) if method.ident.name == rustc_span::sym::into_iter => (arg, ""),
66+
// Only check for arrays edition 2021 or later, as this case will trigger a compiler error otherwise.
67+
ExprKind::Array([arg]) if cx.tcx.sess.edition() >= Edition::Edition2021 => (arg, ""),
1968
_ => return,
2069
};
2170
if_chain! {
22-
if let ExprKind::Array([arg_expression]) = arg_expr.kind;
2371
if let ExprKind::Block(block, _) = body.kind;
2472
if !block.stmts.is_empty();
2573
then {
2674
let mut applicability = Applicability::MachineApplicable;
2775
let pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability);
28-
let arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability);
76+
let mut arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability);
2977
let mut block_str = snippet_with_applicability(cx, block.span, "..", &mut applicability).into_owned();
3078
block_str.remove(0);
3179
block_str.pop();
3280
let indent = " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0));
3381

82+
// Reference iterator from `&(mut) []` or `[].iter(_mut)()`.
83+
if !prefix.is_empty() && (
84+
// Precedence of internal expression is less than or equal to precedence of `&expr`.
85+
arg_expression.precedence().order() <= PREC_PREFIX || is_range_literal(arg_expression)
86+
) {
87+
arg_snip = format!("({arg_snip})").into();
88+
}
89+
3490
span_lint_and_sugg(
3591
cx,
3692
SINGLE_ELEMENT_LOOP,
3793
expr.span,
3894
"for loop over a single element",
3995
"try",
40-
format!("{{\n{}let {} = &{};{}}}", indent, pat_snip, arg_snip, block_str),
96+
format!("{{\n{indent}let {pat_snip} = {prefix}{arg_snip};{block_str}}}"),
4197
applicability,
4298
)
4399
}

0 commit comments

Comments
 (0)