use crate::utils::{
- match_def_path, match_type, method_calls, paths, span_help_and_lint, span_lint, span_lint_and_sugg, walk_ptrs_ty,
+ is_expn_of, match_def_path, match_type, method_calls, paths, span_lint, span_lint_and_help, span_lint_and_sugg,
+ walk_ptrs_ty,
};
use if_chain::if_chain;
-use rustc::hir;
-use rustc::hir::def::{DefKind, Res};
-use rustc::hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
-use rustc::hir::*;
-use rustc::lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintArray, LintPass};
-use rustc::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
+use rustc::hir::map::Map;
+use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, Name, NodeId};
+use rustc_ast::visit::FnKind;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::Applicability;
-use syntax::ast::{Crate as AstCrate, ItemKind, Name};
-use syntax::source_map::Span;
-use syntax_pos::symbol::LocalInternedString;
+use rustc_hir as hir;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
+use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Path, Ty, TyKind};
+use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
+use rustc_span::source_map::{Span, Spanned};
+use rustc_span::symbol::SymbolStr;
declare_clippy_lint! {
/// **What it does:** Checks for various things we like to keep tidy in clippy.
"using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`"
}
+declare_clippy_lint! {
+ /// **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
+ ///
+ /// **Known problems:** None
+ ///
+ /// **Example:**
+ /// Bad:
+ /// ```rust,ignore
+ /// 🍦🍦🍦🍦🍦
+ /// ```
+ pub PRODUCE_ICE,
+ internal,
+ "this message should not appear anywhere as we ICE before and don't emit the lint"
+}
+
+declare_clippy_lint! {
+ /// **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
+ ///
+ /// **Example:**
+ /// Bad:
+ /// ```rust,ignore
+ /// declare_lint! { pub COOL_LINT, nursery, "default lint description" }
+ /// ```
+ ///
+ /// Good:
+ /// ```rust,ignore
+ /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" }
+ /// ```
+ pub DEFAULT_LINT,
+ internal,
+ "found 'default lint description' in a lint declaration"
+}
+
declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
impl EarlyLintPass for ClippyLintsInternal {
.iter()
.find(|item| item.ident.name.as_str() == "utils")
{
- if let ItemKind::Mod(ref utils_mod) = utils.node {
+ if let ItemKind::Mod(ref utils_mod) = utils.kind {
if let Some(paths) = utils_mod.items.iter().find(|item| item.ident.name.as_str() == "paths") {
- if let ItemKind::Mod(ref paths_mod) = paths.node {
- let mut last_name: Option<LocalInternedString> = None;
+ if let ItemKind::Mod(ref paths_mod) = paths.kind {
+ let mut last_name: Option<SymbolStr> = None;
for item in &*paths_mod.items {
let name = item.ident.as_str();
if let Some(ref last_name) = last_name {
registered_lints: FxHashSet<Name>,
}
-impl_lint_pass!(LintWithoutLintPass => [LINT_WITHOUT_LINT_PASS]);
+impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS]);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LintWithoutLintPass {
- fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
- if let hir::ItemKind::Static(ref ty, MutImmutable, _) = item.node {
+ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
+ if let hir::ItemKind::Static(ref ty, Mutability::Not, body_id) = item.kind {
if is_lint_ref_type(cx, ty) {
+ let expr = &cx.tcx.hir().body(body_id).value;
+ if_chain! {
+ if let ExprKind::AddrOf(_, _, ref inner_exp) = expr.kind;
+ if let ExprKind::Struct(_, ref fields, _) = inner_exp.kind;
+ let field = fields.iter()
+ .find(|f| f.ident.as_str() == "desc")
+ .expect("lints must have a description field");
+ if let ExprKind::Lit(Spanned {
+ node: LitKind::Str(ref sym, _),
+ ..
+ }) = field.expr.kind;
+ if sym.as_str() == "default lint description";
+
+ then {
+ span_lint(
+ cx,
+ DEFAULT_LINT,
+ item.span,
+ &format!("the lint `{}` has the default lint description", item.ident.name),
+ );
+ }
+ }
self.declared_lints.insert(item.ident.name, item.span);
}
- } else if let hir::ItemKind::Impl(.., Some(ref trait_ref), _, ref impl_item_refs) = item.node {
- if_chain! {
- if let hir::TraitRef{path, ..} = trait_ref;
- if let Res::Def(DefKind::Trait, def_id) = path.res;
- if match_def_path(cx, def_id, &paths::LINT_PASS);
- then {
- let mut collector = LintCollector {
- output: &mut self.registered_lints,
- cx,
- };
- let body_id = cx.tcx.hir().body_owned_by(
- impl_item_refs
- .iter()
- .find(|iiref| iiref.ident.as_str() == "get_lints")
- .expect("LintPass needs to implement get_lints")
- .id.hir_id
- );
- collector.visit_expr(&cx.tcx.hir().body(body_id).value);
- }
+ } else if is_expn_of(item.span, "impl_lint_pass").is_some()
+ || is_expn_of(item.span, "declare_lint_pass").is_some()
+ {
+ if let hir::ItemKind::Impl {
+ of_trait: None,
+ items: ref impl_item_refs,
+ ..
+ } = item.kind
+ {
+ let mut collector = LintCollector {
+ output: &mut self.registered_lints,
+ cx,
+ };
+ let body_id = cx.tcx.hir().body_owned_by(
+ impl_item_refs
+ .iter()
+ .find(|iiref| iiref.ident.as_str() == "get_lints")
+ .expect("LintPass needs to implement get_lints")
+ .id
+ .hir_id,
+ );
+ collector.visit_expr(&cx.tcx.hir().body(body_id).value);
}
}
}
- fn check_crate_post(&mut self, cx: &LateContext<'a, 'tcx>, _: &'tcx Crate) {
+ fn check_crate_post(&mut self, cx: &LateContext<'a, 'tcx>, _: &'tcx Crate<'_>) {
for (lint_name, &lint_span) in &self.declared_lints {
// When using the `declare_tool_lint!` macro, the original `lint_span`'s
// file points to "<rustc macros>".
}
}
-fn is_lint_ref_type<'tcx>(cx: &LateContext<'_, 'tcx>, ty: &Ty) -> bool {
+fn is_lint_ref_type<'tcx>(cx: &LateContext<'_, 'tcx>, ty: &Ty<'_>) -> bool {
if let TyKind::Rptr(
_,
MutTy {
ty: ref inner,
- mutbl: MutImmutable,
+ mutbl: Mutability::Not,
},
- ) = ty.node
+ ) = ty.kind
{
- if let TyKind::Path(ref path) = inner.node {
+ if let TyKind::Path(ref path) = inner.kind {
if let Res::Def(DefKind::Struct, def_id) = cx.tables.qpath_res(path, inner.hir_id) {
return match_def_path(cx, def_id, &paths::LINT);
}
}
impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> {
- fn visit_expr(&mut self, expr: &'tcx Expr) {
+ type Map = Map<'tcx>;
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
walk_expr(self, expr);
}
- fn visit_path(&mut self, path: &'tcx Path, _: HirId) {
+ fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) {
if path.segments.len() == 1 {
self.output.insert(path.segments[0].ident.name);
}
}
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
- NestedVisitorMap::All(&self.cx.tcx.hir())
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::All(self.cx.tcx.hir())
}
}
}
impl CompilerLintFunctions {
+ #[must_use]
pub fn new() -> Self {
let mut map = FxHashMap::default();
map.insert("span_lint", "utils::span_lint");
map.insert("struct_span_lint", "utils::span_lint");
map.insert("lint", "utils::span_lint");
- map.insert("span_lint_note", "utils::span_note_and_lint");
- map.insert("span_lint_help", "utils::span_help_and_lint");
+ map.insert("span_lint_note", "utils::span_lint_and_note");
+ map.insert("span_lint_help", "utils::span_lint_and_help");
Self { map }
}
}
impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CompilerLintFunctions {
- fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
+ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
if_chain! {
- if let ExprKind::MethodCall(ref path, _, ref args) = expr.node;
+ if let ExprKind::MethodCall(ref path, _, ref args) = expr.kind;
let fn_name = path.ident;
if let Some(sugg) = self.map.get(&*fn_name.as_str());
let ty = walk_ptrs_ty(cx.tables.expr_ty(&args[0]));
if match_type(cx, ty, &paths::EARLY_CONTEXT)
|| match_type(cx, ty, &paths::LATE_CONTEXT);
then {
- span_help_and_lint(
+ span_lint_and_help(
cx,
COMPILER_LINT_FUNCTIONS,
path.ident.span,
}
}
-pub struct OuterExpnDataPass;
-
-impl_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]);
+declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OuterExpnDataPass {
- fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
- let (method_names, arg_lists) = method_calls(expr, 2);
- let method_names: Vec<LocalInternedString> = method_names.iter().map(|s| s.as_str()).collect();
- let method_names: Vec<&str> = method_names.iter().map(std::convert::AsRef::as_ref).collect();
+ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
+ 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();
if_chain! {
if let ["expn_data", "outer_expn"] = method_names.as_slice();
let args = arg_lists[1];
span_lint_and_sugg(
cx,
OUTER_EXPN_EXPN_DATA,
- expr.span.trim_start(self_arg.span).unwrap_or(expr.span),
+ spans[1].with_hi(expr.span.hi()),
"usage of `outer_expn().expn_data()`",
"try",
- ".outer_expn_data()".to_string(),
+ "outer_expn_data()".to_string(),
Applicability::MachineApplicable,
);
}
}
}
}
+
+declare_lint_pass!(ProduceIce => [PRODUCE_ICE]);
+
+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?");
+ }
+ }
+}
+
+fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool {
+ match fn_kind {
+ FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy",
+ FnKind::Closure(..) => false,
+ }
+}