diff options
| author | Michael Wright <mikerite@lavabit.com> | 2020-10-27 07:42:13 +0200 |
|---|---|---|
| committer | Michael Wright <mikerite@lavabit.com> | 2020-10-27 07:42:13 +0200 |
| commit | 66d56fefc5b7ce22d2db65ee9dc1a5f9f6bf2f09 (patch) | |
| tree | b030677266fb104fce1f9240465000a6a6a75b13 | |
| parent | afbac8906e614a63ff5825710c3ebe45a3b5e01a (diff) | |
| download | rust-66d56fefc5b7ce22d2db65ee9dc1a5f9f6bf2f09.tar.gz rust-66d56fefc5b7ce22d2db65ee9dc1a5f9f6bf2f09.zip | |
Add `invalid_paths` internal lint
| -rw-r--r-- | clippy_lints/src/lib.rs | 3 | ||||
| -rw-r--r-- | clippy_lints/src/utils/internal_lints.rs | 79 | ||||
| -rw-r--r-- | clippy_lints/src/utils/mod.rs | 25 | ||||
| -rw-r--r-- | tests/ui/invalid_paths.rs | 23 | ||||
| -rw-r--r-- | tests/ui/invalid_paths.stderr | 16 |
5 files changed, 146 insertions, 0 deletions
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3be8bc0e36d..7c8cb90fe1c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -892,6 +892,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, &utils::internal_lints::COMPILER_LINT_FUNCTIONS, &utils::internal_lints::DEFAULT_LINT, + &utils::internal_lints::INVALID_PATHS, &utils::internal_lints::LINT_WITHOUT_LINT_PASS, &utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, &utils::internal_lints::OUTER_EXPN_EXPN_DATA, @@ -919,6 +920,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); + store.register_late_pass(|| box utils::internal_lints::InvalidPaths); store.register_late_pass(|| box utils::inspector::DeepCodeInspector); store.register_late_pass(|| box utils::author::Author); let vec_box_size_threshold = conf.vec_box_size_threshold; @@ -1280,6 +1282,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS), LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS), LintId::of(&utils::internal_lints::DEFAULT_LINT), + LintId::of(&utils::internal_lints::INVALID_PATHS), LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS), LintId::of(&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM), LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA), diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index bfe426a25eb..6ca72d895c8 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,3 +1,4 @@ +use crate::consts::{constant_simple, Constant}; use crate::utils::{ is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res, paths, qpath_res, run_lints, snippet, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq, @@ -14,9 +15,11 @@ use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_middle::hir::map::Map; +use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; use rustc_span::symbol::{Symbol, SymbolStr}; +use rustc_typeck::hir_ty_to_ty; use std::borrow::{Borrow, Cow}; @@ -229,6 +232,21 @@ declare_clippy_lint! { "using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`" } +declare_clippy_lint! { + /// **What it does:** + /// Checks the paths module for invalid paths. + /// + /// **Why is this bad?** + /// It indicates a bug in the code. + /// + /// **Known problems:** None. + /// + /// **Example:** None. + pub INVALID_PATHS, + internal, + "invalid path" +} + declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); impl EarlyLintPass for ClippyLintsInternal { @@ -761,3 +779,64 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Ve None } + +// This is not a complete resolver for paths. It works on all the paths currently used in the paths +// module. That's all it does and all it needs to do. +pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { + if path_to_res(cx, path).is_some() { + return true; + } + + // Some implementations can't be found by `path_to_res`, particularly inherent + // implementations of native types. Check lang items. + let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect(); + let lang_items = cx.tcx.lang_items(); + for lang_item in lang_items.items() { + if let Some(def_id) = lang_item { + let lang_item_path = cx.get_def_path(*def_id); + if path_syms.starts_with(&lang_item_path) { + if let [item] = &path_syms[lang_item_path.len()..] { + for child in cx.tcx.item_children(*def_id) { + if child.ident.name == *item { + return true; + } + } + } + } + } + } + + false +} + +declare_lint_pass!(InvalidPaths => [INVALID_PATHS]); + +impl<'tcx> LateLintPass<'tcx> for InvalidPaths { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + let local_def_id = &cx.tcx.parent_module(item.hir_id); + let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); + if_chain! { + if mod_name.as_str() == "paths"; + if let hir::ItemKind::Const(ty, body_id) = item.kind; + let ty = hir_ty_to_ty(cx.tcx, ty); + if let ty::Array(el_ty, _) = &ty.kind(); + if let ty::Ref(_, el_ty, _) = &el_ty.kind(); + if el_ty.is_str(); + let body = cx.tcx.hir().body(body_id); + let typeck_results = cx.tcx.typeck_body(body_id); + if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, &body.value); + let path: Vec<&str> = path.iter().map(|x| { + if let Constant::Str(s) = x { + s.as_str() + } else { + // We checked the type of the constant above + unreachable!() + } + }).collect(); + if !check_path(cx, &path[..]); + then { + span_lint(cx, CLIPPY_LINTS_INTERNAL, item.span, "invalid path"); + } + } + } +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 8297b9d128d..a1ecca0961a 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -268,6 +268,7 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option<def::Res> { krate: *krate, index: CRATE_DEF_INDEX, }; + let mut current_item = None; let mut items = cx.tcx.item_children(krate); let mut path_it = path.iter().skip(1).peekable(); @@ -277,6 +278,12 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option<def::Res> { None => return None, }; + // `get_def_path` seems to generate these empty segments for extern blocks. + // We can just ignore them. + if segment.is_empty() { + continue; + } + let result = SmallVec::<[_; 8]>::new(); for item in mem::replace(&mut items, cx.tcx.arena.alloc_slice(&result)).iter() { if item.ident.name.as_str() == *segment { @@ -284,10 +291,28 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option<def::Res> { return Some(item.res); } + current_item = Some(item); items = cx.tcx.item_children(item.res.def_id()); break; } } + + // The segment isn't a child_item. + // Try to find it under an inherent impl. + if_chain! { + if path_it.peek().is_none(); + if let Some(current_item) = current_item; + let item_def_id = current_item.res.def_id(); + if cx.tcx.def_kind(item_def_id) == DefKind::Struct; + then { + // Bad `find_map` suggestion. See #4193. + #[allow(clippy::find_map)] + return cx.tcx.inherent_impls(item_def_id).iter() + .flat_map(|&impl_def_id| cx.tcx.item_children(impl_def_id)) + .find(|item| item.ident.name.as_str() == *segment) + .map(|item| item.res); + } + } } } else { None diff --git a/tests/ui/invalid_paths.rs b/tests/ui/invalid_paths.rs new file mode 100644 index 00000000000..01e28ae5e9d --- /dev/null +++ b/tests/ui/invalid_paths.rs @@ -0,0 +1,23 @@ +#![warn(clippy::internal)] + +mod paths { + // Good path + pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"]; + + // Path to method on inherent impl of a primitive type + pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"]; + + // Path to method on inherent impl + pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; + + // Path with empty segment + pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; + + // Path with bad crate + pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; + + // Path with bad module + pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; +} + +fn main() {} diff --git a/tests/ui/invalid_paths.stderr b/tests/ui/invalid_paths.stderr new file mode 100644 index 00000000000..bd69d661b71 --- /dev/null +++ b/tests/ui/invalid_paths.stderr @@ -0,0 +1,16 @@ +error: invalid path + --> $DIR/invalid_paths.rs:17:5 + | +LL | pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::clippy-lints-internal` implied by `-D warnings` + +error: invalid path + --> $DIR/invalid_paths.rs:20:5 + | +LL | pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + |
