Skip to content

let_unit_with_type_underscore: make early-pass #15458

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
24 changes: 9 additions & 15 deletions clippy_lints/src/let_with_type_underscore.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::is_from_proc_macro;
use clippy_utils::source::{IntoSpan, SpanRangeExt};
use rustc_ast::{Local, TyKind};
use rustc_errors::Applicability;
use rustc_hir::{LetStmt, TyKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_session::declare_lint_pass;

declare_clippy_lint! {
Expand All @@ -26,28 +25,23 @@ declare_clippy_lint! {
}
declare_lint_pass!(UnderscoreTyped => [LET_WITH_TYPE_UNDERSCORE]);

impl<'tcx> LateLintPass<'tcx> for UnderscoreTyped {
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) {
if let Some(ty) = local.ty // Ensure that it has a type defined
&& let TyKind::Infer(()) = &ty.kind // that type is '_'
impl EarlyLintPass for UnderscoreTyped {
fn check_local(&mut self, cx: &EarlyContext<'_>, local: &Local) {
if let Some(ty) = &local.ty // Ensure that it has a type defined
&& let TyKind::Infer = ty.kind // that type is '_'
&& local.span.eq_ctxt(ty.span)
&& let sm = cx.tcx.sess.source_map()
&& let sm = cx.sess().source_map()
&& !local.span.in_external_macro(sm)
&& !is_from_proc_macro(cx, ty)
&& !is_from_proc_macro(cx, &**ty)
{
let span_to_remove = sm
.span_extend_to_prev_char_before(ty.span, ':', true)
.with_leading_whitespace(cx)
.into_span();

span_lint_and_then(
cx,
LET_WITH_TYPE_UNDERSCORE,
local.span,
"variable declared with type underscore",
|diag| {
diag.span_suggestion_verbose(
span_to_remove,
ty.span.with_lo(local.pat.span.hi()),
"remove the explicit type `_` declaration",
"",
Applicability::MachineApplicable,
Expand Down
2 changes: 1 addition & 1 deletion clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -742,7 +742,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage));
store.register_late_pass(|_| Box::new(needless_maybe_sized::NeedlessMaybeSized));
store.register_late_pass(|_| Box::new(redundant_async_block::RedundantAsyncBlock));
store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped));
store.register_early_pass(|| Box::new(let_with_type_underscore::UnderscoreTyped));
store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(conf)));
store.register_late_pass(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct));
store.register_late_pass(move |_| Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(conf)));
Expand Down
116 changes: 114 additions & 2 deletions clippy_utils/src/check_proc_macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@
//! if the span is not from a `macro_rules` based macro.

use rustc_abi::ExternAbi;
use rustc_ast as ast;
use rustc_ast::AttrStyle;
use rustc_ast::ast::{AttrKind, Attribute, IntTy, LitIntType, LitKind, StrStyle, TraitObjectSyntax, UintTy};
use rustc_ast::ast::{
AttrKind, Attribute, GenericArgs, IntTy, LitIntType, LitKind, StrStyle, TraitObjectSyntax, UintTy,
};
use rustc_ast::token::CommentKind;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{
Expand All @@ -26,7 +29,7 @@ use rustc_lint::{EarlyContext, LateContext, LintContext};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
use rustc_span::symbol::{Ident, kw};
use rustc_span::{Span, Symbol};
use rustc_span::{Span, Symbol, sym};

/// The search pattern to look for. Used by `span_matches_pat`
#[derive(Clone)]
Expand Down Expand Up @@ -400,6 +403,7 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) {
TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")),
TyKind::Path(qpath) => qpath_search_pat(&qpath),
TyKind::Infer(()) => (Pat::Str("_"), Pat::Str("_")),
TyKind::UnsafeBinder(binder_ty) => (Pat::Str("unsafe"), ty_search_pat(binder_ty.inner_ty).1),
TyKind::TraitObject(_, tagged_ptr) if let TraitObjectSyntax::Dyn = tagged_ptr.tag() => {
(Pat::Str("dyn"), Pat::Str(""))
},
Expand All @@ -408,6 +412,113 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) {
}
}

fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) {
use ast::{Extern, FnRetTy, MutTy, Safety, TraitObjectSyntax, TyKind};

match &ty.kind {
TyKind::Slice(..) | TyKind::Array(..) => (Pat::Str("["), Pat::Str("]")),
TyKind::Ptr(MutTy { ty, .. }) => (Pat::Str("*"), ast_ty_search_pat(ty).1),
TyKind::Ref(_, MutTy { ty, .. }) | TyKind::PinnedRef(_, MutTy { ty, .. }) => {
(Pat::Str("&"), ast_ty_search_pat(ty).1)
},
TyKind::FnPtr(fn_ptr) => (
if let Safety::Unsafe(_) = fn_ptr.safety {
Pat::Str("unsafe")
} else if let Extern::Explicit(strlit, _) = fn_ptr.ext
&& strlit.symbol == sym::rust
{
Pat::MultiStr(&["fn", "extern"])
} else {
Pat::Str("extern")
},
match &fn_ptr.decl.output {
FnRetTy::Default(_) => {
if let [.., param] = &*fn_ptr.decl.inputs {
ast_ty_search_pat(&param.ty).1
} else {
Pat::Str("(")
}
},
FnRetTy::Ty(ty) => ast_ty_search_pat(ty).1,
},
),
TyKind::Never => (Pat::Str("!"), Pat::Str("!")),
// Parenthesis are trimmed from the text before the search patterns are matched.
// See: `span_matches_pat`
TyKind::Tup(tup) => match &**tup {
[] => (Pat::Str(")"), Pat::Str("(")),
[ty] => ast_ty_search_pat(ty),
[head, .., tail] => (ast_ty_search_pat(head).0, ast_ty_search_pat(tail).1),
},
TyKind::ImplTrait(..) => (Pat::Str("impl"), Pat::Str("")),
TyKind::Path(qself_path, path) => {
let start = if qself_path.is_some() {
Pat::Str("<")
} else if let Some(first) = path.segments.first() {
ident_search_pat(first.ident).0
} else {
// this shouldn't be possible, but sure
Pat::Str("")
};
let end = if let Some(last) = path.segments.last() {
match last.args.as_deref() {
// last `>` in `std::foo::Bar<T>`
Some(GenericArgs::AngleBracketed(_)) => Pat::Str(">"),
Some(GenericArgs::Parenthesized(par_args)) => match &par_args.output {
// last `)` in `(A, B)`
FnRetTy::Default(_) => Pat::Str(")"),
// `C` in `(A, B) -> C`
FnRetTy::Ty(ty) => ast_ty_search_pat(ty).1,
},
// last `)` in `(..)`
Some(GenericArgs::ParenthesizedElided(_)) => Pat::Str(")"),
// `bar` in `std::foo::bar`
None => ident_search_pat(last.ident).1,
}
} else {
// this shouldn't be possible, but sure
#[allow(
clippy::collapsible_else_if,
reason = "we want to keep these cases together, since they are both impossible"
)]
if qself_path.is_some() {
// last `>` in `<Vec as IntoIterator>`
Pat::Str(">")
} else {
Pat::Str("")
}
};
(start, end)
},
TyKind::Infer => (Pat::Str("_"), Pat::Str("_")),
TyKind::Paren(ty) => ast_ty_search_pat(ty),
TyKind::UnsafeBinder(binder_ty) => (Pat::Str("unsafe"), ast_ty_search_pat(&binder_ty.inner_ty).1),
TyKind::TraitObject(_, trait_obj_syntax) => {
if let TraitObjectSyntax::Dyn = trait_obj_syntax {
(Pat::Str("dyn"), Pat::Str(""))
} else {
// NOTE: `TraitObject` is incomplete. It will always return true then.
(Pat::Str(""), Pat::Str(""))
}
},
TyKind::MacCall(_) => (Pat::Str(""), Pat::MultiStr(&[")", "]", "}"])), // closing parens of the invocation

// implicit, so has no contents to match against
TyKind::ImplicitSelf

// experimental
|TyKind::Pat(..)

// unused
| TyKind::CVarArgs
| TyKind::Typeof(_)

// placeholder
| TyKind::Dummy
| TyKind::Err(_) => (Pat::Str(""), Pat::Str("")),
}
}

fn ident_search_pat(ident: Ident) -> (Pat, Pat) {
(Pat::Sym(ident.name), Pat::Sym(ident.name))
}
Expand Down Expand Up @@ -442,6 +553,7 @@ impl_with_search_pat!((_cx: LateContext<'tcx>, self: Lit) => lit_search_pat(&sel
impl_with_search_pat!((_cx: LateContext<'tcx>, self: Path<'_>) => path_search_pat(self));

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

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