Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
0b43e03
Add applicability visually
SpencerAWill Apr 5, 2024
e4ce248
Complete filter functionality. Formatted upper filters to take more v…
SpencerAWill Apr 9, 2024
c8a7e6e
Remove debug helper.
SpencerAWill Apr 11, 2024
dff9164
consider `copy_deref` a possible borrower
y21 May 2, 2024
20d1bb1
[`assigning_clones`]: bail out when the source borrows from target
y21 May 2, 2024
60508f5
deal with non-`Drop` types and add a test case for projections
y21 May 2, 2024
5e60afb
[ arc_with_non_send_sync ]: fix doc nits
bitfield Jun 3, 2024
7ab4af3
[ needless_for_each ]: fix doc nits
bitfield Jun 3, 2024
35d284f
[ allow_attributes ]: fix doc nits
bitfield Jun 3, 2024
8da5d64
[ allow_attributes_without_reason ]: fix doc nits
bitfield Jun 3, 2024
7c86db4
[ mut_range_bound ]: fix doc nits
bitfield Jun 3, 2024
059eaf1
Fix ICE in `upper_case_acronyms` and remove most of the string alloc…
Jarcho Jun 8, 2024
70ca9a1
Lint `manual_unwrap_or` for it let cases
lochetti Jun 8, 2024
c3d4633
Fixup clippy tests
WaffleLapkin May 23, 2024
3bff119
Merge commit '3e5a02b13b1244545454752c6629b767522a44b1' into clippy-s…
flip1995 Jun 13, 2024
ba347f9
Auto merge of #126398 - flip1995:clippy-subtree-update, r=Manishearth
bors Jun 13, 2024
fa9274c
Tweak output of import suggestions
estebank Jun 12, 2024
82f0dc9
[`missing_const_for_fn`]: add machine-applicable suggestion
DaniPopes Jun 14, 2024
73c1bfb
Auto merge of #12880 - bitfield:fix_doc_nits_b, r=Alexendoo
bors Jun 15, 2024
477b0c6
lintcheck: Add JSON output, diff subcommand
Alexendoo Nov 1, 2022
feb0671
Add lintcheck diff github action
Alexendoo Feb 24, 2023
0dc265f
Auto merge of #12756 - y21:assigning_clones_lifetimes, r=Alexendoo
bors Jun 15, 2024
63388cb
add MSRV for manual_pattern_char_comparison
AurelienFT Jun 15, 2024
51c6630
Change MSRV check for manual_pattern_char_comparison only for pattern…
AurelienFT Jun 15, 2024
bcc7b0e
Fix minor typo
elijah-potter Jun 15, 2024
82345c3
Auto merge of #12937 - AurelienFT:add_msrv_manual_patter_char_compari…
bors Jun 15, 2024
a2c9782
Auto merge of #10398 - Alexendoo:auto-lintcheck, r=xFrednet
bors Jun 16, 2024
3a983c3
Merge lintcheck popular-crates bin as a subcommand
Alexendoo Jun 16, 2024
8065e0f
Auto merge of #12939 - Alexendoo:lintcheck-popular-crates, r=xFrednet
bors Jun 16, 2024
3405ce3
Add field_scoped_visibility_modifiers lint
kyleoneill Jun 16, 2024
9f5d60f
Auto merge of #12893 - kyleoneill:field_scoped_visibility_modifiers, …
bors Jun 16, 2024
a002f93
`expr_use_ctxt` changes:
Jarcho Feb 12, 2024
22710f3
Add lint `manual_inspect`
Jarcho Feb 12, 2024
16efa56
Auto merge of #12287 - Jarcho:issue_12250, r=llogiq
bors Jun 16, 2024
e18b310
Add more types to `is_from_proc_macro`
Jarcho Jun 17, 2024
f09650b
Use symbols when raising range expressions.
Jarcho Jun 16, 2024
4b16e26
Rework `octal_escapes`.
Jarcho Jun 15, 2024
6172178
Auto merge of #12938 - elijah-potter:patch-1, r=flip1995
bors Jun 17, 2024
2ad53f4
Auto merge of #12943 - Jarcho:range_sym, r=llogiq
bors Jun 17, 2024
198bbf8
Pause assignments to @xFrednet for summer break :beach_umbrella:
xFrednet Jun 17, 2024
9550481
Auto merge of #12947 - xFrednet:00000-summer-assignments, r=xFrednet
bors Jun 17, 2024
0625183
Auto merge of #12903 - Jarcho:issue_12284, r=y21
bors Jun 17, 2024
51c5eee
Auto merge of #12945 - Jarcho:octal_escape, r=Alexendoo
bors Jun 17, 2024
9e54ff2
Auto merge of #12906 - lochetti:manual_unwrap_or_if_let, r=y21
bors Jun 17, 2024
61fc1ae
Rework precise capturing syntax
compiler-errors Jun 5, 2024
7218fdd
Fix other tools
compiler-errors Jun 6, 2024
e28b998
Add myself back to reviewer rotation
Centri3 Jun 18, 2024
7e1ed1a
Auto merge of #12953 - Centri3:back, r=Centri3
bors Jun 18, 2024
bd75e44
Auto merge of #12655 - SpencerAWill:Add-applicability-filter, r=xFrednet
bors Jun 18, 2024
4b7ae63
Use a dedicated type instead of a reference for the diagnostic context
oli-obk Jun 18, 2024
29cc5c6
Auto merge of #12942 - Jarcho:ex_proc_macro, r=Manishearth
bors Jun 18, 2024
8cde354
Resolve Clippy `f16` and `f128` `unimplemented!`/`FIXME`s
tgross35 Jun 18, 2024
c693f31
Update float tests to include `f16` and `f128`
tgross35 Jun 18, 2024
b147b6d
Don't lint implicit_return on proc macros
lochetti Jun 19, 2024
4aee08f
Auto merge of #12963 - lochetti:fix_12874, r=Centri3
bors Jun 19, 2024
9749d99
resolve `clippy::invalid_paths` on `bool::then`
KisaragiEffective Jun 20, 2024
3baafd2
Fix `...` in multline code-skips in suggestions
estebank Jun 20, 2024
2f9f204
feat: unnecessary_min_max lint
l0ngvh Feb 28, 2024
3e84ca8
Auto merge of #12368 - vohoanglong0107:unnecessary-min, r=Alexendoo
bors Jun 20, 2024
f6661f5
StaticForeignItem and StaticItem are the same
compiler-errors Jun 20, 2024
4baae5d
Add new `Span` utils to avoid both allocating and
Jarcho Jun 20, 2024
fe2fe7f
Auto merge of #12972 - Jarcho:span_ex, r=Manishearth
bors Jun 21, 2024
54b45f7
Fix incorrect suggestion for `manual_unwrap_or_default`
GuillaumeGomez Jun 19, 2024
0ce07f6
Auto merge of #12961 - GuillaumeGomez:fix-manual_unwrap_or_default, r…
bors Jun 21, 2024
58fc27f
Rollup merge of #126723 - estebank:dot-dot-dot, r=Nadrieril
GuillaumeGomez Jun 22, 2024
26c556d
Auto merge of #12965 - KisaragiEffective:resolve-invalid-paths-on-boo…
bors Jun 22, 2024
51ccad6
use short message format in integration test
llogiq Jun 23, 2024
9628130
Auto merge of #12985 - llogiq:fix-integration-test, r=Alexendoo
bors Jun 23, 2024
2194304
Cache lintcheck binary in ci
Alexendoo Jun 22, 2024
32374a1
Auto merge of #12930 - DaniPopes:missing-const-for-fn-suggestion, r=J…
bors Jun 23, 2024
35ec4eb
Rename the 2 unambiguous precedence levels to PREC_UNAMBIGUOUS
dtolnay Jun 24, 2024
8c718e5
ast: Standardize visiting order for attributes and node IDs
petrochenkov May 29, 2024
8631790
Auto merge of #12986 - Alexendoo:cache-lintcheck-bin, r=flip1995
bors Jun 24, 2024
606ada1
bump strip-ansi-escapes
klensy Jun 24, 2024
a155c38
Split out IntoIterator and non-Iterator constructors for AliasTy/Alia…
compiler-errors Jun 21, 2024
8998ce2
Replace Deref bounds on Interner in favor of a SliceLike trait
compiler-errors Jun 21, 2024
dfaa53f
Auto merge of #125741 - petrochenkov:atvisord, r=davidtwco
bors Jun 25, 2024
1b4c281
RFC 2383: Stabilize `lint_reasons` in Clippy :paperclips:
xFrednet Feb 10, 2024
01b3c24
Rollup merge of #126893 - dtolnay:prec, r=compiler-errors
matthiaskrgr Jun 25, 2024
3bbec6a
`sudo CI=green` && Review changes <3
xFrednet Jun 4, 2024
f90d702
Auto merge of #120924 - xFrednet:rfc-2383-stabilization-party, r=Urga…
bors Jun 26, 2024
80b25b4
Fix doc_markdown DevOps false positive
reillysiemens Jun 25, 2024
aaaa926
Auto merge of #12995 - reillysiemens:fix-doc-markdown-devops-false-po…
bors Jun 27, 2024
4ddc8a2
Auto merge of #12992 - klensy:lintcheck-bump, r=Alexendoo
bors Jun 27, 2024
e9e7a81
Merge remote-tracking branch 'upstream/master' into rustup
flip1995 Jun 27, 2024
585170e
Bump nightly version -> 2024-06-27
flip1995 Jun 27, 2024
68a799a
Auto merge of #12999 - flip1995:rustup, r=flip1995
bors Jun 27, 2024
3ce7f9e
Merge commit '68a799aea9b65e2444fbecfe32217ce7d5a3604f' into clippy-s…
flip1995 Jun 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add lint manual_inspect
  • Loading branch information
Jarcho committed Jun 16, 2024
commit 22710f33a8ad99be51bb69f91339f8d815bd9e92
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5521,6 +5521,7 @@ Released 2018-09-13
[`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
[`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
[`manual_hash_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one
[`manual_inspect`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_inspect
[`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed
[`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check
[`manual_is_finite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_finite
Expand Down
2 changes: 1 addition & 1 deletion clippy_config/src/msrvs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ macro_rules! msrv_aliases {
// names may refer to stabilized feature flags or library items
msrv_aliases! {
1,77,0 { C_STR_LITERALS }
1,76,0 { PTR_FROM_REF }
1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT }
1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE }
1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN }
1,68,0 { PATH_MAIN_SEPARATOR_STR }
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::methods::MANUAL_C_STR_LITERALS_INFO,
crate::methods::MANUAL_FILTER_MAP_INFO,
crate::methods::MANUAL_FIND_MAP_INFO,
crate::methods::MANUAL_INSPECT_INFO,
crate::methods::MANUAL_IS_VARIANT_AND_INFO,
crate::methods::MANUAL_NEXT_BACK_INFO,
crate::methods::MANUAL_OK_OR_INFO,
Expand Down
233 changes: 233 additions & 0 deletions clippy_lints/src/methods/manual_inspect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::{get_source_text, with_leading_whitespace, SpanRange};
use clippy_utils::ty::get_field_by_name;
use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures};
use clippy_utils::{expr_use_ctxt, is_diag_item_method, is_diag_trait_item, path_to_local_id, ExprUseNode};
use core::ops::ControlFlow;
use rustc_errors::Applicability;
use rustc_hir::{BindingMode, BorrowKind, ByRef, ClosureKind, Expr, ExprKind, Mutability, Node, PatKind};
use rustc_lint::LateContext;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
use rustc_span::{sym, BytePos, Span, Symbol, DUMMY_SP};

use super::MANUAL_INSPECT;

#[expect(clippy::too_many_lines)]
pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: &str, name_span: Span, msrv: &Msrv) {
if let ExprKind::Closure(c) = arg.kind
&& matches!(c.kind, ClosureKind::Closure)
&& let typeck = cx.typeck_results()
&& let Some(fn_id) = typeck.type_dependent_def_id(expr.hir_id)
&& (is_diag_trait_item(cx, fn_id, sym::Iterator)
|| (msrv.meets(msrvs::OPTION_RESULT_INSPECT)
&& (is_diag_item_method(cx, fn_id, sym::Option) || is_diag_item_method(cx, fn_id, sym::Result))))
&& let body = cx.tcx.hir().body(c.body)
&& let [param] = body.params
&& let PatKind::Binding(BindingMode(ByRef::No, Mutability::Not), arg_id, _, None) = param.pat.kind
&& let arg_ty = typeck.node_type(arg_id)
&& let ExprKind::Block(block, _) = body.value.kind
&& let Some(final_expr) = block.expr
&& !block.stmts.is_empty()
&& path_to_local_id(final_expr, arg_id)
&& typeck.expr_adjustments(final_expr).is_empty()
{
let mut requires_copy = false;
let mut requires_deref = false;

// The number of unprocessed return expressions.
let mut ret_count = 0u32;

// The uses for which processing is delayed until after the visitor.
let mut delayed = vec![];

let ctxt = arg.span.ctxt();
let can_lint = for_each_expr_without_closures(block.stmts, |e| {
if let ExprKind::Closure(c) = e.kind {
// Nested closures don't need to treat returns specially.
let _: Option<!> = for_each_expr(cx, cx.tcx.hir().body(c.body).value, |e| {
if path_to_local_id(e, arg_id) {
let (kind, same_ctxt) = check_use(cx, e);
match (kind, same_ctxt && e.span.ctxt() == ctxt) {
(_, false) | (UseKind::Deref | UseKind::Return(..), true) => {
requires_copy = true;
requires_deref = true;
},
(UseKind::AutoBorrowed, true) => {},
(UseKind::WillAutoDeref, true) => {
requires_copy = true;
},
(kind, true) => delayed.push(kind),
}
}
ControlFlow::Continue(())
});
} else if matches!(e.kind, ExprKind::Ret(_)) {
ret_count += 1;
} else if path_to_local_id(e, arg_id) {
let (kind, same_ctxt) = check_use(cx, e);
match (kind, same_ctxt && e.span.ctxt() == ctxt) {
(UseKind::Return(..), false) => {
return ControlFlow::Break(());
},
(_, false) | (UseKind::Deref, true) => {
requires_copy = true;
requires_deref = true;
},
(UseKind::AutoBorrowed, true) => {},
(UseKind::WillAutoDeref, true) => {
requires_copy = true;
},
(kind @ UseKind::Return(_), true) => {
ret_count -= 1;
delayed.push(kind);
},
(kind, true) => delayed.push(kind),
}
}
ControlFlow::Continue(())
})
.is_none();

if ret_count != 0 {
// A return expression that didn't return the original value was found.
return;
}

let mut edits = Vec::with_capacity(delayed.len() + 3);
let mut addr_of_edits = Vec::with_capacity(delayed.len());
for x in delayed {
match x {
UseKind::Return(s) => edits.push((with_leading_whitespace(cx, s).set_span_pos(s), String::new())),
UseKind::Borrowed(s) => {
if let Some(src) = get_source_text(cx, s)
&& let Some(src) = src.as_str()
&& let trim_src = src.trim_start_matches([' ', '\t', '\n', '\r', '('])
&& trim_src.starts_with('&')
{
let range = s.into_range();
#[expect(clippy::cast_possible_truncation)]
let start = BytePos(range.start.0 + (src.len() - trim_src.len()) as u32);
addr_of_edits.push(((start..BytePos(start.0 + 1)).set_span_pos(s), String::new()));
} else {
requires_copy = true;
requires_deref = true;
}
},
UseKind::FieldAccess(name, e) => {
let Some(mut ty) = get_field_by_name(cx.tcx, arg_ty.peel_refs(), name) else {
requires_copy = true;
continue;
};
let mut prev_expr = e;

for (_, parent) in cx.tcx.hir().parent_iter(e.hir_id) {
if let Node::Expr(e) = parent {
match e.kind {
ExprKind::Field(_, name)
if let Some(fty) = get_field_by_name(cx.tcx, ty.peel_refs(), name.name) =>
{
ty = fty;
prev_expr = e;
continue;
},
ExprKind::AddrOf(BorrowKind::Ref, ..) => break,
_ if matches!(
typeck.expr_adjustments(prev_expr).first(),
Some(Adjustment {
kind: Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Not))
| Adjust::Deref(_),
..
})
) =>
{
break;
},
_ => {},
}
}
requires_copy |= !ty.is_copy_modulo_regions(cx.tcx, cx.param_env);
break;
}
},
// Already processed uses.
UseKind::AutoBorrowed | UseKind::WillAutoDeref | UseKind::Deref => {},
}
}

