From 09bded443745a05daaeeca76e4b032381ca1a126 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Wed, 17 Feb 2021 12:42:21 -0500 Subject: [PATCH] Factor out `clippy_utils` crate --- .gitignore | 1 + clippy_lints/Cargo.toml | 1 + clippy_lints/src/consts.rs | 575 +----------------- clippy_lints/src/lib.rs | 110 +--- clippy_lints/src/utils.rs | 1 + clippy_utils/Cargo.toml | 24 + .../utils => clippy_utils/src}/ast_utils.rs | 2 +- .../src}/ast_utils/ident_iter.rs | 0 .../src/utils => clippy_utils/src}/attrs.rs | 0 .../src/utils => clippy_utils/src}/author.rs | 2 +- .../utils => clippy_utils/src}/camel_case.rs | 0 .../utils => clippy_utils/src}/comparisons.rs | 0 .../src/utils => clippy_utils/src}/conf.rs | 0 clippy_utils/src/constants.rs | 13 + clippy_utils/src/consts.rs | 574 +++++++++++++++++ .../utils => clippy_utils/src}/diagnostics.rs | 0 .../src}/eager_or_lazy.rs | 2 +- .../src/utils => clippy_utils/src}/higher.rs | 2 +- .../utils => clippy_utils/src}/hir_utils.rs | 2 +- .../utils => clippy_utils/src}/inspector.rs | 2 +- .../src}/internal_lints.rs | 2 +- .../utils/mod.rs => clippy_utils/src/lib.rs | 140 ++++- .../src}/numeric_literal.rs | 0 .../src/utils => clippy_utils/src}/paths.rs | 0 .../src/utils => clippy_utils/src}/ptr.rs | 2 +- .../src}/qualify_min_const_fn.rs | 0 .../src/utils => clippy_utils/src}/sugg.rs | 2 +- .../utils => clippy_utils/src}/sym_helper.rs | 0 .../src/utils => clippy_utils/src}/usage.rs | 2 +- .../utils => clippy_utils/src}/visitors.rs | 0 30 files changed, 778 insertions(+), 681 deletions(-) create mode 100644 clippy_lints/src/utils.rs create mode 100644 clippy_utils/Cargo.toml rename {clippy_lints/src/utils => clippy_utils/src}/ast_utils.rs (99%) rename {clippy_lints/src/utils => clippy_utils/src}/ast_utils/ident_iter.rs (100%) rename {clippy_lints/src/utils => clippy_utils/src}/attrs.rs (100%) rename {clippy_lints/src/utils => clippy_utils/src}/author.rs (99%) rename {clippy_lints/src/utils => clippy_utils/src}/camel_case.rs (100%) rename {clippy_lints/src/utils => clippy_utils/src}/comparisons.rs (100%) rename {clippy_lints/src/utils => clippy_utils/src}/conf.rs (100%) create mode 100644 clippy_utils/src/constants.rs create mode 100644 clippy_utils/src/consts.rs rename {clippy_lints/src/utils => clippy_utils/src}/diagnostics.rs (100%) rename {clippy_lints/src/utils => clippy_utils/src}/eager_or_lazy.rs (97%) rename {clippy_lints/src/utils => clippy_utils/src}/higher.rs (99%) rename {clippy_lints/src/utils => clippy_utils/src}/hir_utils.rs (99%) rename {clippy_lints/src/utils => clippy_utils/src}/inspector.rs (99%) rename {clippy_lints/src/utils => clippy_utils/src}/internal_lints.rs (99%) rename clippy_lints/src/utils/mod.rs => clippy_utils/src/lib.rs (92%) rename {clippy_lints/src/utils => clippy_utils/src}/numeric_literal.rs (100%) rename {clippy_lints/src/utils => clippy_utils/src}/paths.rs (100%) rename {clippy_lints/src/utils => clippy_utils/src}/ptr.rs (97%) rename {clippy_lints/src/utils => clippy_utils/src}/qualify_min_const_fn.rs (100%) rename {clippy_lints/src/utils => clippy_utils/src}/sugg.rs (99%) rename {clippy_lints/src/utils => clippy_utils/src}/sym_helper.rs (100%) rename {clippy_lints/src/utils => clippy_utils/src}/usage.rs (99%) rename {clippy_lints/src/utils => clippy_utils/src}/visitors.rs (100%) diff --git a/.gitignore b/.gitignore index adf5e8feddf4..139129d55e33 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ out *Cargo.lock /target /clippy_lints/target +/clippy_utils/target /clippy_workspace_tests/target /clippy_dev/target /rustc_tools_util/target diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 840341fefc6a..f09df3393d7f 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -18,6 +18,7 @@ edition = "2018" [dependencies] cargo_metadata = "0.12" +clippy_utils = { path = "../clippy_utils" } if_chain = "1.0.0" itertools = "0.9" pulldown-cmark = { version = "0.8", default-features = false } diff --git a/clippy_lints/src/consts.rs b/clippy_lints/src/consts.rs index 1b89d0bbe386..7e87f53e3fba 100644 --- a/clippy_lints/src/consts.rs +++ b/clippy_lints/src/consts.rs @@ -1,574 +1 @@ -#![allow(clippy::float_cmp)] - -use crate::utils::{clip, sext, unsext}; -use if_chain::if_chain; -use rustc_ast::ast::{self, LitFloatType, LitKind}; -use rustc_data_structures::sync::Lrc; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp}; -use rustc_lint::LateContext; -use rustc_middle::mir::interpret::Scalar; -use rustc_middle::ty::subst::{Subst, SubstsRef}; -use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty, TyCtxt}; -use rustc_middle::{bug, span_bug}; -use rustc_span::symbol::Symbol; -use std::cmp::Ordering::{self, Equal}; -use std::convert::TryInto; -use std::hash::{Hash, Hasher}; - -/// A `LitKind`-like enum to fold constant `Expr`s into. -#[derive(Debug, Clone)] -pub enum Constant { - /// A `String` (e.g., "abc"). - Str(String), - /// A binary string (e.g., `b"abc"`). - Binary(Lrc<[u8]>), - /// A single `char` (e.g., `'a'`). - Char(char), - /// An integer's bit representation. - Int(u128), - /// An `f32`. - F32(f32), - /// An `f64`. - F64(f64), - /// `true` or `false`. - Bool(bool), - /// An array of constants. - Vec(Vec), - /// Also an array, but with only one constant, repeated N times. - Repeat(Box, u64), - /// A tuple of constants. - Tuple(Vec), - /// A raw pointer. - RawPtr(u128), - /// A reference - Ref(Box), - /// A literal with syntax error. - Err(Symbol), -} - -impl PartialEq for Constant { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - (&Self::Str(ref ls), &Self::Str(ref rs)) => ls == rs, - (&Self::Binary(ref l), &Self::Binary(ref r)) => l == r, - (&Self::Char(l), &Self::Char(r)) => l == r, - (&Self::Int(l), &Self::Int(r)) => l == r, - (&Self::F64(l), &Self::F64(r)) => { - // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have - // `Fw32 == Fw64`, so don’t compare them. - // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs. - l.to_bits() == r.to_bits() - }, - (&Self::F32(l), &Self::F32(r)) => { - // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have - // `Fw32 == Fw64`, so don’t compare them. - // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs. - f64::from(l).to_bits() == f64::from(r).to_bits() - }, - (&Self::Bool(l), &Self::Bool(r)) => l == r, - (&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r, - (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => ls == rs && lv == rv, - (&Self::Ref(ref lb), &Self::Ref(ref rb)) => *lb == *rb, - // TODO: are there inter-type equalities? - _ => false, - } - } -} - -impl Hash for Constant { - fn hash(&self, state: &mut H) - where - H: Hasher, - { - std::mem::discriminant(self).hash(state); - match *self { - Self::Str(ref s) => { - s.hash(state); - }, - Self::Binary(ref b) => { - b.hash(state); - }, - Self::Char(c) => { - c.hash(state); - }, - Self::Int(i) => { - i.hash(state); - }, - Self::F32(f) => { - f64::from(f).to_bits().hash(state); - }, - Self::F64(f) => { - f.to_bits().hash(state); - }, - Self::Bool(b) => { - b.hash(state); - }, - Self::Vec(ref v) | Self::Tuple(ref v) => { - v.hash(state); - }, - Self::Repeat(ref c, l) => { - c.hash(state); - l.hash(state); - }, - Self::RawPtr(u) => { - u.hash(state); - }, - Self::Ref(ref r) => { - r.hash(state); - }, - Self::Err(ref s) => { - s.hash(state); - }, - } - } -} - -impl Constant { - pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option { - match (left, right) { - (&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)), - (&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)), - (&Self::Int(l), &Self::Int(r)) => { - if let ty::Int(int_ty) = *cmp_type.kind() { - Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty))) - } else { - Some(l.cmp(&r)) - } - }, - (&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r), - (&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r), - (&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)), - (&Self::Tuple(ref l), &Self::Tuple(ref r)) | (&Self::Vec(ref l), &Self::Vec(ref r)) => l - .iter() - .zip(r.iter()) - .map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri)) - .find(|r| r.map_or(true, |o| o != Ordering::Equal)) - .unwrap_or_else(|| Some(l.len().cmp(&r.len()))), - (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => { - match Self::partial_cmp(tcx, cmp_type, lv, rv) { - Some(Equal) => Some(ls.cmp(rs)), - x => x, - } - }, - (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp(tcx, cmp_type, lb, rb), - // TODO: are there any useful inter-type orderings? - _ => None, - } - } -} - -/// Parses a `LitKind` to a `Constant`. -pub fn lit_to_constant(lit: &LitKind, ty: Option>) -> Constant { - match *lit { - LitKind::Str(ref is, _) => Constant::Str(is.to_string()), - LitKind::Byte(b) => Constant::Int(u128::from(b)), - LitKind::ByteStr(ref s) => Constant::Binary(Lrc::clone(s)), - LitKind::Char(c) => Constant::Char(c), - LitKind::Int(n, _) => Constant::Int(n), - LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty { - ast::FloatTy::F32 => Constant::F32(is.as_str().parse().unwrap()), - ast::FloatTy::F64 => Constant::F64(is.as_str().parse().unwrap()), - }, - LitKind::Float(ref is, LitFloatType::Unsuffixed) => match ty.expect("type of float is known").kind() { - ty::Float(FloatTy::F32) => Constant::F32(is.as_str().parse().unwrap()), - ty::Float(FloatTy::F64) => Constant::F64(is.as_str().parse().unwrap()), - _ => bug!(), - }, - LitKind::Bool(b) => Constant::Bool(b), - LitKind::Err(s) => Constant::Err(s), - } -} - -pub fn constant<'tcx>( - lcx: &LateContext<'tcx>, - typeck_results: &ty::TypeckResults<'tcx>, - e: &Expr<'_>, -) -> Option<(Constant, bool)> { - let mut cx = ConstEvalLateContext { - lcx, - typeck_results, - param_env: lcx.param_env, - needed_resolution: false, - substs: lcx.tcx.intern_substs(&[]), - }; - cx.expr(e).map(|cst| (cst, cx.needed_resolution)) -} - -pub fn constant_simple<'tcx>( - lcx: &LateContext<'tcx>, - typeck_results: &ty::TypeckResults<'tcx>, - e: &Expr<'_>, -) -> Option { - constant(lcx, typeck_results, e).and_then(|(cst, res)| if res { None } else { Some(cst) }) -} - -/// Creates a `ConstEvalLateContext` from the given `LateContext` and `TypeckResults`. -pub fn constant_context<'a, 'tcx>( - lcx: &'a LateContext<'tcx>, - typeck_results: &'a ty::TypeckResults<'tcx>, -) -> ConstEvalLateContext<'a, 'tcx> { - ConstEvalLateContext { - lcx, - typeck_results, - param_env: lcx.param_env, - needed_resolution: false, - substs: lcx.tcx.intern_substs(&[]), - } -} - -pub struct ConstEvalLateContext<'a, 'tcx> { - lcx: &'a LateContext<'tcx>, - typeck_results: &'a ty::TypeckResults<'tcx>, - param_env: ty::ParamEnv<'tcx>, - needed_resolution: bool, - substs: SubstsRef<'tcx>, -} - -impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { - /// Simple constant folding: Insert an expression, get a constant or none. - pub fn expr(&mut self, e: &Expr<'_>) -> Option { - match e.kind { - ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)), - ExprKind::Block(ref block, _) => self.block(block), - ExprKind::Lit(ref lit) => Some(lit_to_constant(&lit.node, self.typeck_results.expr_ty_opt(e))), - ExprKind::Array(ref vec) => self.multi(vec).map(Constant::Vec), - ExprKind::Tup(ref tup) => self.multi(tup).map(Constant::Tuple), - ExprKind::Repeat(ref value, _) => { - let n = match self.typeck_results.expr_ty(e).kind() { - ty::Array(_, n) => n.try_eval_usize(self.lcx.tcx, self.lcx.param_env)?, - _ => span_bug!(e.span, "typeck error"), - }; - self.expr(value).map(|v| Constant::Repeat(Box::new(v), n)) - }, - ExprKind::Unary(op, ref operand) => self.expr(operand).and_then(|o| match op { - UnOp::Not => self.constant_not(&o, self.typeck_results.expr_ty(e)), - UnOp::Neg => self.constant_negate(&o, self.typeck_results.expr_ty(e)), - UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }), - }), - ExprKind::If(ref cond, ref then, ref otherwise) => self.ifthenelse(cond, then, *otherwise), - ExprKind::Binary(op, ref left, ref right) => self.binop(op, left, right), - ExprKind::Call(ref callee, ref args) => { - // We only handle a few const functions for now. - if_chain! { - if args.is_empty(); - if let ExprKind::Path(qpath) = &callee.kind; - let res = self.typeck_results.qpath_res(qpath, callee.hir_id); - if let Some(def_id) = res.opt_def_id(); - let def_path: Vec<_> = self.lcx.get_def_path(def_id).into_iter().map(Symbol::as_str).collect(); - let def_path: Vec<&str> = def_path.iter().take(4).map(|s| &**s).collect(); - if let ["core", "num", int_impl, "max_value"] = *def_path; - then { - let value = match int_impl { - "" => i8::MAX as u128, - "" => i16::MAX as u128, - "" => i32::MAX as u128, - "" => i64::MAX as u128, - "" => i128::MAX as u128, - _ => return None, - }; - Some(Constant::Int(value)) - } - else { - None - } - } - }, - ExprKind::Index(ref arr, ref index) => self.index(arr, index), - ExprKind::AddrOf(_, _, ref inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))), - // TODO: add other expressions. - _ => None, - } - } - - #[allow(clippy::cast_possible_wrap)] - fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option { - use self::Constant::{Bool, Int}; - match *o { - Bool(b) => Some(Bool(!b)), - Int(value) => { - let value = !value; - match *ty.kind() { - ty::Int(ity) => Some(Int(unsext(self.lcx.tcx, value as i128, ity))), - ty::Uint(ity) => Some(Int(clip(self.lcx.tcx, value, ity))), - _ => None, - } - }, - _ => None, - } - } - - fn constant_negate(&self, o: &Constant, ty: Ty<'_>) -> Option { - use self::Constant::{Int, F32, F64}; - match *o { - Int(value) => { - let ity = match *ty.kind() { - ty::Int(ity) => ity, - _ => return None, - }; - // sign extend - let value = sext(self.lcx.tcx, value, ity); - let value = value.checked_neg()?; - // clear unused bits - Some(Int(unsext(self.lcx.tcx, value, ity))) - }, - F32(f) => Some(F32(-f)), - F64(f) => Some(F64(-f)), - _ => None, - } - } - - /// Create `Some(Vec![..])` of all constants, unless there is any - /// non-constant part. - fn multi(&mut self, vec: &[Expr<'_>]) -> Option> { - vec.iter().map(|elem| self.expr(elem)).collect::>() - } - - /// Lookup a possibly constant expression from a `ExprKind::Path`. - fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>) -> Option { - let res = self.typeck_results.qpath_res(qpath, id); - match res { - Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => { - let substs = self.typeck_results.node_substs(id); - let substs = if self.substs.is_empty() { - substs - } else { - substs.subst(self.lcx.tcx, self.substs) - }; - - let result = self - .lcx - .tcx - .const_eval_resolve( - self.param_env, - ty::WithOptConstParam::unknown(def_id), - substs, - None, - None, - ) - .ok() - .map(|val| rustc_middle::ty::Const::from_value(self.lcx.tcx, val, ty))?; - let result = miri_to_const(&result); - if result.is_some() { - self.needed_resolution = true; - } - result - }, - // FIXME: cover all usable cases. - _ => None, - } - } - - fn index(&mut self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option { - let lhs = self.expr(lhs); - let index = self.expr(index); - - match (lhs, index) { - (Some(Constant::Vec(vec)), Some(Constant::Int(index))) => match vec.get(index as usize) { - Some(Constant::F32(x)) => Some(Constant::F32(*x)), - Some(Constant::F64(x)) => Some(Constant::F64(*x)), - _ => None, - }, - (Some(Constant::Vec(vec)), _) => { - if !vec.is_empty() && vec.iter().all(|x| *x == vec[0]) { - match vec.get(0) { - Some(Constant::F32(x)) => Some(Constant::F32(*x)), - Some(Constant::F64(x)) => Some(Constant::F64(*x)), - _ => None, - } - } else { - None - } - }, - _ => None, - } - } - - /// A block can only yield a constant if it only has one constant expression. - fn block(&mut self, block: &Block<'_>) -> Option { - if block.stmts.is_empty() { - block.expr.as_ref().and_then(|b| self.expr(b)) - } else { - None - } - } - - fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option { - if let Some(Constant::Bool(b)) = self.expr(cond) { - if b { - self.expr(&*then) - } else { - otherwise.as_ref().and_then(|expr| self.expr(expr)) - } - } else { - None - } - } - - fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option { - let l = self.expr(left)?; - let r = self.expr(right); - match (l, r) { - (Constant::Int(l), Some(Constant::Int(r))) => match *self.typeck_results.expr_ty_opt(left)?.kind() { - ty::Int(ity) => { - let l = sext(self.lcx.tcx, l, ity); - let r = sext(self.lcx.tcx, r, ity); - let zext = |n: i128| Constant::Int(unsext(self.lcx.tcx, n, ity)); - match op.node { - BinOpKind::Add => l.checked_add(r).map(zext), - BinOpKind::Sub => l.checked_sub(r).map(zext), - BinOpKind::Mul => l.checked_mul(r).map(zext), - BinOpKind::Div if r != 0 => l.checked_div(r).map(zext), - BinOpKind::Rem if r != 0 => l.checked_rem(r).map(zext), - BinOpKind::Shr => l.checked_shr(r.try_into().expect("invalid shift")).map(zext), - BinOpKind::Shl => l.checked_shl(r.try_into().expect("invalid shift")).map(zext), - BinOpKind::BitXor => Some(zext(l ^ r)), - BinOpKind::BitOr => Some(zext(l | r)), - BinOpKind::BitAnd => Some(zext(l & r)), - BinOpKind::Eq => Some(Constant::Bool(l == r)), - BinOpKind::Ne => Some(Constant::Bool(l != r)), - BinOpKind::Lt => Some(Constant::Bool(l < r)), - BinOpKind::Le => Some(Constant::Bool(l <= r)), - BinOpKind::Ge => Some(Constant::Bool(l >= r)), - BinOpKind::Gt => Some(Constant::Bool(l > r)), - _ => None, - } - }, - ty::Uint(_) => match op.node { - BinOpKind::Add => l.checked_add(r).map(Constant::Int), - BinOpKind::Sub => l.checked_sub(r).map(Constant::Int), - BinOpKind::Mul => l.checked_mul(r).map(Constant::Int), - BinOpKind::Div => l.checked_div(r).map(Constant::Int), - BinOpKind::Rem => l.checked_rem(r).map(Constant::Int), - BinOpKind::Shr => l.checked_shr(r.try_into().expect("shift too large")).map(Constant::Int), - BinOpKind::Shl => l.checked_shl(r.try_into().expect("shift too large")).map(Constant::Int), - BinOpKind::BitXor => Some(Constant::Int(l ^ r)), - BinOpKind::BitOr => Some(Constant::Int(l | r)), - BinOpKind::BitAnd => Some(Constant::Int(l & r)), - BinOpKind::Eq => Some(Constant::Bool(l == r)), - BinOpKind::Ne => Some(Constant::Bool(l != r)), - BinOpKind::Lt => Some(Constant::Bool(l < r)), - BinOpKind::Le => Some(Constant::Bool(l <= r)), - BinOpKind::Ge => Some(Constant::Bool(l >= r)), - BinOpKind::Gt => Some(Constant::Bool(l > r)), - _ => None, - }, - _ => None, - }, - (Constant::F32(l), Some(Constant::F32(r))) => match op.node { - BinOpKind::Add => Some(Constant::F32(l + r)), - BinOpKind::Sub => Some(Constant::F32(l - r)), - BinOpKind::Mul => Some(Constant::F32(l * r)), - BinOpKind::Div => Some(Constant::F32(l / r)), - BinOpKind::Rem => Some(Constant::F32(l % r)), - BinOpKind::Eq => Some(Constant::Bool(l == r)), - BinOpKind::Ne => Some(Constant::Bool(l != r)), - BinOpKind::Lt => Some(Constant::Bool(l < r)), - BinOpKind::Le => Some(Constant::Bool(l <= r)), - BinOpKind::Ge => Some(Constant::Bool(l >= r)), - BinOpKind::Gt => Some(Constant::Bool(l > r)), - _ => None, - }, - (Constant::F64(l), Some(Constant::F64(r))) => match op.node { - BinOpKind::Add => Some(Constant::F64(l + r)), - BinOpKind::Sub => Some(Constant::F64(l - r)), - BinOpKind::Mul => Some(Constant::F64(l * r)), - BinOpKind::Div => Some(Constant::F64(l / r)), - BinOpKind::Rem => Some(Constant::F64(l % r)), - BinOpKind::Eq => Some(Constant::Bool(l == r)), - BinOpKind::Ne => Some(Constant::Bool(l != r)), - BinOpKind::Lt => Some(Constant::Bool(l < r)), - BinOpKind::Le => Some(Constant::Bool(l <= r)), - BinOpKind::Ge => Some(Constant::Bool(l >= r)), - BinOpKind::Gt => Some(Constant::Bool(l > r)), - _ => None, - }, - (l, r) => match (op.node, l, r) { - (BinOpKind::And, Constant::Bool(false), _) => Some(Constant::Bool(false)), - (BinOpKind::Or, Constant::Bool(true), _) => Some(Constant::Bool(true)), - (BinOpKind::And, Constant::Bool(true), Some(r)) | (BinOpKind::Or, Constant::Bool(false), Some(r)) => { - Some(r) - }, - (BinOpKind::BitXor, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l ^ r)), - (BinOpKind::BitAnd, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l & r)), - (BinOpKind::BitOr, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l | r)), - _ => None, - }, - } - } -} - -pub fn miri_to_const(result: &ty::Const<'_>) -> Option { - use rustc_middle::mir::interpret::ConstValue; - match result.val { - ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int(int))) => { - match result.ty.kind() { - ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)), - ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))), - ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits( - int.try_into().expect("invalid f32 bit representation"), - ))), - ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits( - int.try_into().expect("invalid f64 bit representation"), - ))), - ty::RawPtr(type_and_mut) => { - if let ty::Uint(_) = type_and_mut.ty.kind() { - return Some(Constant::RawPtr(int.assert_bits(int.size()))); - } - None - }, - // FIXME: implement other conversions. - _ => None, - } - }, - ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => match result.ty.kind() { - ty::Ref(_, tam, _) => match tam.kind() { - ty::Str => String::from_utf8( - data.inspect_with_uninit_and_ptr_outside_interpreter(start..end) - .to_owned(), - ) - .ok() - .map(Constant::Str), - _ => None, - }, - _ => None, - }, - ty::ConstKind::Value(ConstValue::ByRef { alloc, offset: _ }) => match result.ty.kind() { - ty::Array(sub_type, len) => match sub_type.kind() { - ty::Float(FloatTy::F32) => match miri_to_const(len) { - Some(Constant::Int(len)) => alloc - .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * len as usize)) - .to_owned() - .chunks(4) - .map(|chunk| { - Some(Constant::F32(f32::from_le_bytes( - chunk.try_into().expect("this shouldn't happen"), - ))) - }) - .collect::>>() - .map(Constant::Vec), - _ => None, - }, - ty::Float(FloatTy::F64) => match miri_to_const(len) { - Some(Constant::Int(len)) => alloc - .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * len as usize)) - .to_owned() - .chunks(8) - .map(|chunk| { - Some(Constant::F64(f64::from_le_bytes( - chunk.try_into().expect("this shouldn't happen"), - ))) - }) - .collect::>>() - .map(Constant::Vec), - _ => None, - }, - // FIXME: implement other array type conversions. - _ => None, - }, - _ => None, - }, - // FIXME: implement other conversions. - _ => None, - } -} +pub use clippy_utils::consts::*; diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d66e3720be0d..381e6f9369f3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -50,103 +50,23 @@ use rustc_data_structures::fx::FxHashSet; use rustc_lint::LintId; use rustc_session::Session; -/// Macro used to declare a Clippy lint. -/// -/// Every lint declaration consists of 4 parts: -/// -/// 1. The documentation, which is used for the website -/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions. -/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or -/// `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of. -/// 4. The `description` that contains a short explanation on what's wrong with code where the -/// lint is triggered. -/// -/// Currently the categories `style`, `correctness`, `complexity` and `perf` are enabled by default. -/// As said in the README.md of this repository, if the lint level mapping changes, please update -/// README.md. -/// -/// # Example -/// -/// ``` -/// #![feature(rustc_private)] -/// extern crate rustc_session; -/// use rustc_session::declare_tool_lint; -/// use clippy_lints::declare_clippy_lint; -/// -/// declare_clippy_lint! { -/// /// **What it does:** Checks for ... (describe what the lint matches). -/// /// -/// /// **Why is this bad?** Supply the reason for linting the code. -/// /// -/// /// **Known problems:** None. (Or describe where it could go wrong.) -/// /// -/// /// **Example:** -/// /// -/// /// ```rust -/// /// // Bad -/// /// Insert a short example of code that triggers the lint -/// /// -/// /// // Good -/// /// Insert a short example of improved code that doesn't trigger the lint -/// /// ``` -/// pub LINT_NAME, -/// pedantic, -/// "description" -/// } -/// ``` -/// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints #[macro_export] macro_rules! declare_clippy_lint { - { $(#[$attr:meta])* pub $name:tt, style, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, correctness, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Deny, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, perf, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, pedantic, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, restriction, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, cargo, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, nursery, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, internal, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, internal_warn, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; + ( $($x:tt)* ) => { clippy_utils::declare_clippy_lint!($($x)*); } +} + +#[macro_export] +macro_rules! sym { + ( $($x:tt)* ) => { clippy_utils::sym!($($x)*); } +} + +#[macro_export] +macro_rules! unwrap_cargo_metadata { + ( $($x:tt)* ) => { clippy_utils::unwrap_cargo_metadata!($($x)*); } +} + +macro_rules! extract_msrv_attr { + ( $($x:tt)* ) => { clippy_utils::extract_msrv_attr!($($x)*); } } mod consts; diff --git a/clippy_lints/src/utils.rs b/clippy_lints/src/utils.rs new file mode 100644 index 000000000000..bf54e39769cb --- /dev/null +++ b/clippy_lints/src/utils.rs @@ -0,0 +1 @@ +pub use clippy_utils::*; diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml new file mode 100644 index 000000000000..1b05b5431869 --- /dev/null +++ b/clippy_utils/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "clippy_utils" +version = "0.1.0" +authors = ["The Rust Project Developers"] +edition = "2018" + +[dependencies] +cargo_metadata = "0.12" +if_chain = "1.0.0" +itertools = "0.9" +pulldown-cmark = { version = "0.8", default-features = false } +quine-mc_cluskey = "0.2.2" +regex-syntax = "0.6" +serde = { version = "1.0", features = ["derive"] } +smallvec = { version = "1", features = ["union"] } +toml = "0.5.3" +unicode-normalization = "0.1" +semver = "0.11" +rustc-semver="1.1.0" +# NOTE: cargo requires serde feat in its url dep +# see +url = { version = "2.1.0", features = ["serde"] } +quote = "1" +syn = { version = "1", features = ["full"] } diff --git a/clippy_lints/src/utils/ast_utils.rs b/clippy_utils/src/ast_utils.rs similarity index 99% rename from clippy_lints/src/utils/ast_utils.rs rename to clippy_utils/src/ast_utils.rs index 44eb3968ae73..7ec0e103c000 100644 --- a/clippy_lints/src/utils/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -4,7 +4,7 @@ #![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)] -use crate::utils::{both, over}; +use crate::{both, over}; use rustc_ast::ptr::P; use rustc_ast::{self as ast, *}; use rustc_span::symbol::Ident; diff --git a/clippy_lints/src/utils/ast_utils/ident_iter.rs b/clippy_utils/src/ast_utils/ident_iter.rs similarity index 100% rename from clippy_lints/src/utils/ast_utils/ident_iter.rs rename to clippy_utils/src/ast_utils/ident_iter.rs diff --git a/clippy_lints/src/utils/attrs.rs b/clippy_utils/src/attrs.rs similarity index 100% rename from clippy_lints/src/utils/attrs.rs rename to clippy_utils/src/attrs.rs diff --git a/clippy_lints/src/utils/author.rs b/clippy_utils/src/author.rs similarity index 99% rename from clippy_lints/src/utils/author.rs rename to clippy_utils/src/author.rs index ca60d335262b..ba83566b5e65 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_utils/src/author.rs @@ -1,7 +1,7 @@ //! A group of attributes that can be attached to Rust code in order //! to generate a clippy lint detecting said code automatically. -use crate::utils::get_attr; +use crate::{declare_clippy_lint, get_attr}; use rustc_ast::ast::{Attribute, LitFloatType, LitKind}; use rustc_ast::walk_list; use rustc_data_structures::fx::FxHashMap; diff --git a/clippy_lints/src/utils/camel_case.rs b/clippy_utils/src/camel_case.rs similarity index 100% rename from clippy_lints/src/utils/camel_case.rs rename to clippy_utils/src/camel_case.rs diff --git a/clippy_lints/src/utils/comparisons.rs b/clippy_utils/src/comparisons.rs similarity index 100% rename from clippy_lints/src/utils/comparisons.rs rename to clippy_utils/src/comparisons.rs diff --git a/clippy_lints/src/utils/conf.rs b/clippy_utils/src/conf.rs similarity index 100% rename from clippy_lints/src/utils/conf.rs rename to clippy_utils/src/conf.rs diff --git a/clippy_utils/src/constants.rs b/clippy_utils/src/constants.rs new file mode 100644 index 000000000000..522932f054d8 --- /dev/null +++ b/clippy_utils/src/constants.rs @@ -0,0 +1,13 @@ +//! This module contains some useful constants. + +#![deny(clippy::missing_docs_in_private_items)] + +/// List of the built-in types names. +/// +/// See also [the reference][reference-types] for a list of such types. +/// +/// [reference-types]: https://doc.rust-lang.org/reference/types.html +pub const BUILTIN_TYPES: &[&str] = &[ + "i8", "u8", "i16", "u16", "i32", "u32", "i64", "u64", "i128", "u128", "isize", "usize", "f32", "f64", "bool", + "str", "char", +]; diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs new file mode 100644 index 000000000000..802c01055a68 --- /dev/null +++ b/clippy_utils/src/consts.rs @@ -0,0 +1,574 @@ +#![allow(clippy::float_cmp)] + +use crate::{clip, sext, unsext}; +use if_chain::if_chain; +use rustc_ast::ast::{self, LitFloatType, LitKind}; +use rustc_data_structures::sync::Lrc; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp}; +use rustc_lint::LateContext; +use rustc_middle::mir::interpret::Scalar; +use rustc_middle::ty::subst::{Subst, SubstsRef}; +use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty, TyCtxt}; +use rustc_middle::{bug, span_bug}; +use rustc_span::symbol::Symbol; +use std::cmp::Ordering::{self, Equal}; +use std::convert::TryInto; +use std::hash::{Hash, Hasher}; + +/// A `LitKind`-like enum to fold constant `Expr`s into. +#[derive(Debug, Clone)] +pub enum Constant { + /// A `String` (e.g., "abc"). + Str(String), + /// A binary string (e.g., `b"abc"`). + Binary(Lrc<[u8]>), + /// A single `char` (e.g., `'a'`). + Char(char), + /// An integer's bit representation. + Int(u128), + /// An `f32`. + F32(f32), + /// An `f64`. + F64(f64), + /// `true` or `false`. + Bool(bool), + /// An array of constants. + Vec(Vec), + /// Also an array, but with only one constant, repeated N times. + Repeat(Box, u64), + /// A tuple of constants. + Tuple(Vec), + /// A raw pointer. + RawPtr(u128), + /// A reference + Ref(Box), + /// A literal with syntax error. + Err(Symbol), +} + +impl PartialEq for Constant { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (&Self::Str(ref ls), &Self::Str(ref rs)) => ls == rs, + (&Self::Binary(ref l), &Self::Binary(ref r)) => l == r, + (&Self::Char(l), &Self::Char(r)) => l == r, + (&Self::Int(l), &Self::Int(r)) => l == r, + (&Self::F64(l), &Self::F64(r)) => { + // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have + // `Fw32 == Fw64`, so don’t compare them. + // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs. + l.to_bits() == r.to_bits() + }, + (&Self::F32(l), &Self::F32(r)) => { + // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have + // `Fw32 == Fw64`, so don’t compare them. + // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs. + f64::from(l).to_bits() == f64::from(r).to_bits() + }, + (&Self::Bool(l), &Self::Bool(r)) => l == r, + (&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r, + (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => ls == rs && lv == rv, + (&Self::Ref(ref lb), &Self::Ref(ref rb)) => *lb == *rb, + // TODO: are there inter-type equalities? + _ => false, + } + } +} + +impl Hash for Constant { + fn hash(&self, state: &mut H) + where + H: Hasher, + { + std::mem::discriminant(self).hash(state); + match *self { + Self::Str(ref s) => { + s.hash(state); + }, + Self::Binary(ref b) => { + b.hash(state); + }, + Self::Char(c) => { + c.hash(state); + }, + Self::Int(i) => { + i.hash(state); + }, + Self::F32(f) => { + f64::from(f).to_bits().hash(state); + }, + Self::F64(f) => { + f.to_bits().hash(state); + }, + Self::Bool(b) => { + b.hash(state); + }, + Self::Vec(ref v) | Self::Tuple(ref v) => { + v.hash(state); + }, + Self::Repeat(ref c, l) => { + c.hash(state); + l.hash(state); + }, + Self::RawPtr(u) => { + u.hash(state); + }, + Self::Ref(ref r) => { + r.hash(state); + }, + Self::Err(ref s) => { + s.hash(state); + }, + } + } +} + +impl Constant { + pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option { + match (left, right) { + (&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)), + (&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)), + (&Self::Int(l), &Self::Int(r)) => { + if let ty::Int(int_ty) = *cmp_type.kind() { + Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty))) + } else { + Some(l.cmp(&r)) + } + }, + (&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r), + (&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r), + (&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)), + (&Self::Tuple(ref l), &Self::Tuple(ref r)) | (&Self::Vec(ref l), &Self::Vec(ref r)) => l + .iter() + .zip(r.iter()) + .map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri)) + .find(|r| r.map_or(true, |o| o != Ordering::Equal)) + .unwrap_or_else(|| Some(l.len().cmp(&r.len()))), + (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => { + match Self::partial_cmp(tcx, cmp_type, lv, rv) { + Some(Equal) => Some(ls.cmp(rs)), + x => x, + } + }, + (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp(tcx, cmp_type, lb, rb), + // TODO: are there any useful inter-type orderings? + _ => None, + } + } +} + +/// Parses a `LitKind` to a `Constant`. +pub fn lit_to_constant(lit: &LitKind, ty: Option>) -> Constant { + match *lit { + LitKind::Str(ref is, _) => Constant::Str(is.to_string()), + LitKind::Byte(b) => Constant::Int(u128::from(b)), + LitKind::ByteStr(ref s) => Constant::Binary(Lrc::clone(s)), + LitKind::Char(c) => Constant::Char(c), + LitKind::Int(n, _) => Constant::Int(n), + LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty { + ast::FloatTy::F32 => Constant::F32(is.as_str().parse().unwrap()), + ast::FloatTy::F64 => Constant::F64(is.as_str().parse().unwrap()), + }, + LitKind::Float(ref is, LitFloatType::Unsuffixed) => match ty.expect("type of float is known").kind() { + ty::Float(FloatTy::F32) => Constant::F32(is.as_str().parse().unwrap()), + ty::Float(FloatTy::F64) => Constant::F64(is.as_str().parse().unwrap()), + _ => bug!(), + }, + LitKind::Bool(b) => Constant::Bool(b), + LitKind::Err(s) => Constant::Err(s), + } +} + +pub fn constant<'tcx>( + lcx: &LateContext<'tcx>, + typeck_results: &ty::TypeckResults<'tcx>, + e: &Expr<'_>, +) -> Option<(Constant, bool)> { + let mut cx = ConstEvalLateContext { + lcx, + typeck_results, + param_env: lcx.param_env, + needed_resolution: false, + substs: lcx.tcx.intern_substs(&[]), + }; + cx.expr(e).map(|cst| (cst, cx.needed_resolution)) +} + +pub fn constant_simple<'tcx>( + lcx: &LateContext<'tcx>, + typeck_results: &ty::TypeckResults<'tcx>, + e: &Expr<'_>, +) -> Option { + constant(lcx, typeck_results, e).and_then(|(cst, res)| if res { None } else { Some(cst) }) +} + +/// Creates a `ConstEvalLateContext` from the given `LateContext` and `TypeckResults`. +pub fn constant_context<'a, 'tcx>( + lcx: &'a LateContext<'tcx>, + typeck_results: &'a ty::TypeckResults<'tcx>, +) -> ConstEvalLateContext<'a, 'tcx> { + ConstEvalLateContext { + lcx, + typeck_results, + param_env: lcx.param_env, + needed_resolution: false, + substs: lcx.tcx.intern_substs(&[]), + } +} + +pub struct ConstEvalLateContext<'a, 'tcx> { + lcx: &'a LateContext<'tcx>, + typeck_results: &'a ty::TypeckResults<'tcx>, + param_env: ty::ParamEnv<'tcx>, + needed_resolution: bool, + substs: SubstsRef<'tcx>, +} + +impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { + /// Simple constant folding: Insert an expression, get a constant or none. + pub fn expr(&mut self, e: &Expr<'_>) -> Option { + match e.kind { + ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)), + ExprKind::Block(ref block, _) => self.block(block), + ExprKind::Lit(ref lit) => Some(lit_to_constant(&lit.node, self.typeck_results.expr_ty_opt(e))), + ExprKind::Array(ref vec) => self.multi(vec).map(Constant::Vec), + ExprKind::Tup(ref tup) => self.multi(tup).map(Constant::Tuple), + ExprKind::Repeat(ref value, _) => { + let n = match self.typeck_results.expr_ty(e).kind() { + ty::Array(_, n) => n.try_eval_usize(self.lcx.tcx, self.lcx.param_env)?, + _ => span_bug!(e.span, "typeck error"), + }; + self.expr(value).map(|v| Constant::Repeat(Box::new(v), n)) + }, + ExprKind::Unary(op, ref operand) => self.expr(operand).and_then(|o| match op { + UnOp::Not => self.constant_not(&o, self.typeck_results.expr_ty(e)), + UnOp::Neg => self.constant_negate(&o, self.typeck_results.expr_ty(e)), + UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }), + }), + ExprKind::If(ref cond, ref then, ref otherwise) => self.ifthenelse(cond, then, *otherwise), + ExprKind::Binary(op, ref left, ref right) => self.binop(op, left, right), + ExprKind::Call(ref callee, ref args) => { + // We only handle a few const functions for now. + if_chain! { + if args.is_empty(); + if let ExprKind::Path(qpath) = &callee.kind; + let res = self.typeck_results.qpath_res(qpath, callee.hir_id); + if let Some(def_id) = res.opt_def_id(); + let def_path: Vec<_> = self.lcx.get_def_path(def_id).into_iter().map(Symbol::as_str).collect(); + let def_path: Vec<&str> = def_path.iter().take(4).map(|s| &**s).collect(); + if let ["core", "num", int_impl, "max_value"] = *def_path; + then { + let value = match int_impl { + "" => i8::MAX as u128, + "" => i16::MAX as u128, + "" => i32::MAX as u128, + "" => i64::MAX as u128, + "" => i128::MAX as u128, + _ => return None, + }; + Some(Constant::Int(value)) + } + else { + None + } + } + }, + ExprKind::Index(ref arr, ref index) => self.index(arr, index), + ExprKind::AddrOf(_, _, ref inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))), + // TODO: add other expressions. + _ => None, + } + } + + #[allow(clippy::cast_possible_wrap)] + fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option { + use self::Constant::{Bool, Int}; + match *o { + Bool(b) => Some(Bool(!b)), + Int(value) => { + let value = !value; + match *ty.kind() { + ty::Int(ity) => Some(Int(unsext(self.lcx.tcx, value as i128, ity))), + ty::Uint(ity) => Some(Int(clip(self.lcx.tcx, value, ity))), + _ => None, + } + }, + _ => None, + } + } + + fn constant_negate(&self, o: &Constant, ty: Ty<'_>) -> Option { + use self::Constant::{Int, F32, F64}; + match *o { + Int(value) => { + let ity = match *ty.kind() { + ty::Int(ity) => ity, + _ => return None, + }; + // sign extend + let value = sext(self.lcx.tcx, value, ity); + let value = value.checked_neg()?; + // clear unused bits + Some(Int(unsext(self.lcx.tcx, value, ity))) + }, + F32(f) => Some(F32(-f)), + F64(f) => Some(F64(-f)), + _ => None, + } + } + + /// Create `Some(Vec![..])` of all constants, unless there is any + /// non-constant part. + fn multi(&mut self, vec: &[Expr<'_>]) -> Option> { + vec.iter().map(|elem| self.expr(elem)).collect::>() + } + + /// Lookup a possibly constant expression from a `ExprKind::Path`. + fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>) -> Option { + let res = self.typeck_results.qpath_res(qpath, id); + match res { + Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => { + let substs = self.typeck_results.node_substs(id); + let substs = if self.substs.is_empty() { + substs + } else { + substs.subst(self.lcx.tcx, self.substs) + }; + + let result = self + .lcx + .tcx + .const_eval_resolve( + self.param_env, + ty::WithOptConstParam::unknown(def_id), + substs, + None, + None, + ) + .ok() + .map(|val| rustc_middle::ty::Const::from_value(self.lcx.tcx, val, ty))?; + let result = miri_to_const(&result); + if result.is_some() { + self.needed_resolution = true; + } + result + }, + // FIXME: cover all usable cases. + _ => None, + } + } + + fn index(&mut self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option { + let lhs = self.expr(lhs); + let index = self.expr(index); + + match (lhs, index) { + (Some(Constant::Vec(vec)), Some(Constant::Int(index))) => match vec.get(index as usize) { + Some(Constant::F32(x)) => Some(Constant::F32(*x)), + Some(Constant::F64(x)) => Some(Constant::F64(*x)), + _ => None, + }, + (Some(Constant::Vec(vec)), _) => { + if !vec.is_empty() && vec.iter().all(|x| *x == vec[0]) { + match vec.get(0) { + Some(Constant::F32(x)) => Some(Constant::F32(*x)), + Some(Constant::F64(x)) => Some(Constant::F64(*x)), + _ => None, + } + } else { + None + } + }, + _ => None, + } + } + + /// A block can only yield a constant if it only has one constant expression. + fn block(&mut self, block: &Block<'_>) -> Option { + if block.stmts.is_empty() { + block.expr.as_ref().and_then(|b| self.expr(b)) + } else { + None + } + } + + fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option { + if let Some(Constant::Bool(b)) = self.expr(cond) { + if b { + self.expr(&*then) + } else { + otherwise.as_ref().and_then(|expr| self.expr(expr)) + } + } else { + None + } + } + + fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option { + let l = self.expr(left)?; + let r = self.expr(right); + match (l, r) { + (Constant::Int(l), Some(Constant::Int(r))) => match *self.typeck_results.expr_ty_opt(left)?.kind() { + ty::Int(ity) => { + let l = sext(self.lcx.tcx, l, ity); + let r = sext(self.lcx.tcx, r, ity); + let zext = |n: i128| Constant::Int(unsext(self.lcx.tcx, n, ity)); + match op.node { + BinOpKind::Add => l.checked_add(r).map(zext), + BinOpKind::Sub => l.checked_sub(r).map(zext), + BinOpKind::Mul => l.checked_mul(r).map(zext), + BinOpKind::Div if r != 0 => l.checked_div(r).map(zext), + BinOpKind::Rem if r != 0 => l.checked_rem(r).map(zext), + BinOpKind::Shr => l.checked_shr(r.try_into().expect("invalid shift")).map(zext), + BinOpKind::Shl => l.checked_shl(r.try_into().expect("invalid shift")).map(zext), + BinOpKind::BitXor => Some(zext(l ^ r)), + BinOpKind::BitOr => Some(zext(l | r)), + BinOpKind::BitAnd => Some(zext(l & r)), + BinOpKind::Eq => Some(Constant::Bool(l == r)), + BinOpKind::Ne => Some(Constant::Bool(l != r)), + BinOpKind::Lt => Some(Constant::Bool(l < r)), + BinOpKind::Le => Some(Constant::Bool(l <= r)), + BinOpKind::Ge => Some(Constant::Bool(l >= r)), + BinOpKind::Gt => Some(Constant::Bool(l > r)), + _ => None, + } + }, + ty::Uint(_) => match op.node { + BinOpKind::Add => l.checked_add(r).map(Constant::Int), + BinOpKind::Sub => l.checked_sub(r).map(Constant::Int), + BinOpKind::Mul => l.checked_mul(r).map(Constant::Int), + BinOpKind::Div => l.checked_div(r).map(Constant::Int), + BinOpKind::Rem => l.checked_rem(r).map(Constant::Int), + BinOpKind::Shr => l.checked_shr(r.try_into().expect("shift too large")).map(Constant::Int), + BinOpKind::Shl => l.checked_shl(r.try_into().expect("shift too large")).map(Constant::Int), + BinOpKind::BitXor => Some(Constant::Int(l ^ r)), + BinOpKind::BitOr => Some(Constant::Int(l | r)), + BinOpKind::BitAnd => Some(Constant::Int(l & r)), + BinOpKind::Eq => Some(Constant::Bool(l == r)), + BinOpKind::Ne => Some(Constant::Bool(l != r)), + BinOpKind::Lt => Some(Constant::Bool(l < r)), + BinOpKind::Le => Some(Constant::Bool(l <= r)), + BinOpKind::Ge => Some(Constant::Bool(l >= r)), + BinOpKind::Gt => Some(Constant::Bool(l > r)), + _ => None, + }, + _ => None, + }, + (Constant::F32(l), Some(Constant::F32(r))) => match op.node { + BinOpKind::Add => Some(Constant::F32(l + r)), + BinOpKind::Sub => Some(Constant::F32(l - r)), + BinOpKind::Mul => Some(Constant::F32(l * r)), + BinOpKind::Div => Some(Constant::F32(l / r)), + BinOpKind::Rem => Some(Constant::F32(l % r)), + BinOpKind::Eq => Some(Constant::Bool(l == r)), + BinOpKind::Ne => Some(Constant::Bool(l != r)), + BinOpKind::Lt => Some(Constant::Bool(l < r)), + BinOpKind::Le => Some(Constant::Bool(l <= r)), + BinOpKind::Ge => Some(Constant::Bool(l >= r)), + BinOpKind::Gt => Some(Constant::Bool(l > r)), + _ => None, + }, + (Constant::F64(l), Some(Constant::F64(r))) => match op.node { + BinOpKind::Add => Some(Constant::F64(l + r)), + BinOpKind::Sub => Some(Constant::F64(l - r)), + BinOpKind::Mul => Some(Constant::F64(l * r)), + BinOpKind::Div => Some(Constant::F64(l / r)), + BinOpKind::Rem => Some(Constant::F64(l % r)), + BinOpKind::Eq => Some(Constant::Bool(l == r)), + BinOpKind::Ne => Some(Constant::Bool(l != r)), + BinOpKind::Lt => Some(Constant::Bool(l < r)), + BinOpKind::Le => Some(Constant::Bool(l <= r)), + BinOpKind::Ge => Some(Constant::Bool(l >= r)), + BinOpKind::Gt => Some(Constant::Bool(l > r)), + _ => None, + }, + (l, r) => match (op.node, l, r) { + (BinOpKind::And, Constant::Bool(false), _) => Some(Constant::Bool(false)), + (BinOpKind::Or, Constant::Bool(true), _) => Some(Constant::Bool(true)), + (BinOpKind::And, Constant::Bool(true), Some(r)) | (BinOpKind::Or, Constant::Bool(false), Some(r)) => { + Some(r) + }, + (BinOpKind::BitXor, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l ^ r)), + (BinOpKind::BitAnd, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l & r)), + (BinOpKind::BitOr, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l | r)), + _ => None, + }, + } + } +} + +pub fn miri_to_const(result: &ty::Const<'_>) -> Option { + use rustc_middle::mir::interpret::ConstValue; + match result.val { + ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int(int))) => { + match result.ty.kind() { + ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)), + ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))), + ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits( + int.try_into().expect("invalid f32 bit representation"), + ))), + ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits( + int.try_into().expect("invalid f64 bit representation"), + ))), + ty::RawPtr(type_and_mut) => { + if let ty::Uint(_) = type_and_mut.ty.kind() { + return Some(Constant::RawPtr(int.assert_bits(int.size()))); + } + None + }, + // FIXME: implement other conversions. + _ => None, + } + }, + ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => match result.ty.kind() { + ty::Ref(_, tam, _) => match tam.kind() { + ty::Str => String::from_utf8( + data.inspect_with_uninit_and_ptr_outside_interpreter(start..end) + .to_owned(), + ) + .ok() + .map(Constant::Str), + _ => None, + }, + _ => None, + }, + ty::ConstKind::Value(ConstValue::ByRef { alloc, offset: _ }) => match result.ty.kind() { + ty::Array(sub_type, len) => match sub_type.kind() { + ty::Float(FloatTy::F32) => match miri_to_const(len) { + Some(Constant::Int(len)) => alloc + .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * len as usize)) + .to_owned() + .chunks(4) + .map(|chunk| { + Some(Constant::F32(f32::from_le_bytes( + chunk.try_into().expect("this shouldn't happen"), + ))) + }) + .collect::>>() + .map(Constant::Vec), + _ => None, + }, + ty::Float(FloatTy::F64) => match miri_to_const(len) { + Some(Constant::Int(len)) => alloc + .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * len as usize)) + .to_owned() + .chunks(8) + .map(|chunk| { + Some(Constant::F64(f64::from_le_bytes( + chunk.try_into().expect("this shouldn't happen"), + ))) + }) + .collect::>>() + .map(Constant::Vec), + _ => None, + }, + // FIXME: implement other array type conversions. + _ => None, + }, + _ => None, + }, + // FIXME: implement other conversions. + _ => None, + } +} diff --git a/clippy_lints/src/utils/diagnostics.rs b/clippy_utils/src/diagnostics.rs similarity index 100% rename from clippy_lints/src/utils/diagnostics.rs rename to clippy_utils/src/diagnostics.rs diff --git a/clippy_lints/src/utils/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs similarity index 97% rename from clippy_lints/src/utils/eager_or_lazy.rs rename to clippy_utils/src/eager_or_lazy.rs index 2f157c5030f4..52a33e9b1704 100644 --- a/clippy_lints/src/utils/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -9,7 +9,7 @@ //! - or-fun-call //! - option-if-let-else -use crate::utils::{is_ctor_or_promotable_const_function, is_type_diagnostic_item, match_type, paths}; +use crate::{is_ctor_or_promotable_const_function, is_type_diagnostic_item, match_type, paths}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit; diff --git a/clippy_lints/src/utils/higher.rs b/clippy_utils/src/higher.rs similarity index 99% rename from clippy_lints/src/utils/higher.rs rename to clippy_utils/src/higher.rs index 1cf1aa363d5d..be22df7109af 100644 --- a/clippy_lints/src/utils/higher.rs +++ b/clippy_utils/src/higher.rs @@ -3,7 +3,7 @@ #![deny(clippy::missing_docs_in_private_items)] -use crate::utils::{is_expn_of, match_def_path, paths}; +use crate::{is_expn_of, match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast; use rustc_hir as hir; diff --git a/clippy_lints/src/utils/hir_utils.rs b/clippy_utils/src/hir_utils.rs similarity index 99% rename from clippy_lints/src/utils/hir_utils.rs rename to clippy_utils/src/hir_utils.rs index c042990b759e..81be9254cbe1 100644 --- a/clippy_lints/src/utils/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -1,5 +1,5 @@ use crate::consts::{constant_context, constant_simple}; -use crate::utils::differing_macro_contexts; +use crate::differing_macro_contexts; use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; diff --git a/clippy_lints/src/utils/inspector.rs b/clippy_utils/src/inspector.rs similarity index 99% rename from clippy_lints/src/utils/inspector.rs rename to clippy_utils/src/inspector.rs index 9bec24be9e4e..b082bb6cfda7 100644 --- a/clippy_lints/src/utils/inspector.rs +++ b/clippy_utils/src/inspector.rs @@ -1,6 +1,6 @@ //! checks for attributes -use crate::utils::get_attr; +use crate::{declare_clippy_lint, get_attr}; use rustc_ast::ast::{Attribute, InlineAsmTemplatePiece}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_utils/src/internal_lints.rs similarity index 99% rename from clippy_lints/src/utils/internal_lints.rs rename to clippy_utils/src/internal_lints.rs index d8c602fab22c..164ecb23b14d 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_utils/src/internal_lints.rs @@ -1,5 +1,5 @@ use crate::consts::{constant_simple, Constant}; -use crate::utils::{ +use crate::{ is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res, paths, run_lints, snippet, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq, }; diff --git a/clippy_lints/src/utils/mod.rs b/clippy_utils/src/lib.rs similarity index 92% rename from clippy_lints/src/utils/mod.rs rename to clippy_utils/src/lib.rs index 40771449264e..213945a63a3d 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_utils/src/lib.rs @@ -1,3 +1,38 @@ +#![feature(bindings_after_at)] +#![feature(box_patterns)] +#![feature(box_syntax)] +#![feature(concat_idents)] +#![feature(crate_visibility_modifier)] +#![feature(drain_filter)] +#![feature(in_band_lifetimes)] +#![feature(once_cell)] +#![feature(or_patterns)] +#![feature(rustc_private)] +#![feature(stmt_expr_attributes)] +#![feature(control_flow_enum)] + +extern crate rustc_ast; +extern crate rustc_ast_pretty; +extern crate rustc_attr; +extern crate rustc_data_structures; +extern crate rustc_driver; +extern crate rustc_errors; +extern crate rustc_hir; +extern crate rustc_hir_pretty; +extern crate rustc_index; +extern crate rustc_infer; +extern crate rustc_lexer; +extern crate rustc_lint; +extern crate rustc_middle; +extern crate rustc_mir; +extern crate rustc_parse; +extern crate rustc_parse_format; +extern crate rustc_session; +extern crate rustc_span; +extern crate rustc_target; +extern crate rustc_trait_selection; +extern crate rustc_typeck; + #[macro_use] pub mod sym_helper; @@ -8,6 +43,7 @@ pub mod author; pub mod camel_case; pub mod comparisons; pub mod conf; +pub mod consts; mod diagnostics; pub mod eager_or_lazy; pub mod higher; @@ -63,6 +99,105 @@ use smallvec::SmallVec; use crate::consts::{constant, Constant}; +/// Macro used to declare a Clippy lint. +/// +/// Every lint declaration consists of 4 parts: +/// +/// 1. The documentation, which is used for the website +/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions. +/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or +/// `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of. +/// 4. The `description` that contains a short explanation on what's wrong with code where the +/// lint is triggered. +/// +/// Currently the categories `style`, `correctness`, `complexity` and `perf` are enabled by default. +/// As said in the README.md of this repository, if the lint level mapping changes, please update +/// README.md. +/// +/// # Example +/// +/// ``` +/// #![feature(rustc_private)] +/// extern crate rustc_session; +/// use rustc_session::declare_tool_lint; +/// use clippy_lints::declare_clippy_lint; +/// +/// declare_clippy_lint! { +/// /// **What it does:** Checks for ... (describe what the lint matches). +/// /// +/// /// **Why is this bad?** Supply the reason for linting the code. +/// /// +/// /// **Known problems:** None. (Or describe where it could go wrong.) +/// /// +/// /// **Example:** +/// /// +/// /// ```rust +/// /// // Bad +/// /// Insert a short example of code that triggers the lint +/// /// +/// /// // Good +/// /// Insert a short example of improved code that doesn't trigger the lint +/// /// ``` +/// pub LINT_NAME, +/// pedantic, +/// "description" +/// } +/// ``` +/// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints +#[macro_export] +macro_rules! declare_clippy_lint { + { $(#[$attr:meta])* pub $name:tt, style, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, correctness, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Deny, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, perf, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, pedantic, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, restriction, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, cargo, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, nursery, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, internal, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, internal_warn, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true + } + }; +} + pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { if let Ok(version) = RustcVersion::parse(msrv) { return Some(version); @@ -78,6 +213,7 @@ pub fn meets_msrv(msrv: Option<&RustcVersion>, lint_msrv: &RustcVersion) -> bool msrv.map_or(true, |msrv| msrv.meets(*lint_msrv)) } +#[macro_export] macro_rules! extract_msrv_attr { (LateContext) => { extract_msrv_attr!(@LateContext, ()); @@ -87,11 +223,11 @@ macro_rules! extract_msrv_attr { }; (@$context:ident$(, $call:tt)?) => { fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'tcx>, attrs: &'tcx [rustc_ast::ast::Attribute]) { - use $crate::utils::get_unique_inner_attr; + use $crate::get_unique_inner_attr; match get_unique_inner_attr(cx.sess$($call)?, attrs, "msrv") { Some(msrv_attr) => { if let Some(msrv) = msrv_attr.value_str() { - self.msrv = $crate::utils::parse_msrv( + self.msrv = $crate::parse_msrv( &msrv.to_string(), Some(cx.sess$($call)?), Some(msrv_attr.span), diff --git a/clippy_lints/src/utils/numeric_literal.rs b/clippy_utils/src/numeric_literal.rs similarity index 100% rename from clippy_lints/src/utils/numeric_literal.rs rename to clippy_utils/src/numeric_literal.rs diff --git a/clippy_lints/src/utils/paths.rs b/clippy_utils/src/paths.rs similarity index 100% rename from clippy_lints/src/utils/paths.rs rename to clippy_utils/src/paths.rs diff --git a/clippy_lints/src/utils/ptr.rs b/clippy_utils/src/ptr.rs similarity index 97% rename from clippy_lints/src/utils/ptr.rs rename to clippy_utils/src/ptr.rs index b330f3d890e9..baeff08e02cd 100644 --- a/clippy_lints/src/utils/ptr.rs +++ b/clippy_utils/src/ptr.rs @@ -1,4 +1,4 @@ -use crate::utils::{get_pat_name, match_var, snippet}; +use crate::{get_pat_name, match_var, snippet}; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{Body, BodyId, Expr, ExprKind, Param}; use rustc_lint::LateContext; diff --git a/clippy_lints/src/utils/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs similarity index 100% rename from clippy_lints/src/utils/qualify_min_const_fn.rs rename to clippy_utils/src/qualify_min_const_fn.rs diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_utils/src/sugg.rs similarity index 99% rename from clippy_lints/src/utils/sugg.rs rename to clippy_utils/src/sugg.rs index 03678db575f0..d4f6f4281d36 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -1,7 +1,7 @@ //! Contains utility functions to generate suggestions. #![deny(clippy::missing_docs_in_private_items)] -use crate::utils::{higher, snippet, snippet_opt, snippet_with_macro_callsite}; +use crate::{higher, snippet, snippet_opt, snippet_with_macro_callsite}; use rustc_ast::util::parser::AssocOp; use rustc_ast::{ast, token}; use rustc_ast_pretty::pprust::token_kind_to_string; diff --git a/clippy_lints/src/utils/sym_helper.rs b/clippy_utils/src/sym_helper.rs similarity index 100% rename from clippy_lints/src/utils/sym_helper.rs rename to clippy_utils/src/sym_helper.rs diff --git a/clippy_lints/src/utils/usage.rs b/clippy_utils/src/usage.rs similarity index 99% rename from clippy_lints/src/utils/usage.rs rename to clippy_utils/src/usage.rs index 7c7580a2c661..d577827dcf3c 100644 --- a/clippy_lints/src/utils/usage.rs +++ b/clippy_utils/src/usage.rs @@ -1,4 +1,4 @@ -use crate::utils; +use crate as utils; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::Res; diff --git a/clippy_lints/src/utils/visitors.rs b/clippy_utils/src/visitors.rs similarity index 100% rename from clippy_lints/src/utils/visitors.rs rename to clippy_utils/src/visitors.rs