Skip to content

Commit d5ea1d6

Browse files
committed
Merge remote-tracking branch 'upstream/master' into rustup
2 parents 7c15f30 + 7427065 commit d5ea1d6

12 files changed

+404
-63
lines changed

clippy_lints/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1062,7 +1062,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10621062
store.register_early_pass(|| box option_env_unwrap::OptionEnvUnwrap);
10631063
let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports;
10641064
store.register_late_pass(move || box wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports));
1065-
store.register_early_pass(|| box macro_use::MacroUseImports);
10661065
store.register_late_pass(|| box verbose_file_reads::VerboseFileReads);
10671066
store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default());
10681067
store.register_late_pass(|| box unnamed_address::UnnamedAddress);
@@ -1080,6 +1079,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10801079
single_char_binding_names_threshold,
10811080
});
10821081
store.register_early_pass(|| box unnested_or_patterns::UnnestedOrPatterns);
1082+
store.register_late_pass(|| box macro_use::MacroUseImports::default());
10831083

10841084
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
10851085
LintId::of(&arithmetic::FLOAT_ARITHMETIC),

clippy_lints/src/macro_use.rs

+196-17
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
1-
use crate::utils::{snippet, span_lint_and_sugg};
1+
use crate::utils::{in_macro, snippet, span_lint_and_sugg};
2+
use hir::def::{DefKind, Res};
23
use if_chain::if_chain;
34
use rustc_ast::ast;
5+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
46
use rustc_errors::Applicability;
5-
use rustc_lint::{EarlyContext, EarlyLintPass};
6-
use rustc_session::{declare_lint_pass, declare_tool_lint};
7-
use rustc_span::edition::Edition;
7+
use rustc_hir as hir;
8+
use rustc_lint::{LateContext, LateLintPass, LintContext};
9+
use rustc_session::{declare_tool_lint, impl_lint_pass};
10+
use rustc_span::{edition::Edition, Span};
811