if can_lint
&& (!requires_copy || arg_ty.is_copy_modulo_regions(cx.tcx, cx.param_env))
// This case could be handled, but a fair bit of care would need to be taken.
&& (!requires_deref || arg_ty.is_freeze(cx.tcx, cx.param_env))
{
if requires_deref {
edits.push((param.span.shrink_to_lo(), "&".into()));
} else {
edits.extend(addr_of_edits);
}
edits.push((
name_span,
String::from(match name {
"map" => "inspect",
"map_err" => "inspect_err",
_ => return,
}),
));
edits.push((
with_leading_whitespace(cx, final_expr.span).set_span_pos(final_expr.span),
String::new(),
));
let app = if edits.iter().any(|(s, _)| s.from_expansion()) {
Applicability::MaybeIncorrect
} else {
Applicability::MachineApplicable
};
span_lint_and_then(cx, MANUAL_INSPECT, name_span, "", |diag| {
diag.multipart_suggestion("try", edits, app);
});
}
}
}

enum UseKind<'tcx> {
AutoBorrowed,
WillAutoDeref,
Deref,
Return(Span),
Borrowed(Span),
FieldAccess(Symbol, &'tcx Expr<'tcx>),
}

/// Checks how the value is used, and whether it was used in the same `SyntaxContext`.
fn check_use<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (UseKind<'tcx>, bool) {
let use_cx = expr_use_ctxt(cx, e);
if use_cx
.adjustments
.first()
.is_some_and(|a| matches!(a.kind, Adjust::Deref(_)))
{
return (UseKind::AutoBorrowed, use_cx.same_ctxt);
}
let res = match use_cx.use_node(cx) {
ExprUseNode::Return(_) => {
if let ExprKind::Ret(Some(e)) = use_cx.node.expect_expr().kind {
UseKind::Return(e.span)
} else {
return (UseKind::Return(DUMMY_SP), false);
}
},
ExprUseNode::FieldAccess(name) => UseKind::FieldAccess(name.name, use_cx.node.expect_expr()),
ExprUseNode::Callee | ExprUseNode::MethodArg(_, _, 0)
if use_cx
.adjustments
.first()
.is_some_and(|a| matches!(a.kind, Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Not)))) =>
{
UseKind::AutoBorrowed
},
ExprUseNode::Callee | ExprUseNode::MethodArg(_, _, 0) => UseKind::WillAutoDeref,
ExprUseNode::AddrOf(BorrowKind::Ref, _) => UseKind::Borrowed(use_cx.node.expect_expr().span),
_ => UseKind::Deref,
};
(res, use_cx.same_ctxt)
}
24 changes: 24 additions & 0 deletions clippy_lints/src/methods/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ mod iter_with_drain;
mod iterator_step_by_zero;
mod join_absolute_paths;
mod manual_c_str_literals;
mod manual_inspect;
mod manual_is_variant_and;
mod manual_next_back;
mod manual_ok_or;
Expand Down Expand Up @@ -4079,6 +4080,27 @@ declare_clippy_lint! {
"is_ascii() called on a char iterator"
}

