Skip to content
16 changes: 8 additions & 8 deletions clippy_lints/src/let_with_type_underscore.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
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,14 +26,14 @@ 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)
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 @@ -743,7 +743,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
130 changes: 128 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 @@ -403,6 +406,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 @@ -411,6 +415,127 @@ 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 {
FnRetTy::Default(_) => {
if let Some(last) = par_args.inputs.last() {
// `B` in `(A, B)` -- `)` gets stripped
ast_ty_search_pat(last).1
} else {
// `(` in `()` -- `)` gets stripped
Pat::Str("(")
}
},
// `C` in `(A, B) -> C`
FnRetTy::Ty(ty) => ast_ty_search_pat(ty).1,
},
// last `..` in `(..)` -- `)` gets stripped
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(mac_call) => {
let start = if let Some(first) = mac_call.path.segments.first() {
ident_search_pat(first.ident).0
} else {
Pat::Str("")
};
(start, Pat::Str(""))
},

// 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 @@ -445,6 +570,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