Skip to content

Commit 847ca73

Browse files
authored
let_unit_with_type_underscore: make early-pass (#15458)
This kind of supersedes #15386 -- by making the lint early-pass, we get access to `TyKind::Paren`s that surround the type ascription. And with that, we can return to the simpler calculation of `span_to_remove`. The biggest hurdle was `is_from_proc_macro` -- calling that function required me to `impl WithSearchPat for rustc_ast::Ty`, i.e. `ast_ty_search_pat`, which I based on `ty_search_pat`. Since that's a larger change, I could extract it into a PR of its own. changelog: none
2 parents 2147bb9 + 2459ad6 commit 847ca73

File tree

3 files changed

+137
-11
lines changed

3 files changed

+137
-11
lines changed

clippy_lints/src/let_with_type_underscore.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::is_from_proc_macro;
33
use clippy_utils::source::{IntoSpan, SpanRangeExt};
4+
use rustc_ast::{Local, TyKind};
45
use rustc_errors::Applicability;
5-
use rustc_hir::{LetStmt, TyKind};
6-
use rustc_lint::{LateContext, LateLintPass};
6+
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
77
use rustc_session::declare_lint_pass;
88

99
declare_clippy_lint! {
@@ -26,14 +26,14 @@ declare_clippy_lint! {
2626
}
2727
declare_lint_pass!(UnderscoreTyped => [LET_WITH_TYPE_UNDERSCORE]);
2828

29-
impl<'tcx> LateLintPass<'tcx> for UnderscoreTyped {
30-
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) {
31-
if let Some(ty) = local.ty // Ensure that it has a type defined
32-
&& let TyKind::Infer(()) = &ty.kind // that type is '_'
29+
impl EarlyLintPass for UnderscoreTyped {
30+
fn check_local(&mut self, cx: &EarlyContext<'_>, local: &Local) {
31+
if let Some(ty) = &local.ty // Ensure that it has a type defined
32+
&& let TyKind::Infer = ty.kind // that type is '_'
3333
&& local.span.eq_ctxt(ty.span)
34-
&& let sm = cx.tcx.sess.source_map()
34+
&& let sm = cx.sess().source_map()
3535
&& !local.span.in_external_macro(sm)
36-
&& !is_from_proc_macro(cx, ty)
36+
&& !is_from_proc_macro(cx, &**ty)
3737
{
3838
let span_to_remove = sm
3939
.span_extend_to_prev_char_before(ty.span, ':', true)

clippy_lints/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -744,7 +744,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
744744
store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage));
745745
store.register_late_pass(|_| Box::new(needless_maybe_sized::NeedlessMaybeSized));
746746
store.register_late_pass(|_| Box::new(redundant_async_block::RedundantAsyncBlock));
747-
store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped));
747+
store.register_early_pass(|| Box::new(let_with_type_underscore::UnderscoreTyped));
748748
store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(conf)));
749749
store.register_late_pass(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct));
750750
store.register_late_pass(move |_| Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(conf)));

clippy_utils/src/check_proc_macro.rs

Lines changed: 128 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@
1313
//! if the span is not from a `macro_rules` based macro.
1414
1515
use rustc_abi::ExternAbi;
16+
use rustc_ast as ast;
1617
use rustc_ast::AttrStyle;
17-
use rustc_ast::ast::{AttrKind, Attribute, IntTy, LitIntType, LitKind, StrStyle, TraitObjectSyntax, UintTy};
18+
use rustc_ast::ast::{
19+
AttrKind, Attribute, GenericArgs, IntTy, LitIntType, LitKind, StrStyle, TraitObjectSyntax, UintTy,
20+
};
1821
use rustc_ast::token::CommentKind;
1922
use rustc_hir::intravisit::FnKind;
2023
use rustc_hir::{
@@ -26,7 +29,7 @@ use rustc_lint::{EarlyContext, LateContext, LintContext};
2629
use rustc_middle::ty::TyCtxt;
2730
use rustc_session::Session;
2831
use rustc_span::symbol::{Ident, kw};
29-
use rustc_span::{Span, Symbol};
32+
use rustc_span::{Span, Symbol, sym};
3033

3134
/// The search pattern to look for. Used by `span_matches_pat`
3235
#[derive(Clone)]
@@ -403,6 +406,7 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) {
403406
TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")),
404407
TyKind::Path(qpath) => qpath_search_pat(&qpath),
405408
TyKind::Infer(()) => (Pat::Str("_"), Pat::Str("_")),
409+
TyKind::UnsafeBinder(binder_ty) => (Pat::Str("unsafe"), ty_search_pat(binder_ty.inner_ty).1),
406410
TyKind::TraitObject(_, tagged_ptr) if let TraitObjectSyntax::Dyn = tagged_ptr.tag() => {
407411
(Pat::Str("dyn"), Pat::Str(""))
408412
},
@@ -411,6 +415,127 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) {
411415
}
412416
}
413417

