mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-15 20:45:45 +03:00
let_unit_with_type_underscore: make early-pass (#15458)
This kind of supersedes https://github.com/rust-lang/rust-clippy/pull/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
This commit is contained in:
@@ -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! {
|
||||
@@ -26,14 +26,14 @@
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -744,7 +744,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)));
|
||||
|
||||
@@ -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::{
|
||||
@@ -26,7 +29,7 @@
|
||||
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)]
|
||||
@@ -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(""))
|
||||
},
|
||||
@@ -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(¶m.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))
|
||||
}
|
||||
@@ -445,6 +570,7 @@ fn span(&self) -> Span {
|
||||
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>;
|
||||
|
||||
Reference in New Issue
Block a user