use clippy_utils::consts::{constant_simple, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::macros::root_macro_call_first_node;
use clippy_utils::source::snippet;
use clippy_utils::ty::match_type;
use clippy_utils::{
- is_else_clause, is_expn_of, is_expr_path_def_path, match_def_path, method_calls, path_to_res, paths, run_lints,
- SpanlessEq,
+ def_path_res, higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path,
+ method_calls, paths, peel_blocks_with_stmt, SpanlessEq,
};
use if_chain::if_chain;
-use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, ModKind, NodeId};
+use rustc_ast as ast;
+use rustc_ast::ast::{Crate, ItemKind, LitKind, ModKind, NodeId};
use rustc_ast::visit::FnKind;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::hir_id::CRATE_HIR_ID;
-use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
+use rustc_hir::intravisit::Visitor;
use rustc_hir::{
- BinOpKind, Block, Crate, Expr, ExprKind, HirId, Item, Local, MatchSource, MutTy, Mutability, Node, Path, Stmt,
- StmtKind, Ty, TyKind, UnOp,
+ BinOpKind, Block, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, Ty, TyKind,
+ UnOp,
};
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
-use rustc_middle::hir::map::Map;
+use rustc_middle::hir::nested_filter;
use rustc_middle::mir::interpret::ConstValue;
-use rustc_middle::ty;
+use rustc_middle::ty::{self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, FloatTy};
+use rustc_semver::RustcVersion;
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Spanned;
-use rustc_span::symbol::{Symbol, SymbolStr};
-use rustc_span::{BytePos, Span};
+use rustc_span::symbol::Symbol;
+use rustc_span::{sym, BytePos, Span};
use rustc_typeck::hir_ty_to_ty;
use std::borrow::{Borrow, Cow};
-#[cfg(feature = "metadata-collector-lint")]
+#[cfg(feature = "internal")]
pub mod metadata_collector;
declare_clippy_lint! {
- /// **What it does:** Checks for various things we like to keep tidy in clippy.
+ /// ### What it does
+ /// Checks for various things we like to keep tidy in clippy.
///
- /// **Why is this bad?** We like to pretend we're an example of tidy code.
+ /// ### Why is this bad?
+ /// We like to pretend we're an example of tidy code.
///
- /// **Known problems:** None.
- ///
- /// **Example:** Wrong ordering of the util::paths constants.
+ /// ### Example
+ /// Wrong ordering of the util::paths constants.
pub CLIPPY_LINTS_INTERNAL,
internal,
"various things that will negatively affect your clippy experience"
}
declare_clippy_lint! {
- /// **What it does:** Ensures every lint is associated to a `LintPass`.
+ /// ### What it does
+ /// Ensures every lint is associated to a `LintPass`.
///
- /// **Why is this bad?** The compiler only knows lints via a `LintPass`. Without
+ /// ### Why is this bad?
+ /// The compiler only knows lints via a `LintPass`. Without
/// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not
/// know the name of the lint.
///
- /// **Known problems:** Only checks for lints associated using the
+ /// ### Known problems
+ /// Only checks for lints associated using the
/// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros.
///
- /// **Example:**
+ /// ### Example
/// ```rust,ignore
/// declare_lint! { pub LINT_1, ... }
/// declare_lint! { pub LINT_2, ... }
}
declare_clippy_lint! {
- /// **What it does:** Checks for calls to `cx.span_lint*` and suggests to use the `utils::*`
+ /// ### What it does
+ /// Checks for calls to `cx.span_lint*` and suggests to use the `utils::*`
/// variant of the function.
///
- /// **Why is this bad?** The `utils::*` variants also add a link to the Clippy documentation to the
+ /// ### Why is this bad?
+ /// The `utils::*` variants also add a link to the Clippy documentation to the
/// warning/error messages.
///
- /// **Known problems:** None.
- ///
- /// **Example:**
- /// Bad:
+ /// ### Example
/// ```rust,ignore
/// cx.span_lint(LINT_NAME, "message");
/// ```
///
- /// Good:
+ /// Use instead:
/// ```rust,ignore
/// utils::span_lint(cx, LINT_NAME, "message");
/// ```
}
declare_clippy_lint! {
- /// **What it does:** Checks for calls to `cx.outer().expn_data()` and suggests to use
+ /// ### What it does
+ /// Checks for calls to `cx.outer().expn_data()` and suggests to use
/// the `cx.outer_expn_data()`
///
- /// **Why is this bad?** `cx.outer_expn_data()` is faster and more concise.
- ///
- /// **Known problems:** None.
+ /// ### Why is this bad?
+ /// `cx.outer_expn_data()` is faster and more concise.
///
- /// **Example:**
- /// Bad:
+ /// ### Example
/// ```rust,ignore
/// expr.span.ctxt().outer().expn_data()
/// ```
///
- /// Good:
+ /// Use instead:
/// ```rust,ignore
/// expr.span.ctxt().outer_expn_data()
/// ```
}
declare_clippy_lint! {
- /// **What it does:** Not an actual lint. This lint is only meant for testing our customized internal compiler
+ /// ### What it does
+ /// Not an actual lint. This lint is only meant for testing our customized internal compiler
/// error message by calling `panic`.
///
- /// **Why is this bad?** ICE in large quantities can damage your teeth
+ /// ### Why is this bad?
+ /// ICE in large quantities can damage your teeth
///
- /// **Known problems:** None
- ///
- /// **Example:**
- /// Bad:
+ /// ### Example
/// ```rust,ignore
/// 🍦🍦🍦🍦🍦
/// ```
}
declare_clippy_lint! {
- /// **What it does:** Checks for cases of an auto-generated lint without an updated description,
+ /// ### What it does
+ /// Checks for cases of an auto-generated lint without an updated description,
/// i.e. `default lint description`.
///
- /// **Why is this bad?** Indicates that the lint is not finished.
- ///
- /// **Known problems:** None
+ /// ### Why is this bad?
+ /// Indicates that the lint is not finished.
///
- /// **Example:**
- /// Bad:
+ /// ### Example
/// ```rust,ignore
/// declare_lint! { pub COOL_LINT, nursery, "default lint description" }
/// ```
///
- /// Good:
+ /// Use instead:
/// ```rust,ignore
/// declare_lint! { pub COOL_LINT, nursery, "a great new lint" }
/// ```
}
declare_clippy_lint! {
- /// **What it does:** Lints `span_lint_and_then` function calls, where the
+ /// ### What it does
+ /// Lints `span_lint_and_then` function calls, where the
/// closure argument has only one statement and that statement is a method
/// call to `span_suggestion`, `span_help`, `span_note` (using the same
/// span), `help` or `note`.
/// wrapper functions `span_lint_and_sugg`, span_lint_and_help`, or
/// `span_lint_and_note`.
///
- /// **Why is this bad?** Using the wrapper `span_lint_and_*` functions, is more
+ /// ### Why is this bad?
+ /// Using the wrapper `span_lint_and_*` functions, is more
/// convenient, readable and less error prone.
///
- /// **Known problems:** None
- ///
- /// *Example:**
- /// Bad:
+ /// ### Example
/// ```rust,ignore
/// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
/// diag.span_suggestion(
/// });
/// ```
///
- /// Good:
+ /// Use instead:
/// ```rust,ignore
/// span_lint_and_sugg(
/// cx,
}
declare_clippy_lint! {
- /// **What it does:** Checks for calls to `utils::match_type()` on a type diagnostic item
+ /// ### What it does
+ /// Checks for calls to `utils::match_type()` on a type diagnostic item
/// and suggests to use `utils::is_type_diagnostic_item()` instead.
///
- /// **Why is this bad?** `utils::is_type_diagnostic_item()` does not require hardcoded paths.
- ///
- /// **Known problems:** None.
+ /// ### Why is this bad?
+ /// `utils::is_type_diagnostic_item()` does not require hardcoded paths.
///
- /// **Example:**
- /// Bad:
+ /// ### Example
/// ```rust,ignore
/// utils::match_type(cx, ty, &paths::VEC)
/// ```
///
- /// Good:
+ /// Use instead:
/// ```rust,ignore
- /// utils::is_type_diagnostic_item(cx, ty, sym::vec_type)
+ /// utils::is_type_diagnostic_item(cx, ty, sym::Vec)
/// ```
pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
internal,
}
declare_clippy_lint! {
- /// **What it does:**
+ /// ### What it does
/// Checks the paths module for invalid paths.
///
- /// **Why is this bad?**
+ /// ### Why is this bad?
/// It indicates a bug in the code.
///
- /// **Known problems:** None.
- ///
- /// **Example:** None.
+ /// ### Example
+ /// None.
pub INVALID_PATHS,
internal,
"invalid path"
}
declare_clippy_lint! {
- /// **What it does:**
+ /// ### What it does
/// Checks for interning symbols that have already been pre-interned and defined as constants.
///
- /// **Why is this bad?**
+ /// ### Why is this bad?
/// It's faster and easier to use the symbol constant.
///
- /// **Known problems:** None.
- ///
- /// **Example:**
- /// Bad:
+ /// ### Example
/// ```rust,ignore
/// let _ = sym!(f32);
/// ```
///
- /// Good:
+ /// Use instead:
/// ```rust,ignore
/// let _ = sym::f32;
/// ```
}
declare_clippy_lint! {
- /// **What it does:** Checks for unnecessary conversion from Symbol to a string.
+ /// ### What it does
+ /// Checks for unnecessary conversion from Symbol to a string.
///
- /// **Why is this bad?** It's faster use symbols directly intead of strings.
+ /// ### Why is this bad?
+ /// It's faster use symbols directly instead of strings.
///
- /// **Known problems:** None.
- ///
- /// **Example:**
- /// Bad:
+ /// ### Example
/// ```rust,ignore
/// symbol.as_str() == "clippy";
/// ```
///
- /// Good:
+ /// Use instead:
/// ```rust,ignore
/// symbol == sym::clippy;
/// ```
"non-idiomatic `if_chain!` usage"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for invalid `clippy::version` attributes.
+ ///
+ /// Valid values are:
+ /// * "pre 1.29.0"
+ /// * any valid semantic version
+ pub INVALID_CLIPPY_VERSION_ATTRIBUTE,
+ internal,
+ "found an invalid `clippy::version` attribute"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for declared clippy lints without the `clippy::version` attribute.
+ ///
+ pub MISSING_CLIPPY_VERSION_ATTRIBUTE,
+ internal,
+ "found clippy lint without `clippy::version` attribute"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Check that the `extract_msrv_attr!` macro is used, when a lint has a MSRV.
+ ///
+ pub MISSING_MSRV_ATTR_IMPL,
+ internal,
+ "checking if all necessary steps were taken when adding a MSRV to a lint"
+}
+
declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
impl EarlyLintPass for ClippyLintsInternal {
- fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &AstCrate) {
+ fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") {
if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind {
if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") {
if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind {
- let mut last_name: Option<SymbolStr> = None;
+ let mut last_name: Option<&str> = None;
for item in items {
let name = item.ident.as_str();
- if let Some(ref last_name) = last_name {
- if **last_name > *name {
+ if let Some(last_name) = last_name {
+ if *last_name > *name {
span_lint(
cx,
CLIPPY_LINTS_INTERNAL,
registered_lints: FxHashSet<Symbol>,
}
-impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS]);
+impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE]);
impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
- if !run_lints(cx, &[DEFAULT_LINT], item.hir_id()) {
+ if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) {
return;
}
if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind {
if is_lint_ref_type(cx, ty) {
+ check_invalid_clippy_version_attribute(cx, item);
+
let expr = &cx.tcx.hir().body(body_id).value;
if_chain! {
if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind;
}
self.declared_lints.insert(item.ident.name, item.span);
}
- } else if is_expn_of(item.span, "impl_lint_pass").is_some()
- || is_expn_of(item.span, "declare_lint_pass").is_some()
- {
+ } else if let Some(macro_call) = root_macro_call_first_node(cx, item) {
+ if !matches!(
+ cx.tcx.item_name(macro_call.def_id).as_str(),
+ "impl_lint_pass" | "declare_lint_pass"
+ ) {
+ return;
+ }
if let hir::ItemKind::Impl(hir::Impl {
of_trait: None,
items: impl_item_refs,
}
}
- fn check_crate_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx Crate<'_>) {
- if !run_lints(cx, &[LINT_WITHOUT_LINT_PASS], CRATE_HIR_ID) {
+ fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
+ if is_lint_allowed(cx, LINT_WITHOUT_LINT_PASS, CRATE_HIR_ID) {
return;
}
false
}
+fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) {
+ if let Some(value) = extract_clippy_version_value(cx, item) {
+ // The `sym!` macro doesn't work as it only expects a single token.
+ // It's better to keep it this way and have a direct `Symbol::intern` call here.
+ if value == Symbol::intern("pre 1.29.0") {
+ return;
+ }
+
+ if RustcVersion::parse(value.as_str()).is_err() {
+ span_lint_and_help(
+ cx,
+ INVALID_CLIPPY_VERSION_ATTRIBUTE,
+ item.span,
+ "this item has an invalid `clippy::version` attribute",
+ None,
+ "please use a valid sematic version, see `doc/adding_lints.md`",
+ );
+ }
+ } else {
+ span_lint_and_help(
+ cx,
+ MISSING_CLIPPY_VERSION_ATTRIBUTE,
+ item.span,
+ "this lint is missing the `clippy::version` attribute or version value",
+ None,
+ "please use a `clippy::version` attribute, see `doc/adding_lints.md`",
+ );
+ }
+}
+
+/// This function extracts the version value of a `clippy::version` attribute if the given value has
+/// one
+fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option<Symbol> {
+ let attrs = cx.tcx.hir().attrs(item.hir_id());
+ attrs.iter().find_map(|attr| {
+ if_chain! {
+ // Identify attribute
+ if let ast::AttrKind::Normal(ref attr_kind, _) = &attr.kind;
+ if let [tool_name, attr_name] = &attr_kind.path.segments[..];
+ if tool_name.ident.name == sym::clippy;
+ if attr_name.ident.name == sym::version;
+ if let Some(version) = attr.value_str();
+ then {
+ Some(version)
+ } else {
+ None
+ }
+ }
+ })
+}
+
struct LintCollector<'a, 'tcx> {
output: &'a mut FxHashSet<Symbol>,
cx: &'a LateContext<'tcx>,
}
impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> {
- type Map = Map<'tcx>;
+ type NestedFilter = nested_filter::All;
fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) {
if path.segments.len() == 1 {
}
}
- fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
- NestedVisitorMap::All(self.cx.tcx.hir())
+ fn nested_visit_map(&mut self) -> Self::Map {
+ self.cx.tcx.hir()
}
}
impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if !run_lints(cx, &[COMPILER_LINT_FUNCTIONS], expr.hir_id) {
+ if is_lint_allowed(cx, COMPILER_LINT_FUNCTIONS, expr.hir_id) {
return;
}
if_chain! {
- if let ExprKind::MethodCall(path, _, args, _) = expr.kind;
+ if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
let fn_name = path.ident;
- if let Some(sugg) = self.map.get(&*fn_name.as_str());
- let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
+ if let Some(sugg) = self.map.get(fn_name.as_str());
+ let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
if match_type(cx, ty, &paths::EARLY_CONTEXT)
|| match_type(cx, ty, &paths::LATE_CONTEXT);
then {
impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
- if !run_lints(cx, &[OUTER_EXPN_EXPN_DATA], expr.hir_id) {
+ if is_lint_allowed(cx, OUTER_EXPN_EXPN_DATA, expr.hir_id) {
return;
}
let (method_names, arg_lists, spans) = method_calls(expr, 2);
- let method_names: Vec<SymbolStr> = method_names.iter().map(|s| s.as_str()).collect();
- let method_names: Vec<&str> = method_names.iter().map(|s| &**s).collect();
+ let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect();
if_chain! {
if let ["expn_data", "outer_expn"] = method_names.as_slice();
let args = arg_lists[1];
impl EarlyLintPass for ProduceIce {
fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
- if is_trigger_fn(fn_kind) {
- panic!("Would you like some help with that?");
- }
+ assert!(!is_trigger_fn(fn_kind), "Would you like some help with that?");
}
}
impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
- if !run_lints(cx, &[COLLAPSIBLE_SPAN_LINT_CALLS], expr.hir_id) {
+ if is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) {
return;
}
if let ExprKind::Call(func, and_then_args) = expr.kind;
if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]);
if and_then_args.len() == 5;
- if let ExprKind::Closure(_, _, body_id, _, _) = &and_then_args[4].kind;
- let body = cx.tcx.hir().body(*body_id);
- if let ExprKind::Block(block, _) = &body.value.kind;
- let stmts = &block.stmts;
- if stmts.len() == 1 && block.expr.is_none();
- if let StmtKind::Semi(only_expr) = &stmts[0].kind;
- if let ExprKind::MethodCall(ps, _, span_call_args, _) = &only_expr.kind;
+ if let ExprKind::Closure { body, .. } = &and_then_args[4].kind;
+ let body = cx.tcx.hir().body(*body);
+ let only_expr = peel_blocks_with_stmt(&body.value);
+ if let ExprKind::MethodCall(ps, span_call_args, _) = &only_expr.kind;
then {
let and_then_snippets = get_and_then_snippets(cx, and_then_args);
let mut sle = SpanlessEq::new(cx).deny_side_effects();
- match &*ps.ident.as_str() {
+ match ps.ident.as_str() {
"span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args));
},
cx,
COLLAPSIBLE_SPAN_LINT_CALLS,
expr.span,
- "this call is collspible",
+ "this call is collapsible",
"collapse into",
format!(
"span_lint_and_note({}, {}, {}, {}, {}, {})",
impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
- if !run_lints(cx, &[MATCH_TYPE_ON_DIAGNOSTIC_ITEM], expr.hir_id) {
+ if is_lint_allowed(cx, MATCH_TYPE_ON_DIAGNOSTIC_ITEM, expr.hir_id) {
return;
}
if is_expr_path_def_path(cx, fn_path, &["clippy_utils", "ty", "match_type"]);
// Extract the path to the matched type
if let Some(segments) = path_to_matched_type(cx, ty_path);
- let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect();
- if let Some(ty_did) = path_to_res(cx, &segments[..]).opt_def_id();
+ let segments: Vec<&str> = segments.iter().map(Symbol::as_str).collect();
+ if let Some(ty_did) = def_path_res(cx, &segments[..]).opt_def_id();
// Check if the matched type is a diagnostic item
- let diag_items = cx.tcx.diagnostic_items(ty_did.krate);
- if let Some(item_name) = diag_items.iter().find_map(|(k, v)| if *v == ty_did { Some(k) } else { None });
+ if let Some(item_name) = cx.tcx.get_diagnostic_name(ty_did);
then {
// TODO: check paths constants from external crates.
let cx_snippet = snippet(cx, context.span, "_");
}
}
-fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Vec<SymbolStr>> {
+fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Vec<Symbol>> {
use rustc_hir::ItemKind;
match &expr.kind {
}
}
},
- Res::Def(DefKind::Const | DefKind::Static, def_id) => {
+ Res::Def(DefKind::Const | DefKind::Static(..), def_id) => {
if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) {
if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind {
let body = cx.tcx.hir().body(body_id);
_ => {},
},
ExprKind::Array(exprs) => {
- let segments: Vec<SymbolStr> = exprs
+ let segments: Vec<Symbol> = exprs
.iter()
.filter_map(|expr| {
if let ExprKind::Lit(lit) = &expr.kind {
if let LitKind::Str(sym, _) = lit.node {
- return Some(sym.as_str());
+ return Some(sym);
}
}
// 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) != Res::Err {
+ if def_path_res(cx, path) != Res::Err {
return true;
}
// 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 item_def_id in lang_items.items().iter().flatten() {
+ // This list isn't complete, but good enough for our current list of paths.
+ let incoherent_impls = [
+ SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F32),
+ SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F64),
+ SimplifiedTypeGen::SliceSimplifiedType,
+ SimplifiedTypeGen::StrSimplifiedType,
+ ]
+ .iter()
+ .flat_map(|&ty| cx.tcx.incoherent_impls(ty));
+ for item_def_id in lang_items.items().iter().flatten().chain(incoherent_impls) {
let lang_item_path = cx.get_def_path(*item_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(*item_def_id) {
- if child.ident.name == *item {
- return true;
+ if matches!(
+ cx.tcx.def_kind(*item_def_id),
+ DefKind::Mod | DefKind::Enum | DefKind::Trait
+ ) {
+ for child in cx.tcx.module_children(*item_def_id) {
+ if child.ident.name == *item {
+ return true;
+ }
+ }
+ } else {
+ for child in cx.tcx.associated_item_def_ids(*item_def_id) {
+ if cx.tcx.item_name(*child) == *item {
+ return true;
+ }
}
}
}
}).collect();
if !check_path(cx, &path[..]);
then {
- span_lint(cx, CLIPPY_LINTS_INTERNAL, item.span, "invalid path");
+ span_lint(cx, INVALID_PATHS, item.span, "invalid path");
}
}
}
impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]);
impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
- fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
+ fn check_crate(&mut self, cx: &LateContext<'_>) {
if !self.symbol_map.is_empty() {
return;
}
for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] {
- if let Some(def_id) = path_to_res(cx, module).opt_def_id() {
- for item in cx.tcx.item_children(def_id).iter() {
+ if let Some(def_id) = def_path_res(cx, module).opt_def_id() {
+ for item in cx.tcx.module_children(def_id).iter() {
if_chain! {
if let Res::Def(DefKind::Const, item_def_id) = item.res;
let ty = cx.tcx.type_of(item_def_id);
&paths::SYMBOL_TO_IDENT_STRING,
&paths::TO_STRING_METHOD,
];
- // SymbolStr might be de-referenced: `&*symbol.as_str()`
let call = if_chain! {
if let ExprKind::AddrOf(_, _, e) = expr.kind;
if let ExprKind::Unary(UnOp::Deref, e) = e.kind;
};
if_chain! {
// is a method call
- if let ExprKind::MethodCall(_, _, [item], _) = call.kind;
+ if let ExprKind::MethodCall(_, [item], _) = call.kind;
if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id);
let ty = cx.typeck_results().expr_ty(item);
// ...on either an Ident or a Symbol
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
- let (cond, then, els) = match expr.kind {
- ExprKind::If(cond, then, els) => (Some(cond), then, els.is_some()),
- ExprKind::Match(
- _,
- [arm, ..],
- MatchSource::IfLetDesugar {
- contains_else_clause: els,
- },
- ) => (None, arm.body, els),
- _ => return,
+ let (cond, then, els) = if let Some(higher::IfOrIfLet { cond, r#else, then }) = higher::IfOrIfLet::hir(expr) {
+ (cond, then, r#else.is_some())
+ } else {
+ return;
};
let then_block = match then.kind {
ExprKind::Block(block, _) => block,
};
// check for `if a && b;`
if_chain! {
- if let Some(cond) = cond;
if let ExprKind::Binary(op, _, _) = cond.kind;
if op.node == BinOpKind::And;
if cx.sess().source_map().is_multiline(cond.span);
_ => return,
};
if_chain! {
- if matches!(tail.kind,
- ExprKind::If(_, _, None)
- | ExprKind::Match(.., MatchSource::IfLetDesugar { contains_else_clause: false }));
+ if let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail);
let sm = cx.sess().source_map();
if head
.iter()
}
span.adjust(if_chain_span.ctxt().outer_expn());
let sm = cx.sess().source_map();
- let span = sm.span_extend_to_prev_str(span, "let", false);
+ let span = sm.span_extend_to_prev_str(span, "let", false, true).unwrap_or(span);
let span = sm.span_extend_to_next_char(span, ';', false);
- Span::new(span.lo() - BytePos(3), span.hi() + BytePos(1), span.ctxt())
+ Span::new(
+ span.lo() - BytePos(3),
+ span.hi() + BytePos(1),
+ span.ctxt(),
+ span.parent(),
+ )
+}
+
+declare_lint_pass!(MsrvAttrImpl => [MISSING_MSRV_ATTR_IMPL]);
+
+impl LateLintPass<'_> for MsrvAttrImpl {
+ fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
+ if_chain! {
+ if let hir::ItemKind::Impl(hir::Impl {
+ of_trait: Some(lint_pass_trait_ref),
+ self_ty,
+ items,
+ ..
+ }) = &item.kind;
+ if let Some(lint_pass_trait_def_id) = lint_pass_trait_ref.trait_def_id();
+ let is_late_pass = match_def_path(cx, lint_pass_trait_def_id, &paths::LATE_LINT_PASS);
+ if is_late_pass || match_def_path(cx, lint_pass_trait_def_id, &paths::EARLY_LINT_PASS);
+ let self_ty = hir_ty_to_ty(cx.tcx, self_ty);
+ if let ty::Adt(self_ty_def, _) = self_ty.kind();
+ if self_ty_def.is_struct();
+ if self_ty_def.all_fields().any(|f| {
+ cx.tcx
+ .type_of(f.did)
+ .walk()
+ .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_)))
+ .any(|t| match_type(cx, t.expect_ty(), &paths::RUSTC_VERSION))
+ });
+ if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs));
+ then {
+ let context = if is_late_pass { "LateContext" } else { "EarlyContext" };
+ let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" };
+ let span = cx.sess().source_map().span_through_char(item.span, '{');
+ span_lint_and_sugg(
+ cx,
+ MISSING_MSRV_ATTR_IMPL,
+ span,
+ &format!("`extract_msrv_attr!` macro missing from `{lint_pass}` implementation"),
+ &format!("add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation"),
+ format!("{}\n extract_msrv_attr!({context});", snippet(cx, span, "..")),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ }
}