418+
fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) {
419+
use ast::{Extern, FnRetTy, MutTy, Safety, TraitObjectSyntax, TyKind};
420+
421+
match &ty.kind {
422+
TyKind::Slice(..) | TyKind::Array(..) => (Pat::Str("["), Pat::Str("]")),
423+
TyKind::Ptr(MutTy { ty, .. }) => (Pat::Str("*"), ast_ty_search_pat(ty).1),
424+
TyKind::Ref(_, MutTy { ty, .. }) | TyKind::PinnedRef(_, MutTy { ty, .. }) => {
425+
(Pat::Str("&"), ast_ty_search_pat(ty).1)
426+
},
427+
TyKind::FnPtr(fn_ptr) => (
428+
if let Safety::Unsafe(_) = fn_ptr.safety {
429+
Pat::Str("unsafe")
430+
} else if let Extern::Explicit(strlit, _) = fn_ptr.ext
431+
&& strlit.symbol == sym::rust
432+
{
433+
Pat::MultiStr(&["fn", "extern"])
434+
} else {
435+
Pat::Str("extern")
436+
},
437+
match &fn_ptr.decl.output {
438+
FnRetTy::Default(_) => {
439+
if let [.., param] = &*fn_ptr.decl.inputs {
440+
ast_ty_search_pat(&param.ty).1
441+
} else {
442+
Pat::Str("(")
443+
}
444+
},
445+
FnRetTy::Ty(ty) => ast_ty_search_pat(ty).1,
446+
},
447+
),
448+
TyKind::Never => (Pat::Str("!"), Pat::Str("!")),
449+
// Parenthesis are trimmed from the text before the search patterns are matched.
450+
// See: `span_matches_pat`
451+
TyKind::Tup(tup) => match &**tup {
452+
[] => (Pat::Str(")"), Pat::Str("(")),
453+
[ty] => ast_ty_search_pat(ty),
454+
[head, .., tail] => (ast_ty_search_pat(head).0, ast_ty_search_pat(tail).1),
455+
},
456+
TyKind::ImplTrait(..) => (Pat::Str("impl"), Pat::Str("")),
457+
TyKind::Path(qself_path, path) => {
458+
let start = if qself_path.is_some() {
459+
Pat::Str("<")
460+
} else if let Some(first) = path.segments.first() {
461+
ident_search_pat(first.ident).0
462+
} else {
463+
// this shouldn't be possible, but sure
464+
Pat::Str("")
465+
};
466+
let end = if let Some(last) = path.segments.last() {
467+
match last.args.as_deref() {
468+
// last `>` in `std::foo::Bar<T>`
469+
Some(GenericArgs::AngleBracketed(_)) => Pat::Str(">"),
470+
Some(GenericArgs::Parenthesized(par_args)) => match &par_args.output {
471+
FnRetTy::Default(_) => {
472+
if let Some(last) = par_args.inputs.last() {
473+
// `B` in `(A, B)` -- `)` gets stripped
474+
ast_ty_search_pat(last).1
475+
} else {
476+
// `(` in `()` -- `)` gets stripped
477+
Pat::Str("(")
478+
}
479+
},
480+
// `C` in `(A, B) -> C`
481+
FnRetTy::Ty(ty) => ast_ty_search_pat(ty).1,
482+
},
483+
// last `..` in `(..)` -- `)` gets stripped
484+
Some(GenericArgs::ParenthesizedElided(_)) => Pat::Str(".."),
485+
// `bar` in `std::foo::bar`
486+
None => ident_search_pat(last.ident).1,
487+
}
488+
} else {
489+
// this shouldn't be possible, but sure
490+
#[allow(
491+
clippy::collapsible_else_if,
492+
reason = "we want to keep these cases together, since they are both impossible"
493+
)]
494+
if qself_path.is_some() {
495+
// last `>` in `<Vec as IntoIterator>`
496+
Pat::Str(">")
497+
} else {
498+
Pat::Str("")
499+
}
500+
};
501+
(start, end)
502+
},
503+
TyKind::Infer => (Pat::Str("_"), Pat::Str("_")),
504+
TyKind::Paren(ty) => ast_ty_search_pat(ty),
505+
TyKind::UnsafeBinder(binder_ty) => (Pat::Str("unsafe"), ast_ty_search_pat(&binder_ty.inner_ty).1),
506+
TyKind::TraitObject(_, trait_obj_syntax) => {
507+
if let TraitObjectSyntax::Dyn = trait_obj_syntax {
508+
(Pat::Str("dyn"), Pat::Str(""))
509+
} else {
510+
// NOTE: `TraitObject` is incomplete. It will always return true then.
511+
(Pat::Str(""), Pat::Str(""))
512+
}
513+
},
514+
TyKind::MacCall(mac_call) => {
515+
let start = if let Some(first) = mac_call.path.segments.first() {
516+
ident_search_pat(first.ident).0
517+
} else {
518+
Pat::Str("")
519+
};
520+
(start, Pat::Str(""))
521+
},
522+
523+
// implicit, so has no contents to match against
524+
TyKind::ImplicitSelf
525+
526+
// experimental
527+
|TyKind::Pat(..)
528+
529+
// unused
530+
| TyKind::CVarArgs
531+
| TyKind::Typeof(_)
532+
533+
// placeholder
534+
| TyKind::Dummy
535+
| TyKind::Err(_) => (Pat::Str(""), Pat::Str("")),
536+
}
537+
}
538+
414539
fn ident_search_pat(ident: Ident) -> (Pat, Pat) {
415540
(Pat::Sym(ident.name), Pat::Sym(ident.name))
416541
}
@@ -445,6 +570,7 @@ impl_with_search_pat!((_cx: LateContext<'tcx>, self: Lit) => lit_search_pat(&sel
445570
impl_with_search_pat!((_cx: LateContext<'tcx>, self: Path<'_>) => path_search_pat(self));
446571

447572
impl_with_search_pat!((_cx: EarlyContext<'tcx>, self: Attribute) => attr_search_pat(self));
573+
impl_with_search_pat!((_cx: EarlyContext<'tcx>, self: ast::Ty) => ast_ty_search_pat(self));
448574

449575
impl<'cx> WithSearchPat<'cx> for (&FnKind<'cx>, &Body<'cx>, HirId, Span) {
450576
type Context = LateContext<'cx>;

0 commit comments

Comments
 (0)