declare_clippy_lint! {
/// ### What it does
/// Checks for uses of `map` which return the original item.
///
/// ### Why is this bad?
/// `inspect` is both clearer in intent and shorter.
///
/// ### Example
/// ```no_run
/// let x = Some(0).map(|x| { println!("{x}"); x });
/// ```
/// Use instead:
/// ```no_run
/// let x = Some(0).inspect(|x| println!("{x}"));
/// ```
#[clippy::version = "1.78.0"]
pub MANUAL_INSPECT,
complexity,
"use of `map` returning the original item"
}

pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Msrv,
Expand Down Expand Up @@ -4244,6 +4266,7 @@ impl_lint_pass!(Methods => [
MANUAL_C_STR_LITERALS,
UNNECESSARY_GET_THEN_CHECK,
NEEDLESS_CHARACTER_ITERATION,
MANUAL_INSPECT,
]);

/// Extracts a method call name, args, and `Span` of the method name.
Expand Down Expand Up @@ -4747,6 +4770,7 @@ impl Methods {
}
}
map_identity::check(cx, expr, recv, m_arg, name, span);
manual_inspect::check(cx, expr, m_arg, name, span, &self.msrv);
},
("map_or", [def, map]) => {
option_map_or_none::check(cx, expr, recv, def, map);
Expand Down
27 changes: 26 additions & 1 deletion clippy_utils/src/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,17 @@ use std::borrow::Cow;
use std::ops::Range;

/// A type which can be converted to the range portion of a `Span`.
pub trait SpanRange {
pub trait SpanRange: Sized {
fn into_range(self) -> Range<BytePos>;
fn set_span_pos(self, sp: Span) -> Span {
let range = self.into_range();
SpanData {
lo: range.start,
hi: range.end,
..sp.data()
}
.span()
}
}
impl SpanRange for Span {
fn into_range(self) -> Range<BytePos> {
Expand Down Expand Up @@ -61,6 +70,22 @@ pub fn get_source_text(cx: &impl LintContext, sp: impl SpanRange) -> Option<Sour
f(cx.sess().source_map(), sp.into_range())
}

pub fn with_leading_whitespace(cx: &impl LintContext, sp: impl SpanRange) -> Range<BytePos> {
#[expect(clippy::needless_pass_by_value, clippy::cast_possible_truncation)]
fn f(src: SourceFileRange, sp: Range<BytePos>) -> Range<BytePos> {
let Some(text) = &src.sf.src else {
return sp;
};
let len = src.range.start - text[..src.range.start].trim_end().len();
BytePos(sp.start.0 - len as u32)..sp.end
}
let sp = sp.into_range();
match get_source_text(cx, sp.clone()) {
Some(src) => f(src, sp),
None => sp,
}
}

/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
pub fn expr_block<T: LintContext>(
cx: &T,
Expand Down
14 changes: 14 additions & 0 deletions clippy_utils/src/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1349,3 +1349,17 @@ pub fn get_adt_inherent_method<'a>(cx: &'a LateContext<'_>, ty: Ty<'_>, method_n
None
}
}

/// Get's the type of a field by name.
pub fn get_field_by_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, name: Symbol) -> Option<Ty<'tcx>> {
match *ty.kind() {
ty::Adt(def, args) if def.is_union() || def.is_struct() => def
.non_enum_variant()
.fields
.iter()
.find(|f| f.name == name)
.map(|f| f.ty(tcx, args)),
ty::Tuple(args) => name.as_str().parse::<usize>().ok().and_then(|i| args.get(i).copied()),
_ => None,
}
}
1 change: 1 addition & 0 deletions tests/ui/copy_iterator.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![warn(clippy::copy_iterator)]
#![allow(clippy::manual_inspect)]

#[derive(Copy, Clone)]
struct Countdown(u8);
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/copy_iterator.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: you are implementing `Iterator` on a `Copy` type
--> tests/ui/copy_iterator.rs:6:1
--> tests/ui/copy_iterator.rs:7:1
|
LL | / impl Iterator for Countdown {
LL | |
Expand Down
Loading