912
declare_clippy_lint! {
1013
/// **What it does:** Checks for `#[macro_use] use...`.
1114
///
1215
/// **Why is this bad?** Since the Rust 2018 edition you can import
1316
/// macro's directly, this is considered idiomatic.
1417
///
15-
/// **Known problems:** This lint does not generate an auto-applicable suggestion.
18+
/// **Known problems:** None.
1619
///
1720
/// **Example:**
1821
/// ```rust
@@ -24,29 +27,205 @@ declare_clippy_lint! {
2427
"#[macro_use] is no longer needed"
2528
}
2629

27-
declare_lint_pass!(MacroUseImports => [MACRO_USE_IMPORTS]);
30+
const BRACKETS: &[char] = &['<', '>'];
2831

29-
impl EarlyLintPass for MacroUseImports {
30-
fn check_item(&mut self, ecx: &EarlyContext<'_>, item: &ast::Item) {
32+
#[derive(Clone, Debug, PartialEq, Eq)]
33+
struct PathAndSpan {
34+
path: String,
35+
span: Span,
36+
}
37+
38+
/// `MacroRefData` includes the name of the macro
39+
/// and the path from `SourceMap::span_to_filename`.
40+
#[derive(Debug, Clone)]
41+
pub struct MacroRefData {
42+
name: String,
43+
path: String,
44+
}
45+
46+
impl MacroRefData {
47+
pub fn new(name: String, callee: Span, cx: &LateContext<'_, '_>) -> Self {
48+
let mut path = cx.sess().source_map().span_to_filename(callee).to_string();
49+
50+
// std lib paths are <::std::module::file type>
51+
// so remove brackets, space and type.
52+
if path.contains('<') {
53+
path = path.replace(BRACKETS, "");
54+
}
55+
if path.contains(' ') {
56+
path = path.split(' ').next().unwrap().to_string();
57+
}
58+
Self { name, path }
59+
}
60+
}
61+
62+
#[derive(Default)]
63+
#[allow(clippy::module_name_repetitions)]
64+
pub struct MacroUseImports {
65+
/// the actual import path used and the span of the attribute above it.
66+
imports: Vec<(String, Span)>,
67+
/// the span of the macro reference, kept to ensure only one reference is used per macro call.
68+
collected: FxHashSet<Span>,
69+
mac_refs: Vec<MacroRefData>,
70+
}
71+
72+
impl_lint_pass!(MacroUseImports => [MACRO_USE_IMPORTS]);
73+
74+
impl MacroUseImports {
75+
fn push_unique_macro(&mut self, cx: &LateContext<'_, '_>, span: Span) {
76+
let call_site = span.source_callsite();
77+
let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_");
78+
if let Some(callee) = span.source_callee() {
79+
if !self.collected.contains(&call_site) {
80+
let name = if name.contains("::") {
81+
name.split("::").last().unwrap().to_string()
82+
} else {
83+
name.to_string()
84+
};
85+
86+
self.mac_refs.push(MacroRefData::new(name, callee.def_site, cx));
87+
self.collected.insert(call_site);
88+
}
89+
}
90+
}
91+
92+
fn push_unique_macro_pat_ty(&mut self, cx: &LateContext<'_, '_>, span: Span) {
93+
let call_site = span.source_callsite();
94+
let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_");
95+
if let Some(callee) = span.source_callee() {
96+
if !self.collected.contains(&call_site) {
97+
self.mac_refs
98+
.push(MacroRefData::new(name.to_string(), callee.def_site, cx));
99+
self.collected.insert(call_site);
100+
}
101+
}
102+
}
103+
}
104+
105+
impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports {
106+
fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &hir::Item<'_>) {
31107
if_chain! {
32-
if ecx.sess.opts.edition == Edition::Edition2018;
33-
if let ast::ItemKind::Use(use_tree) = &item.kind;
108+
if cx.sess().opts.edition == Edition::Edition2018;
109+
if let hir::ItemKind::Use(path, _kind) = &item.kind;
34110
if let Some(mac_attr) = item
35111
.attrs
36112
.iter()
37113
.find(|attr| attr.ident().map(|s| s.to_string()) == Some("macro_use".to_string()));
114+
if let Res::Def(DefKind::Mod, id) = path.res;
38115
then {
39-
let msg = "`macro_use` attributes are no longer needed in the Rust 2018 edition";
40-
let help = format!("use {}::<macro name>", snippet(ecx, use_tree.span, "_"));
116+
for kid in cx.tcx.item_children(id).iter() {
117+
if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res {
118+
let span = mac_attr.span;
119+
let def_path = cx.tcx.def_path_str(mac_id);
120+
self.imports.push((def_path, span));
121+
}
122+
}
123+
} else {
124+
if in_macro(item.span) {
125+
self.push_unique_macro_pat_ty(cx, item.span);
126+
}
127+
}
128+
}
129+
}
130+
fn check_attribute(&mut self, cx: &LateContext<'_, '_>, attr: &ast::Attribute) {
131+
if in_macro(attr.span) {
132+
self.push_unique_macro(cx, attr.span);
133+
}
134+
}
135+
fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) {
136+
if in_macro(expr.span) {
137+
self.push_unique_macro(cx, expr.span);
138+
}
139+
}
140+
fn check_stmt(&mut self, cx: &LateContext<'_, '_>, stmt: &hir::Stmt<'_>) {
141+
if in_macro(stmt.span) {
142+
self.push_unique_macro(cx, stmt.span);
143+
}
144+
}
145+
fn check_pat(&mut self, cx: &LateContext<'_, '_>, pat: &hir::Pat<'_>) {
146+
if in_macro(pat.span) {
147+
self.push_unique_macro_pat_ty(cx, pat.span);
148+
}
149+
}
150+
fn check_ty(&mut self, cx: &LateContext<'_, '_>, ty: &hir::Ty<'_>) {
151+
if in_macro(ty.span) {
152+
self.push_unique_macro_pat_ty(cx, ty.span);
153+
}
154+
}
155+
#[allow(clippy::too_many_lines)]
156+
fn check_crate_post(&mut self, cx: &LateContext<'_, '_>, _krate: &hir::Crate<'_>) {
157+
let mut used = FxHashMap::default();
158+
let mut check_dup = vec![];
159+
for (import, span) in &self.imports {
160+
let found_idx = self.mac_refs.iter().position(|mac| import.ends_with(&mac.name));
161+
162+
if let Some(idx) = found_idx {
163+
let _ = self.mac_refs.remove(idx);
164+
let seg = import.split("::").collect::<Vec<_>>();
165+
166+
match seg.as_slice() {
167+
// an empty path is impossible
168+
// a path should always consist of 2 or more segments
169+
[] | [_] => return,
170+
[root, item] => {
171+
if !check_dup.contains(&(*item).to_string()) {
172+
used.entry(((*root).to_string(), span))
173+
.or_insert_with(Vec::new)
174+
.push((*item).to_string());
175+
check_dup.push((*item).to_string());
176+
}
177+
},
178+
[root, rest @ ..] => {
179+
if rest.iter().all(|item| !check_dup.contains(&(*item).to_string())) {
180+
let filtered = rest
181+
.iter()
182+
.filter_map(|item| {
183+
if check_dup.contains(&(*item).to_string()) {
184+
None
185+
} else {
186+
Some((*item).to_string())
187+
}
188+
})
189+
.collect::<Vec<_>>();
190+
used.entry(((*root).to_string(), span))
191+
.or_insert_with(Vec::new)
192+
.push(filtered.join("::"));
193+
check_dup.extend(filtered);
194+
} else {
195+
let rest = rest.to_vec();
196+
used.entry(((*root).to_string(), span))
197+
.or_insert_with(Vec::new)
198+
.push(rest.join("::"));
199+
check_dup.extend(rest.iter().map(ToString::to_string));
200+
}
201+
},
202+
}
203+
}
204+
}
205+
206+
let mut suggestions = vec![];
207+
for ((root, span), path) in used {
208+
if path.len() == 1 {
209+
suggestions.push((span, format!("{}::{}", root, path[0])))
210+
} else {
211+
suggestions.push((span, format!("{}::{{{}}}", root, path.join(", "))))
212+
}
213+
}
214+
215+
// If mac_refs is not empty we have encountered an import we could not handle
216+
// such as `std::prelude::v1::foo` or some other macro that expands to an import.
217+
if self.mac_refs.is_empty() {
218+
for (span, import) in suggestions {
219+
let help = format!("use {};", import);
41220
span_lint_and_sugg(
42-
ecx,
221+
cx,
43222
MACRO_USE_IMPORTS,
44-
mac_attr.span,
45-
msg,
223+
*span,
224+
"`macro_use` attributes are no longer needed in the Rust 2018 edition",
46225
"remove the attribute and import the macro directly, try",
47226
help,
48-
Applicability::HasPlaceholders,
49-
);
227+
Applicability::MaybeIncorrect,
228+
)
50229
}
51230
}
52231
}

clippy_lints/src/needless_pass_by_value.rs

+2-9
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,8 @@ declare_clippy_lint! {
4040
/// assert_eq!(v.len(), 42);
4141
/// }
4242
/// ```
43-
///
43+
/// should be
4444
/// ```rust
45-
/// // should be
4645
/// fn foo(v: &[i32]) {
4746
/// assert_eq!(v.len(), 42);
4847
/// }
@@ -173,13 +172,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
173172
!preds.is_empty() && {
174173
let ty_empty_region = cx.tcx.mk_imm_ref(cx.tcx.lifetimes.re_root_empty, ty);
175174
preds.iter().all(|t| {
176-
let ty_params = &t
177-
.skip_binder()
178-
.trait_ref
179-
.substs
180-
.iter()
181-
.skip(1)
182-
.collect::<Vec<_>>();
175+
let ty_params = &t.skip_binder().trait_ref.substs.iter().skip(1).collect::<Vec<_>>();
183176
implements_trait(cx, ty_empty_region, t.def_id(), ty_params)
184177
})
185178
},

clippy_lints/src/utils/hir_utils.rs

+5-8
Original file line numberDiff line numberDiff line change
@@ -309,18 +309,15 @@ fn swap_binop<'a>(
309309
rhs: &'a Expr<'a>,
310310
) -> Option<(BinOpKind, &'a Expr<'a>, &'a Expr<'a>)> {
311311
match binop {
312-
BinOpKind::Add
313-
| BinOpKind::Mul
314-
| BinOpKind::Eq
315-
| BinOpKind::Ne
316-
| BinOpKind::BitAnd
317-
| BinOpKind::BitXor
318-
| BinOpKind::BitOr => Some((binop, rhs, lhs)),
312+
BinOpKind::Add | BinOpKind::Eq | BinOpKind::Ne | BinOpKind::BitAnd | BinOpKind::BitXor | BinOpKind::BitOr => {
313+
Some((binop, rhs, lhs))
314+
},
319315
BinOpKind::Lt => Some((BinOpKind::Gt, rhs, lhs)),
320316
BinOpKind::Le => Some((BinOpKind::Ge, rhs, lhs)),
321317
BinOpKind::Ge => Some((BinOpKind::Le, rhs, lhs)),
322318
BinOpKind::Gt => Some((BinOpKind::Lt, rhs, lhs)),
323-
BinOpKind::Shl
319+
BinOpKind::Mul // Not always commutative, e.g. with matrices. See issue #5698
320+
| BinOpKind::Shl
324321
| BinOpKind::Shr
325322
| BinOpKind::Rem
326323
| BinOpKind::Sub

clippy_lints/src/write.rs

+25-8
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@ declare_clippy_lint! {
2323
///
2424
/// **Example:**
2525
/// ```rust
26+
/// // Bad
2627
/// println!("");
28+
///
29+
/// // Good
30+
/// println!();
2731
/// ```
2832
pub PRINTLN_EMPTY_STRING,
2933
style,
@@ -32,8 +36,7 @@ declare_clippy_lint! {
3236

3337
declare_clippy_lint! {
3438
/// **What it does:** This lint warns when you use `print!()` with a format
35-
/// string that
36-
/// ends in a newline.
39+
/// string that ends in a newline.
3740
///
3841
/// **Why is this bad?** You should use `println!()` instead, which appends the
3942
/// newline.
@@ -125,7 +128,12 @@ declare_clippy_lint! {
125128
/// ```rust
126129
/// # use std::fmt::Write;
127130
/// # let mut buf = String::new();
131+
///
132+
/// // Bad
128133
/// writeln!(buf, "");
134+
///
135+
/// // Good
136+
/// writeln!(buf);
129137
/// ```
130138
pub WRITELN_EMPTY_STRING,
131139
style,
@@ -147,7 +155,12 @@ declare_clippy_lint! {
147155
/// # use std::fmt::Write;
148156
/// # let mut buf = String::new();
149157
/// # let name = "World";
158+
///
159+
/// // Bad
150160
/// write!(buf, "Hello {}!\n", name);
161+
///
162+
/// // Good
163+
/// writeln!(buf, "Hello {}!", name);
151164
/// ```
152165
pub WRITE_WITH_NEWLINE,
153166
style,
@@ -168,7 +181,12 @@ declare_clippy_lint! {
168181
/// ```rust
169182
/// # use std::fmt::Write;
170183
/// # let mut buf = String::new();
184+
///
185+
/// // Bad
171186
/// writeln!(buf, "{}", "foo");
187+
///
188+
/// // Good
189+
/// writeln!(buf, "foo");
172190
/// ```
173191
pub WRITE_LITERAL,
174192
style,
@@ -279,12 +297,11 @@ impl EarlyLintPass for Write {
279297
if let (Some(fmt_str), expr) = self.check_tts(cx, &mac.args.inner_tokens(), true) {
280298
if fmt_str.symbol == Symbol::intern("") {
281299
let mut applicability = Applicability::MachineApplicable;
282-
let suggestion = match expr {
283-
Some(expr) => snippet_with_applicability(cx, expr.span, "v", &mut applicability),
284-
None => {
285-
applicability = Applicability::HasPlaceholders;
286-
Cow::Borrowed("v")
287-
},
300+
let suggestion = if let Some(e) = expr {
301+
snippet_with_applicability(cx, e.span, "v", &mut applicability)
302+
} else {
303+
applicability = Applicability::HasPlaceholders;
304+
Cow::Borrowed("v")
288305
};
289306

290307
span_lint_and_sugg(

tests/compile-test.rs

-3
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,6 @@ fn run_ui_toml(config: &mut compiletest::Config) {
153153
}
154154

155155
fn run_ui_cargo(config: &mut compiletest::Config) {
156-
if cargo::is_rustc_test_suite() {
157-
return;
158-
}
159156
fn run_tests(
160157
config: &compiletest::Config,
161158
filter: &Option<String>,

0 commit comments

Comments
 (0)