//! Checks for usage of `&Vec[_]` and `&String`.
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::ptr::get_spans;
use clippy_utils::source::snippet_opt;
-use clippy_utils::ty::{is_type_diagnostic_item, match_type, walk_ptrs_hir_ty};
-use clippy_utils::{expr_path_res, is_lint_allowed, match_any_diagnostic_items, paths};
+use clippy_utils::ty::expr_sig;
+use clippy_utils::{
+ expr_path_res, get_expr_use_or_unification_node, is_lint_allowed, match_any_diagnostic_items, path_to_local, paths,
+};
use if_chain::if_chain;
use rustc_errors::Applicability;
+use rustc_hir::def_id::DefId;
+use rustc_hir::hir_id::HirIdMap;
+use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{
- BinOpKind, BodyId, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, HirId, Impl, ImplItem, ImplItemKind, Item,
- ItemKind, Lifetime, MutTy, Mutability, Node, PathSegment, QPath, TraitFn, TraitItem, TraitItemKind, Ty, TyKind,
+ self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArg,
+ ImplItemKind, ItemKind, Lifetime, LifetimeName, Mutability, Node, Param, ParamName, PatKind, QPath, TraitFn,
+ TraitItem, TraitItemKind, TyKind,
};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
+use rustc_middle::hir::nested_filter;
+use rustc_middle::ty::{self, AssocItems, AssocKind, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
use rustc_span::symbol::Symbol;
use rustc_span::{sym, MultiSpan};
-use std::borrow::Cow;
+use std::fmt;
+use std::iter;
declare_clippy_lint! {
/// ### What it does
- /// This lint checks for function arguments of type `&String`
- /// or `&Vec` unless the references are mutable. It will also suggest you
- /// replace `.clone()` calls with the appropriate `.to_owned()`/`to_string()`
- /// calls.
+ /// This lint checks for function arguments of type `&String`, `&Vec`,
+ /// `&PathBuf`, and `Cow<_>`. It will also suggest you replace `.clone()` calls
+ /// with the appropriate `.to_owned()`/`to_string()` calls.
///
/// ### Why is this bad?
/// Requiring the argument to be of the specific size
/// or `&str` usually suffice and can be obtained from other types, too.
///
/// ### Known problems
- /// The lint does not follow data. So if you have an
- /// argument `x` and write `let y = x; y.clone()` the lint will not suggest
- /// changing that `.clone()` to `.to_owned()`.
- ///
- /// Other functions called from this function taking a `&String` or `&Vec`
- /// argument may also fail to compile if you change the argument. Applying
- /// this lint on them will fix the problem, but they may be in other crates.
- ///
- /// One notable example of a function that may cause issues, and which cannot
- /// easily be changed due to being in the standard library is `Vec::contains`.
- /// when called on a `Vec<Vec<T>>`. If a `&Vec` is passed to that method then
- /// it will compile, but if a `&[T]` is passed then it will not compile.
- ///
- /// ```ignore
- /// fn cannot_take_a_slice(v: &Vec<u8>) -> bool {
- /// let vec_of_vecs: Vec<Vec<u8>> = some_other_fn();
- ///
- /// vec_of_vecs.contains(v)
- /// }
- /// ```
- ///
- /// Also there may be `fn(&Vec)`-typed references pointing to your function.
+ /// There may be `fn(&Vec)`-typed references pointing to your function.
/// If you have them, you will get a compiler error after applying this lint's
/// suggestions. You then have the choice to undo your changes or change the
/// type of the reference.
/// // Good
/// fn foo(&[u32]) { .. }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub PTR_ARG,
style,
"fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively"
/// ..
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub CMP_NULL,
style,
"comparing a pointer to a null pointer, suggesting to use `.is_null()` instead"
/// ```ignore
/// fn foo(&Foo) -> &mut Bar { .. }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MUT_FROM_REF,
correctness,
"fns that create mutable refs from immutable ref args"
/// unsafe { std::slice::from_raw_parts(ptr::null(), 0); }
/// ```
///
+ /// ```ignore
/// // Good
/// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); }
/// ```
+ #[clippy::version = "1.53.0"]
pub INVALID_NULL_PTR_USAGE,
correctness,
"invalid usage of a null pointer, suggesting `NonNull::dangling()` instead"
declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, INVALID_NULL_PTR_USAGE]);
impl<'tcx> LateLintPass<'tcx> for Ptr {
- fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
- if let ItemKind::Fn(ref sig, _, body_id) = item.kind {
- check_fn(cx, sig.decl, item.hir_id(), Some(body_id));
- }
- }
+ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
+ if let TraitItemKind::Fn(sig, trait_method) = &item.kind {
+ if matches!(trait_method, TraitFn::Provided(_)) {
+ // Handled by check body.
+ return;
+ }
- fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
- if let ImplItemKind::Fn(ref sig, body_id) = item.kind {
- let parent_item = cx.tcx.hir().get_parent_item(item.hir_id());
- if let Some(Node::Item(it)) = cx.tcx.hir().find(parent_item) {
- if let ItemKind::Impl(Impl { of_trait: Some(_), .. }) = it.kind {
- return; // ignore trait impls
- }
+ check_mut_from_ref(cx, sig.decl);
+ for arg in check_fn_args(
+ cx,
+ cx.tcx.fn_sig(item.def_id).skip_binder().inputs(),
+ sig.decl.inputs,
+ &[],
+ ) {
+ span_lint_and_sugg(
+ cx,
+ PTR_ARG,
+ arg.span,
+ &arg.build_msg(),
+ "change this to",
+ format!("{}{}", arg.ref_prefix, arg.deref_ty.display(cx)),
+ Applicability::Unspecified,
+ );
}
- check_fn(cx, sig.decl, item.hir_id(), Some(body_id));
}
}
- fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
- if let TraitItemKind::Fn(ref sig, ref trait_method) = item.kind {
- let body_id = if let TraitFn::Provided(b) = *trait_method {
- Some(b)
- } else {
- None
- };
- check_fn(cx, sig.decl, item.hir_id(), body_id);
+ fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
+ let hir = cx.tcx.hir();
+ let mut parents = hir.parent_iter(body.value.hir_id);
+ let (item_id, decl) = match parents.next() {
+ Some((_, Node::Item(i))) => {
+ if let ItemKind::Fn(sig, ..) = &i.kind {
+ (i.def_id, sig.decl)
+ } else {
+ return;
+ }
+ },
+ Some((_, Node::ImplItem(i))) => {
+ if !matches!(parents.next(),
+ Some((_, Node::Item(i))) if matches!(&i.kind, ItemKind::Impl(i) if i.of_trait.is_none())
+ ) {
+ return;
+ }
+ if let ImplItemKind::Fn(sig, _) = &i.kind {
+ (i.def_id, sig.decl)
+ } else {
+ return;
+ }
+ },
+ Some((_, Node::TraitItem(i))) => {
+ if let TraitItemKind::Fn(sig, _) = &i.kind {
+ (i.def_id, sig.decl)
+ } else {
+ return;
+ }
+ },
+ _ => return,
+ };
+
+ check_mut_from_ref(cx, decl);
+ let sig = cx.tcx.fn_sig(item_id).skip_binder();
+ let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, body.params).collect();
+ let results = check_ptr_arg_usage(cx, body, &lint_args);
+
+ for (result, args) in results.iter().zip(lint_args.iter()).filter(|(r, _)| !r.skip) {
+ span_lint_and_then(cx, PTR_ARG, args.span, &args.build_msg(), |diag| {
+ diag.multipart_suggestion(
+ "change this to",
+ iter::once((args.span, format!("{}{}", args.ref_prefix, args.deref_ty.display(cx))))
+ .chain(result.replacements.iter().map(|r| {
+ (
+ r.expr_span,
+ format!("{}{}", snippet_opt(cx, r.self_span).unwrap(), r.replacement),
+ )
+ }))
+ .collect(),
+ Applicability::Unspecified,
+ );
+ });
}
}
}
}
-#[allow(clippy::too_many_lines)]
-fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: Option<BodyId>) {
- let fn_def_id = cx.tcx.hir().local_def_id(fn_id);
- let sig = cx.tcx.fn_sig(fn_def_id);
- let fn_ty = sig.skip_binder();
- let body = opt_body_id.map(|id| cx.tcx.hir().body(id));
-
- for (idx, (arg, ty)) in decl.inputs.iter().zip(fn_ty.inputs()).enumerate() {
- // Honor the allow attribute on parameters. See issue 5644.
- if let Some(body) = &body {
- if is_lint_allowed(cx, PTR_ARG, body.params[idx].hir_id) {
- continue;
- }
+#[derive(Default)]
+struct PtrArgResult {
+ skip: bool,
+ replacements: Vec<PtrArgReplacement>,
+}
+
+struct PtrArgReplacement {
+ expr_span: Span,
+ self_span: Span,
+ replacement: &'static str,
+}
+
+struct PtrArg<'tcx> {
+ idx: usize,
+ span: Span,
+ ty_did: DefId,
+ ty_name: Symbol,
+ method_renames: &'static [(&'static str, &'static str)],
+ ref_prefix: RefPrefix,
+ deref_ty: DerefTy<'tcx>,
+ deref_assoc_items: Option<(DefId, &'tcx AssocItems<'tcx>)>,
+}
+impl PtrArg<'_> {
+ fn build_msg(&self) -> String {
+ format!(
+ "writing `&{}{}` instead of `&{}{}` involves a new object where a slice will do",
+ self.ref_prefix.mutability.prefix_str(),
+ self.ty_name,
+ self.ref_prefix.mutability.prefix_str(),
+ self.deref_ty.argless_str(),
+ )
+ }
+}
+
+struct RefPrefix {
+ lt: LifetimeName,
+ mutability: Mutability,
+}
+impl fmt::Display for RefPrefix {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ use fmt::Write;
+ f.write_char('&')?;
+ match self.lt {
+ LifetimeName::Param(ParamName::Plain(name)) => {
+ name.fmt(f)?;
+ f.write_char(' ')?;
+ },
+ LifetimeName::Underscore => f.write_str("'_ ")?,
+ LifetimeName::Static => f.write_str("'static ")?,
+ _ => (),
}
+ f.write_str(self.mutability.prefix_str())
+ }
+}
- if let ty::Ref(_, ty, Mutability::Not) = ty.kind() {
- if is_type_diagnostic_item(cx, ty, sym::vec_type) {
- if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_owned()")]) {
- span_lint_and_then(
- cx,
- PTR_ARG,
- arg.span,
- "writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used \
- with non-Vec-based slices",
- |diag| {
- if let Some(ref snippet) = get_only_generic_arg_snippet(cx, arg) {
- diag.span_suggestion(
- arg.span,
- "change this to",
- format!("&[{}]", snippet),
- Applicability::Unspecified,
- );
- }
- for (clonespan, suggestion) in spans {
- diag.span_suggestion(
- clonespan,
- &snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| {
- Cow::Owned(format!("change `{}` to", x))
- }),
- suggestion.into(),
- Applicability::Unspecified,
- );
- }
- },
- );
+struct DerefTyDisplay<'a, 'tcx>(&'a LateContext<'tcx>, &'a DerefTy<'tcx>);
+impl fmt::Display for DerefTyDisplay<'_, '_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ use std::fmt::Write;
+ match self.1 {
+ DerefTy::Str => f.write_str("str"),
+ DerefTy::Path => f.write_str("Path"),
+ DerefTy::Slice(hir_ty, ty) => {
+ f.write_char('[')?;
+ match hir_ty.and_then(|s| snippet_opt(self.0, s)) {
+ Some(s) => f.write_str(&s)?,
+ None => ty.fmt(f)?,
}
- } else if is_type_diagnostic_item(cx, ty, sym::string_type) {
- if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_string()"), ("as_str", "")]) {
- span_lint_and_then(
- cx,
- PTR_ARG,
- arg.span,
- "writing `&String` instead of `&str` involves a new object where a slice will do",
- |diag| {
- diag.span_suggestion(arg.span, "change this to", "&str".into(), Applicability::Unspecified);
- for (clonespan, suggestion) in spans {
- diag.span_suggestion_short(
- clonespan,
- &snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| {
- Cow::Owned(format!("change `{}` to", x))
+ f.write_char(']')
+ },
+ }
+ }
+}
+
+enum DerefTy<'tcx> {
+ Str,
+ Path,
+ Slice(Option<Span>, Ty<'tcx>),
+}
+impl<'tcx> DerefTy<'tcx> {
+ fn argless_str(&self) -> &'static str {
+ match *self {
+ Self::Str => "str",
+ Self::Path => "Path",
+ Self::Slice(..) => "[_]",
+ }
+ }
+
+ fn display<'a>(&'a self, cx: &'a LateContext<'tcx>) -> DerefTyDisplay<'a, 'tcx> {
+ DerefTyDisplay(cx, self)
+ }
+}
+
+fn check_fn_args<'cx, 'tcx: 'cx>(
+ cx: &'cx LateContext<'tcx>,
+ tys: &'tcx [Ty<'_>],
+ hir_tys: &'tcx [hir::Ty<'_>],
+ params: &'tcx [Param<'_>],
+) -> impl Iterator<Item = PtrArg<'tcx>> + 'cx {
+ tys.iter()
+ .zip(hir_tys.iter())
+ .enumerate()
+ .filter_map(|(i, (ty, hir_ty))| {
+ if_chain! {
+ if let ty::Ref(_, ty, mutability) = *ty.kind();
+ if let ty::Adt(adt, substs) = *ty.kind();
+
+ if let TyKind::Rptr(lt, ref ty) = hir_ty.kind;
+ if let TyKind::Path(QPath::Resolved(None, path)) = ty.ty.kind;
+
+ // Check that the name as typed matches the actual name of the type.
+ // e.g. `fn foo(_: &Foo)` shouldn't trigger the lint when `Foo` is an alias for `Vec`
+ if let [.., name] = path.segments;
+ if cx.tcx.item_name(adt.did) == name.ident.name;
+
+ if !is_lint_allowed(cx, PTR_ARG, hir_ty.hir_id);
+ if params.get(i).map_or(true, |p| !is_lint_allowed(cx, PTR_ARG, p.hir_id));
+
+ then {
+ let (method_renames, deref_ty, deref_impl_id) = match cx.tcx.get_diagnostic_name(adt.did) {
+ Some(sym::Vec) => (
+ [("clone", ".to_owned()")].as_slice(),
+ DerefTy::Slice(
+ name.args
+ .and_then(|args| args.args.first())
+ .and_then(|arg| if let GenericArg::Type(ty) = arg {
+ Some(ty.span)
+ } else {
+ None
}),
- suggestion.into(),
- Applicability::Unspecified,
- );
- }
- },
- );
- }
- } else if is_type_diagnostic_item(cx, ty, sym::PathBuf) {
- if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_path_buf()"), ("as_path", "")]) {
- span_lint_and_then(
- cx,
- PTR_ARG,
- arg.span,
- "writing `&PathBuf` instead of `&Path` involves a new object where a slice will do",
- |diag| {
- diag.span_suggestion(
- arg.span,
+ substs.type_at(0),
+ ),
+ cx.tcx.lang_items().slice_impl()
+ ),
+ Some(sym::String) => (
+ [("clone", ".to_owned()"), ("as_str", "")].as_slice(),
+ DerefTy::Str,
+ cx.tcx.lang_items().str_impl()
+ ),
+ Some(sym::PathBuf) => (
+ [("clone", ".to_path_buf()"), ("as_path", "")].as_slice(),
+ DerefTy::Path,
+ None,
+ ),
+ Some(sym::Cow) => {
+ let ty_name = name.args
+ .and_then(|args| {
+ args.args.iter().find_map(|a| match a {
+ GenericArg::Type(x) => Some(x),
+ _ => None,
+ })
+ })
+ .and_then(|arg| snippet_opt(cx, arg.span))
+ .unwrap_or_else(|| substs.type_at(1).to_string());
+ span_lint_and_sugg(
+ cx,
+ PTR_ARG,
+ hir_ty.span,
+ "using a reference to `Cow` is not recommended",
"change this to",
- "&Path".into(),
+ format!("&{}{}", mutability.prefix_str(), ty_name),
Applicability::Unspecified,
);
- for (clonespan, suggestion) in spans {
- diag.span_suggestion_short(
- clonespan,
- &snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| {
- Cow::Owned(format!("change `{}` to", x))
- }),
- suggestion.into(),
- Applicability::Unspecified,
- );
- }
+ return None;
},
- );
- }
- } else if match_type(cx, ty, &paths::COW) {
- if_chain! {
- if let TyKind::Rptr(_, MutTy { ty, ..} ) = arg.kind;
- if let TyKind::Path(QPath::Resolved(None, pp)) = ty.kind;
- if let [ref bx] = *pp.segments;
- if let Some(params) = bx.args;
- if !params.parenthesized;
- if let Some(inner) = params.args.iter().find_map(|arg| match arg {
- GenericArg::Type(ty) => Some(ty),
- _ => None,
+ _ => return None,
+ };
+ return Some(PtrArg {
+ idx: i,
+ span: hir_ty.span,
+ ty_did: adt.did,
+ ty_name: name.ident.name,
+ method_renames,
+ ref_prefix: RefPrefix {
+ lt: lt.name,
+ mutability,
+ },
+ deref_ty,
+ deref_assoc_items: deref_impl_id.map(|id| (id, cx.tcx.associated_items(id))),
});
- let replacement = snippet_opt(cx, inner.span);
- if let Some(r) = replacement;
- then {
- span_lint_and_sugg(
- cx,
- PTR_ARG,
- arg.span,
- "using a reference to `Cow` is not recommended",
- "change this to",
- "&".to_owned() + &r,
- Applicability::Unspecified,
- );
- }
}
}
- }
- }
+ None
+ })
+}
+fn check_mut_from_ref(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
if let FnRetTy::Return(ty) = decl.output {
if let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) {
let mut immutables = vec![];
- for (_, ref mutbl, ref argspan) in decl
+ for (_, mutbl, argspan) in decl
.inputs
.iter()
- .filter_map(|ty| get_rptr_lm(ty))
+ .filter_map(get_rptr_lm)
.filter(|&(lt, _, _)| lt.name == out.name)
{
- if *mutbl == Mutability::Mut {
+ if mutbl == Mutability::Mut {
return;
}
- immutables.push(*argspan);
+ immutables.push(argspan);
}
if immutables.is_empty() {
return;
}
}
-fn get_only_generic_arg_snippet(cx: &LateContext<'_>, arg: &Ty<'_>) -> Option<String> {
- if_chain! {
- if let TyKind::Path(QPath::Resolved(_, path)) = walk_ptrs_hir_ty(arg).kind;
- if let Some(&PathSegment{args: Some(parameters), ..}) = path.segments.last();
- let types: Vec<_> = parameters.args.iter().filter_map(|arg| match arg {
- GenericArg::Type(ty) => Some(ty),
- _ => None,
- }).collect();
- if types.len() == 1;
- then {
- snippet_opt(cx, types[0].span)
- } else {
- None
+#[allow(clippy::too_many_lines)]
+fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: &[PtrArg<'tcx>]) -> Vec<PtrArgResult> {
+ struct V<'cx, 'tcx> {
+ cx: &'cx LateContext<'tcx>,
+ /// Map from a local id to which argument it came from (index into `Self::args` and
+ /// `Self::results`)
+ bindings: HirIdMap<usize>,
+ /// The arguments being checked.
+ args: &'cx [PtrArg<'tcx>],
+ /// The results for each argument (len should match args.len)
+ results: Vec<PtrArgResult>,
+ /// The number of arguments which can't be linted. Used to return early.
+ skip_count: usize,
+ }
+ impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
+ type NestedFilter = nested_filter::OnlyBodies;
+ fn nested_visit_map(&mut self) -> Self::Map {
+ self.cx.tcx.hir()
+ }
+
+ fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
+
+ fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
+ if self.skip_count == self.args.len() {
+ return;
+ }
+
+ // Check if this is local we care about
+ let args_idx = match path_to_local(e).and_then(|id| self.bindings.get(&id)) {
+ Some(&i) => i,
+ None => return walk_expr(self, e),
+ };
+ let args = &self.args[args_idx];
+ let result = &mut self.results[args_idx];
+
+ // Helper function to handle early returns.
+ let mut set_skip_flag = || {
+ if result.skip {
+ self.skip_count += 1;
+ }
+ result.skip = true;
+ };
+
+ match get_expr_use_or_unification_node(self.cx.tcx, e) {
+ Some((Node::Stmt(_), _)) => (),
+ Some((Node::Local(l), _)) => {
+ // Only trace simple bindings. e.g `let x = y;`
+ if let PatKind::Binding(BindingAnnotation::Unannotated, id, _, None) = l.pat.kind {
+ self.bindings.insert(id, args_idx);
+ } else {
+ set_skip_flag();
+ }
+ },
+ Some((Node::Expr(e), child_id)) => match e.kind {
+ ExprKind::Call(f, expr_args) => {
+ let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0);
+ if expr_sig(self.cx, f)
+ .map(|sig| sig.input(i).skip_binder().peel_refs())
+ .map_or(true, |ty| match *ty.kind() {
+ ty::Param(_) => true,
+ ty::Adt(def, _) => def.did == args.ty_did,
+ _ => false,
+ })
+ {
+ // Passed to a function taking the non-dereferenced type.
+ set_skip_flag();
+ }
+ },
+ ExprKind::MethodCall(name, expr_args @ [self_arg, ..], _) => {
+ let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0);
+ if i == 0 {
+ // Check if the method can be renamed.
+ let name = name.ident.as_str();
+ if let Some((_, replacement)) = args.method_renames.iter().find(|&&(x, _)| x == name) {
+ result.replacements.push(PtrArgReplacement {
+ expr_span: e.span,
+ self_span: self_arg.span,
+ replacement,
+ });
+ return;
+ }
+ }
+
+ let id = if let Some(x) = self.cx.typeck_results().type_dependent_def_id(e.hir_id) {
+ x
+ } else {
+ set_skip_flag();
+ return;
+ };
+
+ match *self.cx.tcx.fn_sig(id).skip_binder().inputs()[i].peel_refs().kind() {
+ ty::Param(_) => {
+ set_skip_flag();
+ },
+ // If the types match check for methods which exist on both types. e.g. `Vec::len` and
+ // `slice::len`
+ ty::Adt(def, _)
+ if def.did == args.ty_did
+ && (i != 0
+ || self.cx.tcx.trait_of_item(id).is_some()
+ || !args.deref_assoc_items.map_or(false, |(id, items)| {
+ items
+ .find_by_name_and_kind(self.cx.tcx, name.ident, AssocKind::Fn, id)
+ .is_some()
+ })) =>
+ {
+ set_skip_flag();
+ },
+ _ => (),
+ }
+ },
+ // Indexing is fine for currently supported types.
+ ExprKind::Index(e, _) if e.hir_id == child_id => (),
+ _ => set_skip_flag(),
+ },
+ _ => set_skip_flag(),
+ }
}
}
+
+ let mut skip_count = 0;
+ let mut results = args.iter().map(|_| PtrArgResult::default()).collect::<Vec<_>>();
+ let mut v = V {
+ cx,
+ bindings: args
+ .iter()
+ .enumerate()
+ .filter_map(|(i, arg)| {
+ let param = &body.params[arg.idx];
+ match param.pat.kind {
+ PatKind::Binding(BindingAnnotation::Unannotated, id, _, None)
+ if !is_lint_allowed(cx, PTR_ARG, param.hir_id) =>
+ {
+ Some((id, i))
+ },
+ _ => {
+ skip_count += 1;
+ results[arg.idx].skip = true;
+ None
+ },
+ }
+ })
+ .collect(),
+ args,
+ results,
+ skip_count,
+ };
+ v.visit_expr(&body.value);
+ v.results
}
-fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> {
+fn get_rptr_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> {
if let TyKind::Rptr(ref lt, ref m) = ty.kind {
Some((lt, m.mutbl, ty.span))
} else {