mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-02 07:22:42 +00:00
Auto merge of #80253 - Dylan-DPC:rollup-bkmn74z, r=Dylan-DPC
Rollup of 11 pull requests Successful merges: - #80159 (Add array search aliases) - #80166 (Edit rustc_middle docs) - #80170 (Fix ICE when lookup method in trait for type that have bound vars) - #80171 (Edit rustc_middle::ty::TyKind docs) - #80199 (also const-check FakeRead) - #80211 (Handle desugaring in impl trait bound suggestion) - #80236 (Use pointer type in AtomicPtr::swap implementation) - #80239 (Update Clippy) - #80240 (make sure installer only creates directories in DESTDIR) - #80244 (Cleanup markdown span handling) - #80250 (Minor cleanups in LateResolver) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
15d1f81196
@ -524,8 +524,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||
};
|
||||
|
||||
let ty = substs.type_at(0);
|
||||
if int_type_width_signed(ty, bx.tcx()).is_some() {
|
||||
bx.atomic_rmw(atom_op, args[0].immediate(), args[1].immediate(), order)
|
||||
if int_type_width_signed(ty, bx.tcx()).is_some()
|
||||
|| (ty.is_unsafe_ptr() && op == "xchg")
|
||||
{
|
||||
let mut ptr = args[0].immediate();
|
||||
let mut val = args[1].immediate();
|
||||
if ty.is_unsafe_ptr() {
|
||||
// Some platforms do not support atomic operations on pointers,
|
||||
// so we cast to integer first.
|
||||
let ptr_llty = bx.type_ptr_to(bx.type_isize());
|
||||
ptr = bx.pointercast(ptr, ptr_llty);
|
||||
val = bx.ptrtoint(val, bx.type_isize());
|
||||
}
|
||||
bx.atomic_rmw(atom_op, ptr, val, order)
|
||||
} else {
|
||||
return invalid_monomorphization(ty);
|
||||
}
|
||||
|
@ -17,13 +17,13 @@ use rustc_target::abi::VariantIdx;
|
||||
HashStable
|
||||
)]
|
||||
pub enum PlaceBase {
|
||||
/// A temporary variable
|
||||
/// A temporary variable.
|
||||
Rvalue,
|
||||
/// A named `static` item
|
||||
/// A named `static` item.
|
||||
StaticItem,
|
||||
/// A named local variable
|
||||
/// A named local variable.
|
||||
Local(HirId),
|
||||
/// An upvar referenced by closure env
|
||||
/// An upvar referenced by closure env.
|
||||
Upvar(ty::UpvarId),
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ pub enum PlaceBase {
|
||||
HashStable
|
||||
)]
|
||||
pub enum ProjectionKind {
|
||||
/// A dereference of a pointer, reference or `Box<T>` of the given type
|
||||
/// A dereference of a pointer, reference or `Box<T>` of the given type.
|
||||
Deref,
|
||||
|
||||
/// `B.F` where `B` is the base expression and `F` is
|
||||
@ -71,16 +71,16 @@ pub enum ProjectionKind {
|
||||
HashStable
|
||||
)]
|
||||
pub struct Projection<'tcx> {
|
||||
/// Type after the projection is being applied.
|
||||
/// Type after the projection is applied.
|
||||
pub ty: Ty<'tcx>,
|
||||
|
||||
/// Defines the type of access
|
||||
/// Defines the kind of access made by the projection.
|
||||
pub kind: ProjectionKind,
|
||||
}
|
||||
|
||||
/// A `Place` represents how a value is located in memory.
|
||||
///
|
||||
/// This is an HIR version of `mir::Place`
|
||||
/// This is an HIR version of [`rustc_middle::mir::Place`].
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeFoldable, HashStable)]
|
||||
pub struct Place<'tcx> {
|
||||
/// The type of the `PlaceBase`
|
||||
@ -93,13 +93,13 @@ pub struct Place<'tcx> {
|
||||
|
||||
/// A `PlaceWithHirId` represents how a value is located in memory.
|
||||
///
|
||||
/// This is an HIR version of `mir::Place`
|
||||
/// This is an HIR version of [`rustc_middle::mir::Place`].
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeFoldable, HashStable)]
|
||||
pub struct PlaceWithHirId<'tcx> {
|
||||
/// `HirId` of the expression or pattern producing this value.
|
||||
pub hir_id: HirId,
|
||||
|
||||
/// Information about the `Place`
|
||||
/// Information about the `Place`.
|
||||
pub place: Place<'tcx>,
|
||||
}
|
||||
|
||||
|
@ -88,6 +88,8 @@ impl BoundRegionKind {
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines the kinds of types.
|
||||
///
|
||||
/// N.B., if you change this, you'll probably want to change the corresponding
|
||||
/// AST structure in `librustc_ast/ast.rs` as well.
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable, Debug)]
|
||||
@ -110,7 +112,7 @@ pub enum TyKind<'tcx> {
|
||||
/// A primitive floating-point type. For example, `f64`.
|
||||
Float(ast::FloatTy),
|
||||
|
||||
/// Structures, enumerations and unions.
|
||||
/// Algebraic data types (ADT). For example: structures, enumerations and unions.
|
||||
///
|
||||
/// InternalSubsts here, possibly against intuition, *may* contain `Param`s.
|
||||
/// That is, even after substitution it is possible that there are type
|
||||
@ -170,11 +172,11 @@ pub enum TyKind<'tcx> {
|
||||
/// `|a| yield a`.
|
||||
Generator(DefId, SubstsRef<'tcx>, hir::Movability),
|
||||
|
||||
/// A type representin the types stored inside a generator.
|
||||
/// A type representing the types stored inside a generator.
|
||||
/// This should only appear in GeneratorInteriors.
|
||||
GeneratorWitness(Binder<&'tcx List<Ty<'tcx>>>),
|
||||
|
||||
/// The never type `!`
|
||||
/// The never type `!`.
|
||||
Never,
|
||||
|
||||
/// A tuple type. For example, `(i32, bool)`.
|
||||
|
@ -722,17 +722,16 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
|
||||
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
|
||||
trace!("visit_statement: statement={:?} location={:?}", statement, location);
|
||||
|
||||
match statement.kind {
|
||||
StatementKind::Assign(..) | StatementKind::SetDiscriminant { .. } => {
|
||||
self.super_statement(statement, location);
|
||||
}
|
||||
self.super_statement(statement, location);
|
||||
|
||||
match statement.kind {
|
||||
StatementKind::LlvmInlineAsm { .. } => {
|
||||
self.super_statement(statement, location);
|
||||
self.check_op(ops::InlineAsm);
|
||||
}
|
||||
|
||||
StatementKind::FakeRead(..)
|
||||
StatementKind::Assign(..)
|
||||
| StatementKind::SetDiscriminant { .. }
|
||||
| StatementKind::FakeRead(..)
|
||||
| StatementKind::StorageLive(_)
|
||||
| StatementKind::StorageDead(_)
|
||||
| StatementKind::Retag { .. }
|
||||
|
@ -29,7 +29,7 @@ use rustc_span::Span;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
use rustc_span::source_map::{respan, Spanned};
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::{hash_map::Entry, BTreeSet};
|
||||
use std::mem::{replace, take};
|
||||
use tracing::debug;
|
||||
|
||||
@ -953,8 +953,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
});
|
||||
};
|
||||
|
||||
for item in trait_items {
|
||||
this.with_trait_items(trait_items, |this| {
|
||||
this.with_trait_items(trait_items, |this| {
|
||||
for item in trait_items {
|
||||
match &item.kind {
|
||||
AssocItemKind::Const(_, ty, default) => {
|
||||
this.visit_ty(ty);
|
||||
@ -983,8 +983,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
panic!("unexpanded macro in resolve!")
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -1060,36 +1060,29 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
continue;
|
||||
}
|
||||
|
||||
let def_kind = match param.kind {
|
||||
GenericParamKind::Type { .. } => DefKind::TyParam,
|
||||
GenericParamKind::Const { .. } => DefKind::ConstParam,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let ident = param.ident.normalize_to_macros_2_0();
|
||||
debug!("with_generic_param_rib: {}", param.id);
|
||||
|
||||
if seen_bindings.contains_key(&ident) {
|
||||
let span = seen_bindings.get(&ident).unwrap();
|
||||
let err = ResolutionError::NameAlreadyUsedInParameterList(ident.name, *span);
|
||||
self.report_error(param.ident.span, err);
|
||||
match seen_bindings.entry(ident) {
|
||||
Entry::Occupied(entry) => {
|
||||
let span = *entry.get();
|
||||
let err = ResolutionError::NameAlreadyUsedInParameterList(ident.name, span);
|
||||
self.report_error(param.ident.span, err);
|
||||
}
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(param.ident.span);
|
||||
}
|
||||
}
|
||||
seen_bindings.entry(ident).or_insert(param.ident.span);
|
||||
|
||||
// Plain insert (no renaming).
|
||||
let res = Res::Def(def_kind, self.r.local_def_id(param.id).to_def_id());
|
||||
|
||||
match param.kind {
|
||||
GenericParamKind::Type { .. } => {
|
||||
function_type_rib.bindings.insert(ident, res);
|
||||
self.r.record_partial_res(param.id, PartialRes::new(res));
|
||||
}
|
||||
GenericParamKind::Const { .. } => {
|
||||
function_value_rib.bindings.insert(ident, res);
|
||||
self.r.record_partial_res(param.id, PartialRes::new(res));
|
||||
}
|
||||
let (rib, def_kind) = match param.kind {
|
||||
GenericParamKind::Type { .. } => (&mut function_type_rib, DefKind::TyParam),
|
||||
GenericParamKind::Const { .. } => (&mut function_value_rib, DefKind::ConstParam),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
};
|
||||
let res = Res::Def(def_kind, self.r.local_def_id(param.id).to_def_id());
|
||||
self.r.record_partial_res(param.id, PartialRes::new(res));
|
||||
rib.bindings.insert(ident, res);
|
||||
}
|
||||
|
||||
self.ribs[ValueNS].push(function_value_rib);
|
||||
@ -1778,7 +1771,6 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
path
|
||||
);
|
||||
let ns = source.namespace();
|
||||
let is_expected = &|res| source.is_expected(res);
|
||||
|
||||
let report_errors = |this: &mut Self, res: Option<Res>| {
|
||||
if this.should_report_errs() {
|
||||
@ -1881,7 +1873,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
crate_lint,
|
||||
) {
|
||||
Ok(Some(partial_res)) if partial_res.unresolved_segments() == 0 => {
|
||||
if is_expected(partial_res.base_res()) || partial_res.base_res() == Res::Err {
|
||||
if source.is_expected(partial_res.base_res()) || partial_res.base_res() == Res::Err
|
||||
{
|
||||
partial_res
|
||||
} else {
|
||||
report_errors(self, Some(partial_res.base_res()))
|
||||
@ -1898,11 +1891,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
self.r.trait_map.insert(id, traits);
|
||||
}
|
||||
|
||||
let mut std_path = vec![Segment::from_ident(Ident::with_dummy_span(sym::std))];
|
||||
|
||||
std_path.extend(path);
|
||||
|
||||
if self.r.primitive_type_table.primitive_types.contains_key(&path[0].ident.name) {
|
||||
let mut std_path = Vec::with_capacity(1 + path.len());
|
||||
|
||||
std_path.push(Segment::from_ident(Ident::with_dummy_span(sym::std)));
|
||||
std_path.extend(path);
|
||||
if let PathResult::Module(_) | PathResult::NonModule(_) =
|
||||
self.resolve_path(&std_path, Some(ns), false, span, CrateLint::No)
|
||||
{
|
||||
@ -1983,7 +1976,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
) -> Result<Option<PartialRes>, Spanned<ResolutionError<'a>>> {
|
||||
let mut fin_res = None;
|
||||
|
||||
for (i, ns) in [primary_ns, TypeNS, ValueNS].iter().cloned().enumerate() {
|
||||
for (i, &ns) in [primary_ns, TypeNS, ValueNS].iter().enumerate() {
|
||||
if i == 0 || ns != primary_ns {
|
||||
match self.resolve_qpath(id, qself, path, ns, span, crate_lint)? {
|
||||
Some(partial_res)
|
||||
@ -1993,7 +1986,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
}
|
||||
partial_res => {
|
||||
if fin_res.is_none() {
|
||||
fin_res = partial_res
|
||||
fin_res = partial_res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -254,27 +254,21 @@ fn suggest_restriction(
|
||||
let pred = trait_ref.without_const().to_predicate(tcx).to_string();
|
||||
let pred = pred.replace(&impl_trait_str, &type_param_name);
|
||||
let mut sugg = vec![
|
||||
// Find the last of the generic parameters contained within the span of
|
||||
// the generics
|
||||
match generics
|
||||
.params
|
||||
.iter()
|
||||
.filter(|p| match p.kind {
|
||||
hir::GenericParamKind::Type {
|
||||
synthetic: Some(hir::SyntheticTyParamKind::ImplTrait),
|
||||
..
|
||||
} => false,
|
||||
_ => true,
|
||||
})
|
||||
.last()
|
||||
.map(|p| p.bounds_span().unwrap_or(p.span))
|
||||
.filter(|&span| generics.span.contains(span) && span.desugaring_kind().is_none())
|
||||
.max_by_key(|span| span.hi())
|
||||
{
|
||||
// `fn foo(t: impl Trait)`
|
||||
// ^ suggest `<T: Trait>` here
|
||||
None => (generics.span, format!("<{}>", type_param)),
|
||||
// `fn foo<A>(t: impl Trait)`
|
||||
// ^^^ suggest `<A, T: Trait>` here
|
||||
Some(param) => (
|
||||
param.bounds_span().unwrap_or(param.span).shrink_to_hi(),
|
||||
format!(", {}", type_param),
|
||||
),
|
||||
Some(span) => (span.shrink_to_hi(), format!(", {}", type_param)),
|
||||
},
|
||||
// `fn foo(t: impl Trait)`
|
||||
// ^ suggest `where <T as Trait>::A: Bound`
|
||||
|
@ -31,7 +31,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
_ => (false, false, false),
|
||||
};
|
||||
|
||||
// Type check the descriminant and get its type.
|
||||
// Type check the discriminant and get its type.
|
||||
let scrutinee_ty = if force_scrutinee_bool {
|
||||
// Here we want to ensure:
|
||||
//
|
||||
|
@ -503,8 +503,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
if !self.tcx.has_typeck_results(def_id) {
|
||||
return false;
|
||||
}
|
||||
// We're emitting a suggestion, so we can just ignore regions
|
||||
let fn_sig = self.tcx.fn_sig(def_id).skip_binder();
|
||||
// FIXME: Instead of exiting early when encountering bound vars in
|
||||
// the function signature, consider keeping the binder here and
|
||||
// propagating it downwards.
|
||||
let fn_sig = if let Some(fn_sig) = self.tcx.fn_sig(def_id).no_bound_vars() {
|
||||
fn_sig
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let other_ty = if let FnDef(def_id, _) = *other_ty.kind() {
|
||||
if !self.tcx.has_typeck_results(def_id) {
|
||||
|
@ -1040,8 +1040,16 @@ impl<T> AtomicPtr<T> {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[cfg(target_has_atomic = "ptr")]
|
||||
pub fn swap(&self, ptr: *mut T, order: Ordering) -> *mut T {
|
||||
#[cfg(bootstrap)]
|
||||
// SAFETY: data races are prevented by atomic intrinsics.
|
||||
unsafe { atomic_swap(self.p.get() as *mut usize, ptr as usize, order) as *mut T }
|
||||
unsafe {
|
||||
atomic_swap(self.p.get() as *mut usize, ptr as usize, order) as *mut T
|
||||
}
|
||||
#[cfg(not(bootstrap))]
|
||||
// SAFETY: data races are prevented by atomic intrinsics.
|
||||
unsafe {
|
||||
atomic_swap(self.p.get(), ptr, order)
|
||||
}
|
||||
}
|
||||
|
||||
/// Stores a value into the pointer if the current value is the same as the `current` value.
|
||||
|
@ -478,8 +478,10 @@ mod prim_unit {}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
mod prim_pointer {}
|
||||
|
||||
#[doc(alias = "[]")]
|
||||
#[doc(alias = "[T;N]")] // unfortunately, rustdoc doesn't have fuzzy search for aliases
|
||||
#[doc(alias = "[T; N]")]
|
||||
#[doc(primitive = "array")]
|
||||
//
|
||||
/// A fixed-size array, denoted `[T; N]`, for the element type, `T`, and the
|
||||
/// non-negative compile-time constant size, `N`.
|
||||
///
|
||||
|
@ -73,12 +73,7 @@ fn install_sh(
|
||||
let docdir_default = datadir_default.join("doc/rust");
|
||||
let libdir_default = PathBuf::from("lib");
|
||||
let mandir_default = datadir_default.join("man");
|
||||
let prefix = builder.config.prefix.as_ref().map_or(prefix_default, |p| {
|
||||
fs::create_dir_all(p)
|
||||
.unwrap_or_else(|err| panic!("could not create {}: {}", p.display(), err));
|
||||
fs::canonicalize(p)
|
||||
.unwrap_or_else(|err| panic!("could not canonicalize {}: {}", p.display(), err))
|
||||
});
|
||||
let prefix = builder.config.prefix.as_ref().unwrap_or(&prefix_default);
|
||||
let sysconfdir = builder.config.sysconfdir.as_ref().unwrap_or(&sysconfdir_default);
|
||||
let datadir = builder.config.datadir.as_ref().unwrap_or(&datadir_default);
|
||||
let docdir = builder.config.docdir.as_ref().unwrap_or(&docdir_default);
|
||||
@ -103,6 +98,13 @@ fn install_sh(
|
||||
let libdir = add_destdir(&libdir, &destdir);
|
||||
let mandir = add_destdir(&mandir, &destdir);
|
||||
|
||||
let prefix = {
|
||||
fs::create_dir_all(&prefix)
|
||||
.unwrap_or_else(|err| panic!("could not create {}: {}", prefix.display(), err));
|
||||
fs::canonicalize(&prefix)
|
||||
.unwrap_or_else(|err| panic!("could not canonicalize {}: {}", prefix.display(), err))
|
||||
};
|
||||
|
||||
let empty_dir = builder.out.join("tmp/empty_dir");
|
||||
|
||||
t!(fs::create_dir_all(&empty_dir));
|
||||
|
@ -447,21 +447,23 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
|
||||
}
|
||||
|
||||
/// Make headings links with anchor IDs and build up TOC.
|
||||
struct HeadingLinks<'a, 'b, 'ids, I: Iterator<Item = Event<'a>>> {
|
||||
struct HeadingLinks<'a, 'b, 'ids, I> {
|
||||
inner: I,
|
||||
toc: Option<&'b mut TocBuilder>,
|
||||
buf: VecDeque<Event<'a>>,
|
||||
buf: VecDeque<(Event<'a>, Range<usize>)>,
|
||||
id_map: &'ids mut IdMap,
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'ids, I: Iterator<Item = Event<'a>>> HeadingLinks<'a, 'b, 'ids, I> {
|
||||
impl<'a, 'b, 'ids, I> HeadingLinks<'a, 'b, 'ids, I> {
|
||||
fn new(iter: I, toc: Option<&'b mut TocBuilder>, ids: &'ids mut IdMap) -> Self {
|
||||
HeadingLinks { inner: iter, toc, buf: VecDeque::new(), id_map: ids }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'ids, I: Iterator<Item = Event<'a>>> Iterator for HeadingLinks<'a, 'b, 'ids, I> {
|
||||
type Item = Event<'a>;
|
||||
impl<'a, 'b, 'ids, I: Iterator<Item = (Event<'a>, Range<usize>)>> Iterator
|
||||
for HeadingLinks<'a, 'b, 'ids, I>
|
||||
{
|
||||
type Item = (Event<'a>, Range<usize>);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(e) = self.buf.pop_front() {
|
||||
@ -469,31 +471,29 @@ impl<'a, 'b, 'ids, I: Iterator<Item = Event<'a>>> Iterator for HeadingLinks<'a,
|
||||
}
|
||||
|
||||
let event = self.inner.next();
|
||||
if let Some(Event::Start(Tag::Heading(level))) = event {
|
||||
if let Some((Event::Start(Tag::Heading(level)), _)) = event {
|
||||
let mut id = String::new();
|
||||
for event in &mut self.inner {
|
||||
match &event {
|
||||
match &event.0 {
|
||||
Event::End(Tag::Heading(..)) => break,
|
||||
Event::Start(Tag::Link(_, _, _)) | Event::End(Tag::Link(..)) => {}
|
||||
Event::Text(text) | Event::Code(text) => {
|
||||
id.extend(text.chars().filter_map(slugify));
|
||||
self.buf.push_back(event);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
match event {
|
||||
Event::Start(Tag::Link(_, _, _)) | Event::End(Tag::Link(..)) => {}
|
||||
event => self.buf.push_back(event),
|
||||
_ => self.buf.push_back(event),
|
||||
}
|
||||
}
|
||||
let id = self.id_map.derive(id);
|
||||
|
||||
if let Some(ref mut builder) = self.toc {
|
||||
let mut html_header = String::new();
|
||||
html::push_html(&mut html_header, self.buf.iter().cloned());
|
||||
html::push_html(&mut html_header, self.buf.iter().map(|(ev, _)| ev.clone()));
|
||||
let sec = builder.push(level as u32, html_header, id.clone());
|
||||
self.buf.push_front(Event::Html(format!("{} ", sec).into()));
|
||||
self.buf.push_front((Event::Html(format!("{} ", sec).into()), 0..0));
|
||||
}
|
||||
|
||||
self.buf.push_back(Event::Html(format!("</a></h{}>", level).into()));
|
||||
self.buf.push_back((Event::Html(format!("</a></h{}>", level).into()), 0..0));
|
||||
|
||||
let start_tags = format!(
|
||||
"<h{level} id=\"{id}\" class=\"section-header\">\
|
||||
@ -501,7 +501,7 @@ impl<'a, 'b, 'ids, I: Iterator<Item = Event<'a>>> Iterator for HeadingLinks<'a,
|
||||
id = id,
|
||||
level = level
|
||||
);
|
||||
return Some(Event::Html(start_tags.into()));
|
||||
return Some((Event::Html(start_tags.into()), 0..0));
|
||||
}
|
||||
event
|
||||
}
|
||||
@ -575,15 +575,16 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for SummaryLine<'a, I> {
|
||||
|
||||
/// Moves all footnote definitions to the end and add back links to the
|
||||
/// references.
|
||||
struct Footnotes<'a, I: Iterator<Item = Event<'a>>> {
|
||||
struct Footnotes<'a, I> {
|
||||
inner: I,
|
||||
footnotes: FxHashMap<String, (Vec<Event<'a>>, u16)>,
|
||||
}
|
||||
|
||||
impl<'a, I: Iterator<Item = Event<'a>>> Footnotes<'a, I> {
|
||||
impl<'a, I> Footnotes<'a, I> {
|
||||
fn new(iter: I) -> Self {
|
||||
Footnotes { inner: iter, footnotes: FxHashMap::default() }
|
||||
}
|
||||
|
||||
fn get_entry(&mut self, key: &str) -> &mut (Vec<Event<'a>>, u16) {
|
||||
let new_id = self.footnotes.keys().count() + 1;
|
||||
let key = key.to_owned();
|
||||
@ -591,23 +592,23 @@ impl<'a, I: Iterator<Item = Event<'a>>> Footnotes<'a, I> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, I: Iterator<Item = Event<'a>>> Iterator for Footnotes<'a, I> {
|
||||
type Item = Event<'a>;
|
||||
impl<'a, I: Iterator<Item = (Event<'a>, Range<usize>)>> Iterator for Footnotes<'a, I> {
|
||||
type Item = (Event<'a>, Range<usize>);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
match self.inner.next() {
|
||||
Some(Event::FootnoteReference(ref reference)) => {
|
||||
Some((Event::FootnoteReference(ref reference), range)) => {
|
||||
let entry = self.get_entry(&reference);
|
||||
let reference = format!(
|
||||
"<sup id=\"fnref{0}\"><a href=\"#fn{0}\">{0}</a></sup>",
|
||||
(*entry).1
|
||||
);
|
||||
return Some(Event::Html(reference.into()));
|
||||
return Some((Event::Html(reference.into()), range));
|
||||
}
|
||||
Some(Event::Start(Tag::FootnoteDefinition(def))) => {
|
||||
Some((Event::Start(Tag::FootnoteDefinition(def)), _)) => {
|
||||
let mut content = Vec::new();
|
||||
for event in &mut self.inner {
|
||||
for (event, _) in &mut self.inner {
|
||||
if let Event::End(Tag::FootnoteDefinition(..)) = event {
|
||||
break;
|
||||
}
|
||||
@ -638,7 +639,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for Footnotes<'a, I> {
|
||||
ret.push_str("</li>");
|
||||
}
|
||||
ret.push_str("</ol></div>");
|
||||
return Some(Event::Html(ret.into()));
|
||||
return Some((Event::Html(ret.into()), 0..0));
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
@ -946,13 +947,14 @@ impl Markdown<'_> {
|
||||
};
|
||||
|
||||
let p = Parser::new_with_broken_link_callback(md, opts(), Some(&mut replacer));
|
||||
let p = p.into_offset_iter();
|
||||
|
||||
let mut s = String::with_capacity(md.len() * 3 / 2);
|
||||
|
||||
let p = HeadingLinks::new(p, None, &mut ids);
|
||||
let p = LinkReplacer::new(p, links);
|
||||
let p = CodeBlocks::new(p, codes, edition, playground);
|
||||
let p = Footnotes::new(p);
|
||||
let p = LinkReplacer::new(p.map(|(ev, _)| ev), links);
|
||||
let p = CodeBlocks::new(p, codes, edition, playground);
|
||||
html::push_html(&mut s, p);
|
||||
|
||||
s
|
||||
@ -963,7 +965,7 @@ impl MarkdownWithToc<'_> {
|
||||
crate fn into_string(self) -> String {
|
||||
let MarkdownWithToc(md, mut ids, codes, edition, playground) = self;
|
||||
|
||||
let p = Parser::new_ext(md, opts());
|
||||
let p = Parser::new_ext(md, opts()).into_offset_iter();
|
||||
|
||||
let mut s = String::with_capacity(md.len() * 3 / 2);
|
||||
|
||||
@ -971,8 +973,8 @@ impl MarkdownWithToc<'_> {
|
||||
|
||||
{
|
||||
let p = HeadingLinks::new(p, Some(&mut toc), &mut ids);
|
||||
let p = CodeBlocks::new(p, codes, edition, playground);
|
||||
let p = Footnotes::new(p);
|
||||
let p = CodeBlocks::new(p.map(|(ev, _)| ev), codes, edition, playground);
|
||||
html::push_html(&mut s, p);
|
||||
}
|
||||
|
||||
@ -988,19 +990,19 @@ impl MarkdownHtml<'_> {
|
||||
if md.is_empty() {
|
||||
return String::new();
|
||||
}
|
||||
let p = Parser::new_ext(md, opts());
|
||||
let p = Parser::new_ext(md, opts()).into_offset_iter();
|
||||
|
||||
// Treat inline HTML as plain text.
|
||||
let p = p.map(|event| match event {
|
||||
Event::Html(text) => Event::Text(text),
|
||||
let p = p.map(|event| match event.0 {
|
||||
Event::Html(text) => (Event::Text(text), event.1),
|
||||
_ => event,
|
||||
});
|
||||
|
||||
let mut s = String::with_capacity(md.len() * 3 / 2);
|
||||
|
||||
let p = HeadingLinks::new(p, None, &mut ids);
|
||||
let p = CodeBlocks::new(p, codes, edition, playground);
|
||||
let p = Footnotes::new(p);
|
||||
let p = CodeBlocks::new(p.map(|(ev, _)| ev), codes, edition, playground);
|
||||
html::push_html(&mut s, p);
|
||||
|
||||
s
|
||||
@ -1153,50 +1155,45 @@ crate fn plain_text_summary(md: &str) -> String {
|
||||
s
|
||||
}
|
||||
|
||||
crate fn markdown_links(md: &str) -> Vec<(String, Option<Range<usize>>)> {
|
||||
crate fn markdown_links(md: &str) -> Vec<(String, Range<usize>)> {
|
||||
if md.is_empty() {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
let mut links = vec![];
|
||||
// Used to avoid mutable borrow issues in the `push` closure
|
||||
// Probably it would be more efficient to use a `RefCell` but it doesn't seem worth the churn.
|
||||
let mut shortcut_links = vec![];
|
||||
|
||||
{
|
||||
let locate = |s: &str| unsafe {
|
||||
let s_start = s.as_ptr();
|
||||
let s_end = s_start.add(s.len());
|
||||
let md_start = md.as_ptr();
|
||||
let md_end = md_start.add(md.len());
|
||||
if md_start <= s_start && s_end <= md_end {
|
||||
let start = s_start.offset_from(md_start) as usize;
|
||||
let end = s_end.offset_from(md_start) as usize;
|
||||
Some(start..end)
|
||||
} else {
|
||||
None
|
||||
let span_for_link = |link: &str, span: Range<usize>| {
|
||||
// Pulldown includes the `[]` as well as the URL. Only highlight the relevant span.
|
||||
// NOTE: uses `rfind` in case the title and url are the same: `[Ok][Ok]`
|
||||
match md[span.clone()].rfind(link) {
|
||||
Some(start) => {
|
||||
let start = span.start + start;
|
||||
start..start + link.len()
|
||||
}
|
||||
};
|
||||
// This can happen for things other than intra-doc links, like `#1` expanded to `https://github.com/rust-lang/rust/issues/1`.
|
||||
None => span,
|
||||
}
|
||||
};
|
||||
let mut push = |link: BrokenLink<'_>| {
|
||||
let span = span_for_link(link.reference, link.span);
|
||||
shortcut_links.push((link.reference.to_owned(), span));
|
||||
None
|
||||
};
|
||||
let p = Parser::new_with_broken_link_callback(md, opts(), Some(&mut push));
|
||||
|
||||
let mut push = |link: BrokenLink<'_>| {
|
||||
// FIXME: use `link.span` instead of `locate`
|
||||
// (doing it now includes the `[]` as well as the text)
|
||||
shortcut_links.push((link.reference.to_owned(), locate(link.reference)));
|
||||
None
|
||||
};
|
||||
let p = Parser::new_with_broken_link_callback(md, opts(), Some(&mut push));
|
||||
// There's no need to thread an IdMap through to here because
|
||||
// the IDs generated aren't going to be emitted anywhere.
|
||||
let mut ids = IdMap::new();
|
||||
let iter = Footnotes::new(HeadingLinks::new(p.into_offset_iter(), None, &mut ids));
|
||||
|
||||
// There's no need to thread an IdMap through to here because
|
||||
// the IDs generated aren't going to be emitted anywhere.
|
||||
let mut ids = IdMap::new();
|
||||
let iter = Footnotes::new(HeadingLinks::new(p, None, &mut ids));
|
||||
|
||||
for ev in iter {
|
||||
if let Event::Start(Tag::Link(_, dest, _)) = ev {
|
||||
debug!("found link: {}", dest);
|
||||
links.push(match dest {
|
||||
CowStr::Borrowed(s) => (s.to_owned(), locate(s)),
|
||||
s @ (CowStr::Boxed(..) | CowStr::Inlined(..)) => (s.into_string(), None),
|
||||
});
|
||||
}
|
||||
for ev in iter {
|
||||
if let Event::Start(Tag::Link(_, dest, _)) = ev.0 {
|
||||
debug!("found link: {}", dest);
|
||||
let span = span_for_link(&dest, ev.1);
|
||||
links.push((dest.into_string(), span));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,7 +180,7 @@ struct DiagnosticInfo<'a> {
|
||||
item: &'a Item,
|
||||
dox: &'a str,
|
||||
ori_link: &'a str,
|
||||
link_range: Option<Range<usize>>,
|
||||
link_range: Range<usize>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash)]
|
||||
@ -920,7 +920,7 @@ impl LinkCollector<'_, '_> {
|
||||
parent_node: Option<DefId>,
|
||||
krate: CrateNum,
|
||||
ori_link: String,
|
||||
link_range: Option<Range<usize>>,
|
||||
link_range: Range<usize>,
|
||||
) -> Option<ItemLink> {
|
||||
trace!("considering link '{}'", ori_link);
|
||||
|
||||
@ -1566,7 +1566,7 @@ fn report_diagnostic(
|
||||
msg: &str,
|
||||
item: &Item,
|
||||
dox: &str,
|
||||
link_range: &Option<Range<usize>>,
|
||||
link_range: &Range<usize>,
|
||||
decorate: impl FnOnce(&mut DiagnosticBuilder<'_>, Option<rustc_span::Span>),
|
||||
) {
|
||||
let hir_id = match cx.as_local_hir_id(item.def_id) {
|
||||
@ -1584,31 +1584,26 @@ fn report_diagnostic(
|
||||
cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |lint| {
|
||||
let mut diag = lint.build(msg);
|
||||
|
||||
let span = link_range
|
||||
.as_ref()
|
||||
.and_then(|range| super::source_span_for_markdown_range(cx, dox, range, attrs));
|
||||
let span = super::source_span_for_markdown_range(cx, dox, link_range, attrs);
|
||||
if let Some(sp) = span {
|
||||
diag.set_span(sp);
|
||||
} else {
|
||||
// blah blah blah\nblah\nblah [blah] blah blah\nblah blah
|
||||
// ^ ~~~~
|
||||
// | link_range
|
||||
// last_new_line_offset
|
||||
let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
|
||||
let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
|
||||
|
||||
if let Some(link_range) = link_range {
|
||||
if let Some(sp) = span {
|
||||
diag.set_span(sp);
|
||||
} else {
|
||||
// blah blah blah\nblah\nblah [blah] blah blah\nblah blah
|
||||
// ^ ~~~~
|
||||
// | link_range
|
||||
// last_new_line_offset
|
||||
let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
|
||||
let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
|
||||
|
||||
// Print the line containing the `link_range` and manually mark it with '^'s.
|
||||
diag.note(&format!(
|
||||
"the link appears in this line:\n\n{line}\n\
|
||||
{indicator: <before$}{indicator:^<found$}",
|
||||
line = line,
|
||||
indicator = "",
|
||||
before = link_range.start - last_new_line_offset,
|
||||
found = link_range.len(),
|
||||
));
|
||||
}
|
||||
// Print the line containing the `link_range` and manually mark it with '^'s.
|
||||
diag.note(&format!(
|
||||
"the link appears in this line:\n\n{line}\n\
|
||||
{indicator: <before$}{indicator:^<found$}",
|
||||
line = line,
|
||||
indicator = "",
|
||||
before = link_range.start - last_new_line_offset,
|
||||
found = link_range.len(),
|
||||
));
|
||||
}
|
||||
|
||||
decorate(&mut diag, span);
|
||||
@ -1628,7 +1623,7 @@ fn resolution_failure(
|
||||
path_str: &str,
|
||||
disambiguator: Option<Disambiguator>,
|
||||
dox: &str,
|
||||
link_range: Option<Range<usize>>,
|
||||
link_range: Range<usize>,
|
||||
kinds: SmallVec<[ResolutionFailure<'_>; 3]>,
|
||||
) {
|
||||
report_diagnostic(
|
||||
@ -1862,7 +1857,7 @@ fn anchor_failure(
|
||||
item: &Item,
|
||||
path_str: &str,
|
||||
dox: &str,
|
||||
link_range: Option<Range<usize>>,
|
||||
link_range: Range<usize>,
|
||||
failure: AnchorFailure,
|
||||
) {
|
||||
let msg = match failure {
|
||||
@ -1887,7 +1882,7 @@ fn ambiguity_error(
|
||||
item: &Item,
|
||||
path_str: &str,
|
||||
dox: &str,
|
||||
link_range: Option<Range<usize>>,
|
||||
link_range: Range<usize>,
|
||||
candidates: Vec<Res>,
|
||||
) {
|
||||
let mut msg = format!("`{}` is ", path_str);
|
||||
@ -1936,13 +1931,12 @@ fn suggest_disambiguator(
|
||||
path_str: &str,
|
||||
dox: &str,
|
||||
sp: Option<rustc_span::Span>,
|
||||
link_range: &Option<Range<usize>>,
|
||||
link_range: &Range<usize>,
|
||||
) {
|
||||
let suggestion = disambiguator.suggestion();
|
||||
let help = format!("to link to the {}, {}", disambiguator.descr(), suggestion.descr());
|
||||
|
||||
if let Some(sp) = sp {
|
||||
let link_range = link_range.as_ref().expect("must have a link range if we have a span");
|
||||
let msg = if dox.bytes().nth(link_range.start) == Some(b'`') {
|
||||
format!("`{}`", suggestion.as_help(path_str))
|
||||
} else {
|
||||
@ -1961,7 +1955,7 @@ fn privacy_error(
|
||||
item: &Item,
|
||||
path_str: &str,
|
||||
dox: &str,
|
||||
link_range: Option<Range<usize>>,
|
||||
link_range: Range<usize>,
|
||||
) {
|
||||
let sym;
|
||||
let item_name = match item.name {
|
||||
|
11
src/test/ui/binop/issue-77910-1.rs
Normal file
11
src/test/ui/binop/issue-77910-1.rs
Normal file
@ -0,0 +1,11 @@
|
||||
fn foo(s: &i32) -> &i32 {
|
||||
let xs;
|
||||
xs
|
||||
}
|
||||
fn main() {
|
||||
let y;
|
||||
// we shouldn't ice with the bound var here.
|
||||
assert_eq!(foo, y);
|
||||
//~^ ERROR binary operation `==` cannot be applied to type
|
||||
//~| ERROR `for<'r> fn(&'r i32) -> &'r i32 {foo}` doesn't implement `Debug`
|
||||
}
|
26
src/test/ui/binop/issue-77910-1.stderr
Normal file
26
src/test/ui/binop/issue-77910-1.stderr
Normal file
@ -0,0 +1,26 @@
|
||||
error[E0369]: binary operation `==` cannot be applied to type `for<'r> fn(&'r i32) -> &'r i32 {foo}`
|
||||
--> $DIR/issue-77910-1.rs:8:5
|
||||
|
|
||||
LL | assert_eq!(foo, y);
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| for<'r> fn(&'r i32) -> &'r i32 {foo}
|
||||
| _
|
||||
|
|
||||
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0277]: `for<'r> fn(&'r i32) -> &'r i32 {foo}` doesn't implement `Debug`
|
||||
--> $DIR/issue-77910-1.rs:8:5
|
||||
|
|
||||
LL | assert_eq!(foo, y);
|
||||
| ^^^^^^^^^^^^^^^^^^^ `for<'r> fn(&'r i32) -> &'r i32 {foo}` cannot be formatted using `{:?}` because it doesn't implement `Debug`
|
||||
|
|
||||
= help: the trait `Debug` is not implemented for `for<'r> fn(&'r i32) -> &'r i32 {foo}`
|
||||
= note: required because of the requirements on the impl of `Debug` for `&for<'r> fn(&'r i32) -> &'r i32 {foo}`
|
||||
= note: required by `std::fmt::Debug::fmt`
|
||||
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0277, E0369.
|
||||
For more information about an error, try `rustc --explain E0277`.
|
9
src/test/ui/binop/issue-77910-2.rs
Normal file
9
src/test/ui/binop/issue-77910-2.rs
Normal file
@ -0,0 +1,9 @@
|
||||
fn foo(s: &i32) -> &i32 {
|
||||
let xs;
|
||||
xs
|
||||
}
|
||||
fn main() {
|
||||
let y;
|
||||
if foo == y {}
|
||||
//~^ ERROR binary operation `==` cannot be applied to type
|
||||
}
|
11
src/test/ui/binop/issue-77910-2.stderr
Normal file
11
src/test/ui/binop/issue-77910-2.stderr
Normal file
@ -0,0 +1,11 @@
|
||||
error[E0369]: binary operation `==` cannot be applied to type `for<'r> fn(&'r i32) -> &'r i32 {foo}`
|
||||
--> $DIR/issue-77910-2.rs:7:12
|
||||
|
|
||||
LL | if foo == y {}
|
||||
| --- ^^ - _
|
||||
| |
|
||||
| for<'r> fn(&'r i32) -> &'r i32 {foo}
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0369`.
|
@ -146,6 +146,11 @@ help: skipping check that does not even have a feature gate
|
||||
|
|
||||
LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
help: skipping check that does not even have a feature gate
|
||||
--> $DIR/const_refers_to_static_cross_crate.rs:32:20
|
||||
|
|
||||
LL | unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
help: skipping check for `const_panic` feature
|
||||
--> $DIR/const_refers_to_static_cross_crate.rs:32:77
|
||||
|
|
||||
|
@ -5,5 +5,16 @@ const REG_ADDR: *const u8 = 0x5f3759df as *const u8;
|
||||
const VALUE: u8 = unsafe { *REG_ADDR };
|
||||
//~^ ERROR dereferencing raw pointers in constants is unstable
|
||||
|
||||
const unsafe fn unreachable() -> ! {
|
||||
use std::convert::Infallible;
|
||||
|
||||
const INFALLIBLE: *const Infallible = [].as_ptr();
|
||||
match *INFALLIBLE {}
|
||||
//~^ ERROR dereferencing raw pointers in constant functions is unstable
|
||||
|
||||
const BAD: () = unsafe { match *INFALLIBLE {} };
|
||||
//~^ ERROR dereferencing raw pointers in constants is unstable
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
||||
|
@ -7,6 +7,24 @@ LL | const VALUE: u8 = unsafe { *REG_ADDR };
|
||||
= note: see issue #51911 <https://github.com/rust-lang/rust/issues/51911> for more information
|
||||
= help: add `#![feature(const_raw_ptr_deref)]` to the crate attributes to enable
|
||||
|
||||
error: aborting due to previous error
|
||||
error[E0658]: dereferencing raw pointers in constant functions is unstable
|
||||
--> $DIR/E0396.rs:12:11
|
||||
|
|
||||
LL | match *INFALLIBLE {}
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #51911 <https://github.com/rust-lang/rust/issues/51911> for more information
|
||||
= help: add `#![feature(const_raw_ptr_deref)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: dereferencing raw pointers in constants is unstable
|
||||
--> $DIR/E0396.rs:15:36
|
||||
|
|
||||
LL | const BAD: () = unsafe { match *INFALLIBLE {} };
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #51911 <https://github.com/rust-lang/rust/issues/51911> for more information
|
||||
= help: add `#![feature(const_raw_ptr_deref)]` to the crate attributes to enable
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
||||
|
@ -39,6 +39,14 @@ fn bak(constraints: impl Iterator + std::fmt::Debug) {
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn baw<>(constraints: impl Iterator) {
|
||||
for constraint in constraints {
|
||||
qux(constraint);
|
||||
//~^ ERROR `<impl Iterator as Iterator>::Item` doesn't implement `Debug`
|
||||
}
|
||||
}
|
||||
|
||||
fn qux(_: impl std::fmt::Debug) {}
|
||||
|
||||
fn main() {}
|
||||
|
@ -73,6 +73,21 @@ help: introduce a type parameter with a trait bound instead of using `impl Trait
|
||||
LL | fn bak<I: Iterator + std::fmt::Debug>(constraints: I) where <I as Iterator>::Item: Debug {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
error[E0277]: `<impl Iterator as Iterator>::Item` doesn't implement `Debug`
|
||||
--> $DIR/impl-trait-with-missing-bounds.rs:45:13
|
||||
|
|
||||
LL | qux(constraint);
|
||||
| ^^^^^^^^^^ `<impl Iterator as Iterator>::Item` cannot be formatted using `{:?}` because it doesn't implement `Debug`
|
||||
...
|
||||
LL | fn qux(_: impl std::fmt::Debug) {}
|
||||
| --------------- required by this bound in `qux`
|
||||
|
|
||||
= help: the trait `Debug` is not implemented for `<impl Iterator as Iterator>::Item`
|
||||
help: introduce a type parameter with a trait bound instead of using `impl Trait`
|
||||
|
|
||||
LL | fn baw<I: Iterator>(constraints: I) where <I as Iterator>::Item: Debug {
|
||||
| ^^^^^^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
||||
|
@ -0,0 +1,32 @@
|
||||
// Regression test: if we suggest replacing an `impl Trait` argument to an async
|
||||
// fn with a named type parameter in order to add bounds, the suggested function
|
||||
// signature should be well-formed.
|
||||
//
|
||||
// edition:2018
|
||||
|
||||
trait Foo {
|
||||
type Bar;
|
||||
fn bar(&self) -> Self::Bar;
|
||||
}
|
||||
|
||||
async fn run(_: &(), foo: impl Foo) -> std::io::Result<()> {
|
||||
let bar = foo.bar();
|
||||
assert_is_send(&bar);
|
||||
//~^ ERROR: `<impl Foo as Foo>::Bar` cannot be sent between threads safely
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Test our handling of cases where there is a generic parameter list in the
|
||||
// source, but only synthetic generic parameters
|
||||
async fn run2< >(_: &(), foo: impl Foo) -> std::io::Result<()> {
|
||||
let bar = foo.bar();
|
||||
assert_is_send(&bar);
|
||||
//~^ ERROR: `<impl Foo as Foo>::Bar` cannot be sent between threads safely
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn assert_is_send<T: Send>(_: &T) {}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,33 @@
|
||||
error[E0277]: `<impl Foo as Foo>::Bar` cannot be sent between threads safely
|
||||
--> $DIR/issue-79843-impl-trait-with-missing-bounds-on-async-fn.rs:14:20
|
||||
|
|
||||
LL | assert_is_send(&bar);
|
||||
| ^^^^ `<impl Foo as Foo>::Bar` cannot be sent between threads safely
|
||||
...
|
||||
LL | fn assert_is_send<T: Send>(_: &T) {}
|
||||
| ---- required by this bound in `assert_is_send`
|
||||
|
|
||||
= help: the trait `Send` is not implemented for `<impl Foo as Foo>::Bar`
|
||||
help: introduce a type parameter with a trait bound instead of using `impl Trait`
|
||||
|
|
||||
LL | async fn run<F: Foo>(_: &(), foo: F) -> std::io::Result<()> where <F as Foo>::Bar: Send {
|
||||
| ^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0277]: `<impl Foo as Foo>::Bar` cannot be sent between threads safely
|
||||
--> $DIR/issue-79843-impl-trait-with-missing-bounds-on-async-fn.rs:24:20
|
||||
|
|
||||
LL | assert_is_send(&bar);
|
||||
| ^^^^ `<impl Foo as Foo>::Bar` cannot be sent between threads safely
|
||||
...
|
||||
LL | fn assert_is_send<T: Send>(_: &T) {}
|
||||
| ---- required by this bound in `assert_is_send`
|
||||
|
|
||||
= help: the trait `Send` is not implemented for `<impl Foo as Foo>::Bar`
|
||||
help: introduce a type parameter with a trait bound instead of using `impl Trait`
|
||||
|
|
||||
LL | async fn run2<F: Foo>(_: &(), foo: F) -> std::io::Result<()> where <F as Foo>::Bar: Send {
|
||||
| ^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
34
src/tools/clippy/.github/workflows/clippy.yml
vendored
34
src/tools/clippy/.github/workflows/clippy.yml
vendored
@ -35,29 +35,11 @@ jobs:
|
||||
with:
|
||||
github_token: "${{ secrets.github_token }}"
|
||||
|
||||
- name: rust-toolchain
|
||||
uses: actions-rs/toolchain@v1.0.6
|
||||
with:
|
||||
toolchain: nightly
|
||||
target: x86_64-unknown-linux-gnu
|
||||
profile: minimal
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
|
||||
- name: Run cargo update
|
||||
run: cargo update
|
||||
|
||||
- name: Cache cargo dir
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cargo
|
||||
key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-x86_64-unknown-linux-gnu
|
||||
|
||||
- name: Master Toolchain Setup
|
||||
run: bash setup-toolchain.sh
|
||||
- name: Install toolchain
|
||||
run: rustup show active-toolchain
|
||||
|
||||
# Run
|
||||
- name: Set LD_LIBRARY_PATH (Linux)
|
||||
@ -66,13 +48,13 @@ jobs:
|
||||
echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV
|
||||
|
||||
- name: Build
|
||||
run: cargo build --features deny-warnings
|
||||
run: cargo build --features deny-warnings,internal-lints
|
||||
|
||||
- name: Test
|
||||
run: cargo test --features deny-warnings
|
||||
run: cargo test --features deny-warnings,internal-lints
|
||||
|
||||
- name: Test clippy_lints
|
||||
run: cargo test --features deny-warnings
|
||||
run: cargo test --features deny-warnings,internal-lints
|
||||
working-directory: clippy_lints
|
||||
|
||||
- name: Test rustc_tools_util
|
||||
@ -98,9 +80,3 @@ jobs:
|
||||
cargo dev new_lint --name new_late_pass --pass late
|
||||
cargo check
|
||||
git reset --hard HEAD
|
||||
|
||||
# Cleanup
|
||||
- name: Run cargo-cache --autoclean
|
||||
run: |
|
||||
cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache
|
||||
cargo cache
|
||||
|
@ -23,6 +23,7 @@ jobs:
|
||||
- uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
|
||||
with:
|
||||
github_token: "${{ secrets.github_token }}"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
with:
|
||||
@ -84,31 +85,11 @@ jobs:
|
||||
sudo apt-get install gcc-multilib libssl-dev:i386 libgit2-dev:i386
|
||||
if: matrix.host == 'i686-unknown-linux-gnu'
|
||||
|
||||
- name: rust-toolchain
|
||||
uses: actions-rs/toolchain@v1.0.6
|
||||
with:
|
||||
toolchain: nightly
|
||||
target: ${{ matrix.host }}
|
||||
profile: minimal
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
|
||||
- name: Run cargo update
|
||||
run: cargo update
|
||||
|
||||
- name: Cache cargo dir
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cargo
|
||||
key: ${{ runner.os }}-${{ matrix.host }}-${{ hashFiles('Cargo.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ matrix.host }}
|
||||
|
||||
- name: Master Toolchain Setup
|
||||
run: bash setup-toolchain.sh
|
||||
env:
|
||||
HOST_TOOLCHAIN: ${{ matrix.host }}
|
||||
- name: Install toolchain
|
||||
run: rustup show active-toolchain
|
||||
|
||||
# Run
|
||||
- name: Set LD_LIBRARY_PATH (Linux)
|
||||
@ -128,13 +109,13 @@ jobs:
|
||||
SYSROOT=$(rustc --print sysroot)
|
||||
echo "$SYSROOT/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Build with internal lints
|
||||
- name: Build
|
||||
run: cargo build --features deny-warnings,internal-lints
|
||||
|
||||
- name: Test with internal lints
|
||||
- name: Test
|
||||
run: cargo test --features deny-warnings,internal-lints
|
||||
|
||||
- name: Test clippy_lints with internal lints
|
||||
- name: Test clippy_lints
|
||||
run: cargo test --features deny-warnings,internal-lints
|
||||
working-directory: clippy_lints
|
||||
|
||||
@ -155,12 +136,6 @@ jobs:
|
||||
env:
|
||||
OS: ${{ runner.os }}
|
||||
|
||||
# Cleanup
|
||||
- name: Run cargo-cache --autoclean
|
||||
run: |
|
||||
cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache
|
||||
cargo cache
|
||||
|
||||
integration_build:
|
||||
needs: changelog
|
||||
runs-on: ubuntu-latest
|
||||
@ -171,29 +146,11 @@ jobs:
|
||||
with:
|
||||
github_token: "${{ secrets.github_token }}"
|
||||
|
||||
- name: rust-toolchain
|
||||
uses: actions-rs/toolchain@v1.0.6
|
||||
with:
|
||||
toolchain: nightly
|
||||
target: x86_64-unknown-linux-gnu
|
||||
profile: minimal
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
|
||||
- name: Run cargo update
|
||||
run: cargo update
|
||||
|
||||
- name: Cache cargo dir
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cargo
|
||||
key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-x86_64-unknown-linux-gnu
|
||||
|
||||
- name: Master Toolchain Setup
|
||||
run: bash setup-toolchain.sh
|
||||
- name: Install toolchain
|
||||
run: rustup show active-toolchain
|
||||
|
||||
# Run
|
||||
- name: Build Integration Test
|
||||
@ -214,11 +171,6 @@ jobs:
|
||||
name: target
|
||||
path: target
|
||||
|
||||
# Cleanup
|
||||
- name: Run cargo-cache --autoclean
|
||||
run: |
|
||||
cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache
|
||||
cargo cache
|
||||
integration:
|
||||
needs: integration_build
|
||||
strategy:
|
||||
@ -252,29 +204,11 @@ jobs:
|
||||
with:
|
||||
github_token: "${{ secrets.github_token }}"
|
||||
|
||||
- name: rust-toolchain
|
||||
uses: actions-rs/toolchain@v1.0.6
|
||||
with:
|
||||
toolchain: nightly
|
||||
target: x86_64-unknown-linux-gnu
|
||||
profile: minimal
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
|
||||
- name: Run cargo update
|
||||
run: cargo update
|
||||
|
||||
- name: Cache cargo dir
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cargo
|
||||
key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-x86_64-unknown-linux-gnu
|
||||
|
||||
- name: Master Toolchain Setup
|
||||
run: bash setup-toolchain.sh
|
||||
- name: Install toolchain
|
||||
run: rustup show active-toolchain
|
||||
|
||||
# Download
|
||||
- name: Download target dir
|
||||
@ -288,16 +222,11 @@ jobs:
|
||||
|
||||
# Run
|
||||
- name: Test ${{ matrix.integration }}
|
||||
run: $CARGO_TARGET_DIR/debug/integration
|
||||
run: |
|
||||
RUSTUP_TOOLCHAIN="$(rustup show active-toolchain | grep -o -E "nightly-[0-9]{4}-[0-9]{2}-[0-9]{2}")" \
|
||||
$CARGO_TARGET_DIR/debug/integration
|
||||
env:
|
||||
INTEGRATION: ${{ matrix.integration }}
|
||||
RUSTUP_TOOLCHAIN: master
|
||||
|
||||
# Cleanup
|
||||
- name: Run cargo-cache --autoclean
|
||||
run: |
|
||||
cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache
|
||||
cargo cache
|
||||
|
||||
# These jobs doesn't actually test anything, but they're only used to tell
|
||||
# bors the build completed, as there is no practical way to detect when a
|
||||
|
@ -22,6 +22,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
# Setup
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
|
||||
- name: remove toolchain file
|
||||
run: rm rust-toolchain
|
||||
|
||||
- name: rust-toolchain
|
||||
uses: actions-rs/toolchain@v1.0.6
|
||||
with:
|
||||
@ -29,9 +35,7 @@ jobs:
|
||||
target: x86_64-unknown-linux-gnu
|
||||
profile: minimal
|
||||
components: rustfmt
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
default: true
|
||||
|
||||
# Run
|
||||
- name: Build
|
||||
|
@ -2006,6 +2006,7 @@ Released 2018-09-13
|
||||
[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
|
||||
[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
|
||||
[`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal
|
||||
[`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr
|
||||
[`print_stdout`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout
|
||||
[`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline
|
||||
[`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string
|
||||
@ -2024,6 +2025,7 @@ Released 2018-09-13
|
||||
[`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
|
||||
[`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
|
||||
[`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls
|
||||
[`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else
|
||||
[`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names
|
||||
[`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern
|
||||
[`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching
|
||||
@ -2169,5 +2171,6 @@ Released 2018-09-13
|
||||
[`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero
|
||||
[`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal
|
||||
[`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr
|
||||
[`zero_sized_map_values`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_sized_map_values
|
||||
[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
|
||||
<!-- end autogenerated links to lint list -->
|
||||
|
@ -19,10 +19,10 @@ All contributors are expected to follow the [Rust Code of Conduct].
|
||||
- [Writing code](#writing-code)
|
||||
- [Getting code-completion for rustc internals to work](#getting-code-completion-for-rustc-internals-to-work)
|
||||
- [How Clippy works](#how-clippy-works)
|
||||
- [Fixing build failures caused by Rust](#fixing-build-failures-caused-by-rust)
|
||||
- [Syncing changes between Clippy and `rust-lang/rust`](#syncing-changes-between-clippy-and-rust-langrust)
|
||||
- [Patching git-subtree to work with big repos](#patching-git-subtree-to-work-with-big-repos)
|
||||
- [Performing the sync](#performing-the-sync)
|
||||
- [Syncing back changes in Clippy to [`rust-lang/rust`]](#syncing-back-changes-in-clippy-to-rust-langrust)
|
||||
- [Performing the sync from `rust-lang/rust` to Clippy](#performing-the-sync-from-rust-langrust-to-clippy)
|
||||
- [Performing the sync from Clippy to `rust-lang/rust`](#performing-the-sync-from-clippy-to-rust-langrust)
|
||||
- [Defining remotes](#defining-remotes)
|
||||
- [Issue and PR triage](#issue-and-pr-triage)
|
||||
- [Bors and Homu](#bors-and-homu)
|
||||
@ -49,7 +49,7 @@ first read the [Basics docs](doc/basics.md).**
|
||||
All issues on Clippy are mentored, if you want help with a bug just ask
|
||||
@Manishearth, @flip1995, @phansch or @yaahc.
|
||||
|
||||
Some issues are easier than others. The [`good first issue`] label can be used to find the easy issues.
|
||||
Some issues are easier than others. The [`good-first-issue`] label can be used to find the easy issues.
|
||||
If you want to work on an issue, please leave a comment so that we can assign it to you!
|
||||
|
||||
There are also some abandoned PRs, marked with [`S-inactive-closed`].
|
||||
@ -68,16 +68,16 @@ To figure out how this syntax structure is encoded in the AST, it is recommended
|
||||
Usually the lint will end up to be a nested series of matches and ifs, [like so][deep-nesting].
|
||||
But we can make it nest-less by using [if_chain] macro, [like this][nest-less].
|
||||
|
||||
[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good first issue`]
|
||||
[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good-first-issue`]
|
||||
first. Sometimes they are only somewhat involved code wise, but not difficult per-se.
|
||||
Note that [`E-medium`] issues may require some knowledge of Clippy internals or some
|
||||
debugging to find the actual problem behind the issue.
|
||||
Note that [`E-medium`] issues may require some knowledge of Clippy internals or some
|
||||
debugging to find the actual problem behind the issue.
|
||||
|
||||
[`T-middle`] issues can be more involved and require verifying types. The [`ty`] module contains a
|
||||
lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of
|
||||
an AST expression). `match_def_path()` in Clippy's `utils` module can also be useful.
|
||||
|
||||
[`good first issue`]: https://github.com/rust-lang/rust-clippy/labels/good%20first%20issue
|
||||
[`good-first-issue`]: https://github.com/rust-lang/rust-clippy/labels/good-first-issue
|
||||
[`S-inactive-closed`]: https://github.com/rust-lang/rust-clippy/pulls?q=is%3Aclosed+label%3AS-inactive-closed
|
||||
[`T-AST`]: https://github.com/rust-lang/rust-clippy/labels/T-AST
|
||||
[`T-middle`]: https://github.com/rust-lang/rust-clippy/labels/T-middle
|
||||
@ -111,7 +111,7 @@ To work around this, you need to have a copy of the [rustc-repo][rustc_repo] ava
|
||||
`git clone https://github.com/rust-lang/rust/`.
|
||||
Then you can run a `cargo dev` command to automatically make Clippy use the rustc-repo via path-dependencies
|
||||
which rust-analyzer will be able to understand.
|
||||
Run `cargo dev ra-setup --repo-path <repo-path>` where `<repo-path>` is an absolute path to the rustc repo
|
||||
Run `cargo dev ra_setup --repo-path <repo-path>` where `<repo-path>` is an absolute path to the rustc repo
|
||||
you just cloned.
|
||||
The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to
|
||||
Clippys `Cargo.toml`s and should allow rust-analyzer to understand most of the types that Clippy uses.
|
||||
@ -182,18 +182,26 @@ That's why the `else_if_without_else` example uses the `register_early_pass` fun
|
||||
[early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html
|
||||
[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
|
||||
|
||||
## Fixing build failures caused by Rust
|
||||
## Syncing changes between Clippy and [`rust-lang/rust`]
|
||||
|
||||
Clippy currently gets built with `rustc` of the `rust-lang/rust` `master`
|
||||
branch. Most of the times we have to adapt to the changes and only very rarely
|
||||
there's an actual bug in Rust.
|
||||
Clippy currently gets built with a pinned nightly version.
|
||||
|
||||
If you decide to make Clippy work again with a Rust commit that breaks it, you
|
||||
have to sync the `rust-lang/rust-clippy` repository with the `subtree` copy of
|
||||
Clippy in the `rust-lang/rust` repository.
|
||||
In the `rust-lang/rust` repository, where rustc resides, there's a copy of Clippy
|
||||
that compiler hackers modify from time to time to adapt to changes in the unstable
|
||||
API of the compiler.
|
||||
|
||||
For general information about `subtree`s in the Rust repository see [Rust's
|
||||
`CONTRIBUTING.md`][subtree].
|
||||
We need to sync these changes back to this repository periodically, and the changes
|
||||
made to this repository in the meantime also need to be synced to the `rust-lang/rust` repository.
|
||||
|
||||
To avoid flooding the `rust-lang/rust` PR queue, this two-way sync process is done
|
||||
in a bi-weekly basis if there's no urgent changes. This is done starting on the day of
|
||||
the Rust stable release and then every other week. That way we guarantee that we keep
|
||||
this repo up to date with the latest compiler API, and every feature in Clippy is available
|
||||
for 2 weeks in nightly, before it can get to beta. For reference, the first sync
|
||||
following this cadence was performed the 2020-08-27.
|
||||
|
||||
This process is described in detail in the following sections. For general information
|
||||
about `subtree`s in the Rust repository see [Rust's `CONTRIBUTING.md`][subtree].
|
||||
|
||||
### Patching git-subtree to work with big repos
|
||||
|
||||
@ -222,13 +230,14 @@ This shell has a hardcoded recursion limit set to 1000. In order to make this pr
|
||||
you need to force the script to run `bash` instead. You can do this by editing the first
|
||||
line of the `git-subtree` script and changing `sh` to `bash`.
|
||||
|
||||
### Performing the sync
|
||||
### Performing the sync from [`rust-lang/rust`] to Clippy
|
||||
|
||||
Here is a TL;DR version of the sync process (all of the following commands have
|
||||
to be run inside the `rust` directory):
|
||||
|
||||
1. Clone the [`rust-lang/rust`] repository
|
||||
2. Sync the changes to the rust-copy of Clippy to your Clippy fork:
|
||||
1. Clone the [`rust-lang/rust`] repository or make sure it is up to date.
|
||||
2. Checkout the commit from the latest available nightly. You can get it using `rustup check`.
|
||||
3. Sync the changes to the rust-copy of Clippy to your Clippy fork:
|
||||
```bash
|
||||
# Make sure to change `your-github-name` to your github name in the following command
|
||||
git subtree push -P src/tools/clippy git@github.com:your-github-name/rust-clippy sync-from-rust
|
||||
@ -246,17 +255,11 @@ to be run inside the `rust` directory):
|
||||
git checkout sync-from-rust
|
||||
git merge upstream/master
|
||||
```
|
||||
3. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to
|
||||
4. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to
|
||||
accelerate the process ping the `@rust-lang/clippy` team in your PR and/or
|
||||
~~annoy~~ ask them in the [Zulip] stream.)
|
||||
|
||||
### Syncing back changes in Clippy to [`rust-lang/rust`]
|
||||
|
||||
To avoid flooding the [`rust-lang/rust`] PR queue, changes in Clippy's repo are synced back
|
||||
in a bi-weekly basis if there's no urgent changes. This is done starting on the day of
|
||||
the Rust stable release and then every other week. That way we guarantee that
|
||||
every feature in Clippy is available for 2 weeks in nightly, before it can get to beta.
|
||||
For reference, the first sync following this cadence was performed the 2020-08-27.
|
||||
### Performing the sync from Clippy to [`rust-lang/rust`]
|
||||
|
||||
All of the following commands have to be run inside the `rust` directory.
|
||||
|
||||
|
@ -20,7 +20,6 @@ publish = false
|
||||
|
||||
[[bin]]
|
||||
name = "cargo-clippy"
|
||||
test = false
|
||||
path = "src/main.rs"
|
||||
|
||||
[[bin]]
|
||||
|
@ -82,6 +82,22 @@ Note that this is still experimental and only supported on the nightly channel:
|
||||
cargo clippy --fix -Z unstable-options
|
||||
```
|
||||
|
||||
#### Workspaces
|
||||
|
||||
All the usual workspace options should work with Clippy. For example the following command
|
||||
will run Clippy on the `example` crate:
|
||||
|
||||
```terminal
|
||||
cargo clippy -p example
|
||||
```
|
||||
|
||||
As with `cargo check`, this includes dependencies that are members of the workspace, like path dependencies.
|
||||
If you want to run Clippy **only** on the given crate, use the `--no-deps` option like this:
|
||||
|
||||
```terminal
|
||||
cargo clippy -p example -- --no-deps
|
||||
```
|
||||
|
||||
### Running Clippy from the command line without installing it
|
||||
|
||||
To have cargo compile your crate with Clippy without Clippy installation
|
||||
@ -192,7 +208,6 @@ the lint(s) you are interested in:
|
||||
```terminal
|
||||
cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::...
|
||||
```
|
||||
Note that if you've run clippy before, this may only take effect after you've modified a file or ran `cargo clean`.
|
||||
|
||||
### Specifying the minimum supported Rust version
|
||||
|
||||
|
74
src/tools/clippy/clippy_dev/src/bless.rs
Normal file
74
src/tools/clippy/clippy_dev/src/bless.rs
Normal file
@ -0,0 +1,74 @@
|
||||
//! `bless` updates the reference files in the repo with changed output files
|
||||
//! from the last test run.
|
||||
|
||||
use std::env;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs;
|
||||
use std::lazy::SyncLazy;
|
||||
use std::path::PathBuf;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use crate::clippy_project_root;
|
||||
|
||||
// NOTE: this is duplicated with tests/cargo/mod.rs What to do?
|
||||
pub static CARGO_TARGET_DIR: SyncLazy<PathBuf> = SyncLazy::new(|| match env::var_os("CARGO_TARGET_DIR") {
|
||||
Some(v) => v.into(),
|
||||
None => env::current_dir().unwrap().join("target"),
|
||||
});
|
||||
|
||||
pub fn bless() {
|
||||
let test_dirs = [
|
||||
clippy_project_root().join("tests").join("ui"),
|
||||
clippy_project_root().join("tests").join("ui-toml"),
|
||||
clippy_project_root().join("tests").join("ui-cargo"),
|
||||
];
|
||||
for test_dir in &test_dirs {
|
||||
WalkDir::new(test_dir)
|
||||
.into_iter()
|
||||
.filter_map(Result::ok)
|
||||
.filter(|f| f.path().extension() == Some(OsStr::new("rs")))
|
||||
.for_each(|f| {
|
||||
update_reference_file(f.path().with_extension("stdout"));
|
||||
update_reference_file(f.path().with_extension("stderr"));
|
||||
update_reference_file(f.path().with_extension("fixed"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn update_reference_file(reference_file_path: PathBuf) {
|
||||
let test_output_path = build_dir().join(PathBuf::from(reference_file_path.file_name().unwrap()));
|
||||
let relative_reference_file_path = reference_file_path.strip_prefix(clippy_project_root()).unwrap();
|
||||
|
||||
// If compiletest did not write any changes during the test run,
|
||||
// we don't have to update anything
|
||||
if !test_output_path.exists() {
|
||||
return;
|
||||
}
|
||||
|
||||
let test_output_file = fs::read(&test_output_path).expect("Unable to read test output file");
|
||||
let reference_file = fs::read(&reference_file_path).unwrap_or_default();
|
||||
|
||||
if test_output_file != reference_file {
|
||||
// If a test run caused an output file to change, update the reference file
|
||||
println!("updating {}", &relative_reference_file_path.display());
|
||||
fs::copy(test_output_path, &reference_file_path).expect("Could not update reference file");
|
||||
|
||||
// We need to re-read the file now because it was potentially updated from copying
|
||||
let reference_file = fs::read(&reference_file_path).unwrap_or_default();
|
||||
|
||||
if reference_file.is_empty() {
|
||||
// If we copied over an empty output file, we remove the now empty reference file
|
||||
println!("removing {}", &relative_reference_file_path.display());
|
||||
fs::remove_file(reference_file_path).expect("Could not remove reference file");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_dir() -> PathBuf {
|
||||
let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".to_string());
|
||||
let mut path = PathBuf::new();
|
||||
path.push(CARGO_TARGET_DIR.clone());
|
||||
path.push(profile);
|
||||
path.push("test_build_base");
|
||||
path
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
use crate::clippy_project_root;
|
||||
use shell_escape::escape;
|
||||
use std::ffi::OsStr;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use std::process::{self, Command};
|
||||
use std::{fs, io};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -12,6 +12,7 @@ pub enum CliError {
|
||||
IoError(io::Error),
|
||||
RustfmtNotInstalled,
|
||||
WalkDirError(walkdir::Error),
|
||||
RaSetupActive,
|
||||
}
|
||||
|
||||
impl From<io::Error> for CliError {
|
||||
@ -31,12 +32,23 @@ struct FmtContext {
|
||||
verbose: bool,
|
||||
}
|
||||
|
||||
// the "main" function of cargo dev fmt
|
||||
pub fn run(check: bool, verbose: bool) {
|
||||
fn try_run(context: &FmtContext) -> Result<bool, CliError> {
|
||||
let mut success = true;
|
||||
|
||||
let project_root = clippy_project_root();
|
||||
|
||||
// if we added a local rustc repo as path dependency to clippy for rust analyzer, we do NOT want to
|
||||
// format because rustfmt would also format the entire rustc repo as it is a local
|
||||
// dependency
|
||||
if fs::read_to_string(project_root.join("Cargo.toml"))
|
||||
.expect("Failed to read clippy Cargo.toml")
|
||||
.contains(&"[target.'cfg(NOT_A_PLATFORM)'.dependencies]")
|
||||
{
|
||||
return Err(CliError::RaSetupActive);
|
||||
}
|
||||
|
||||
rustfmt_test(context)?;
|
||||
|
||||
success &= cargo_fmt(context, project_root.as_path())?;
|
||||
@ -75,6 +87,13 @@ pub fn run(check: bool, verbose: bool) {
|
||||
CliError::WalkDirError(err) => {
|
||||
eprintln!("error: {}", err);
|
||||
},
|
||||
CliError::RaSetupActive => {
|
||||
eprintln!(
|
||||
"error: a local rustc repo is enabled as path dependency via `cargo dev ra_setup`.
|
||||
Not formatting because that would format the local repo as well!
|
||||
Please revert the changes to Cargo.tomls first."
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ use std::lazy::SyncLazy;
|
||||
use std::path::{Path, PathBuf};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
pub mod bless;
|
||||
pub mod fmt;
|
||||
pub mod new_lint;
|
||||
pub mod ra_setup;
|
||||
|
@ -1,10 +1,53 @@
|
||||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
|
||||
use clap::{App, Arg, SubCommand};
|
||||
use clippy_dev::{fmt, new_lint, ra_setup, serve, stderr_length_check, update_lints};
|
||||
use clap::{App, Arg, ArgMatches, SubCommand};
|
||||
use clippy_dev::{bless, fmt, new_lint, ra_setup, serve, stderr_length_check, update_lints};
|
||||
|
||||
fn main() {
|
||||
let matches = App::new("Clippy developer tooling")
|
||||
let matches = get_clap_config();
|
||||
|
||||
match matches.subcommand() {
|
||||
("bless", Some(_)) => {
|
||||
bless::bless();
|
||||
},
|
||||
("fmt", Some(matches)) => {
|
||||
fmt::run(matches.is_present("check"), matches.is_present("verbose"));
|
||||
},
|
||||
("update_lints", Some(matches)) => {
|
||||
if matches.is_present("print-only") {
|
||||
update_lints::print_lints();
|
||||
} else if matches.is_present("check") {
|
||||
update_lints::run(update_lints::UpdateMode::Check);
|
||||
} else {
|
||||
update_lints::run(update_lints::UpdateMode::Change);
|
||||
}
|
||||
},
|
||||
("new_lint", Some(matches)) => {
|
||||
match new_lint::create(
|
||||
matches.value_of("pass"),
|
||||
matches.value_of("name"),
|
||||
matches.value_of("category"),
|
||||
) {
|
||||
Ok(_) => update_lints::run(update_lints::UpdateMode::Change),
|
||||
Err(e) => eprintln!("Unable to create lint: {}", e),
|
||||
}
|
||||
},
|
||||
("limit_stderr_length", _) => {
|
||||
stderr_length_check::check();
|
||||
},
|
||||
("ra_setup", Some(matches)) => ra_setup::run(matches.value_of("rustc-repo-path")),
|
||||
("serve", Some(matches)) => {
|
||||
let port = matches.value_of("port").unwrap().parse().unwrap();
|
||||
let lint = matches.value_of("lint");
|
||||
serve::run(port, lint);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn get_clap_config<'a>() -> ArgMatches<'a> {
|
||||
App::new("Clippy developer tooling")
|
||||
.subcommand(SubCommand::with_name("bless").about("bless the test output changes"))
|
||||
.subcommand(
|
||||
SubCommand::with_name("fmt")
|
||||
.about("Run rustfmt on all projects and tests")
|
||||
@ -25,16 +68,16 @@ fn main() {
|
||||
.about("Updates lint registration and information from the source code")
|
||||
.long_about(
|
||||
"Makes sure that:\n \
|
||||
* the lint count in README.md is correct\n \
|
||||
* the changelog contains markdown link references at the bottom\n \
|
||||
* all lint groups include the correct lints\n \
|
||||
* lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \
|
||||
* all lints are registered in the lint store",
|
||||
* the lint count in README.md is correct\n \
|
||||
* the changelog contains markdown link references at the bottom\n \
|
||||
* all lint groups include the correct lints\n \
|
||||
* lint modules in `clippy_lints/*` are visible in `src/lifb.rs` via `pub mod`\n \
|
||||
* all lints are registered in the lint store",
|
||||
)
|
||||
.arg(Arg::with_name("print-only").long("print-only").help(
|
||||
"Print a table of lints to STDOUT. \
|
||||
This does not include deprecated and internal lints. \
|
||||
(Does not modify any files)",
|
||||
This does not include deprecated and internal lints. \
|
||||
(Does not modify any files)",
|
||||
))
|
||||
.arg(
|
||||
Arg::with_name("check")
|
||||
@ -88,7 +131,7 @@ fn main() {
|
||||
.about("Ensures that stderr files do not grow longer than a certain amount of lines."),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("ra-setup")
|
||||
SubCommand::with_name("ra_setup")
|
||||
.about("Alter dependencies so rust-analyzer can find rustc internals")
|
||||
.arg(
|
||||
Arg::with_name("rustc-repo-path")
|
||||
@ -113,40 +156,5 @@ fn main() {
|
||||
)
|
||||
.arg(Arg::with_name("lint").help("Which lint's page to load initially (optional)")),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
match matches.subcommand() {
|
||||
("fmt", Some(matches)) => {
|
||||
fmt::run(matches.is_present("check"), matches.is_present("verbose"));
|
||||
},
|
||||
("update_lints", Some(matches)) => {
|
||||
if matches.is_present("print-only") {
|
||||
update_lints::print_lints();
|
||||
} else if matches.is_present("check") {
|
||||
update_lints::run(update_lints::UpdateMode::Check);
|
||||
} else {
|
||||
update_lints::run(update_lints::UpdateMode::Change);
|
||||
}
|
||||
},
|
||||
("new_lint", Some(matches)) => {
|
||||
match new_lint::create(
|
||||
matches.value_of("pass"),
|
||||
matches.value_of("name"),
|
||||
matches.value_of("category"),
|
||||
) {
|
||||
Ok(_) => update_lints::run(update_lints::UpdateMode::Change),
|
||||
Err(e) => eprintln!("Unable to create lint: {}", e),
|
||||
}
|
||||
},
|
||||
("limit_stderr_length", _) => {
|
||||
stderr_length_check::check();
|
||||
},
|
||||
("ra-setup", Some(matches)) => ra_setup::run(matches.value_of("rustc-repo-path")),
|
||||
("serve", Some(matches)) => {
|
||||
let port = matches.value_of("port").unwrap().parse().unwrap();
|
||||
let lint = matches.value_of("lint");
|
||||
serve::run(port, lint);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
.get_matches()
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ fn inject_deps_into_manifest(
|
||||
// do not inject deps if we have aleady done so
|
||||
if cargo_toml.contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") {
|
||||
eprintln!(
|
||||
"cargo dev ra-setup: warning: deps already found inside {}, doing nothing.",
|
||||
"cargo dev ra_setup: warning: deps already found inside {}, doing nothing.",
|
||||
manifest_path
|
||||
);
|
||||
return Ok(());
|
||||
|
@ -99,7 +99,11 @@ impl LateLintPass<'_> for AwaitHolding {
|
||||
};
|
||||
let def_id = cx.tcx.hir().body_owner_def_id(body_id);
|
||||
let typeck_results = cx.tcx.typeck(def_id);
|
||||
check_interior_types(cx, &typeck_results.generator_interior_types.as_ref().skip_binder(), body.value.span);
|
||||
check_interior_types(
|
||||
cx,
|
||||
&typeck_results.generator_interior_types.as_ref().skip_binder(),
|
||||
body.value.span,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,9 +6,12 @@ use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
use crate::utils::{snippet_with_applicability, span_lint_and_sugg, SpanlessEq};
|
||||
use crate::utils::{meets_msrv, snippet_with_applicability, span_lint_and_sugg, SpanlessEq};
|
||||
|
||||
const CHECKED_CONVERSIONS_MSRV: RustcVersion = RustcVersion::new(1, 34, 0);
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for explicit bounds checking when casting.
|
||||
@ -39,10 +42,25 @@ declare_clippy_lint! {
|
||||
"`try_from` could replace manual bounds checking when casting"
|
||||
}
|
||||
|
||||
declare_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
|
||||
pub struct CheckedConversions {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl CheckedConversions {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) {
|
||||
if !meets_msrv(self.msrv.as_ref(), &CHECKED_CONVERSIONS_MSRV) {
|
||||
return;
|
||||
}
|
||||
|
||||
let result = if_chain! {
|
||||
if !in_external_macro(cx.sess(), item.span);
|
||||
if let ExprKind::Binary(op, ref left, ref right) = &item.kind;
|
||||
@ -74,6 +92,8 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
/// Searches for a single check from unsigned to _ is done
|
||||
|
@ -8,7 +8,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead.
|
||||
///
|
||||
/// **Why is this bad?** Sometimes `std::fs::crate_dir` is mistakenly chosen over `std::fs::create_dir_all`.
|
||||
/// **Why is this bad?** Sometimes `std::fs::create_dir` is mistakenly chosen over `std::fs::create_dir_all`.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
|
@ -14,6 +14,7 @@ use rustc_middle::ty;
|
||||
use rustc_parse::maybe_new_parser_from_source_str;
|
||||
use rustc_session::parse::ParseSess;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span};
|
||||
use rustc_span::{sym, FileName, Pos};
|
||||
use std::io;
|
||||
@ -377,7 +378,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs
|
||||
check_doc(cx, valid_idents, events, &spans)
|
||||
}
|
||||
|
||||
const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail", "edition2018"];
|
||||
const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"];
|
||||
|
||||
fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize>)>>(
|
||||
cx: &LateContext<'_>,
|
||||
@ -400,13 +401,24 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
||||
let mut in_link = None;
|
||||
let mut in_heading = false;
|
||||
let mut is_rust = false;
|
||||
let mut edition = None;
|
||||
for (event, range) in events {
|
||||
match event {
|
||||
Start(CodeBlock(ref kind)) => {
|
||||
in_code = true;
|
||||
if let CodeBlockKind::Fenced(lang) = kind {
|
||||
is_rust =
|
||||
lang.is_empty() || !lang.contains("ignore") && lang.split(',').any(|i| RUST_CODE.contains(&i));
|
||||
for item in lang.split(',') {
|
||||
if item == "ignore" {
|
||||
is_rust = false;
|
||||
break;
|
||||
}
|
||||
if let Some(stripped) = item.strip_prefix("edition") {
|
||||
is_rust = true;
|
||||
edition = stripped.parse::<Edition>().ok();
|
||||
} else if item.is_empty() || RUST_CODE.contains(&item) {
|
||||
is_rust = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
End(CodeBlock(_)) => {
|
||||
@ -436,7 +448,8 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
||||
let (begin, span) = spans[index];
|
||||
if in_code {
|
||||
if is_rust {
|
||||
check_code(cx, &text, span);
|
||||
let edition = edition.unwrap_or_else(|| cx.tcx.sess.edition());
|
||||
check_code(cx, &text, edition, span);
|
||||
}
|
||||
} else {
|
||||
// Adjust for the beginning of the current `Event`
|
||||
@ -450,67 +463,73 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
||||
headers
|
||||
}
|
||||
|
||||
fn check_code(cx: &LateContext<'_>, text: &str, span: Span) {
|
||||
fn has_needless_main(code: &str) -> bool {
|
||||
let filename = FileName::anon_source_code(code);
|
||||
fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
|
||||
fn has_needless_main(code: &str, edition: Edition) -> bool {
|
||||
rustc_driver::catch_fatal_errors(|| {
|
||||
rustc_span::with_session_globals(edition, || {
|
||||
let filename = FileName::anon_source_code(code);
|
||||
|
||||
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
|
||||
let emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false);
|
||||
let handler = Handler::with_emitter(false, None, box emitter);
|
||||
let sess = ParseSess::with_span_handler(handler, sm);
|
||||
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
|
||||
let emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false);
|
||||
let handler = Handler::with_emitter(false, None, box emitter);
|
||||
let sess = ParseSess::with_span_handler(handler, sm);
|
||||
|
||||
let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code.into()) {
|
||||
Ok(p) => p,
|
||||
Err(errs) => {
|
||||
for mut err in errs {
|
||||
err.cancel();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
||||
let mut relevant_main_found = false;
|
||||
loop {
|
||||
match parser.parse_item() {
|
||||
Ok(Some(item)) => match &item.kind {
|
||||
// Tests with one of these items are ignored
|
||||
ItemKind::Static(..)
|
||||
| ItemKind::Const(..)
|
||||
| ItemKind::ExternCrate(..)
|
||||
| ItemKind::ForeignMod(..) => return false,
|
||||
// We found a main function ...
|
||||
ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym::main => {
|
||||
let is_async = matches!(sig.header.asyncness, Async::Yes{..});
|
||||
let returns_nothing = match &sig.decl.output {
|
||||
FnRetTy::Default(..) => true,
|
||||
FnRetTy::Ty(ty) if ty.kind.is_unit() => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if returns_nothing && !is_async && !block.stmts.is_empty() {
|
||||
// This main function should be linted, but only if there are no other functions
|
||||
relevant_main_found = true;
|
||||
} else {
|
||||
// This main function should not be linted, we're done
|
||||
return false;
|
||||
let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code.into()) {
|
||||
Ok(p) => p,
|
||||
Err(errs) => {
|
||||
for mut err in errs {
|
||||
err.cancel();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
// Another function was found; this case is ignored too
|
||||
ItemKind::Fn(..) => return false,
|
||||
_ => {},
|
||||
},
|
||||
Ok(None) => break,
|
||||
Err(mut e) => {
|
||||
e.cancel();
|
||||
return false;
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
relevant_main_found
|
||||
let mut relevant_main_found = false;
|
||||
loop {
|
||||
match parser.parse_item() {
|
||||
Ok(Some(item)) => match &item.kind {
|
||||
// Tests with one of these items are ignored
|
||||
ItemKind::Static(..)
|
||||
| ItemKind::Const(..)
|
||||
| ItemKind::ExternCrate(..)
|
||||
| ItemKind::ForeignMod(..) => return false,
|
||||
// We found a main function ...
|
||||
ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym::main => {
|
||||
let is_async = matches!(sig.header.asyncness, Async::Yes { .. });
|
||||
let returns_nothing = match &sig.decl.output {
|
||||
FnRetTy::Default(..) => true,
|
||||
FnRetTy::Ty(ty) if ty.kind.is_unit() => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if returns_nothing && !is_async && !block.stmts.is_empty() {
|
||||
// This main function should be linted, but only if there are no other functions
|
||||
relevant_main_found = true;
|
||||
} else {
|
||||
// This main function should not be linted, we're done
|
||||
return false;
|
||||
}
|
||||
},
|
||||
// Another function was found; this case is ignored too
|
||||
ItemKind::Fn(..) => return false,
|
||||
_ => {},
|
||||
},
|
||||
Ok(None) => break,
|
||||
Err(mut e) => {
|
||||
e.cancel();
|
||||
return false;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
relevant_main_found
|
||||
})
|
||||
})
|
||||
.ok()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
if has_needless_main(text) {
|
||||
if has_needless_main(text, edition) {
|
||||
span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
|
||||
}
|
||||
}
|
||||
|
@ -405,13 +405,10 @@ impl<'tcx> Functions {
|
||||
break;
|
||||
}
|
||||
if in_comment {
|
||||
match line.find("*/") {
|
||||
Some(i) => {
|
||||
line = &line[i + 2..];
|
||||
in_comment = false;
|
||||
continue;
|
||||
},
|
||||
None => break,
|
||||
if let Some(i) = line.find("*/") {
|
||||
line = &line[i + 2..];
|
||||
in_comment = false;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
let multi_idx = line.find("/*").unwrap_or_else(|| line.len());
|
||||
@ -423,8 +420,8 @@ impl<'tcx> Functions {
|
||||
in_comment = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if code_in_line {
|
||||
line_count += 1;
|
||||
|
@ -222,9 +222,8 @@ fn check_impl_items(cx: &LateContext<'_>, item: &Item<'_>, impl_items: &[ImplIte
|
||||
let is_empty = if let Some(is_empty) = impl_items.iter().find(|i| is_named_self(cx, i, "is_empty")) {
|
||||
if cx.access_levels.is_exported(is_empty.id.hir_id) {
|
||||
return;
|
||||
} else {
|
||||
"a private"
|
||||
}
|
||||
"a private"
|
||||
} else {
|
||||
"no corresponding"
|
||||
};
|
||||
|
@ -27,6 +27,7 @@ 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;
|
||||
@ -294,6 +295,7 @@ mod question_mark;
|
||||
mod ranges;
|
||||
mod redundant_clone;
|
||||
mod redundant_closure_call;
|
||||
mod redundant_else;
|
||||
mod redundant_field_names;
|
||||
mod redundant_pub_crate;
|
||||
mod redundant_static_lifetimes;
|
||||
@ -344,6 +346,7 @@ mod wildcard_dependencies;
|
||||
mod wildcard_imports;
|
||||
mod write;
|
||||
mod zero_div_zero;
|
||||
mod zero_sized_map_values;
|
||||
// end lints modules, do not remove this comment, it’s used in `update_lints`
|
||||
|
||||
pub use crate::utils::conf::Conf;
|
||||
@ -509,6 +512,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
#[cfg(feature = "internal-lints")]
|
||||
&utils::internal_lints::DEFAULT_LINT,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
&utils::internal_lints::INTERNING_DEFINED_SYMBOL,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
&utils::internal_lints::INVALID_PATHS,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
&utils::internal_lints::LINT_WITHOUT_LINT_PASS,
|
||||
@ -831,6 +836,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&ranges::REVERSED_EMPTY_RANGES,
|
||||
&redundant_clone::REDUNDANT_CLONE,
|
||||
&redundant_closure_call::REDUNDANT_CLOSURE_CALL,
|
||||
&redundant_else::REDUNDANT_ELSE,
|
||||
&redundant_field_names::REDUNDANT_FIELD_NAMES,
|
||||
&redundant_pub_crate::REDUNDANT_PUB_CRATE,
|
||||
&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
|
||||
@ -934,6 +940,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&wildcard_imports::WILDCARD_IMPORTS,
|
||||
&write::PRINTLN_EMPTY_STRING,
|
||||
&write::PRINT_LITERAL,
|
||||
&write::PRINT_STDERR,
|
||||
&write::PRINT_STDOUT,
|
||||
&write::PRINT_WITH_NEWLINE,
|
||||
&write::USE_DEBUG,
|
||||
@ -941,6 +948,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&write::WRITE_LITERAL,
|
||||
&write::WRITE_WITH_NEWLINE,
|
||||
&zero_div_zero::ZERO_DIVIDED_BY_ZERO,
|
||||
&zero_sized_map_values::ZERO_SIZED_MAP_VALUES,
|
||||
]);
|
||||
// end register lints, do not remove this comment, it’s used in `update_lints`
|
||||
|
||||
@ -953,6 +961,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls);
|
||||
store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new());
|
||||
store.register_late_pass(|| box utils::internal_lints::InvalidPaths);
|
||||
store.register_late_pass(|| box utils::internal_lints::InterningDefinedSymbol::default());
|
||||
store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default());
|
||||
store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem);
|
||||
store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass);
|
||||
@ -1000,6 +1009,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(move || box matches::Matches::new(msrv));
|
||||
store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv));
|
||||
store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv));
|
||||
store.register_early_pass(move || box redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv));
|
||||
store.register_early_pass(move || box redundant_field_names::RedundantFieldNames::new(msrv));
|
||||
store.register_late_pass(move || box checked_conversions::CheckedConversions::new(msrv));
|
||||
store.register_late_pass(move || box mem_replace::MemReplace::new(msrv));
|
||||
store.register_late_pass(move || box ranges::Ranges::new(msrv));
|
||||
store.register_late_pass(move || box use_self::UseSelf::new(msrv));
|
||||
store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv));
|
||||
|
||||
store.register_late_pass(|| box size_of_in_element_count::SizeOfInElementCount);
|
||||
store.register_late_pass(|| box map_clone::MapClone);
|
||||
store.register_late_pass(|| box map_err_ignore::MapErrIgnore);
|
||||
@ -1010,7 +1027,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| box main_recursion::MainRecursion::default());
|
||||
store.register_late_pass(|| box lifetimes::Lifetimes);
|
||||
store.register_late_pass(|| box entry::HashMapPass);
|
||||
store.register_late_pass(|| box ranges::Ranges);
|
||||
store.register_late_pass(|| box types::Casts);
|
||||
let type_complexity_threshold = conf.type_complexity_threshold;
|
||||
store.register_late_pass(move || box types::TypeComplexity::new(type_complexity_threshold));
|
||||
@ -1055,7 +1071,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| box neg_multiply::NegMultiply);
|
||||
store.register_late_pass(|| box mem_discriminant::MemDiscriminant);
|
||||
store.register_late_pass(|| box mem_forget::MemForget);
|
||||
store.register_late_pass(|| box mem_replace::MemReplace);
|
||||
store.register_late_pass(|| box arithmetic::Arithmetic::default());
|
||||
store.register_late_pass(|| box assign_ops::AssignOps);
|
||||
store.register_late_pass(|| box let_if_seq::LetIfSeq);
|
||||
@ -1077,7 +1092,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(move || box pass_by_ref_or_value);
|
||||
store.register_late_pass(|| box ref_option_ref::RefOptionRef);
|
||||
store.register_late_pass(|| box try_err::TryErr);
|
||||
store.register_late_pass(|| box use_self::UseSelf);
|
||||
store.register_late_pass(|| box bytecount::ByteCount);
|
||||
store.register_late_pass(|| box infinite_iter::InfiniteIter);
|
||||
store.register_late_pass(|| box inline_fn_without_body::InlineFnWithoutBody);
|
||||
@ -1103,10 +1117,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| box unnecessary_wraps::UnnecessaryWraps);
|
||||
store.register_late_pass(|| box types::RefToMut);
|
||||
store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants);
|
||||
store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn);
|
||||
store.register_late_pass(|| box transmuting_null::TransmutingNull);
|
||||
store.register_late_pass(|| box path_buf_push_overwrite::PathBufPushOverwrite);
|
||||
store.register_late_pass(|| box checked_conversions::CheckedConversions);
|
||||
store.register_late_pass(|| box integer_division::IntegerDivision);
|
||||
store.register_late_pass(|| box inherent_to_string::InherentToString);
|
||||
let max_trait_bounds = conf.max_trait_bounds;
|
||||
@ -1132,9 +1144,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_early_pass(|| box items_after_statements::ItemsAfterStatements);
|
||||
store.register_early_pass(|| box precedence::Precedence);
|
||||
store.register_early_pass(|| box needless_continue::NeedlessContinue);
|
||||
store.register_early_pass(|| box redundant_else::RedundantElse);
|
||||
store.register_late_pass(|| box create_dir::CreateDir);
|
||||
store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType);
|
||||
store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes);
|
||||
store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata);
|
||||
store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions);
|
||||
store.register_late_pass(|| box wildcard_dependencies::WildcardDependencies);
|
||||
@ -1174,7 +1186,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| box mut_mutex_lock::MutMutexLock);
|
||||
store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems);
|
||||
store.register_late_pass(|| box manual_async_fn::ManualAsyncFn);
|
||||
store.register_early_pass(|| box redundant_field_names::RedundantFieldNames);
|
||||
store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero);
|
||||
store.register_late_pass(|| box panic_in_result_fn::PanicInResultFn);
|
||||
let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
|
||||
@ -1200,6 +1211,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| box undropped_manually_drops::UndroppedManuallyDrops);
|
||||
store.register_late_pass(|| box strings::StrToString);
|
||||
store.register_late_pass(|| box strings::StringToString);
|
||||
store.register_late_pass(|| box zero_sized_map_values::ZeroSizedMapValues);
|
||||
|
||||
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
|
||||
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
|
||||
@ -1247,6 +1259,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&types::RC_BUFFER),
|
||||
LintId::of(&unwrap_in_result::UNWRAP_IN_RESULT),
|
||||
LintId::of(&verbose_file_reads::VERBOSE_FILE_READS),
|
||||
LintId::of(&write::PRINT_STDERR),
|
||||
LintId::of(&write::PRINT_STDOUT),
|
||||
LintId::of(&write::USE_DEBUG),
|
||||
]);
|
||||
@ -1308,6 +1321,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF),
|
||||
LintId::of(&ranges::RANGE_MINUS_ONE),
|
||||
LintId::of(&ranges::RANGE_PLUS_ONE),
|
||||
LintId::of(&redundant_else::REDUNDANT_ELSE),
|
||||
LintId::of(&ref_option_ref::REF_OPTION_REF),
|
||||
LintId::of(&shadow::SHADOW_UNRELATED),
|
||||
LintId::of(&strings::STRING_ADD_ASSIGN),
|
||||
@ -1330,6 +1344,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&unused_self::UNUSED_SELF),
|
||||
LintId::of(&wildcard_imports::ENUM_GLOB_USE),
|
||||
LintId::of(&wildcard_imports::WILDCARD_IMPORTS),
|
||||
LintId::of(&zero_sized_map_values::ZERO_SIZED_MAP_VALUES),
|
||||
]);
|
||||
|
||||
#[cfg(feature = "internal-lints")]
|
||||
@ -1338,6 +1353,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS),
|
||||
LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS),
|
||||
LintId::of(&utils::internal_lints::DEFAULT_LINT),
|
||||
LintId::of(&utils::internal_lints::INTERNING_DEFINED_SYMBOL),
|
||||
LintId::of(&utils::internal_lints::INVALID_PATHS),
|
||||
LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS),
|
||||
LintId::of(&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM),
|
||||
|
@ -8,6 +8,7 @@ use rustc_lint::LintContext;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:**
|
||||
@ -51,7 +52,7 @@ impl LateLintPass<'_> for ManualOkOr {
|
||||
if args.len() == 3;
|
||||
let method_receiver = &args[0];
|
||||
let ty = cx.typeck_results().expr_ty(method_receiver);
|
||||
if is_type_diagnostic_item(cx, ty, sym!(option_type));
|
||||
if is_type_diagnostic_item(cx, ty, sym::option_type);
|
||||
let or_expr = &args[1];
|
||||
if is_ok_wrapping(cx, &args[2]);
|
||||
if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind;
|
||||
|
@ -4,8 +4,8 @@ use crate::utils::usage::is_unused;
|
||||
use crate::utils::{
|
||||
expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable,
|
||||
is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg, remove_blocks,
|
||||
snippet, snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
|
||||
span_lint_and_then,
|
||||
snippet, snippet_block, snippet_opt, snippet_with_applicability, span_lint_and_help, span_lint_and_note,
|
||||
span_lint_and_sugg, span_lint_and_then,
|
||||
};
|
||||
use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash};
|
||||
use if_chain::if_chain;
|
||||
@ -689,10 +689,9 @@ fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp
|
||||
if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() {
|
||||
// single statement/expr "else" block, don't lint
|
||||
return;
|
||||
} else {
|
||||
// block with 2+ statements or 1 expr and 1+ statement
|
||||
Some(els)
|
||||
}
|
||||
// block with 2+ statements or 1 expr and 1+ statement
|
||||
Some(els)
|
||||
} else {
|
||||
// not a block, don't lint
|
||||
return;
|
||||
@ -1238,6 +1237,24 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A
|
||||
if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
|
||||
return;
|
||||
}
|
||||
|
||||
// HACK:
|
||||
// This is a hack to deal with arms that are excluded by macros like `#[cfg]`. It is only used here
|
||||
// to prevent false positives as there is currently no better way to detect if code was excluded by
|
||||
// a macro. See PR #6435
|
||||
if_chain! {
|
||||
if let Some(match_snippet) = snippet_opt(cx, expr.span);
|
||||
if let Some(arm_snippet) = snippet_opt(cx, arms[0].span);
|
||||
if let Some(ex_snippet) = snippet_opt(cx, ex.span);
|
||||
let rest_snippet = match_snippet.replace(&arm_snippet, "").replace(&ex_snippet, "");
|
||||
if rest_snippet.contains("=>");
|
||||
then {
|
||||
// The code it self contains another thick arrow "=>"
|
||||
// -> Either another arm or a comment
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let matched_vars = ex.span;
|
||||
let bind_names = arms[0].pat.span;
|
||||
let match_body = remove_blocks(&arms[0].body);
|
||||
|
@ -1,13 +1,14 @@
|
||||
use crate::utils::{
|
||||
in_macro, match_def_path, match_qpath, paths, snippet, snippet_with_applicability, span_lint_and_help,
|
||||
in_macro, match_def_path, match_qpath, meets_msrv, paths, snippet, snippet_with_applicability, span_lint_and_help,
|
||||
span_lint_and_sugg, span_lint_and_then,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
@ -94,7 +95,7 @@ declare_clippy_lint! {
|
||||
"replacing a value of type `T` with `T::default()` instead of using `std::mem::take`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(MemReplace =>
|
||||
impl_lint_pass!(MemReplace =>
|
||||
[MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]);
|
||||
|
||||
fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
|
||||
@ -224,6 +225,19 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<
|
||||
}
|
||||
}
|
||||
|
||||
const MEM_REPLACE_WITH_DEFAULT_MSRV: RustcVersion = RustcVersion::new(1, 40, 0);
|
||||
|
||||
pub struct MemReplace {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl MemReplace {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for MemReplace {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
@ -236,8 +250,11 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace {
|
||||
then {
|
||||
check_replace_option_with_none(cx, src, dest, expr.span);
|
||||
check_replace_with_uninit(cx, src, dest, expr.span);
|
||||
check_replace_with_default(cx, src, dest, expr.span);
|
||||
if meets_msrv(self.msrv.as_ref(), &MEM_REPLACE_WITH_DEFAULT_MSRV) {
|
||||
check_replace_with_default(cx, src, dest, expr.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::{sym, SymbolStr};
|
||||
use rustc_typeck::hir_ty_to_ty;
|
||||
|
||||
use crate::consts::{constant, Constant};
|
||||
use crate::utils::eager_or_lazy::is_lazyness_candidate;
|
||||
@ -1487,7 +1488,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
["expect", ..] => lint_expect(cx, expr, arg_lists[0]),
|
||||
["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]),
|
||||
["unwrap_or_else", "map"] => {
|
||||
if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]) {
|
||||
if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0], self.msrv.as_ref()) {
|
||||
unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or");
|
||||
}
|
||||
},
|
||||
@ -1509,7 +1510,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
["next", "iter"] => lint_iter_next(cx, expr, arg_lists[1]),
|
||||
["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1]),
|
||||
["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1], self.msrv.as_ref()),
|
||||
["map", "find"] => lint_find_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
@ -1568,7 +1569,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
lint_expect_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args);
|
||||
|
||||
let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]);
|
||||
if args.len() == 1 && method_call.ident.name == sym!(clone) {
|
||||
if args.len() == 1 && method_call.ident.name == sym::clone {
|
||||
lint_clone_on_copy(cx, expr, &args[0], self_ty);
|
||||
lint_clone_on_ref_ptr(cx, expr, &args[0]);
|
||||
}
|
||||
@ -1592,7 +1593,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
}
|
||||
}
|
||||
},
|
||||
ty::Ref(..) if method_call.ident.name == sym!(into_iter) => {
|
||||
ty::Ref(..) if method_call.ident.name == sym::into_iter => {
|
||||
lint_into_iter(cx, expr, self_ty, *method_span);
|
||||
},
|
||||
_ => (),
|
||||
@ -1623,10 +1624,15 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
let item = cx.tcx.hir().expect_item(parent);
|
||||
let def_id = cx.tcx.hir().local_def_id(item.hir_id);
|
||||
let self_ty = cx.tcx.type_of(def_id);
|
||||
|
||||
// if this impl block implements a trait, lint in trait definition instead
|
||||
if let hir::ItemKind::Impl { of_trait: Some(_), .. } = item.kind {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind;
|
||||
if let Some(first_arg) = iter_input_pats(&sig.decl, cx.tcx.hir().body(id)).next();
|
||||
if let hir::ItemKind::Impl{ of_trait: None, .. } = item.kind;
|
||||
|
||||
let method_def_id = cx.tcx.hir().local_def_id(impl_item.hir_id);
|
||||
let method_sig = cx.tcx.fn_sig(method_def_id);
|
||||
@ -1668,40 +1674,17 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((ref conv, self_kinds)) = &CONVENTIONS
|
||||
.iter()
|
||||
.find(|(ref conv, _)| conv.check(&name))
|
||||
{
|
||||
if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) {
|
||||
let lint = if item.vis.node.is_pub() {
|
||||
WRONG_PUB_SELF_CONVENTION
|
||||
} else {
|
||||
WRONG_SELF_CONVENTION
|
||||
};
|
||||
|
||||
span_lint(
|
||||
cx,
|
||||
lint,
|
||||
first_arg.pat.span,
|
||||
&format!("methods called `{}` usually take {}; consider choosing a less ambiguous name",
|
||||
conv,
|
||||
&self_kinds
|
||||
.iter()
|
||||
.map(|k| k.description())
|
||||
.collect::<Vec<_>>()
|
||||
.join(" or ")
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
lint_wrong_self_convention(
|
||||
cx,
|
||||
&name,
|
||||
item.vis.node.is_pub(),
|
||||
self_ty,
|
||||
first_arg_ty,
|
||||
first_arg.pat.span
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// if this impl block implements a trait, lint in trait definition instead
|
||||
if let hir::ItemKind::Impl { of_trait: Some(_), .. } = item.kind {
|
||||
return;
|
||||
}
|
||||
|
||||
if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
|
||||
let ret_ty = return_ty(cx, impl_item.hir_id);
|
||||
|
||||
@ -1735,8 +1718,23 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
}
|
||||
|
||||
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
|
||||
if in_external_macro(cx.tcx.sess, item.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let TraitItemKind::Fn(ref sig, _) = item.kind;
|
||||
if let Some(first_arg_ty) = sig.decl.inputs.iter().next();
|
||||
let first_arg_span = first_arg_ty.span;
|
||||
let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty);
|
||||
let self_ty = TraitRef::identity(cx.tcx, item.hir_id.owner.to_def_id()).self_ty();
|
||||
|
||||
then {
|
||||
lint_wrong_self_convention(cx, &item.ident.name.as_str(), false, self_ty, first_arg_ty, first_arg_span);
|
||||
}
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if !in_external_macro(cx.tcx.sess, item.span);
|
||||
if item.ident.name == sym::new;
|
||||
if let TraitItemKind::Fn(_, _) = item.kind;
|
||||
let ret_ty = return_ty(cx, item.hir_id);
|
||||
@ -1757,6 +1755,39 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
fn lint_wrong_self_convention<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
item_name: &str,
|
||||
is_pub: bool,
|
||||
self_ty: &'tcx TyS<'tcx>,
|
||||
first_arg_ty: &'tcx TyS<'tcx>,
|
||||
first_arg_span: Span,
|
||||
) {
|
||||
let lint = if is_pub {
|
||||
WRONG_PUB_SELF_CONVENTION
|
||||
} else {
|
||||
WRONG_SELF_CONVENTION
|
||||
};
|
||||
if let Some((ref conv, self_kinds)) = &CONVENTIONS.iter().find(|(ref conv, _)| conv.check(item_name)) {
|
||||
if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) {
|
||||
span_lint(
|
||||
cx,
|
||||
lint,
|
||||
first_arg_span,
|
||||
&format!(
|
||||
"methods called `{}` usually take {}; consider choosing a less ambiguous name",
|
||||
conv,
|
||||
&self_kinds
|
||||
.iter()
|
||||
.map(|k| k.description())
|
||||
.collect::<Vec<_>>()
|
||||
.join(" or ")
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks for the `OR_FUN_CALL` lint.
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn lint_or_fun_call<'tcx>(
|
||||
@ -2100,8 +2131,11 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp
|
||||
cx,
|
||||
CLONE_DOUBLE_REF,
|
||||
expr.span,
|
||||
"using `clone` on a double-reference; \
|
||||
this will copy the reference instead of cloning the inner type",
|
||||
&format!(
|
||||
"using `clone` on a double-reference; \
|
||||
this will copy the reference of type `{}` instead of cloning the inner type",
|
||||
ty
|
||||
),
|
||||
|diag| {
|
||||
if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) {
|
||||
let mut ty = innermost;
|
||||
@ -2174,11 +2208,17 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp
|
||||
} else {
|
||||
snip = None;
|
||||
}
|
||||
span_lint_and_then(cx, CLONE_ON_COPY, expr.span, "using `clone` on a `Copy` type", |diag| {
|
||||
if let Some((text, snip)) = snip {
|
||||
diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable);
|
||||
}
|
||||
});
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
CLONE_ON_COPY,
|
||||
expr.span,
|
||||
&format!("using `clone` on type `{}` which implements the `Copy` trait", ty),
|
||||
|diag| {
|
||||
if let Some((text, snip)) = snip {
|
||||
diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2638,9 +2678,9 @@ fn lint_unwrap(cx: &LateContext<'_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::E
|
||||
fn lint_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, expect_args: &[hir::Expr<'_>]) {
|
||||
let obj_ty = cx.typeck_results().expr_ty(&expect_args[0]).peel_refs();
|
||||
|
||||
let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) {
|
||||
let mess = if is_type_diagnostic_item(cx, obj_ty, sym::option_type) {
|
||||
Some((EXPECT_USED, "an Option", "None"))
|
||||
} else if is_type_diagnostic_item(cx, obj_ty, sym!(result_type)) {
|
||||
} else if is_type_diagnostic_item(cx, obj_ty, sym::result_type) {
|
||||
Some((EXPECT_USED, "a Result", "Err"))
|
||||
} else {
|
||||
None
|
||||
@ -2733,6 +2773,8 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map
|
||||
}
|
||||
}
|
||||
|
||||
const MAP_UNWRAP_OR_MSRV: RustcVersion = RustcVersion::new(1, 41, 0);
|
||||
|
||||
/// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s
|
||||
/// Return true if lint triggered
|
||||
fn lint_map_unwrap_or_else<'tcx>(
|
||||
@ -2740,7 +2782,11 @@ fn lint_map_unwrap_or_else<'tcx>(
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
map_args: &'tcx [hir::Expr<'_>],
|
||||
unwrap_args: &'tcx [hir::Expr<'_>],
|
||||
msrv: Option<&RustcVersion>,
|
||||
) -> bool {
|
||||
if !meets_msrv(msrv, &MAP_UNWRAP_OR_MSRV) {
|
||||
return false;
|
||||
}
|
||||
// lint if the caller of `map()` is an `Option`
|
||||
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type);
|
||||
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::result_type);
|
||||
@ -2923,9 +2969,20 @@ fn lint_filter_map<'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
const FILTER_MAP_NEXT_MSRV: RustcVersion = RustcVersion::new(1, 30, 0);
|
||||
|
||||
/// lint use of `filter_map().next()` for `Iterators`
|
||||
fn lint_filter_map_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, filter_args: &'tcx [hir::Expr<'_>]) {
|
||||
fn lint_filter_map_next<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
filter_args: &'tcx [hir::Expr<'_>],
|
||||
msrv: Option<&RustcVersion>,
|
||||
) {
|
||||
if match_trait_method(cx, expr, &paths::ITERATOR) {
|
||||
if !meets_msrv(msrv, &FILTER_MAP_NEXT_MSRV) {
|
||||
return;
|
||||
}
|
||||
|
||||
let msg = "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling \
|
||||
`.find_map(..)` instead.";
|
||||
let filter_snippet = snippet(cx, filter_args[1].span, "..");
|
||||
@ -3116,7 +3173,7 @@ fn lint_search_is_some<'tcx>(
|
||||
else if search_method == "find" {
|
||||
let is_string_or_str_slice = |e| {
|
||||
let self_ty = cx.typeck_results().expr_ty(e).peel_refs();
|
||||
if is_type_diagnostic_item(cx, self_ty, sym!(string_type)) {
|
||||
if is_type_diagnostic_item(cx, self_ty, sym::string_type) {
|
||||
true
|
||||
} else {
|
||||
*self_ty.kind() == ty::Str
|
||||
|
@ -69,10 +69,9 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc
|
||||
}
|
||||
}
|
||||
return (true, false);
|
||||
} else {
|
||||
// We don't know. It might do anything.
|
||||
return (true, true);
|
||||
}
|
||||
// We don't know. It might do anything.
|
||||
return (true, true);
|
||||
}
|
||||
}
|
||||
(true, true)
|
||||
|
@ -1,14 +1,19 @@
|
||||
use crate::utils::qualify_min_const_fn::is_min_const_fn;
|
||||
use crate::utils::{fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, span_lint, trait_ref_of_method};
|
||||
use crate::utils::{
|
||||
fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, meets_msrv, span_lint, trait_ref_of_method,
|
||||
};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::Span;
|
||||
use rustc_typeck::hir_ty_to_ty;
|
||||
|
||||
const MISSING_CONST_FOR_FN_MSRV: RustcVersion = RustcVersion::new(1, 37, 0);
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:**
|
||||
///
|
||||
@ -69,7 +74,18 @@ declare_clippy_lint! {
|
||||
"Lint functions definitions that could be made `const fn`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]);
|
||||
impl_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]);
|
||||
|
||||
pub struct MissingConstForFn {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl MissingConstForFn {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
|
||||
fn check_fn(
|
||||
@ -81,6 +97,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
|
||||
span: Span,
|
||||
hir_id: HirId,
|
||||
) {
|
||||
if !meets_msrv(self.msrv.as_ref(), &MISSING_CONST_FOR_FN_MSRV) {
|
||||
return;
|
||||
}
|
||||
|
||||
let def_id = cx.tcx.hir().local_def_id(hir_id);
|
||||
|
||||
if in_external_macro(cx.tcx.sess, span) || is_entrypoint_fn(cx, def_id.to_def_id()) {
|
||||
@ -99,7 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
|
||||
let has_const_generic_params = generics
|
||||
.params
|
||||
.iter()
|
||||
.any(|param| matches!(param.kind, GenericParamKind::Const{ .. }));
|
||||
.any(|param| matches!(param.kind, GenericParamKind::Const { .. }));
|
||||
|
||||
if already_const(header) || has_const_generic_params {
|
||||
return;
|
||||
@ -126,6 +146,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
|
||||
span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`");
|
||||
}
|
||||
}
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
/// Returns true if any of the method parameters is a type that implements `Drop`. The method
|
||||
|
@ -2,7 +2,7 @@
|
||||
// *rustc*'s
|
||||
// [`missing_doc`].
|
||||
//
|
||||
// [`missing_doc`]: https://github.com/rust-lang/rust/blob/d6d05904697d89099b55da3331155392f1db9c00/src/librustc_lint/builtin.rs#L246
|
||||
// [`missing_doc`]: https://github.com/rust-lang/rust/blob/cf9cf7c923eb01146971429044f216a3ca905e06/compiler/rustc_lint/src/builtin.rs#L415
|
||||
//
|
||||
|
||||
use crate::utils::span_lint;
|
||||
@ -70,7 +70,14 @@ impl MissingDoc {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_missing_docs_attrs(&self, cx: &LateContext<'_>, attrs: &[ast::Attribute], sp: Span, desc: &'static str) {
|
||||
fn check_missing_docs_attrs(
|
||||
&self,
|
||||
cx: &LateContext<'_>,
|
||||
attrs: &[ast::Attribute],
|
||||
sp: Span,
|
||||
article: &'static str,
|
||||
desc: &'static str,
|
||||
) {
|
||||
// If we're building a test harness, then warning about
|
||||
// documentation is probably not really relevant right now.
|
||||
if cx.sess().opts.test {
|
||||
@ -94,7 +101,7 @@ impl MissingDoc {
|
||||
cx,
|
||||
MISSING_DOCS_IN_PRIVATE_ITEMS,
|
||||
sp,
|
||||
&format!("missing documentation for {}", desc),
|
||||
&format!("missing documentation for {} {}", article, desc),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -120,13 +127,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
||||
}
|
||||
|
||||
fn check_crate(&mut self, cx: &LateContext<'tcx>, krate: &'tcx hir::Crate<'_>) {
|
||||
self.check_missing_docs_attrs(cx, &krate.item.attrs, krate.item.span, "crate");
|
||||
self.check_missing_docs_attrs(cx, &krate.item.attrs, krate.item.span, "the", "crate");
|
||||
}
|
||||
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) {
|
||||
let desc = match it.kind {
|
||||
hir::ItemKind::Const(..) => "a constant",
|
||||
hir::ItemKind::Enum(..) => "an enum",
|
||||
match it.kind {
|
||||
hir::ItemKind::Fn(..) => {
|
||||
// ignore main()
|
||||
if it.ident.name == sym::main {
|
||||
@ -136,16 +141,17 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
||||
return;
|
||||
}
|
||||
}
|
||||
"a function"
|
||||
},
|
||||
hir::ItemKind::Mod(..) => "a module",
|
||||
hir::ItemKind::Static(..) => "a static",
|
||||
hir::ItemKind::Struct(..) => "a struct",
|
||||
hir::ItemKind::Trait(..) => "a trait",
|
||||
hir::ItemKind::TraitAlias(..) => "a trait alias",
|
||||
hir::ItemKind::TyAlias(..) => "a type alias",
|
||||
hir::ItemKind::Union(..) => "a union",
|
||||
hir::ItemKind::OpaqueTy(..) => "an existential type",
|
||||
hir::ItemKind::Const(..)
|
||||
| hir::ItemKind::Enum(..)
|
||||
| hir::ItemKind::Mod(..)
|
||||
| hir::ItemKind::Static(..)
|
||||
| hir::ItemKind::Struct(..)
|
||||
| hir::ItemKind::Trait(..)
|
||||
| hir::ItemKind::TraitAlias(..)
|
||||
| hir::ItemKind::TyAlias(..)
|
||||
| hir::ItemKind::Union(..)
|
||||
| hir::ItemKind::OpaqueTy(..) => {},
|
||||
hir::ItemKind::ExternCrate(..)
|
||||
| hir::ItemKind::ForeignMod { .. }
|
||||
| hir::ItemKind::GlobalAsm(..)
|
||||
@ -153,17 +159,17 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
||||
| hir::ItemKind::Use(..) => return,
|
||||
};
|
||||
|
||||
self.check_missing_docs_attrs(cx, &it.attrs, it.span, desc);
|
||||
let def_id = cx.tcx.hir().local_def_id(it.hir_id);
|
||||
let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id());
|
||||
|
||||
self.check_missing_docs_attrs(cx, &it.attrs, it.span, article, desc);
|
||||
}
|
||||
|
||||
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx hir::TraitItem<'_>) {
|
||||
let desc = match trait_item.kind {
|
||||
hir::TraitItemKind::Const(..) => "an associated constant",
|
||||
hir::TraitItemKind::Fn(..) => "a trait method",
|
||||
hir::TraitItemKind::Type(..) => "an associated type",
|
||||
};
|
||||
let def_id = cx.tcx.hir().local_def_id(trait_item.hir_id);
|
||||
let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id());
|
||||
|
||||
self.check_missing_docs_attrs(cx, &trait_item.attrs, trait_item.span, desc);
|
||||
self.check_missing_docs_attrs(cx, &trait_item.attrs, trait_item.span, article, desc);
|
||||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
|
||||
@ -178,21 +184,17 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
||||
},
|
||||
}
|
||||
|
||||
let desc = match impl_item.kind {
|
||||
hir::ImplItemKind::Const(..) => "an associated constant",
|
||||
hir::ImplItemKind::Fn(..) => "a method",
|
||||
hir::ImplItemKind::TyAlias(_) => "an associated type",
|
||||
};
|
||||
self.check_missing_docs_attrs(cx, &impl_item.attrs, impl_item.span, desc);
|
||||
let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id());
|
||||
self.check_missing_docs_attrs(cx, &impl_item.attrs, impl_item.span, article, desc);
|
||||
}
|
||||
|
||||
fn check_struct_field(&mut self, cx: &LateContext<'tcx>, sf: &'tcx hir::StructField<'_>) {
|
||||
if !sf.is_positional() {
|
||||
self.check_missing_docs_attrs(cx, &sf.attrs, sf.span, "a struct field");
|
||||
self.check_missing_docs_attrs(cx, &sf.attrs, sf.span, "a", "struct field");
|
||||
}
|
||||
}
|
||||
|
||||
fn check_variant(&mut self, cx: &LateContext<'tcx>, v: &'tcx hir::Variant<'_>) {
|
||||
self.check_missing_docs_attrs(cx, &v.attrs, v.span, "a variant");
|
||||
self.check_missing_docs_attrs(cx, &v.attrs, v.span, "a", "variant");
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow {
|
||||
return;
|
||||
}
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = e.kind {
|
||||
if let ty::Ref(..) = cx.typeck_results().expr_ty(inner).kind() {
|
||||
if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(inner).kind() {
|
||||
for adj3 in cx.typeck_results().expr_adjustments(e).windows(3) {
|
||||
if let [Adjustment {
|
||||
kind: Adjust::Deref(_), ..
|
||||
@ -62,8 +62,11 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow {
|
||||
cx,
|
||||
NEEDLESS_BORROW,
|
||||
e.span,
|
||||
"this expression borrows a reference that is immediately dereferenced \
|
||||
&format!(
|
||||
"this expression borrows a reference (`&{}`) that is immediately dereferenced \
|
||||
by the compiler",
|
||||
ty
|
||||
),
|
||||
|diag| {
|
||||
if let Some(snippet) = snippet_opt(cx, inner.span) {
|
||||
diag.span_suggestion(
|
||||
|
@ -90,9 +90,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
||||
|
||||
// Exclude non-inherent impls
|
||||
if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
|
||||
if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } |
|
||||
ItemKind::Trait(..))
|
||||
{
|
||||
if matches!(
|
||||
item.kind,
|
||||
ItemKind::Impl { of_trait: Some(_), .. } | ItemKind::Trait(..)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,9 @@ declare_clippy_lint! {
|
||||
/// **What it does:** Checks for needlessly including a base struct on update
|
||||
/// when all fields are changed anyway.
|
||||
///
|
||||
/// This lint is not applied to structs marked with
|
||||
/// [non_exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html).
|
||||
///
|
||||
/// **Why is this bad?** This will cost resources (because the base has to be
|
||||
/// somewhere), and make the code less readable.
|
||||
///
|
||||
@ -49,7 +52,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate {
|
||||
if let ExprKind::Struct(_, ref fields, Some(ref base)) = expr.kind {
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
if let ty::Adt(def, _) = ty.kind() {
|
||||
if fields.len() == def.non_enum_variant().fields.len() {
|
||||
if fields.len() == def.non_enum_variant().fields.len()
|
||||
&& !def.variants[0_usize.into()].is_field_list_non_exhaustive()
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
NEEDLESS_UPDATE,
|
||||
|
@ -409,11 +409,10 @@ fn levenstein_not_1(a_name: &str, b_name: &str) -> bool {
|
||||
if let Some(b2) = b_chars.next() {
|
||||
// check if there's just one character inserted
|
||||
return a != b2 || a_chars.ne(b_chars);
|
||||
} else {
|
||||
// tuple
|
||||
// ntuple
|
||||
return true;
|
||||
}
|
||||
// tuple
|
||||
// ntuple
|
||||
return true;
|
||||
}
|
||||
// for item in items
|
||||
true
|
||||
|
@ -1,18 +1,16 @@
|
||||
use crate::utils::{is_expn_of, is_type_diagnostic_item, return_ty, span_lint_and_then};
|
||||
use crate::utils::{find_macro_calls, is_type_diagnostic_item, return_ty, span_lint_and_then};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{self, FnKind, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::Expr;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!` or `unreachable!` in a function of type result.
|
||||
/// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result.
|
||||
///
|
||||
/// **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence unimplemented, panic and unreachable should be avoided.
|
||||
/// **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence panicking macros should be avoided.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
/// **Known problems:** Functions called from a function returning a `Result` may invoke a panicking macro. This is not checked.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
@ -22,9 +20,15 @@ declare_clippy_lint! {
|
||||
/// panic!("error");
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn result_without_panic() -> Result<bool, String> {
|
||||
/// Err(String::from("error"))
|
||||
/// }
|
||||
/// ```
|
||||
pub PANIC_IN_RESULT_FN,
|
||||
restriction,
|
||||
"functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` "
|
||||
"functions of type `Result<..>` that contain `panic!()`, `todo!()`, `unreachable()`, `unimplemented()` or assertion"
|
||||
}
|
||||
|
||||
declare_lint_pass!(PanicInResultFn => [PANIC_IN_RESULT_FN]);
|
||||
@ -47,43 +51,33 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn {
|
||||
}
|
||||
}
|
||||
|
||||
struct FindPanicUnimplementedUnreachable {
|
||||
result: Vec<Span>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for FindPanicUnimplementedUnreachable {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
if ["unimplemented", "unreachable", "panic", "todo"]
|
||||
.iter()
|
||||
.any(|fun| is_expn_of(expr.span, fun).is_some())
|
||||
{
|
||||
self.result.push(expr.span);
|
||||
}
|
||||
// and check sub-expressions
|
||||
intravisit::walk_expr(self, expr);
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::None
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) {
|
||||
let mut panics = FindPanicUnimplementedUnreachable { result: Vec::new() };
|
||||
panics.visit_expr(&body.value);
|
||||
if !panics.result.is_empty() {
|
||||
let panics = find_macro_calls(
|
||||
&[
|
||||
"unimplemented",
|
||||
"unreachable",
|
||||
"panic",
|
||||
"todo",
|
||||
"assert",
|
||||
"assert_eq",
|
||||
"assert_ne",
|
||||
"debug_assert",
|
||||
"debug_assert_eq",
|
||||
"debug_assert_ne",
|
||||
],
|
||||
body,
|
||||
);
|
||||
if !panics.is_empty() {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
PANIC_IN_RESULT_FN,
|
||||
impl_span,
|
||||
"used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`",
|
||||
"used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`",
|
||||
move |diag| {
|
||||
diag.help(
|
||||
"`unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing",
|
||||
"`unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing",
|
||||
);
|
||||
diag.span_note(panics.result, "return Err() instead of panicking");
|
||||
diag.span_note(panics, "return Err() instead of panicking");
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -244,9 +244,10 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue {
|
||||
|
||||
// Exclude non-inherent impls
|
||||
if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
|
||||
if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } |
|
||||
ItemKind::Trait(..))
|
||||
{
|
||||
if matches!(
|
||||
item.kind,
|
||||
ItemKind::Impl { of_trait: Some(_), .. } | ItemKind::Trait(..)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,10 @@ use if_chain::if_chain;
|
||||
use rustc_ast::ast::RangeLimits;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::{Span, Spanned};
|
||||
use rustc_span::sym;
|
||||
use rustc_span::symbol::Ident;
|
||||
@ -13,8 +14,8 @@ use std::cmp::Ordering;
|
||||
|
||||
use crate::utils::sugg::Sugg;
|
||||
use crate::utils::{
|
||||
get_parent_expr, is_integer_const, single_segment_path, snippet, snippet_opt, snippet_with_applicability,
|
||||
span_lint, span_lint_and_sugg, span_lint_and_then,
|
||||
get_parent_expr, in_constant, is_integer_const, meets_msrv, single_segment_path, snippet, snippet_opt,
|
||||
snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then,
|
||||
};
|
||||
use crate::utils::{higher, SpanlessEq};
|
||||
|
||||
@ -160,7 +161,20 @@ declare_clippy_lint! {
|
||||
"manually reimplementing {`Range`, `RangeInclusive`}`::contains`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Ranges => [
|
||||
const MANUAL_RANGE_CONTAINS_MSRV: RustcVersion = RustcVersion::new(1, 35, 0);
|
||||
|
||||
pub struct Ranges {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl Ranges {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(Ranges => [
|
||||
RANGE_ZIP_WITH_LEN,
|
||||
RANGE_PLUS_ONE,
|
||||
RANGE_MINUS_ONE,
|
||||
@ -175,7 +189,9 @@ impl<'tcx> LateLintPass<'tcx> for Ranges {
|
||||
check_range_zip_with_len(cx, path, args, expr.span);
|
||||
},
|
||||
ExprKind::Binary(ref op, ref l, ref r) => {
|
||||
check_possible_range_contains(cx, op.node, l, r, expr.span);
|
||||
if meets_msrv(self.msrv.as_ref(), &MANUAL_RANGE_CONTAINS_MSRV) {
|
||||
check_possible_range_contains(cx, op.node, l, r, expr);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
@ -184,9 +200,15 @@ impl<'tcx> LateLintPass<'tcx> for Ranges {
|
||||
check_inclusive_range_minus_one(cx, expr);
|
||||
check_reversed_empty_range(cx, expr);
|
||||
}
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, span: Span) {
|
||||
fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, expr: &Expr<'_>) {
|
||||
if in_constant(cx, expr.hir_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
let span = expr.span;
|
||||
let combine_and = match op {
|
||||
BinOpKind::And | BinOpKind::BitAnd => true,
|
||||
BinOpKind::Or | BinOpKind::BitOr => false,
|
||||
|
@ -390,7 +390,10 @@ impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor {
|
||||
let local = place.local;
|
||||
|
||||
if local == self.used.0
|
||||
&& !matches!(ctx, PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_))
|
||||
&& !matches!(
|
||||
ctx,
|
||||
PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_)
|
||||
)
|
||||
{
|
||||
self.used.1 = true;
|
||||
}
|
||||
|
135
src/tools/clippy/clippy_lints/src/redundant_else.rs
Normal file
135
src/tools/clippy/clippy_lints/src/redundant_else.rs
Normal file
@ -0,0 +1,135 @@
|
||||
use crate::utils::span_lint_and_help;
|
||||
use rustc_ast::ast::{Block, Expr, ExprKind, Stmt, StmtKind};
|
||||
use rustc_ast::visit::{walk_expr, Visitor};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `else` blocks that can be removed without changing semantics.
|
||||
///
|
||||
/// **Why is this bad?** The `else` block adds unnecessary indentation and verbosity.
|
||||
///
|
||||
/// **Known problems:** Some may prefer to keep the `else` block for clarity.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// fn my_func(count: u32) {
|
||||
/// if count == 0 {
|
||||
/// print!("Nothing to do");
|
||||
/// return;
|
||||
/// } else {
|
||||
/// print!("Moving on...");
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn my_func(count: u32) {
|
||||
/// if count == 0 {
|
||||
/// print!("Nothing to do");
|
||||
/// return;
|
||||
/// }
|
||||
/// print!("Moving on...");
|
||||
/// }
|
||||
/// ```
|
||||
pub REDUNDANT_ELSE,
|
||||
pedantic,
|
||||
"`else` branch that can be removed without changing semantics"
|
||||
}
|
||||
|
||||
declare_lint_pass!(RedundantElse => [REDUNDANT_ELSE]);
|
||||
|
||||
impl EarlyLintPass for RedundantElse {
|
||||
fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &Stmt) {
|
||||
if in_external_macro(cx.sess, stmt.span) {
|
||||
return;
|
||||
}
|
||||
// Only look at expressions that are a whole statement
|
||||
let expr: &Expr = match &stmt.kind {
|
||||
StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr,
|
||||
_ => return,
|
||||
};
|
||||
// if else
|
||||
let (mut then, mut els): (&Block, &Expr) = match &expr.kind {
|
||||
ExprKind::If(_, then, Some(els)) => (then, els),
|
||||
_ => return,
|
||||
};
|
||||
loop {
|
||||
if !BreakVisitor::default().check_block(then) {
|
||||
// then block does not always break
|
||||
return;
|
||||
}
|
||||
match &els.kind {
|
||||
// else if else
|
||||
ExprKind::If(_, next_then, Some(next_els)) => {
|
||||
then = next_then;
|
||||
els = next_els;
|
||||
continue;
|
||||
},
|
||||
// else if without else
|
||||
ExprKind::If(..) => return,
|
||||
// done
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
REDUNDANT_ELSE,
|
||||
els.span,
|
||||
"redundant else block",
|
||||
None,
|
||||
"remove the `else` block and move the contents out",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Call `check` functions to check if an expression always breaks control flow
|
||||
#[derive(Default)]
|
||||
struct BreakVisitor {
|
||||
is_break: bool,
|
||||
}
|
||||
|
||||
impl<'ast> Visitor<'ast> for BreakVisitor {
|
||||
fn visit_block(&mut self, block: &'ast Block) {
|
||||
self.is_break = match block.stmts.as_slice() {
|
||||
[.., last] => self.check_stmt(last),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, expr: &'ast Expr) {
|
||||
self.is_break = match expr.kind {
|
||||
ExprKind::Break(..) | ExprKind::Continue(..) | ExprKind::Ret(..) => true,
|
||||
ExprKind::Match(_, ref arms) => arms.iter().all(|arm| self.check_expr(&arm.body)),
|
||||
ExprKind::If(_, ref then, Some(ref els)) => self.check_block(then) && self.check_expr(els),
|
||||
ExprKind::If(_, _, None)
|
||||
// ignore loops for simplicity
|
||||
| ExprKind::While(..) | ExprKind::ForLoop(..) | ExprKind::Loop(..) => false,
|
||||
_ => {
|
||||
walk_expr(self, expr);
|
||||
return;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl BreakVisitor {
|
||||
fn check<T>(&mut self, item: T, visit: fn(&mut Self, T)) -> bool {
|
||||
visit(self, item);
|
||||
std::mem::replace(&mut self.is_break, false)
|
||||
}
|
||||
|
||||
fn check_block(&mut self, block: &Block) -> bool {
|
||||
self.check(block, Self::visit_block)
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, expr: &Expr) -> bool {
|
||||
self.check(expr, Self::visit_expr)
|
||||
}
|
||||
|
||||
fn check_stmt(&mut self, stmt: &Stmt) -> bool {
|
||||
self.check(stmt, Self::visit_stmt)
|
||||
}
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
use crate::utils::span_lint_and_sugg;
|
||||
use crate::utils::{meets_msrv, span_lint_and_sugg};
|
||||
use rustc_ast::ast::{Expr, ExprKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
const REDUNDANT_FIELD_NAMES_MSRV: RustcVersion = RustcVersion::new(1, 17, 0);
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for fields in struct literals where shorthands
|
||||
@ -33,10 +36,25 @@ declare_clippy_lint! {
|
||||
"checks for fields in struct literals where shorthands could be used"
|
||||
}
|
||||
|
||||
declare_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]);
|
||||
pub struct RedundantFieldNames {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl RedundantFieldNames {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]);
|
||||
|
||||
impl EarlyLintPass for RedundantFieldNames {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_FIELD_NAMES_MSRV) {
|
||||
return;
|
||||
}
|
||||
|
||||
if in_external_macro(cx.sess, expr.span) {
|
||||
return;
|
||||
}
|
||||
@ -64,4 +82,5 @@ impl EarlyLintPass for RedundantFieldNames {
|
||||
}
|
||||
}
|
||||
}
|
||||
extract_msrv_attr!(EarlyContext);
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
use crate::utils::{snippet, span_lint_and_then};
|
||||
use crate::utils::{meets_msrv, snippet, span_lint_and_then};
|
||||
use rustc_ast::ast::{Item, ItemKind, Ty, TyKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
const REDUNDANT_STATIC_LIFETIMES_MSRV: RustcVersion = RustcVersion::new(1, 17, 0);
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for constants and statics with an explicit `'static` lifetime.
|
||||
@ -29,7 +32,18 @@ declare_clippy_lint! {
|
||||
"Using explicit `'static` lifetime for constants or statics when elision rules would allow omitting them."
|
||||
}
|
||||
|
||||
declare_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]);
|
||||
pub struct RedundantStaticLifetimes {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl RedundantStaticLifetimes {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]);
|
||||
|
||||
impl RedundantStaticLifetimes {
|
||||
// Recursively visit types
|
||||
@ -84,6 +98,10 @@ impl RedundantStaticLifetimes {
|
||||
|
||||
impl EarlyLintPass for RedundantStaticLifetimes {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_STATIC_LIFETIMES_MSRV) {
|
||||
return;
|
||||
}
|
||||
|
||||
if !item.span.from_expansion() {
|
||||
if let ItemKind::Const(_, ref var_type, _) = item.kind {
|
||||
self.visit_type(var_type, cx, "constants have by default a `'static` lifetime");
|
||||
@ -96,4 +114,6 @@ impl EarlyLintPass for RedundantStaticLifetimes {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(EarlyContext);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ use crate::utils::{last_path_segment, snippet, span_lint_and_sugg};
|
||||
use rustc_hir::{GenericArg, Mutability, Ty, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
@ -41,7 +42,7 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef {
|
||||
if let Some(res) = last.res;
|
||||
if let Some(def_id) = res.opt_def_id();
|
||||
|
||||
if cx.tcx.is_diagnostic_item(sym!(option_type), def_id);
|
||||
if cx.tcx.is_diagnostic_item(sym::option_type, def_id);
|
||||
if let Some(ref params) = last_path_segment(qpath).args ;
|
||||
if !params.parenthesized;
|
||||
if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg {
|
||||
|
@ -372,7 +372,7 @@ impl LateLintPass<'_> for StringToString {
|
||||
if let ExprKind::MethodCall(path, _, args, _) = &expr.kind;
|
||||
if path.ident.name == sym!(to_string);
|
||||
let ty = cx.typeck_results().expr_ty(&args[0]);
|
||||
if is_type_diagnostic_item(cx, ty, sym!(string_type));
|
||||
if is_type_diagnostic_item(cx, ty, sym::string_type);
|
||||
then {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
|
@ -1104,7 +1104,9 @@ fn is_empty_block(expr: &Expr<'_>) -> bool {
|
||||
expr.kind,
|
||||
ExprKind::Block(
|
||||
Block {
|
||||
stmts: &[], expr: None, ..
|
||||
stmts: &[],
|
||||
expr: None,
|
||||
..
|
||||
},
|
||||
_,
|
||||
)
|
||||
|
@ -9,6 +9,7 @@ use rustc_hir::{Body, ExprKind, FnDecl, HirId, ItemKind, Node};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::subst::GenericArgKind;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -74,14 +75,17 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
|
||||
}
|
||||
|
||||
if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
|
||||
if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), ..} | ItemKind::Trait(..)) {
|
||||
if matches!(
|
||||
item.kind,
|
||||
ItemKind::Impl { of_trait: Some(_), .. } | ItemKind::Trait(..)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) {
|
||||
let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::option_type) {
|
||||
("Option", &paths::OPTION_SOME)
|
||||
} else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) {
|
||||
} else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::result_type) {
|
||||
("Result", &paths::RESULT_OK)
|
||||
} else {
|
||||
return;
|
||||
|
@ -12,11 +12,12 @@ use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::{DefIdTree, Ty};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_typeck::hir_ty_to_ty;
|
||||
|
||||
use crate::utils::{differing_macro_contexts, span_lint_and_sugg};
|
||||
use crate::utils::{differing_macro_contexts, meets_msrv, span_lint_and_sugg};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for unnecessary repetition of structure name when a
|
||||
@ -53,7 +54,7 @@ declare_clippy_lint! {
|
||||
"unnecessary structure name repetition whereas `Self` is applicable"
|
||||
}
|
||||
|
||||
declare_lint_pass!(UseSelf => [USE_SELF]);
|
||||
impl_lint_pass!(UseSelf => [USE_SELF]);
|
||||
|
||||
const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element";
|
||||
|
||||
@ -157,8 +158,25 @@ fn check_trait_method_impl_decl<'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
const USE_SELF_MSRV: RustcVersion = RustcVersion::new(1, 37, 0);
|
||||
|
||||
pub struct UseSelf {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl UseSelf {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for UseSelf {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
if !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) {
|
||||
return;
|
||||
}
|
||||
|
||||
if in_external_macro(cx.sess(), item.span) {
|
||||
return;
|
||||
}
|
||||
@ -204,6 +222,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
|
||||
}
|
||||
}
|
||||
}
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
struct UseSelfVisitor<'a, 'tcx> {
|
||||
|
@ -408,7 +408,10 @@ pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool {
|
||||
}
|
||||
|
||||
pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool {
|
||||
matches!((l, r), (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_)))
|
||||
matches!(
|
||||
(l, r),
|
||||
(Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_))
|
||||
)
|
||||
}
|
||||
|
||||
pub fn eq_vis(l: &Visibility, r: &Visibility) -> bool {
|
||||
|
@ -106,7 +106,7 @@ macro_rules! define_Conf {
|
||||
|
||||
pub use self::helpers::Conf;
|
||||
define_Conf! {
|
||||
/// Lint: MANUAL_NON_EXHAUSTIVE, MANUAL_STRIP, OPTION_AS_REF_DEREF, MATCH_LIKE_MATCHES_MACRO. The minimum rust version that the project supports
|
||||
/// Lint: REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN. The minimum rust version that the project supports
|
||||
(msrv, "msrv": Option<String>, None),
|
||||
/// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses
|
||||
(blacklisted_names, "blacklisted_names": Vec<String>, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()),
|
||||
|
@ -15,6 +15,7 @@ use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::mir::interpret::ConstValue;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::{Span, Spanned};
|
||||
@ -247,6 +248,30 @@ declare_clippy_lint! {
|
||||
"invalid path"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:**
|
||||
/// Checks for interning symbols that have already been pre-interned and defined as constants.
|
||||
///
|
||||
/// **Why is this bad?**
|
||||
/// It's faster and easier to use the symbol constant.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// Bad:
|
||||
/// ```rust,ignore
|
||||
/// let _ = sym!(f32);
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// ```rust,ignore
|
||||
/// let _ = sym::f32;
|
||||
/// ```
|
||||
pub INTERNING_DEFINED_SYMBOL,
|
||||
internal,
|
||||
"interning a symbol that is pre-interned and defined as a constant"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
|
||||
|
||||
impl EarlyLintPass for ClippyLintsInternal {
|
||||
@ -840,3 +865,56 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct InterningDefinedSymbol {
|
||||
// Maps the symbol value to the constant name.
|
||||
symbol_map: FxHashMap<u32, String>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
|
||||
fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
|
||||
if !self.symbol_map.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(Res::Def(_, def_id)) = path_to_res(cx, &paths::SYM_MODULE) {
|
||||
for item in cx.tcx.item_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);
|
||||
if match_type(cx, ty, &paths::SYMBOL);
|
||||
if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id);
|
||||
if let Ok(value) = value.to_u32();
|
||||
then {
|
||||
self.symbol_map.insert(value, item.ident.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(func, [arg]) = &expr.kind;
|
||||
if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind();
|
||||
if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN);
|
||||
if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg);
|
||||
let value = Symbol::intern(&arg).as_u32();
|
||||
if let Some(symbol_const) = self.symbol_map.get(&value);
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
INTERNING_DEFINED_SYMBOL,
|
||||
is_expn_of(expr.span, "sym").unwrap_or(expr.span),
|
||||
"interning a defined symbol",
|
||||
"try",
|
||||
format!("rustc_span::symbol::sym::{}", symbol_const),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
|
||||
use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
|
||||
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::Node;
|
||||
use rustc_hir::{
|
||||
def, Arm, Block, Body, Constness, Crate, Expr, ExprKind, FnDecl, HirId, ImplItem, ImplItemKind, Item, ItemKind,
|
||||
@ -603,6 +603,37 @@ pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
|
||||
visitor.found
|
||||
}
|
||||
|
||||
struct FindMacroCalls<'a, 'b> {
|
||||
names: &'a [&'b str],
|
||||
result: Vec<Span>,
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'tcx> Visitor<'tcx> for FindMacroCalls<'a, 'b> {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
if self.names.iter().any(|fun| is_expn_of(expr.span, fun).is_some()) {
|
||||
self.result.push(expr.span);
|
||||
}
|
||||
// and check sub-expressions
|
||||
intravisit::walk_expr(self, expr);
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::None
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds calls of the specified macros in a function body.
|
||||
pub fn find_macro_calls(names: &[&str], body: &Body<'_>) -> Vec<Span> {
|
||||
let mut fmc = FindMacroCalls {
|
||||
names,
|
||||
result: Vec::new(),
|
||||
};
|
||||
fmc.visit_expr(&body.value);
|
||||
fmc.result
|
||||
}
|
||||
|
||||
/// Converts a span to a code snippet if available, otherwise use default.
|
||||
///
|
||||
/// This is useful if you want to provide suggestions for your lint or more generally, if you want
|
||||
@ -1500,7 +1531,7 @@ pub fn is_no_std_crate(krate: &Crate<'_>) -> bool {
|
||||
/// ```
|
||||
pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
|
||||
if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
|
||||
matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. })
|
||||
matches!(item.kind, ItemKind::Impl { of_trait: Some(_), .. })
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
@ -146,6 +146,12 @@ pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
|
||||
pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
|
||||
pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"];
|
||||
#[cfg(feature = "internal-lints")]
|
||||
pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"];
|
||||
#[cfg(feature = "internal-lints")]
|
||||
pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"];
|
||||
#[cfg(feature = "internal-lints")]
|
||||
pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"];
|
||||
#[cfg(feature = "internal-lints")]
|
||||
pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
|
||||
pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"];
|
||||
pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
|
||||
|
@ -116,20 +116,27 @@ pub struct ParamBindingIdCollector {
|
||||
}
|
||||
impl<'tcx> ParamBindingIdCollector {
|
||||
fn collect_binding_hir_ids(body: &'tcx hir::Body<'tcx>) -> Vec<hir::HirId> {
|
||||
let mut finder = ParamBindingIdCollector {
|
||||
binding_hir_ids: Vec::new(),
|
||||
};
|
||||
finder.visit_body(body);
|
||||
finder.binding_hir_ids
|
||||
let mut hir_ids: Vec<hir::HirId> = Vec::new();
|
||||
for param in body.params.iter() {
|
||||
let mut finder = ParamBindingIdCollector {
|
||||
binding_hir_ids: Vec::new(),
|
||||
};
|
||||
finder.visit_param(param);
|
||||
for hir_id in &finder.binding_hir_ids {
|
||||
hir_ids.push(*hir_id);
|
||||
}
|
||||
}
|
||||
hir_ids
|
||||
}
|
||||
}
|
||||
impl<'tcx> intravisit::Visitor<'tcx> for ParamBindingIdCollector {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
|
||||
if let hir::PatKind::Binding(_, hir_id, ..) = param.pat.kind {
|
||||
fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) {
|
||||
if let hir::PatKind::Binding(_, hir_id, ..) = pat.kind {
|
||||
self.binding_hir_ids.push(hir_id);
|
||||
}
|
||||
intravisit::walk_pat(self, pat);
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
|
||||
|
@ -75,6 +75,24 @@ declare_clippy_lint! {
|
||||
"printing on stdout"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for printing on *stderr*. The purpose of this lint
|
||||
/// is to catch debugging remnants.
|
||||
///
|
||||
/// **Why is this bad?** People often print on *stderr* while debugging an
|
||||
/// application and might forget to remove those prints afterward.
|
||||
///
|
||||
/// **Known problems:** Only catches `eprint!` and `eprintln!` calls.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// eprintln!("Hello world!");
|
||||
/// ```
|
||||
pub PRINT_STDERR,
|
||||
restriction,
|
||||
"printing on stderr"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for use of `Debug` formatting. The purpose of this
|
||||
/// lint is to catch debugging remnants.
|
||||
@ -201,6 +219,7 @@ impl_lint_pass!(Write => [
|
||||
PRINT_WITH_NEWLINE,
|
||||
PRINTLN_EMPTY_STRING,
|
||||
PRINT_STDOUT,
|
||||
PRINT_STDERR,
|
||||
USE_DEBUG,
|
||||
PRINT_LITERAL,
|
||||
WRITE_WITH_NEWLINE,
|
||||
@ -243,47 +262,22 @@ impl EarlyLintPass for Write {
|
||||
.map_or(false, |crate_name| crate_name == "build_script_build")
|
||||
}
|
||||
|
||||
if mac.path == sym!(println) {
|
||||
if !is_build_script(cx) {
|
||||
span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`");
|
||||
}
|
||||
if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
|
||||
if fmt_str.symbol == Symbol::intern("") {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
PRINTLN_EMPTY_STRING,
|
||||
mac.span(),
|
||||
"using `println!(\"\")`",
|
||||
"replace it with",
|
||||
"println!()".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if mac.path == sym!(print) {
|
||||
if mac.path == sym!(print) {
|
||||
if !is_build_script(cx) {
|
||||
span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`");
|
||||
}
|
||||
if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
|
||||
if check_newlines(&fmt_str) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
PRINT_WITH_NEWLINE,
|
||||
mac.span(),
|
||||
"using `print!()` with a format string that ends in a single newline",
|
||||
|err| {
|
||||
err.multipart_suggestion(
|
||||
"use `println!` instead",
|
||||
vec![
|
||||
(mac.path.span, String::from("println")),
|
||||
(newline_span(&fmt_str), String::new()),
|
||||
],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
self.lint_print_with_newline(cx, mac);
|
||||
} else if mac.path == sym!(println) {
|
||||
if !is_build_script(cx) {
|
||||
span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`");
|
||||
}
|
||||
self.lint_println_empty_string(cx, mac);
|
||||
} else if mac.path == sym!(eprint) {
|
||||
span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprint!`");
|
||||
self.lint_print_with_newline(cx, mac);
|
||||
} else if mac.path == sym!(eprintln) {
|
||||
span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`");
|
||||
self.lint_println_empty_string(cx, mac);
|
||||
} else if mac.path == sym!(write) {
|
||||
if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), true) {
|
||||
if check_newlines(&fmt_str) {
|
||||
@ -487,6 +481,45 @@ impl Write {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_println_empty_string(&self, cx: &EarlyContext<'_>, mac: &MacCall) {
|
||||
if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
|
||||
if fmt_str.symbol == Symbol::intern("") {
|
||||
let name = mac.path.segments[0].ident.name;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
PRINTLN_EMPTY_STRING,
|
||||
mac.span(),
|
||||
&format!("using `{}!(\"\")`", name),
|
||||
"replace it with",
|
||||
format!("{}!()", name),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_print_with_newline(&self, cx: &EarlyContext<'_>, mac: &MacCall) {
|
||||
if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
|
||||
if check_newlines(&fmt_str) {
|
||||
let name = mac.path.segments[0].ident.name;
|
||||
let suggested = format!("{}ln", name);
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
PRINT_WITH_NEWLINE,
|
||||
mac.span(),
|
||||
&format!("using `{}!()` with a format string that ends in a single newline", name),
|
||||
|err| {
|
||||
err.multipart_suggestion(
|
||||
&format!("use `{}!` instead", suggested),
|
||||
vec![(mac.path.span, suggested), (newline_span(&fmt_str), String::new())],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the format string contains a single newline that terminates it.
|
||||
|
82
src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs
Normal file
82
src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs
Normal file
@ -0,0 +1,82 @@
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::{self as hir, HirId, ItemKind, Node};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{Adt, Ty};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_target::abi::LayoutOf as _;
|
||||
use rustc_typeck::hir_ty_to_ty;
|
||||
|
||||
use crate::utils::{is_type_diagnostic_item, match_type, paths, span_lint_and_help};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for maps with zero-sized value types anywhere in the code.
|
||||
///
|
||||
/// **Why is this bad?** Since there is only a single value for a zero-sized type, a map
|
||||
/// containing zero sized values is effectively a set. Using a set in that case improves
|
||||
/// readability and communicates intent more clearly.
|
||||
///
|
||||
/// **Known problems:**
|
||||
/// * A zero-sized type cannot be recovered later if it contains private fields.
|
||||
/// * This lints the signature of public items
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// # use std::collections::HashMap;
|
||||
/// fn unique_words(text: &str) -> HashMap<&str, ()> {
|
||||
/// todo!();
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # use std::collections::HashSet;
|
||||
/// fn unique_words(text: &str) -> HashSet<&str> {
|
||||
/// todo!();
|
||||
/// }
|
||||
/// ```
|
||||
pub ZERO_SIZED_MAP_VALUES,
|
||||
pedantic,
|
||||
"usage of map with zero-sized value type"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ZeroSizedMapValues => [ZERO_SIZED_MAP_VALUES]);
|
||||
|
||||
impl LateLintPass<'_> for ZeroSizedMapValues {
|
||||
fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
|
||||
if_chain! {
|
||||
if !hir_ty.span.from_expansion();
|
||||
if !in_trait_impl(cx, hir_ty.hir_id);
|
||||
let ty = ty_from_hir_ty(cx, hir_ty);
|
||||
if is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) || match_type(cx, ty, &paths::BTREEMAP);
|
||||
if let Adt(_, ref substs) = ty.kind();
|
||||
let ty = substs.type_at(1);
|
||||
if let Ok(layout) = cx.layout_of(ty);
|
||||
if layout.is_zst();
|
||||
then {
|
||||
span_lint_and_help(cx, ZERO_SIZED_MAP_VALUES, hir_ty.span, "map with zero-sized value type", None, "consider using a set instead");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn in_trait_impl(cx: &LateContext<'_>, hir_id: HirId) -> bool {
|
||||
let parent_id = cx.tcx.hir().get_parent_item(hir_id);
|
||||
if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_item(parent_id)) {
|
||||
if let ItemKind::Impl { of_trait: Some(_), .. } = item.kind {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn ty_from_hir_ty<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> Ty<'tcx> {
|
||||
cx.maybe_typeck_results()
|
||||
.and_then(|results| {
|
||||
if results.hir_owner == hir_ty.hir_id.owner {
|
||||
results.node_type_opt(hir_ty.hir_id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| hir_ty_to_ty(cx.tcx, hir_ty))
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
[package]
|
||||
name = "path_dep"
|
||||
version = "0.1.0"
|
@ -0,0 +1,6 @@
|
||||
#![deny(clippy::empty_loop)]
|
||||
|
||||
#[cfg(feature = "primary_package_test")]
|
||||
pub fn lint_me() {
|
||||
loop {}
|
||||
}
|
@ -1,3 +1,6 @@
|
||||
[package]
|
||||
name = "subcrate"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
path_dep = { path = "../path_dep" }
|
||||
|
@ -98,12 +98,12 @@ While we are working on implementing our lint, we can keep running the UI
|
||||
test. That allows us to check if the output is turning into what we want.
|
||||
|
||||
Once we are satisfied with the output, we need to run
|
||||
`tests/ui/update-all-references.sh` to update the `.stderr` file for our lint.
|
||||
`cargo dev bless` to update the `.stderr` file for our lint.
|
||||
Please note that, we should run `TESTNAME=foo_functions cargo uitest`
|
||||
every time before running `tests/ui/update-all-references.sh`.
|
||||
every time before running `cargo dev bless`.
|
||||
Running `TESTNAME=foo_functions cargo uitest` should pass then. When we commit
|
||||
our lint, we need to commit the generated `.stderr` files, too. In general, you
|
||||
should only commit files changed by `tests/ui/update-all-references.sh` for the
|
||||
should only commit files changed by `cargo dev bless` for the
|
||||
specific lint you are creating/editing. Note that if the generated files are
|
||||
empty, they should be removed.
|
||||
|
||||
@ -122,8 +122,7 @@ we will find by default two new crates, each with its manifest file:
|
||||
If you need more cases, you can copy one of those crates (under `foo_categories`) and rename it.
|
||||
|
||||
The process of generating the `.stderr` file is the same, and prepending the `TESTNAME`
|
||||
variable to `cargo uitest` works too, but the script to update the references
|
||||
is in another path: `tests/ui-cargo/update-all-references.sh`.
|
||||
variable to `cargo uitest` works too.
|
||||
|
||||
## Rustfix tests
|
||||
|
||||
@ -133,7 +132,7 @@ additionally run [rustfix] for that test. Rustfix will apply the suggestions
|
||||
from the lint to the code of the test file and compare that to the contents of
|
||||
a `.fixed` file.
|
||||
|
||||
Use `tests/ui/update-all-references.sh` to automatically generate the
|
||||
Use `cargo dev bless` to automatically generate the
|
||||
`.fixed` file after running the tests.
|
||||
|
||||
[rustfix]: https://github.com/rust-lang/rustfix
|
||||
@ -226,13 +225,13 @@ store.register_early_pass(|| box foo_functions::FooFunctions);
|
||||
```
|
||||
|
||||
As one may expect, there is a corresponding `register_late_pass` method
|
||||
available as well. Without a call to one of `register_early_pass` or
|
||||
available as well. Without a call to one of `register_early_pass` or
|
||||
`register_late_pass`, the lint pass in question will not be run.
|
||||
|
||||
One reason that `cargo dev` does not automate this step is that multiple lints
|
||||
One reason that `cargo dev` does not automate this step is that multiple lints
|
||||
can use the same lint pass, so registering the lint pass may already be done
|
||||
when adding a new lint. Another reason that this step is not automated is that
|
||||
the order that the passes are registered determines the order the passes
|
||||
the order that the passes are registered determines the order the passes
|
||||
actually run, which in turn affects the order that any emitted lints are output
|
||||
in.
|
||||
|
||||
@ -368,7 +367,7 @@ fn is_foo_fn(fn_kind: FnKind<'_>) -> bool {
|
||||
|
||||
Now we should also run the full test suite with `cargo test`. At this point
|
||||
running `cargo test` should produce the expected output. Remember to run
|
||||
`tests/ui/update-all-references.sh` to update the `.stderr` file.
|
||||
`cargo dev bless` to update the `.stderr` file.
|
||||
|
||||
`cargo test` (as opposed to `cargo uitest`) will also ensure that our lint
|
||||
implementation is not violating any Clippy lints itself.
|
||||
@ -380,6 +379,57 @@ pass.
|
||||
[`FnKind::Fn`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html#variant.Fn
|
||||
[ident]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Ident.html
|
||||
|
||||
## Specifying the lint's minimum supported Rust version (msrv)
|
||||
|
||||
Projects supporting older versions of Rust would need to disable a lint if it targets features
|
||||
present in later versions. Support for this can be added by specifying an msrv in your lint like so,
|
||||
|
||||
```rust
|
||||
const MANUAL_STRIP_MSRV: RustcVersion = RustcVersion::new(1, 45, 0);
|
||||
```
|
||||
|
||||
The project's msrv will also have to be an attribute in the lint so you'll have to add a struct
|
||||
and constructor for your lint. The project's msrv needs to be passed when the lint is registered
|
||||
in `lib.rs`
|
||||
|
||||
```rust
|
||||
pub struct ManualStrip {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl ManualStrip {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The project's msrv can then be matched against the lint's msrv in the LintPass using the `meets_msrv` utility
|
||||
function.
|
||||
|
||||
``` rust
|
||||
if !meets_msrv(self.msrv.as_ref(), &MANUAL_STRIP_MSRV) {
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
The project's msrv can also be specified as an inner attribute, which overrides the value from
|
||||
`clippy.toml`. This can be accounted for using the `extract_msrv_attr!(LintContext)` macro and passing
|
||||
LateContext/EarlyContext.
|
||||
|
||||
```rust
|
||||
impl<'tcx> LateLintPass<'tcx> for ManualStrip {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
...
|
||||
}
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
```
|
||||
|
||||
Once the msrv is added to the lint, a relevant test case should be added to `tests/ui/min_rust_version_attr.rs`
|
||||
which verifies that the lint isn't emitted if the project's msrv is lower.
|
||||
|
||||
## Author lint
|
||||
|
||||
If you have trouble implementing your lint, there is also the internal `author`
|
||||
|
@ -1,16 +1,14 @@
|
||||
# Basics for hacking on Clippy
|
||||
|
||||
This document explains the basics for hacking on Clippy. Besides others, this
|
||||
includes how to set-up the development environment, how to build and how to test
|
||||
Clippy. For a more in depth description on the codebase take a look at [Adding
|
||||
Lints] or [Common Tools].
|
||||
includes how to build and test Clippy. For a more in depth description on
|
||||
the codebase take a look at [Adding Lints] or [Common Tools].
|
||||
|
||||
[Adding Lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
|
||||
[Common Tools]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md
|
||||
|
||||
- [Basics for hacking on Clippy](#basics-for-hacking-on-clippy)
|
||||
- [Get the code](#get-the-code)
|
||||
- [Setup](#setup)
|
||||
- [Get the Code](#get-the-code)
|
||||
- [Building and Testing](#building-and-testing)
|
||||
- [`cargo dev`](#cargo-dev)
|
||||
- [PR](#pr)
|
||||
@ -38,29 +36,9 @@ git rebase upstream/master
|
||||
git push
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
Next we need to setup the toolchain to compile Clippy. Since Clippy heavily
|
||||
relies on compiler internals it is build with the latest rustc master. To get
|
||||
this toolchain, you can just use the `setup-toolchain.sh` script or use
|
||||
`rustup-toolchain-install-master`:
|
||||
|
||||
```bash
|
||||
bash setup-toolchain.sh
|
||||
# OR
|
||||
cargo install rustup-toolchain-install-master
|
||||
# For better IDE integration also add `-c rustfmt -c rust-src` (optional)
|
||||
rustup-toolchain-install-master -f -n master -c rustc-dev -c llvm-tools
|
||||
rustup override set master
|
||||
```
|
||||
|
||||
_Note:_ Sometimes you may get compiler errors when building Clippy, even if you
|
||||
didn't change anything. Normally those will be fixed by a maintainer in a few hours.
|
||||
|
||||
## Building and Testing
|
||||
|
||||
Once the `master` toolchain is installed, you can build and test Clippy like
|
||||
every other Rust project:
|
||||
You can build and test Clippy like every other Rust project:
|
||||
|
||||
```bash
|
||||
cargo build # builds Clippy
|
||||
@ -83,7 +61,7 @@ If the output of a [UI test] differs from the expected output, you can update th
|
||||
reference file with:
|
||||
|
||||
```bash
|
||||
sh tests/ui/update-all-references.sh
|
||||
cargo dev bless
|
||||
```
|
||||
|
||||
For example, this is necessary, if you fix a typo in an error message of a lint
|
||||
@ -109,7 +87,7 @@ cargo dev update_lints
|
||||
# create a new lint and register it
|
||||
cargo dev new_lint
|
||||
# (experimental) Setup Clippy to work with rust-analyzer
|
||||
cargo dev ra-setup
|
||||
cargo dev ra_setup
|
||||
```
|
||||
|
||||
## PR
|
||||
|
@ -1 +1,3 @@
|
||||
nightly
|
||||
[toolchain]
|
||||
channel = "nightly-2020-12-20"
|
||||
components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"]
|
||||
|
@ -1,36 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Set up the appropriate rustc toolchain
|
||||
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
RTIM_PATH=$(command -v rustup-toolchain-install-master) || INSTALLED=false
|
||||
CARGO_HOME=${CARGO_HOME:-$HOME/.cargo}
|
||||
|
||||
# Check if RTIM is not installed or installed in other locations not in ~/.cargo/bin
|
||||
if [[ "$INSTALLED" == false || "$RTIM_PATH" == $CARGO_HOME/bin/rustup-toolchain-install-master ]]; then
|
||||
cargo +nightly install rustup-toolchain-install-master
|
||||
else
|
||||
VERSION=$(rustup-toolchain-install-master -V | grep -o "[0-9.]*")
|
||||
REMOTE=$(cargo +nightly search rustup-toolchain-install-master | grep -o "[0-9.]*")
|
||||
echo "info: skipping updating rustup-toolchain-install-master at $RTIM_PATH"
|
||||
echo " current version : $VERSION"
|
||||
echo " remote version : $REMOTE"
|
||||
fi
|
||||
|
||||
RUST_COMMIT=$(git ls-remote https://github.com/rust-lang/rust master | awk '{print $1}')
|
||||
|
||||
if rustc +master -Vv 2>/dev/null | grep -q "$RUST_COMMIT"; then
|
||||
echo "info: master toolchain is up-to-date"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ -n "$HOST_TOOLCHAIN" ]]; then
|
||||
TOOLCHAIN=('--host' "$HOST_TOOLCHAIN")
|
||||
else
|
||||
TOOLCHAIN=()
|
||||
fi
|
||||
|
||||
rustup-toolchain-install-master -f -n master "${TOOLCHAIN[@]}" -c rustc-dev -c llvm-tools -- "$RUST_COMMIT"
|
||||
rustup override set master
|
@ -1,5 +1,6 @@
|
||||
#![feature(rustc_private)]
|
||||
#![feature(once_cell)]
|
||||
#![feature(bool_to_option)]
|
||||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
// warn on lints, that are included in `rust-lang/rust`s bootstrap
|
||||
#![warn(rust_2018_idioms, unused_lifetimes)]
|
||||
@ -19,6 +20,7 @@ use rustc_tools_util::VersionInfo;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::env;
|
||||
use std::iter;
|
||||
use std::lazy::SyncLazy;
|
||||
use std::ops::Deref;
|
||||
use std::panic;
|
||||
@ -41,26 +43,12 @@ fn arg_value<'a, T: Deref<Target = str>>(
|
||||
|
||||
match arg.next().or_else(|| args.next()) {
|
||||
Some(v) if pred(v) => return Some(v),
|
||||
_ => {}
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arg_value() {
|
||||
let args = &["--bar=bar", "--foobar", "123", "--foo"];
|
||||
|
||||
assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None);
|
||||
assert_eq!(arg_value(args, "--bar", |_| false), None);
|
||||
assert_eq!(arg_value(args, "--bar", |_| true), Some("bar"));
|
||||
assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar"));
|
||||
assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None);
|
||||
assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None);
|
||||
assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123"));
|
||||
assert_eq!(arg_value(args, "--foo", |_| true), None);
|
||||
}
|
||||
|
||||
struct DefaultCallbacks;
|
||||
impl rustc_driver::Callbacks for DefaultCallbacks {}
|
||||
|
||||
@ -121,12 +109,11 @@ You can use tool lints to allow or deny lints from your code, eg.:
|
||||
|
||||
const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new";
|
||||
|
||||
static ICE_HOOK: SyncLazy<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> =
|
||||
SyncLazy::new(|| {
|
||||
let hook = panic::take_hook();
|
||||
panic::set_hook(Box::new(|info| report_clippy_ice(info, BUG_REPORT_URL)));
|
||||
hook
|
||||
});
|
||||
static ICE_HOOK: SyncLazy<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> = SyncLazy::new(|| {
|
||||
let hook = panic::take_hook();
|
||||
panic::set_hook(Box::new(|info| report_clippy_ice(info, BUG_REPORT_URL)));
|
||||
hook
|
||||
});
|
||||
|
||||
fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
|
||||
// Invoke our ICE handler, which prints the actual panic message and optionally a backtrace
|
||||
@ -183,6 +170,29 @@ fn toolchain_path(home: Option<String>, toolchain: Option<String>) -> Option<Pat
|
||||
})
|
||||
}
|
||||
|
||||
fn remove_clippy_args<'a, T, U, I>(args: &mut Vec<T>, clippy_args: I)
|
||||
where
|
||||
T: AsRef<str>,
|
||||
U: AsRef<str> + ?Sized + 'a,
|
||||
I: Iterator<Item = &'a U> + Clone,
|
||||
{
|
||||
let args_iter = clippy_args.map(AsRef::as_ref);
|
||||
let args_count = args_iter.clone().count();
|
||||
|
||||
if args_count > 0 {
|
||||
if let Some(start) = args.windows(args_count).enumerate().find_map(|(current, window)| {
|
||||
window
|
||||
.iter()
|
||||
.map(AsRef::as_ref)
|
||||
.eq(args_iter.clone())
|
||||
.then_some(current)
|
||||
}) {
|
||||
args.drain(start..start + args_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn main() {
|
||||
rustc_driver::init_rustc_env_logger();
|
||||
SyncLazy::force(&ICE_HOOK);
|
||||
@ -258,17 +268,14 @@ pub fn main() {
|
||||
|
||||
// Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument.
|
||||
// We're invoking the compiler programmatically, so we ignore this/
|
||||
let wrapper_mode =
|
||||
orig_args.get(1).map(Path::new).and_then(Path::file_stem) == Some("rustc".as_ref());
|
||||
let wrapper_mode = orig_args.get(1).map(Path::new).and_then(Path::file_stem) == Some("rustc".as_ref());
|
||||
|
||||
if wrapper_mode {
|
||||
// we still want to be able to invoke it normally though
|
||||
orig_args.remove(1);
|
||||
}
|
||||
|
||||
if !wrapper_mode
|
||||
&& (orig_args.iter().any(|a| a == "--help" || a == "-h") || orig_args.len() == 1)
|
||||
{
|
||||
if !wrapper_mode && (orig_args.iter().any(|a| a == "--help" || a == "-h") || orig_args.len() == 1) {
|
||||
display_help();
|
||||
exit(0);
|
||||
}
|
||||
@ -281,25 +288,88 @@ pub fn main() {
|
||||
args.extend(vec!["--sysroot".into(), sys_root]);
|
||||
};
|
||||
|
||||
// this check ensures that dependencies are built but not linted and the final
|
||||
// crate is linted but not built
|
||||
let clippy_enabled = env::var("CLIPPY_TESTS").map_or(false, |val| val == "true")
|
||||
|| arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_none();
|
||||
let clippy_args = env::var("CLIPPY_ARGS").unwrap_or_default();
|
||||
let clippy_args = clippy_args.split_whitespace();
|
||||
let no_deps = clippy_args.clone().any(|flag| flag == "--no-deps");
|
||||
|
||||
// We enable Clippy if one of the following conditions is met
|
||||
// - IF Clippy is run on its test suite OR
|
||||
// - IF Clippy is run on the main crate, not on deps (`!cap_lints_allow`) THEN
|
||||
// - IF `--no-deps` is not set (`!no_deps`) OR
|
||||
// - IF `--no-deps` is set and Clippy is run on the specified primary package
|
||||
let clippy_tests_set = env::var("CLIPPY_TESTS").map_or(false, |val| val == "true");
|
||||
let cap_lints_allow = arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_some();
|
||||
let in_primary_package = env::var("CARGO_PRIMARY_PACKAGE").is_ok();
|
||||
|
||||
let clippy_enabled = clippy_tests_set || (!cap_lints_allow && (!no_deps || in_primary_package));
|
||||
if clippy_enabled {
|
||||
remove_clippy_args(&mut args, iter::once("--no-deps"));
|
||||
args.extend(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]);
|
||||
if let Ok(extra_args) = env::var("CLIPPY_ARGS") {
|
||||
args.extend(
|
||||
extra_args
|
||||
.split("__CLIPPY_HACKERY__")
|
||||
.filter_map(|s| if s.is_empty() { None } else { Some(s.to_string()) }),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Remove all flags passed through RUSTFLAGS if Clippy is not enabled.
|
||||
remove_clippy_args(&mut args, clippy_args);
|
||||
}
|
||||
|
||||
let mut clippy = ClippyCallbacks;
|
||||
let mut default = DefaultCallbacks;
|
||||
let callbacks: &mut (dyn rustc_driver::Callbacks + Send) =
|
||||
if clippy_enabled { &mut clippy } else { &mut default };
|
||||
|
||||
rustc_driver::RunCompiler::new(&args, callbacks).run()
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_arg_value() {
|
||||
let args = &["--bar=bar", "--foobar", "123", "--foo"];
|
||||
|
||||
assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None);
|
||||
assert_eq!(arg_value(args, "--bar", |_| false), None);
|
||||
assert_eq!(arg_value(args, "--bar", |_| true), Some("bar"));
|
||||
assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar"));
|
||||
assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None);
|
||||
assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None);
|
||||
assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123"));
|
||||
assert_eq!(arg_value(args, "--foo", |_| true), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn removes_clippy_args_from_start() {
|
||||
let mut args = vec!["-D", "clippy::await_holding_lock", "--cfg", r#"feature="some_feat""#];
|
||||
let clippy_args = ["-D", "clippy::await_holding_lock"].iter();
|
||||
|
||||
remove_clippy_args(&mut args, clippy_args);
|
||||
assert_eq!(args, &["--cfg", r#"feature="some_feat""#]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn removes_clippy_args_from_end() {
|
||||
let mut args = vec!["-Zui-testing", "-A", "clippy::empty_loop", "--no-deps"];
|
||||
let clippy_args = ["-A", "clippy::empty_loop", "--no-deps"].iter();
|
||||
|
||||
remove_clippy_args(&mut args, clippy_args);
|
||||
assert_eq!(args, &["-Zui-testing"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn removes_clippy_args_from_middle() {
|
||||
let mut args = vec!["-Zui-testing", "-W", "clippy::filter_map", "-L", "serde"];
|
||||
let clippy_args = ["-W", "clippy::filter_map"].iter();
|
||||
|
||||
remove_clippy_args(&mut args, clippy_args);
|
||||
assert_eq!(args, &["-Zui-testing", "-L", "serde"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_clippy_args_to_remove() {
|
||||
let mut args = vec!["-Zui-testing", "-L", "serde"];
|
||||
let clippy_args: [&str; 0] = [];
|
||||
|
||||
remove_clippy_args(&mut args, clippy_args.iter());
|
||||
assert_eq!(args, &["-Zui-testing", "-L", "serde"]);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
#![feature(bool_to_option)]
|
||||
#![feature(command_access)]
|
||||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
// warn on lints, that are included in `rust-lang/rust`s bootstrap
|
||||
#![warn(rust_2018_idioms, unused_lifetimes)]
|
||||
@ -62,7 +64,7 @@ struct ClippyCmd {
|
||||
unstable_options: bool,
|
||||
cargo_subcommand: &'static str,
|
||||
args: Vec<String>,
|
||||
clippy_args: String,
|
||||
clippy_args: Option<String>,
|
||||
}
|
||||
|
||||
impl ClippyCmd {
|
||||
@ -99,13 +101,17 @@ impl ClippyCmd {
|
||||
args.insert(0, "+nightly".to_string());
|
||||
}
|
||||
|
||||
let clippy_args: String = old_args.map(|arg| format!("{}__CLIPPY_HACKERY__", arg)).collect();
|
||||
let mut clippy_args = old_args.collect::<Vec<String>>().join(" ");
|
||||
if cargo_subcommand == "fix" && !clippy_args.contains("--no-deps") {
|
||||
clippy_args = format!("{} --no-deps", clippy_args);
|
||||
}
|
||||
|
||||
let has_args = !clippy_args.is_empty();
|
||||
ClippyCmd {
|
||||
unstable_options,
|
||||
cargo_subcommand,
|
||||
args,
|
||||
clippy_args,
|
||||
clippy_args: has_args.then_some(clippy_args),
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,15 +151,24 @@ impl ClippyCmd {
|
||||
.map(|p| ("CARGO_TARGET_DIR", p))
|
||||
}
|
||||
|
||||
fn into_std_cmd(self) -> Command {
|
||||
fn into_std_cmd(self, rustflags: Option<String>) -> Command {
|
||||
let mut cmd = Command::new("cargo");
|
||||
|
||||
cmd.env(self.path_env(), Self::path())
|
||||
.envs(ClippyCmd::target_dir())
|
||||
.env("CLIPPY_ARGS", self.clippy_args)
|
||||
.arg(self.cargo_subcommand)
|
||||
.args(&self.args);
|
||||
|
||||
// HACK: pass Clippy args to the driver *also* through RUSTFLAGS.
|
||||
// This guarantees that new builds will be triggered when Clippy flags change.
|
||||
if let Some(clippy_args) = self.clippy_args {
|
||||
cmd.env(
|
||||
"RUSTFLAGS",
|
||||
rustflags.map_or(clippy_args.clone(), |flags| format!("{} {}", clippy_args, flags)),
|
||||
);
|
||||
cmd.env("CLIPPY_ARGS", clippy_args);
|
||||
}
|
||||
|
||||
cmd
|
||||
}
|
||||
}
|
||||
@ -164,7 +179,7 @@ where
|
||||
{
|
||||
let cmd = ClippyCmd::new(old_args);
|
||||
|
||||
let mut cmd = cmd.into_std_cmd();
|
||||
let mut cmd = cmd.into_std_cmd(env::var("RUSTFLAGS").ok());
|
||||
|
||||
let exit_status = cmd
|
||||
.spawn()
|
||||
@ -182,6 +197,7 @@ where
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ClippyCmd;
|
||||
use std::ffi::OsStr;
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
@ -196,15 +212,37 @@ mod tests {
|
||||
.split_whitespace()
|
||||
.map(ToString::to_string);
|
||||
let cmd = ClippyCmd::new(args);
|
||||
|
||||
assert_eq!("fix", cmd.cargo_subcommand);
|
||||
assert_eq!("RUSTC_WORKSPACE_WRAPPER", cmd.path_env());
|
||||
assert!(cmd.args.iter().any(|arg| arg.ends_with("unstable-options")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fix_implies_no_deps() {
|
||||
let args = "cargo clippy --fix -Zunstable-options"
|
||||
.split_whitespace()
|
||||
.map(ToString::to_string);
|
||||
let cmd = ClippyCmd::new(args);
|
||||
|
||||
assert!(cmd.clippy_args.unwrap().contains("--no-deps"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_deps_not_duplicated_with_fix() {
|
||||
let args = "cargo clippy --fix -Zunstable-options -- --no-deps"
|
||||
.split_whitespace()
|
||||
.map(ToString::to_string);
|
||||
let cmd = ClippyCmd::new(args);
|
||||
|
||||
assert_eq!(1, cmd.clippy_args.unwrap().matches("--no-deps").count());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check() {
|
||||
let args = "cargo clippy".split_whitespace().map(ToString::to_string);
|
||||
let cmd = ClippyCmd::new(args);
|
||||
|
||||
assert_eq!("check", cmd.cargo_subcommand);
|
||||
assert_eq!("RUSTC_WRAPPER", cmd.path_env());
|
||||
}
|
||||
@ -215,7 +253,63 @@ mod tests {
|
||||
.split_whitespace()
|
||||
.map(ToString::to_string);
|
||||
let cmd = ClippyCmd::new(args);
|
||||
|
||||
assert_eq!("check", cmd.cargo_subcommand);
|
||||
assert_eq!("RUSTC_WORKSPACE_WRAPPER", cmd.path_env());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clippy_args_into_rustflags() {
|
||||
let args = "cargo clippy -- -W clippy::as_conversions"
|
||||
.split_whitespace()
|
||||
.map(ToString::to_string);
|
||||
let cmd = ClippyCmd::new(args);
|
||||
|
||||
let rustflags = None;
|
||||
let cmd = cmd.into_std_cmd(rustflags);
|
||||
|
||||
assert!(cmd
|
||||
.get_envs()
|
||||
.any(|(key, val)| key == "RUSTFLAGS" && val == Some(OsStr::new("-W clippy::as_conversions"))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clippy_args_respect_existing_rustflags() {
|
||||
let args = "cargo clippy -- -D clippy::await_holding_lock"
|
||||
.split_whitespace()
|
||||
.map(ToString::to_string);
|
||||
let cmd = ClippyCmd::new(args);
|
||||
|
||||
let rustflags = Some(r#"--cfg feature="some_feat""#.into());
|
||||
let cmd = cmd.into_std_cmd(rustflags);
|
||||
|
||||
assert!(cmd.get_envs().any(|(key, val)| key == "RUSTFLAGS"
|
||||
&& val == Some(OsStr::new(r#"-D clippy::await_holding_lock --cfg feature="some_feat""#))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_env_change_if_no_clippy_args() {
|
||||
let args = "cargo clippy".split_whitespace().map(ToString::to_string);
|
||||
let cmd = ClippyCmd::new(args);
|
||||
|
||||
let rustflags = Some(r#"--cfg feature="some_feat""#.into());
|
||||
let cmd = cmd.into_std_cmd(rustflags);
|
||||
|
||||
assert!(!cmd
|
||||
.get_envs()
|
||||
.any(|(key, _)| key == "RUSTFLAGS" || key == "CLIPPY_ARGS"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_env_change_if_no_clippy_args_nor_rustflags() {
|
||||
let args = "cargo clippy".split_whitespace().map(ToString::to_string);
|
||||
let cmd = ClippyCmd::new(args);
|
||||
|
||||
let rustflags = None;
|
||||
let cmd = cmd.into_std_cmd(rustflags);
|
||||
|
||||
assert!(!cmd
|
||||
.get_envs()
|
||||
.any(|(key, _)| key == "RUSTFLAGS" || key == "CLIPPY_ARGS"))
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
#![feature(once_cell)]
|
||||
|
||||
use std::lazy::SyncLazy;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
mod cargo;
|
||||
@ -23,7 +23,7 @@ fn dogfood_clippy() {
|
||||
.current_dir(root_dir)
|
||||
.env("CLIPPY_DOGFOOD", "1")
|
||||
.env("CARGO_INCREMENTAL", "0")
|
||||
.arg("clippy-preview")
|
||||
.arg("clippy")
|
||||
.arg("--all-targets")
|
||||
.arg("--all-features")
|
||||
.arg("--")
|
||||
@ -47,12 +47,77 @@ fn dogfood_clippy() {
|
||||
|
||||
#[test]
|
||||
fn dogfood_subprojects() {
|
||||
fn test_no_deps_ignores_path_deps_in_workspaces() {
|
||||
fn clean(cwd: &Path, target_dir: &Path) {
|
||||
Command::new("cargo")
|
||||
.current_dir(cwd)
|
||||
.env("CARGO_TARGET_DIR", target_dir)
|
||||
.arg("clean")
|
||||
.args(&["-p", "subcrate"])
|
||||
.args(&["-p", "path_dep"])
|
||||
.output()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if cargo::is_rustc_test_suite() {
|
||||
return;
|
||||
}
|
||||
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
let target_dir = root.join("target").join("dogfood");
|
||||
let cwd = root.join("clippy_workspace_tests");
|
||||
|
||||
// Make sure we start with a clean state
|
||||
clean(&cwd, &target_dir);
|
||||
|
||||
// `path_dep` is a path dependency of `subcrate` that would trigger a denied lint.
|
||||
// Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`.
|
||||
let output = Command::new(&*CLIPPY_PATH)
|
||||
.current_dir(&cwd)
|
||||
.env("CLIPPY_DOGFOOD", "1")
|
||||
.env("CARGO_INCREMENTAL", "0")
|
||||
.arg("clippy")
|
||||
.args(&["-p", "subcrate"])
|
||||
.arg("--")
|
||||
.arg("--no-deps")
|
||||
.arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
|
||||
.args(&["--cfg", r#"feature="primary_package_test""#])
|
||||
.output()
|
||||
.unwrap();
|
||||
println!("status: {}", output.status);
|
||||
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
|
||||
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
|
||||
|
||||
assert!(output.status.success());
|
||||
|
||||
// Make sure we start with a clean state
|
||||
clean(&cwd, &target_dir);
|
||||
|
||||
// Test that without the `--no-deps` argument, `path_dep` is linted.
|
||||
let output = Command::new(&*CLIPPY_PATH)
|
||||
.current_dir(&cwd)
|
||||
.env("CLIPPY_DOGFOOD", "1")
|
||||
.env("CARGO_INCREMENTAL", "0")
|
||||
.arg("clippy")
|
||||
.args(&["-p", "subcrate"])
|
||||
.arg("--")
|
||||
.arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
|
||||
.args(&["--cfg", r#"feature="primary_package_test""#])
|
||||
.output()
|
||||
.unwrap();
|
||||
println!("status: {}", output.status);
|
||||
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
|
||||
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
|
||||
|
||||
assert!(!output.status.success());
|
||||
}
|
||||
|
||||
// run clippy on remaining subprojects and fail the test if lint warnings are reported
|
||||
if cargo::is_rustc_test_suite() {
|
||||
return;
|
||||
}
|
||||
let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
|
||||
// NOTE: `path_dep` crate is omitted on purpose here
|
||||
for d in &[
|
||||
"clippy_workspace_tests",
|
||||
"clippy_workspace_tests/src",
|
||||
@ -78,4 +143,8 @@ fn dogfood_subprojects() {
|
||||
|
||||
assert!(output.status.success());
|
||||
}
|
||||
|
||||
// NOTE: Since tests run in parallel we can't run cargo commands on the same workspace at the
|
||||
// same time, so we test this immediately after the dogfood for workspaces.
|
||||
test_no_deps_ignores_path_deps_in_workspaces();
|
||||
}
|
||||
|
@ -72,6 +72,8 @@ fn integration_test() {
|
||||
panic!("incompatible crate versions");
|
||||
} else if stderr.contains("failed to run `rustc` to learn about target-specific information") {
|
||||
panic!("couldn't find librustc_driver, consider setting `LD_LIBRARY_PATH`");
|
||||
} else if stderr.contains("toolchain") && stderr.contains("is not installed") {
|
||||
panic!("missing required toolchain");
|
||||
}
|
||||
|
||||
match output.status.code() {
|
||||
|
@ -1,18 +1,3 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# A script to update the references for all tests. The idea is that
|
||||
# you do a run, which will generate files in the build directory
|
||||
# containing the (normalized) actual output of the compiler. You then
|
||||
# run this script, which will copy those files over. If you find
|
||||
# yourself manually editing a foo.stderr file, you're doing it wrong.
|
||||
#
|
||||
# See all `update-references.sh`, if you just want to update a single test.
|
||||
|
||||
if [[ "$1" == "--help" || "$1" == "-h" ]]; then
|
||||
echo "usage: $0"
|
||||
fi
|
||||
|
||||
BUILD_DIR=$PWD/target/debug/test_build_base
|
||||
MY_DIR=$(dirname "$0")
|
||||
cd "$MY_DIR" || exit
|
||||
find . -name '*.rs' -exec ./update-references.sh "$BUILD_DIR" {} +
|
||||
echo "Please use 'cargo dev bless' instead."
|
||||
|
@ -1,46 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# A script to update the references for particular tests. The idea is
|
||||
# that you do a run, which will generate files in the build directory
|
||||
# containing the (normalized) actual output of the compiler. This
|
||||
# script will then copy that output and replace the "expected output"
|
||||
# files. You can then commit the changes.
|
||||
#
|
||||
# If you find yourself manually editing a foo.stderr file, you're
|
||||
# doing it wrong.
|
||||
|
||||
if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then
|
||||
echo "usage: $0 <build-directory> <relative-path-to-rs-files>"
|
||||
echo ""
|
||||
echo "For example:"
|
||||
echo " $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs"
|
||||
fi
|
||||
|
||||
MYDIR=$(dirname "$0")
|
||||
|
||||
BUILD_DIR="$1"
|
||||
shift
|
||||
|
||||
while [[ "$1" != "" ]]; do
|
||||
STDERR_NAME="${1/%.rs/.stderr}"
|
||||
STDOUT_NAME="${1/%.rs/.stdout}"
|
||||
shift
|
||||
if [[ -f "$BUILD_DIR"/"$STDOUT_NAME" ]] && \
|
||||
! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then
|
||||
echo updating "$MYDIR"/"$STDOUT_NAME"
|
||||
cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"
|
||||
if [[ ! -s "$MYDIR"/"$STDOUT_NAME" ]]; then
|
||||
echo removing "$MYDIR"/"$STDOUT_NAME"
|
||||
rm "$MYDIR"/"$STDOUT_NAME"
|
||||
fi
|
||||
fi
|
||||
if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \
|
||||
! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then
|
||||
echo updating "$MYDIR"/"$STDERR_NAME"
|
||||
cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"
|
||||
if [[ ! -s "$MYDIR"/"$STDERR_NAME" ]]; then
|
||||
echo removing "$MYDIR"/"$STDERR_NAME"
|
||||
rm "$MYDIR"/"$STDERR_NAME"
|
||||
fi
|
||||
fi
|
||||
done
|
@ -0,0 +1,33 @@
|
||||
// run-rustfix
|
||||
#![deny(clippy::internal)]
|
||||
#![feature(rustc_private)]
|
||||
|
||||
extern crate rustc_span;
|
||||
|
||||
use rustc_span::symbol::Symbol;
|
||||
|
||||
macro_rules! sym {
|
||||
($tt:tt) => {
|
||||
rustc_span::symbol::Symbol::intern(stringify!($tt))
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Direct use of Symbol::intern
|
||||
let _ = rustc_span::symbol::sym::f32;
|
||||
|
||||
// Using a sym macro
|
||||
let _ = rustc_span::symbol::sym::f32;
|
||||
|
||||
// Correct suggestion when symbol isn't stringified constant name
|
||||
let _ = rustc_span::symbol::sym::proc_dash_macro;
|
||||
|
||||
// Interning a symbol that is not defined
|
||||
let _ = Symbol::intern("xyz123");
|
||||
let _ = sym!(xyz123);
|
||||
|
||||
// Using a different `intern` function
|
||||
let _ = intern("f32");
|
||||
}
|
||||
|
||||
fn intern(_: &str) {}
|
@ -0,0 +1,33 @@
|
||||
// run-rustfix
|
||||
#![deny(clippy::internal)]
|
||||
#![feature(rustc_private)]
|
||||
|
||||
extern crate rustc_span;
|
||||
|
||||
use rustc_span::symbol::Symbol;
|
||||
|
||||
macro_rules! sym {
|
||||
($tt:tt) => {
|
||||
rustc_span::symbol::Symbol::intern(stringify!($tt))
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Direct use of Symbol::intern
|
||||
let _ = Symbol::intern("f32");
|
||||
|
||||
// Using a sym macro
|
||||
let _ = sym!(f32);
|
||||
|
||||
// Correct suggestion when symbol isn't stringified constant name
|
||||
let _ = Symbol::intern("proc-macro");
|
||||
|
||||
// Interning a symbol that is not defined
|
||||
let _ = Symbol::intern("xyz123");
|
||||
let _ = sym!(xyz123);
|
||||
|
||||
// Using a different `intern` function
|
||||
let _ = intern("f32");
|
||||
}
|
||||
|
||||
fn intern(_: &str) {}
|
@ -0,0 +1,27 @@
|
||||
error: interning a defined symbol
|
||||
--> $DIR/interning_defined_symbol.rs:17:13
|
||||
|
|
||||
LL | let _ = Symbol::intern("f32");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::sym::f32`
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/interning_defined_symbol.rs:2:9
|
||||
|
|
||||
LL | #![deny(clippy::internal)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
= note: `#[deny(clippy::interning_defined_symbol)]` implied by `#[deny(clippy::internal)]`
|
||||
|
||||
error: interning a defined symbol
|
||||
--> $DIR/interning_defined_symbol.rs:20:13
|
||||
|
|
||||
LL | let _ = sym!(f32);
|
||||
| ^^^^^^^^^ help: try: `rustc_span::symbol::sym::f32`
|
||||
|
||||
error: interning a defined symbol
|
||||
--> $DIR/interning_defined_symbol.rs:23:13
|
||||
|
|
||||
LL | let _ = Symbol::intern("proc-macro");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::sym::proc_dash_macro`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
@ -1,18 +1,3 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# A script to update the references for all tests. The idea is that
|
||||
# you do a run, which will generate files in the build directory
|
||||
# containing the (normalized) actual output of the compiler. You then
|
||||
# run this script, which will copy those files over. If you find
|
||||
# yourself manually editing a foo.stderr file, you're doing it wrong.
|
||||
#
|
||||
# See all `update-references.sh`, if you just want to update a single test.
|
||||
|
||||
if [[ "$1" == "--help" || "$1" == "-h" ]]; then
|
||||
echo "usage: $0"
|
||||
fi
|
||||
|
||||
BUILD_DIR=$PWD/target/debug/test_build_base
|
||||
MY_DIR=$(dirname "$0")
|
||||
cd "$MY_DIR" || exit
|
||||
find . -name '*.rs' -exec ./update-references.sh "$BUILD_DIR" {} +
|
||||
echo "Please use 'cargo dev bless' instead."
|
||||
|
@ -1,46 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# A script to update the references for particular tests. The idea is
|
||||
# that you do a run, which will generate files in the build directory
|
||||
# containing the (normalized) actual output of the compiler. This
|
||||
# script will then copy that output and replace the "expected output"
|
||||
# files. You can then commit the changes.
|
||||
#
|
||||
# If you find yourself manually editing a foo.stderr file, you're
|
||||
# doing it wrong.
|
||||
|
||||
if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then
|
||||
echo "usage: $0 <build-directory> <relative-path-to-rs-files>"
|
||||
echo ""
|
||||
echo "For example:"
|
||||
echo " $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs"
|
||||
fi
|
||||
|
||||
MYDIR=$(dirname "$0")
|
||||
|
||||
BUILD_DIR="$1"
|
||||
shift
|
||||
|
||||
while [[ "$1" != "" ]]; do
|
||||
STDERR_NAME="${1/%.rs/.stderr}"
|
||||
STDOUT_NAME="${1/%.rs/.stdout}"
|
||||
shift
|
||||
if [[ -f "$BUILD_DIR"/"$STDOUT_NAME" ]] && \
|
||||
! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then
|
||||
echo updating "$MYDIR"/"$STDOUT_NAME"
|
||||
cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"
|
||||
if [[ ! -s "$MYDIR"/"$STDOUT_NAME" ]]; then
|
||||
echo removing "$MYDIR"/"$STDOUT_NAME"
|
||||
rm "$MYDIR"/"$STDOUT_NAME"
|
||||
fi
|
||||
fi
|
||||
if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \
|
||||
! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then
|
||||
echo updating "$MYDIR"/"$STDERR_NAME"
|
||||
cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"
|
||||
if [[ ! -s "$MYDIR"/"$STDERR_NAME" ]]; then
|
||||
echo removing "$MYDIR"/"$STDERR_NAME"
|
||||
rm "$MYDIR"/"$STDERR_NAME"
|
||||
fi
|
||||
fi
|
||||
done
|
@ -1,4 +1,4 @@
|
||||
error: using `clone` on a `Copy` type
|
||||
error: using `clone` on type `i32` which implements the `Copy` trait
|
||||
--> $DIR/clone_on_copy.rs:22:5
|
||||
|
|
||||
LL | 42.clone();
|
||||
@ -6,25 +6,25 @@ LL | 42.clone();
|
||||
|
|
||||
= note: `-D clippy::clone-on-copy` implied by `-D warnings`
|
||||
|
||||
error: using `clone` on a `Copy` type
|
||||
error: using `clone` on type `i32` which implements the `Copy` trait
|
||||
--> $DIR/clone_on_copy.rs:26:5
|
||||
|
|
||||
LL | (&42).clone();
|
||||
| ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)`
|
||||
|
||||
error: using `clone` on a `Copy` type
|
||||
error: using `clone` on type `i32` which implements the `Copy` trait
|
||||
--> $DIR/clone_on_copy.rs:29:5
|
||||
|
|
||||
LL | rc.borrow().clone();
|
||||
| ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()`
|
||||
|
||||
error: using `clone` on a `Copy` type
|
||||
error: using `clone` on type `char` which implements the `Copy` trait
|
||||
--> $DIR/clone_on_copy.rs:35:14
|
||||
|
|
||||
LL | is_ascii('z'.clone());
|
||||
| ^^^^^^^^^^^ help: try removing the `clone` call: `'z'`
|
||||
|
||||
error: using `clone` on a `Copy` type
|
||||
error: using `clone` on type `i32` which implements the `Copy` trait
|
||||
--> $DIR/clone_on_copy.rs:39:14
|
||||
|
|
||||
LL | vec.push(42.clone());
|
||||
|
49
src/tools/clippy/tests/ui/eprint_with_newline.rs
Normal file
49
src/tools/clippy/tests/ui/eprint_with_newline.rs
Normal file
@ -0,0 +1,49 @@
|
||||
#![allow(clippy::print_literal)]
|
||||
#![warn(clippy::print_with_newline)]
|
||||
|
||||
fn main() {
|
||||
eprint!("Hello\n");
|
||||
eprint!("Hello {}\n", "world");
|
||||
eprint!("Hello {} {}\n", "world", "#2");
|
||||
eprint!("{}\n", 1265);
|
||||
eprint!("\n");
|
||||
|
||||
// these are all fine
|
||||
eprint!("");
|
||||
eprint!("Hello");
|
||||
eprintln!("Hello");
|
||||
eprintln!("Hello\n");
|
||||
eprintln!("Hello {}\n", "world");
|
||||
eprint!("Issue\n{}", 1265);
|
||||
eprint!("{}", 1265);
|
||||
eprint!("\n{}", 1275);
|
||||
eprint!("\n\n");
|
||||
eprint!("like eof\n\n");
|
||||
eprint!("Hello {} {}\n\n", "world", "#2");
|
||||
eprintln!("\ndon't\nwarn\nfor\nmultiple\nnewlines\n"); // #3126
|
||||
eprintln!("\nbla\n\n"); // #3126
|
||||
|
||||
// Escaping
|
||||
eprint!("\\n"); // #3514
|
||||
eprint!("\\\n"); // should fail
|
||||
eprint!("\\\\n");
|
||||
|
||||
// Raw strings
|
||||
eprint!(r"\n"); // #3778
|
||||
|
||||
// Literal newlines should also fail
|
||||
eprint!(
|
||||
"
|
||||
"
|
||||
);
|
||||
eprint!(
|
||||
r"
|
||||
"
|
||||
);
|
||||
|
||||
// Don't warn on CRLF (#4208)
|
||||
eprint!("\r\n");
|
||||
eprint!("foo\r\n");
|
||||
eprint!("\\r\n"); //~ ERROR
|
||||
eprint!("foo\rbar\n") // ~ ERROR
|
||||
}
|
121
src/tools/clippy/tests/ui/eprint_with_newline.stderr
Normal file
121
src/tools/clippy/tests/ui/eprint_with_newline.stderr
Normal file
@ -0,0 +1,121 @@
|
||||
error: using `eprint!()` with a format string that ends in a single newline
|
||||
--> $DIR/eprint_with_newline.rs:5:5
|
||||
|
|
||||
LL | eprint!("Hello/n");
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::print-with-newline` implied by `-D warnings`
|
||||
help: use `eprintln!` instead
|
||||
|
|
||||
LL | eprintln!("Hello");
|
||||
| ^^^^^^^^ --
|
||||
|
||||
error: using `eprint!()` with a format string that ends in a single newline
|
||||
--> $DIR/eprint_with_newline.rs:6:5
|
||||
|
|
||||
LL | eprint!("Hello {}/n", "world");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: use `eprintln!` instead
|
||||
|
|
||||
LL | eprintln!("Hello {}", "world");
|
||||
| ^^^^^^^^ --
|
||||
|
||||
error: using `eprint!()` with a format string that ends in a single newline
|
||||
--> $DIR/eprint_with_newline.rs:7:5
|
||||
|
|
||||
LL | eprint!("Hello {} {}/n", "world", "#2");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: use `eprintln!` instead
|
||||
|
|
||||
LL | eprintln!("Hello {} {}", "world", "#2");
|
||||
| ^^^^^^^^ --
|
||||
|
||||
error: using `eprint!()` with a format string that ends in a single newline
|
||||
--> $DIR/eprint_with_newline.rs:8:5
|
||||
|
|
||||
LL | eprint!("{}/n", 1265);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: use `eprintln!` instead
|
||||
|
|
||||
LL | eprintln!("{}", 1265);
|
||||
| ^^^^^^^^ --
|
||||
|
||||
error: using `eprint!()` with a format string that ends in a single newline
|
||||
--> $DIR/eprint_with_newline.rs:9:5
|
||||
|
|
||||
LL | eprint!("/n");
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
help: use `eprintln!` instead
|
||||
|
|
||||
LL | eprintln!();
|
||||
| ^^^^^^^^ --
|
||||
|
||||
error: using `eprint!()` with a format string that ends in a single newline
|
||||
--> $DIR/eprint_with_newline.rs:28:5
|
||||
|
|
||||
LL | eprint!("//n"); // should fail
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: use `eprintln!` instead
|
||||
|
|
||||
LL | eprintln!("/"); // should fail
|
||||
| ^^^^^^^^ --
|
||||
|
||||
error: using `eprint!()` with a format string that ends in a single newline
|
||||
--> $DIR/eprint_with_newline.rs:35:5
|
||||
|
|
||||
LL | / eprint!(
|
||||
LL | | "
|
||||
LL | | "
|
||||
LL | | );
|
||||
| |_____^
|
||||
|
|
||||
help: use `eprintln!` instead
|
||||
|
|
||||
LL | eprintln!(
|
||||
LL | ""
|
||||
|
|
||||
|
||||
error: using `eprint!()` with a format string that ends in a single newline
|
||||
--> $DIR/eprint_with_newline.rs:39:5
|
||||
|
|
||||
LL | / eprint!(
|
||||
LL | | r"
|
||||
LL | | "
|
||||
LL | | );
|
||||
| |_____^
|
||||
|
|
||||
help: use `eprintln!` instead
|
||||
|
|
||||
LL | eprintln!(
|
||||
LL | r""
|
||||
|
|
||||
|
||||
error: using `eprint!()` with a format string that ends in a single newline
|
||||
--> $DIR/eprint_with_newline.rs:47:5
|
||||
|
|
||||
LL | eprint!("/r/n"); //~ ERROR
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: use `eprintln!` instead
|
||||
|
|
||||
LL | eprintln!("/r"); //~ ERROR
|
||||
| ^^^^^^^^ --
|
||||
|
||||
error: using `eprint!()` with a format string that ends in a single newline
|
||||
--> $DIR/eprint_with_newline.rs:48:5
|
||||
|
|
||||
LL | eprint!("foo/rbar/n") // ~ ERROR
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: use `eprintln!` instead
|
||||
|
|
||||
LL | eprintln!("foo/rbar") // ~ ERROR
|
||||
| ^^^^^^^^ --
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
|
@ -12,7 +12,7 @@ error: redundant closure found
|
||||
LL | meta(|a| foo(a));
|
||||
| ^^^^^^^^^^ help: remove closure as shown: `foo`
|
||||
|
||||
error: this expression borrows a reference that is immediately dereferenced by the compiler
|
||||
error: this expression borrows a reference (`&u8`) that is immediately dereferenced by the compiler
|
||||
--> $DIR/eta.rs:24:21
|
||||
|
|
||||
LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
|
||||
|
@ -10,91 +10,6 @@ fn foo() -> bool {
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn main() {
|
||||
// weird `else` formatting:
|
||||
if foo() {
|
||||
} {
|
||||
}
|
||||
|
||||
if foo() {
|
||||
} if foo() {
|
||||
}
|
||||
|
||||
let _ = { // if as the last expression
|
||||
let _ = 0;
|
||||
|
||||
if foo() {
|
||||
} if foo() {
|
||||
}
|
||||
else {
|
||||
}
|
||||
};
|
||||
|
||||
let _ = { // if in the middle of a block
|
||||
if foo() {
|
||||
} if foo() {
|
||||
}
|
||||
else {
|
||||
}
|
||||
|
||||
let _ = 0;
|
||||
};
|
||||
|
||||
if foo() {
|
||||
} else
|
||||
{
|
||||
}
|
||||
|
||||
if foo() {
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
|
||||
if foo() {
|
||||
} else
|
||||
if foo() { // the span of the above error should continue here
|
||||
}
|
||||
|
||||
if foo() {
|
||||
}
|
||||
else
|
||||
if foo() { // the span of the above error should continue here
|
||||
}
|
||||
|
||||
// those are ok:
|
||||
if foo() {
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
if foo() {
|
||||
} else {
|
||||
}
|
||||
|
||||
if foo() {
|
||||
}
|
||||
else {
|
||||
}
|
||||
|
||||
if foo() {
|
||||
}
|
||||
if foo() {
|
||||
}
|
||||
|
||||
if foo() {
|
||||
} else if foo() {
|
||||
}
|
||||
|
||||
if foo() {
|
||||
}
|
||||
else if foo() {
|
||||
}
|
||||
|
||||
if foo() {
|
||||
}
|
||||
else if
|
||||
foo() {}
|
||||
|
||||
// weird op_eq formatting:
|
||||
let mut a = 42;
|
||||
a =- 35;
|
||||
@ -146,7 +61,7 @@ fn main() {
|
||||
|
||||
// don't lint if the indentation suggests not to
|
||||
let _ = &[
|
||||
1 + 2, 3
|
||||
1 + 2, 3
|
||||
- 4, 5
|
||||
];
|
||||
// lint if it doesn't
|
||||
|
@ -1,80 +1,5 @@
|
||||
error: this looks like an `else {..}` but the `else` is missing
|
||||
--> $DIR/formatting.rs:15:6
|
||||
|
|
||||
LL | } {
|
||||
| ^
|
||||
|
|
||||
= note: `-D clippy::suspicious-else-formatting` implied by `-D warnings`
|
||||
= note: to remove this lint, add the missing `else` or add a new line before the next block
|
||||
|
||||
error: this looks like an `else if` but the `else` is missing
|
||||
--> $DIR/formatting.rs:19:6
|
||||
|
|
||||
LL | } if foo() {
|
||||
| ^
|
||||
|
|
||||
= note: to remove this lint, add the missing `else` or add a new line before the second `if`
|
||||
|
||||
error: this looks like an `else if` but the `else` is missing
|
||||
--> $DIR/formatting.rs:26:10
|
||||
|
|
||||
LL | } if foo() {
|
||||
| ^
|
||||
|
|
||||
= note: to remove this lint, add the missing `else` or add a new line before the second `if`
|
||||
|
||||
error: this looks like an `else if` but the `else` is missing
|
||||
--> $DIR/formatting.rs:34:10
|
||||
|
|
||||
LL | } if foo() {
|
||||
| ^
|
||||
|
|
||||
= note: to remove this lint, add the missing `else` or add a new line before the second `if`
|
||||
|
||||
error: this is an `else {..}` but the formatting might hide it
|
||||
--> $DIR/formatting.rs:43:6
|
||||
|
|
||||
LL | } else
|
||||
| ______^
|
||||
LL | | {
|
||||
| |____^
|
||||
|
|
||||
= note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
|
||||
|
||||
error: this is an `else {..}` but the formatting might hide it
|
||||
--> $DIR/formatting.rs:48:6
|
||||
|
|
||||
LL | }
|
||||
| ______^
|
||||
LL | | else
|
||||
LL | | {
|
||||
| |____^
|
||||
|
|
||||
= note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
|
||||
|
||||
error: this is an `else if` but the formatting might hide it
|
||||
--> $DIR/formatting.rs:54:6
|
||||
|
|
||||
LL | } else
|
||||
| ______^
|
||||
LL | | if foo() { // the span of the above error should continue here
|
||||
| |____^
|
||||
|
|
||||
= note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
|
||||
|
||||
error: this is an `else if` but the formatting might hide it
|
||||
--> $DIR/formatting.rs:59:6
|
||||
|
|
||||
LL | }
|
||||
| ______^
|
||||
LL | | else
|
||||
LL | | if foo() { // the span of the above error should continue here
|
||||
| |____^
|
||||
|
|
||||
= note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
|
||||
|
||||
error: this looks like you are trying to use `.. -= ..`, but you really are doing `.. = (- ..)`
|
||||
--> $DIR/formatting.rs:100:6
|
||||
--> $DIR/formatting.rs:15:6
|
||||
|
|
||||
LL | a =- 35;
|
||||
| ^^^^
|
||||
@ -83,7 +8,7 @@ LL | a =- 35;
|
||||
= note: to remove this lint, use either `-=` or `= -`
|
||||
|
||||
error: this looks like you are trying to use `.. *= ..`, but you really are doing `.. = (* ..)`
|
||||
--> $DIR/formatting.rs:101:6
|
||||
--> $DIR/formatting.rs:16:6
|
||||
|
|
||||
LL | a =* &191;
|
||||
| ^^^^
|
||||
@ -91,7 +16,7 @@ LL | a =* &191;
|
||||
= note: to remove this lint, use either `*=` or `= *`
|
||||
|
||||
error: this looks like you are trying to use `.. != ..`, but you really are doing `.. = (! ..)`
|
||||
--> $DIR/formatting.rs:104:6
|
||||
--> $DIR/formatting.rs:19:6
|
||||
|
|
||||
LL | b =! false;
|
||||
| ^^^^
|
||||
@ -99,7 +24,7 @@ LL | b =! false;
|
||||
= note: to remove this lint, use either `!=` or `= !`
|
||||
|
||||
error: possibly missing a comma here
|
||||
--> $DIR/formatting.rs:113:19
|
||||
--> $DIR/formatting.rs:28:19
|
||||
|
|
||||
LL | -1, -2, -3 // <= no comma here
|
||||
| ^
|
||||
@ -108,7 +33,7 @@ LL | -1, -2, -3 // <= no comma here
|
||||
= note: to remove this lint, add a comma or write the expr in a single line
|
||||
|
||||
error: possibly missing a comma here
|
||||
--> $DIR/formatting.rs:117:19
|
||||
--> $DIR/formatting.rs:32:19
|
||||
|
|
||||
LL | -1, -2, -3 // <= no comma here
|
||||
| ^
|
||||
@ -116,12 +41,12 @@ LL | -1, -2, -3 // <= no comma here
|
||||
= note: to remove this lint, add a comma or write the expr in a single line
|
||||
|
||||
error: possibly missing a comma here
|
||||
--> $DIR/formatting.rs:154:11
|
||||
--> $DIR/formatting.rs:69:11
|
||||
|
|
||||
LL | -1
|
||||
| ^
|
||||
|
|
||||
= note: to remove this lint, add a comma or write the expr in a single line
|
||||
|
||||
error: aborting due to 14 previous errors
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
|
@ -87,4 +87,32 @@ fn main() {
|
||||
unwrapped
|
||||
})
|
||||
.collect::<Vec<u8>>();
|
||||
// Ok
|
||||
let x = 1;
|
||||
match x {
|
||||
#[cfg(disabled_feature)]
|
||||
0 => println!("Disabled branch"),
|
||||
_ => println!("Enabled branch"),
|
||||
}
|
||||
// Lint
|
||||
let x = 1;
|
||||
let y = 1;
|
||||
println!("Single branch");
|
||||
// Ok
|
||||
let x = 1;
|
||||
let y = 1;
|
||||
match match y {
|
||||
0 => 1,
|
||||
_ => 2,
|
||||
} {
|
||||
#[cfg(disabled_feature)]
|
||||
0 => println!("Array index start"),
|
||||
_ => println!("Not an array index start"),
|
||||
}
|
||||
// False negative
|
||||
let x = 1;
|
||||
match x {
|
||||
// =>
|
||||
_ => println!("Not an array index start"),
|
||||
}
|
||||
}
|
||||
|
@ -99,4 +99,37 @@ fn main() {
|
||||
unwrapped => unwrapped,
|
||||
})
|
||||
.collect::<Vec<u8>>();
|
||||
// Ok
|
||||
let x = 1;
|
||||
match x {
|
||||
#[cfg(disabled_feature)]
|
||||
0 => println!("Disabled branch"),
|
||||
_ => println!("Enabled branch"),
|
||||
}
|
||||
// Lint
|
||||
let x = 1;
|
||||
let y = 1;
|
||||
match match y {
|
||||
0 => 1,
|
||||
_ => 2,
|
||||
} {
|
||||
_ => println!("Single branch"),
|
||||
}
|
||||
// Ok
|
||||
let x = 1;
|
||||
let y = 1;
|
||||
match match y {
|
||||
0 => 1,
|
||||
_ => 2,
|
||||
} {
|
||||
#[cfg(disabled_feature)]
|
||||
0 => println!("Array index start"),
|
||||
_ => println!("Not an array index start"),
|
||||
}
|
||||
// False negative
|
||||
let x = 1;
|
||||
match x {
|
||||
// =>
|
||||
_ => println!("Not an array index start"),
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user