"crossbeam-utils 0.8.3",
"curl",
"curl-sys",
- "env_logger 0.8.1",
+ "env_logger 0.9.0",
"filetime",
"flate2",
"fwdansi",
"termcolor",
]
+[[package]]
+name = "env_logger"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
+dependencies = [
+ "atty",
+ "humantime 2.0.1",
+ "log",
+ "regex",
+ "termcolor",
+]
+
[[package]]
name = "error_index_generator"
version = "0.0.0"
eq_sign_span: Span,
assignments: &mut Vec<hir::Stmt<'hir>>,
) -> &'hir hir::Pat<'hir> {
+ self.arena.alloc(self.destructure_assign_mut(lhs, eq_sign_span, assignments))
+ }
+
+ fn destructure_assign_mut(
+ &mut self,
+ lhs: &Expr,
+ eq_sign_span: Span,
+ assignments: &mut Vec<hir::Stmt<'hir>>,
+ ) -> hir::Pat<'hir> {
match &lhs.kind {
// Underscore pattern.
ExprKind::Underscore => {
let (before, after) = pats.split_at(i);
hir::PatKind::Slice(
before,
- Some(self.pat_without_dbm(span, hir::PatKind::Wild)),
+ Some(self.arena.alloc(self.pat_without_dbm(span, hir::PatKind::Wild))),
after,
)
} else {
let tuple_pat = hir::PatKind::Tuple(&[], Some(0));
return self.pat_without_dbm(lhs.span, tuple_pat);
} else {
- return self.destructure_assign(e, eq_sign_span, assignments);
+ return self.destructure_assign_mut(e, eq_sign_span, assignments);
}
}
_ => {}
}
// Treat all other cases as normal lvalue.
let ident = Ident::new(sym::lhs, lhs.span);
- let (pat, binding) = self.pat_ident(lhs.span, ident);
+ let (pat, binding) = self.pat_ident_mut(lhs.span, ident);
let ident = self.expr_ident(lhs.span, ident, binding);
let assign = hir::ExprKind::Assign(self.lower_expr(lhs), ident, eq_sign_span);
let expr = self.expr(lhs.span, assign, ThinVec::new());
ctx: &str,
eq_sign_span: Span,
assignments: &mut Vec<hir::Stmt<'hir>>,
- ) -> (&'hir [&'hir hir::Pat<'hir>], Option<(usize, Span)>) {
+ ) -> (&'hir [hir::Pat<'hir>], Option<(usize, Span)>) {
let mut rest = None;
let elements =
self.arena.alloc_from_iter(elements.iter().enumerate().filter_map(|(i, e)| {
}
None
} else {
- Some(self.destructure_assign(e, eq_sign_span, assignments))
+ Some(self.destructure_assign_mut(e, eq_sign_span, assignments))
}
}));
(elements, rest)
self.pat_ident_binding_mode(span, ident, hir::BindingAnnotation::Unannotated)
}
+ fn pat_ident_mut(&mut self, span: Span, ident: Ident) -> (hir::Pat<'hir>, hir::HirId) {
+ self.pat_ident_binding_mode_mut(span, ident, hir::BindingAnnotation::Unannotated)
+ }
+
fn pat_ident_binding_mode(
&mut self,
span: Span,
ident: Ident,
bm: hir::BindingAnnotation,
) -> (&'hir hir::Pat<'hir>, hir::HirId) {
+ let (pat, hir_id) = self.pat_ident_binding_mode_mut(span, ident, bm);
+ (self.arena.alloc(pat), hir_id)
+ }
+
+ fn pat_ident_binding_mode_mut(
+ &mut self,
+ span: Span,
+ ident: Ident,
+ bm: hir::BindingAnnotation,
+ ) -> (hir::Pat<'hir>, hir::HirId) {
let hir_id = self.next_id();
(
- self.arena.alloc(hir::Pat {
+ hir::Pat {
hir_id,
kind: hir::PatKind::Binding(bm, hir_id, ident.with_span_pos(span), None),
span,
default_binding_modes: true,
- }),
+ },
hir_id,
)
}
})
}
- fn pat_without_dbm(&mut self, span: Span, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
- self.arena.alloc(hir::Pat {
- hir_id: self.next_id(),
- kind,
- span,
- default_binding_modes: false,
- })
+ fn pat_without_dbm(&mut self, span: Span, kind: hir::PatKind<'hir>) -> hir::Pat<'hir> {
+ hir::Pat { hir_id: self.next_id(), kind, span, default_binding_modes: false }
}
fn ty_path(
use rustc_span::{source_map::Spanned, Span};
impl<'a, 'hir> LoweringContext<'a, 'hir> {
- crate fn lower_pat(&mut self, mut pattern: &Pat) -> &'hir hir::Pat<'hir> {
+ crate fn lower_pat(&mut self, pattern: &Pat) -> &'hir hir::Pat<'hir> {
+ self.arena.alloc(self.lower_pat_mut(pattern))
+ }
+
+ crate fn lower_pat_mut(&mut self, mut pattern: &Pat) -> hir::Pat<'hir> {
ensure_sufficient_stack(|| {
// loop here to avoid recursion
let node = loop {
}
PatKind::Or(ref pats) => {
break hir::PatKind::Or(
- self.arena.alloc_from_iter(pats.iter().map(|x| self.lower_pat(x))),
+ self.arena.alloc_from_iter(pats.iter().map(|x| self.lower_pat_mut(x))),
);
}
PatKind::Path(ref qself, ref path) => {
&mut self,
pats: &[P<Pat>],
ctx: &str,
- ) -> (&'hir [&'hir hir::Pat<'hir>], Option<usize>) {
+ ) -> (&'hir [hir::Pat<'hir>], Option<usize>) {
let mut elems = Vec::with_capacity(pats.len());
let mut rest = None;
}
// It was not a sub-tuple pattern so lower it normally.
- elems.push(self.lower_pat(pat));
+ elems.push(self.lower_pat_mut(pat));
}
for (_, pat) in iter {
// ...but there was one again, so error.
self.ban_extra_rest_pat(pat.span, rest.unwrap().1, ctx);
} else {
- elems.push(self.lower_pat(pat));
+ elems.push(self.lower_pat_mut(pat));
}
}
// Record, lower it to `$binding_mode $ident @ _`, and stop here.
PatKind::Ident(ref bm, ident, Some(ref sub)) if sub.is_rest() => {
prev_rest_span = Some(sub.span);
- slice = Some(lower_rest_sub(self, pat, bm, ident, sub));
+ slice = Some(self.arena.alloc(lower_rest_sub(self, pat, bm, ident, sub)));
break;
}
// It was not a subslice pattern so lower it normally.
- _ => before.push(self.lower_pat(pat)),
+ _ => before.push(self.lower_pat_mut(pat)),
}
}
self.ban_extra_rest_pat(rest_span, prev_rest_span.unwrap(), "slice");
} else {
// Lower the pattern normally.
- after.push(self.lower_pat(pat));
+ after.push(self.lower_pat_mut(pat));
}
}
}
fn pat_wild_with_node_id_of(&mut self, p: &Pat) -> &'hir hir::Pat<'hir> {
- self.pat_with_node_id_of(p, hir::PatKind::Wild)
+ self.arena.alloc(self.pat_with_node_id_of(p, hir::PatKind::Wild))
}
/// Construct a `Pat` with the `HirId` of `p.id` lowered.
- fn pat_with_node_id_of(&mut self, p: &Pat, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
- self.arena.alloc(hir::Pat {
+ fn pat_with_node_id_of(&mut self, p: &Pat, kind: hir::PatKind<'hir>) -> hir::Pat<'hir> {
+ hir::Pat {
hir_id: self.lower_node_id(p.id),
kind,
span: p.span,
default_binding_modes: true,
- })
+ }
}
/// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern.
}
}
-impl<K, V> Extend<(K, V)> for VecMap<K, V> {
+impl<K: PartialEq, V> Extend<(K, V)> for VecMap<K, V> {
fn extend<I: IntoIterator<Item = (K, V)>>(&mut self, iter: I) {
- self.0.extend(iter);
+ for (k, v) in iter {
+ self.insert(k, v);
+ }
}
- fn extend_one(&mut self, item: (K, V)) {
- self.0.extend_one(item);
+ fn extend_one(&mut self, (k, v): (K, V)) {
+ self.insert(k, v);
}
fn extend_reserve(&mut self, additional: usize) {
(active, bindings_after_at, "1.41.0", Some(65490), None),
/// Allows `impl const Trait for T` syntax.
- (incomplete, const_trait_impl, "1.42.0", Some(67792), None),
+ (active, const_trait_impl, "1.42.0", Some(67792), None),
/// Allows `T: ?const Trait` syntax in bounds.
(incomplete, const_trait_bound_opt_out, "1.42.0", Some(67794), None),
}
use PatKind::*;
- match &self.kind {
+ match self.kind {
Wild | Lit(_) | Range(..) | Binding(.., None) | Path(_) => true,
Box(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_short_(it),
Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk_short_(it)),
TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().all(|p| p.walk_short_(it)),
Slice(before, slice, after) => {
- before.iter().chain(slice.iter()).chain(after.iter()).all(|p| p.walk_short_(it))
+ before.iter().chain(slice).chain(after.iter()).all(|p| p.walk_short_(it))
}
}
}
}
use PatKind::*;
- match &self.kind {
+ match self.kind {
Wild | Lit(_) | Range(..) | Binding(.., None) | Path(_) => {}
Box(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_(it),
Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk_(it)),
TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().for_each(|p| p.walk_(it)),
Slice(before, slice, after) => {
- before.iter().chain(slice.iter()).chain(after.iter()).for_each(|p| p.walk_(it))
+ before.iter().chain(slice).chain(after.iter()).for_each(|p| p.walk_(it))
}
}
}
/// A tuple struct/variant pattern `Variant(x, y, .., z)`.
/// If the `..` pattern fragment is present, then `Option<usize>` denotes its position.
/// `0 <= position <= subpats.len()`
- TupleStruct(QPath<'hir>, &'hir [&'hir Pat<'hir>], Option<usize>),
+ TupleStruct(QPath<'hir>, &'hir [Pat<'hir>], Option<usize>),
/// An or-pattern `A | B | C`.
/// Invariant: `pats.len() >= 2`.
- Or(&'hir [&'hir Pat<'hir>]),
+ Or(&'hir [Pat<'hir>]),
/// A path pattern for an unit struct/variant or a (maybe-associated) constant.
Path(QPath<'hir>),
/// A tuple pattern (e.g., `(a, b)`).
/// If the `..` pattern fragment is present, then `Option<usize>` denotes its position.
/// `0 <= position <= subpats.len()`
- Tuple(&'hir [&'hir Pat<'hir>], Option<usize>),
+ Tuple(&'hir [Pat<'hir>], Option<usize>),
/// A `box` pattern.
Box(&'hir Pat<'hir>),
/// ```
/// PatKind::Slice([Binding(a), Binding(b)], Some(Wild), [Binding(c), Binding(d)])
/// ```
- Slice(&'hir [&'hir Pat<'hir>], Option<&'hir Pat<'hir>>, &'hir [&'hir Pat<'hir>]),
+ Slice(&'hir [Pat<'hir>], Option<&'hir Pat<'hir>>, &'hir [Pat<'hir>]),
}
#[derive(Copy, Clone, PartialEq, Encodable, Debug, HashStable_Generic)]
(String::from("d"), Level::Deny),
];
- assert_different_hash(&v1, &v2);
- assert_different_hash(&v1, &v3);
- assert_different_hash(&v2, &v3);
+ assert_non_crate_hash_different(&v1, &v2);
+ assert_non_crate_hash_different(&v1, &v3);
+ assert_non_crate_hash_different(&v2, &v3);
}
#[test]
];
// The hash should be order-dependent
- assert_different_hash(&v1, &v2);
+ assert_non_crate_hash_different(&v1, &v2);
+}
+
+#[test]
+fn test_lint_cap_hash_different() {
+ let mut v1 = Options::default();
+ let mut v2 = Options::default();
+ let v3 = Options::default();
+
+ v1.lint_cap = Some(Level::Forbid);
+ v2.lint_cap = Some(Level::Allow);
+
+ assert_non_crate_hash_different(&v1, &v2);
+ assert_non_crate_hash_different(&v1, &v3);
+ assert_non_crate_hash_different(&v2, &v3);
}
#[test]
untracked!(dump_mir_graphviz, true);
untracked!(emit_future_incompat_report, true);
untracked!(emit_stack_sizes, true);
+ untracked!(future_incompat_test, true);
untracked!(hir_stats, true);
untracked!(identify_regions, true);
untracked!(incremental_ignore_spans, true);
use rustc_ast as ast;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_span::{BytePos, NormalizedPos, SourceFile};
+use std::assert::assert_matches;
use smallvec::SmallVec;
use rustc_index::vec::IndexVec;
use rustc_session::lint::{
builtin::{self, FORBIDDEN_LINT_GROUPS},
- FutureIncompatibilityReason, FutureIncompatibleInfo, Level, Lint, LintId,
+ FutureIncompatibilityReason, Level, Lint, LintId,
};
use rustc_session::{DiagnosticMessageId, Session};
use rustc_span::hygiene::MacroKind;
let lint_id = LintId::of(lint);
let future_incompatible = lint.future_incompatible;
- let has_future_breakage = matches!(
- future_incompatible,
- Some(FutureIncompatibleInfo {
- reason: FutureIncompatibilityReason::FutureReleaseErrorReportNow,
- ..
- })
+ let has_future_breakage = future_incompatible.map_or(
+ // Default allow lints trigger too often for testing.
+ sess.opts.debugging_opts.future_incompat_test && lint.default_level != Level::Allow,
+ |incompat| {
+ matches!(incompat.reason, FutureIncompatibilityReason::FutureReleaseErrorReportNow)
+ },
);
let mut err = match (level, span) {
let dl = self.data_layout();
let pack = repr.pack;
if pack.is_some() && repr.align.is_some() {
- bug!("struct cannot be packed and aligned");
+ self.tcx.sess.delay_span_bug(DUMMY_SP, "struct cannot be packed and aligned");
+ return Err(LayoutError::Unknown(ty));
}
let mut align = if pack.is_some() { dl.i8_align } else { dl.aggregate_align };
if def.is_union() {
if def.repr.pack.is_some() && def.repr.align.is_some() {
- bug!("union cannot be packed and aligned");
+ self.tcx.sess.delay_span_bug(
+ tcx.def_span(def.did),
+ "union cannot be packed and aligned",
+ );
+ return Err(LayoutError::Unknown(ty));
}
let mut align =
// `src/test/ui/polymorphization/normalized_sig_types.rs`), and codegen not keeping
// track of a polymorphization `ParamEnv` to allow normalizing later.
let mut sig = match *ty.kind() {
- ty::FnDef(def_id, substs) => tcx
+ ty::FnDef(def_id, substs) if tcx.sess.opts.debugging_opts.polymorphize => tcx
.normalize_erasing_regions(tcx.param_env(def_id), tcx.fn_sig(def_id))
.subst(tcx, substs),
+ ty::FnDef(def_id, substs) => tcx.fn_sig(def_id).subst(tcx, substs),
_ => unreachable!(),
};
//! integer. It is crucial that these operations call `check_align` *before*
//! short-circuiting the empty case!
+use std::assert::assert_matches;
use std::borrow::Cow;
use std::collections::VecDeque;
use std::convert::{TryFrom, TryInto};
fn lower_tuple_subpats(
&mut self,
- pats: &'tcx [&'tcx hir::Pat<'tcx>],
+ pats: &'tcx [hir::Pat<'tcx>],
expected_len: usize,
gap_pos: Option<usize>,
) -> Vec<FieldPat<'tcx>> {
.collect()
}
- fn lower_patterns(&mut self, pats: &'tcx [&'tcx hir::Pat<'tcx>]) -> Vec<Pat<'tcx>> {
+ fn lower_patterns(&mut self, pats: &'tcx [hir::Pat<'tcx>]) -> Vec<Pat<'tcx>> {
pats.iter().map(|p| self.lower_pattern(p)).collect()
}
&mut self,
span: Span,
ty: Ty<'tcx>,
- prefix: &'tcx [&'tcx hir::Pat<'tcx>],
+ prefix: &'tcx [hir::Pat<'tcx>],
slice: &'tcx Option<&'tcx hir::Pat<'tcx>>,
- suffix: &'tcx [&'tcx hir::Pat<'tcx>],
+ suffix: &'tcx [hir::Pat<'tcx>],
) -> PatKind<'tcx> {
let prefix = self.lower_patterns(prefix);
let slice = self.lower_opt_pattern(slice);
err
}
- ResolutionError::SelfInTyParamDefault => {
+ ResolutionError::SelfInGenericParamDefault => {
let mut err = struct_span_err!(
self.session,
span,
E0735,
- "type parameters cannot use `Self` in their defaults"
+ "generic parameters cannot use `Self` in their defaults"
);
- err.span_label(span, "`Self` in type parameter default".to_string());
+ err.span_label(span, "`Self` in generic parameter default".to_string());
err
}
ResolutionError::UnreachableLabel { name, definition_span, suggestion } => {
/// This error is only emitted when using `min_const_generics`.
ParamInNonTrivialAnonConst { name: Symbol, is_type: bool },
/// Error E0735: generic parameters with a default cannot use `Self`
- SelfInTyParamDefault,
+ SelfInGenericParamDefault,
/// Error E0767: use of unreachable label
UnreachableLabel { name: Symbol, definition_span: Span, suggestion: Option<LabelSuggestion> },
}
if let ForwardGenericParamBanRibKind = all_ribs[rib_index].kind {
if record_used {
let res_error = if rib_ident.name == kw::SelfUpper {
- ResolutionError::SelfInTyParamDefault
+ ResolutionError::SelfInGenericParamDefault
} else {
ResolutionError::ForwardDeclaredGenericParam
};
/// can influence whether overflow checks are done or not.
debug_assertions: bool [TRACKED],
debuginfo: DebugInfo [TRACKED],
- lint_opts: Vec<(String, lint::Level)> [TRACKED],
- lint_cap: Option<lint::Level> [TRACKED],
- force_warns: Vec<String> [TRACKED],
+ lint_opts: Vec<(String, lint::Level)> [TRACKED_NO_CRATE_HASH],
+ lint_cap: Option<lint::Level> [TRACKED_NO_CRATE_HASH],
+ force_warns: Vec<String> [TRACKED_NO_CRATE_HASH],
describe_lints: bool [UNTRACKED],
output_types: OutputTypes [TRACKED],
search_paths: Vec<SearchPath> [UNTRACKED],
"set the optimization fuel quota for a crate"),
function_sections: Option<bool> = (None, parse_opt_bool, [TRACKED],
"whether each function should go in its own section"),
+ future_incompat_test: bool = (false, parse_bool, [UNTRACKED],
+ "forces all lints to be future incompatible, used for internal testing (default: no)"),
gcc_ld: Option<LdImpl> = (None, parse_gcc_ld, [TRACKED], "implementation of ld used by cc"),
graphviz_dark_mode: bool = (false, parse_bool, [UNTRACKED],
"use dark-themed colors in graphviz output (default: no)"),
use rustc_middle::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, WithConstness};
use rustc_span::symbol::sym;
+use std::collections::BTreeMap;
+
pub use rustc_middle::traits::Reveal;
pub type PolyProjectionObligation<'tcx> = Obligation<'tcx, ty::PolyProjectionPredicate<'tcx>>;
cause: ObligationCause<'tcx>,
obligations: &'a mut Vec<PredicateObligation<'tcx>>,
depth: usize,
+ universes: Vec<Option<ty::UniverseIndex>>,
}
impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
depth: usize,
obligations: &'a mut Vec<PredicateObligation<'tcx>>,
) -> AssocTypeNormalizer<'a, 'b, 'tcx> {
- AssocTypeNormalizer { selcx, param_env, cause, obligations, depth }
+ AssocTypeNormalizer { selcx, param_env, cause, obligations, depth, universes: vec![] }
}
fn fold<T: TypeFoldable<'tcx>>(&mut self, value: T) -> T {
let value = self.selcx.infcx().resolve_vars_if_possible(value);
+ assert!(
+ !value.has_escaping_bound_vars(),
+ "Normalizing {:?} without wrapping in a `Binder`",
+ value
+ );
+
if !value.has_projections() { value } else { value.fold_with(self) }
}
}
self.selcx.tcx()
}
+ fn fold_binder<T: TypeFoldable<'tcx>>(
+ &mut self,
+ t: ty::Binder<'tcx, T>,
+ ) -> ty::Binder<'tcx, T> {
+ self.universes.push(None);
+ let t = t.super_fold_with(self);
+ self.universes.pop();
+ t
+ }
+
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
if !ty.has_projections() {
return ty;
normalized_ty
}
+ ty::Projection(data) if !data.trait_ref(self.tcx()).has_escaping_bound_vars() => {
+ // Okay, so you thought the previous branch was hacky. Well, to
+ // extend upon this, when the *trait ref* doesn't have escaping
+ // bound vars, but the associated item *does* (can only occur
+ // with GATs), then we might still be able to project the type.
+ // For this, we temporarily replace the bound vars with
+ // placeholders. Note though, that in the case that we still
+ // can't project for whatever reason (e.g. self type isn't
+ // known enough), we *can't* register an obligation and return
+ // an inference variable (since then that obligation would have
+ // bound vars and that's a can of worms). Instead, we just
+ // give up and fall back to pretending like we never tried!
+
+ let infcx = self.selcx.infcx();
+ let (data, mapped_regions, mapped_types, mapped_consts) =
+ BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
+ let normalized_ty = opt_normalize_projection_type(
+ self.selcx,
+ self.param_env,
+ data,
+ self.cause.clone(),
+ self.depth,
+ &mut self.obligations,
+ )
+ .ok()
+ .flatten()
+ .unwrap_or_else(|| ty);
+
+ let normalized_ty = PlaceholderReplacer::replace_placeholders(
+ infcx,
+ mapped_regions,
+ mapped_types,
+ mapped_consts,
+ &self.universes,
+ normalized_ty,
+ );
+ debug!(
+ ?self.depth,
+ ?ty,
+ ?normalized_ty,
+ obligations.len = ?self.obligations.len(),
+ "AssocTypeNormalizer: normalized type"
+ );
+ normalized_ty
+ }
+
_ => ty,
}
}
}
}
+pub struct BoundVarReplacer<'me, 'tcx> {
+ infcx: &'me InferCtxt<'me, 'tcx>,
+ // These three maps track the bound variable that were replaced by placeholders. It might be
+ // nice to remove these since we already have the `kind` in the placeholder; we really just need
+ // the `var` (but we *could* bring that into scope if we were to track them as we pass them).
+ mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
+ mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
+ mapped_consts: BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar>,
+ // The current depth relative to *this* folding, *not* the entire normalization. In other words,
+ // the depth of binders we've passed here.
+ current_index: ty::DebruijnIndex,
+ // The `UniverseIndex` of the binding levels above us. These are optional, since we are lazy:
+ // we don't actually create a universe until we see a bound var we have to replace.
+ universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>,
+}
+
+impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> {
+ /// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that
+ /// use a binding level above `universe_indices.len()`, we fail.
+ pub fn replace_bound_vars<T: TypeFoldable<'tcx>>(
+ infcx: &'me InferCtxt<'me, 'tcx>,
+ universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>,
+ value: T,
+ ) -> (
+ T,
+ BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
+ BTreeMap<ty::PlaceholderType, ty::BoundTy>,
+ BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar>,
+ ) {
+ let mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion> = BTreeMap::new();
+ let mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy> = BTreeMap::new();
+ let mapped_consts: BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar> = BTreeMap::new();
+
+ let mut replacer = BoundVarReplacer {
+ infcx,
+ mapped_regions,
+ mapped_types,
+ mapped_consts,
+ current_index: ty::INNERMOST,
+ universe_indices,
+ };
+
+ let value = value.super_fold_with(&mut replacer);
+
+ (value, replacer.mapped_regions, replacer.mapped_types, replacer.mapped_consts)
+ }
+
+ fn universe_for(&mut self, debruijn: ty::DebruijnIndex) -> ty::UniverseIndex {
+ let infcx = self.infcx;
+ let index =
+ self.universe_indices.len() - debruijn.as_usize() + self.current_index.as_usize() - 1;
+ let universe = self.universe_indices[index].unwrap_or_else(|| {
+ for i in self.universe_indices.iter_mut().take(index + 1) {
+ *i = i.or_else(|| Some(infcx.create_next_universe()))
+ }
+ self.universe_indices[index].unwrap()
+ });
+ universe
+ }
+}
+
+impl TypeFolder<'tcx> for BoundVarReplacer<'_, 'tcx> {
+ fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
+ self.infcx.tcx
+ }
+
+ fn fold_binder<T: TypeFoldable<'tcx>>(
+ &mut self,
+ t: ty::Binder<'tcx, T>,
+ ) -> ty::Binder<'tcx, T> {
+ self.current_index.shift_in(1);
+ let t = t.super_fold_with(self);
+ self.current_index.shift_out(1);
+ t
+ }
+
+ fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+ match *r {
+ ty::ReLateBound(debruijn, _)
+ if debruijn.as_usize() + 1
+ > self.current_index.as_usize() + self.universe_indices.len() =>
+ {
+ bug!("Bound vars outside of `self.universe_indices`");
+ }
+ ty::ReLateBound(debruijn, br) if debruijn >= self.current_index => {
+ let universe = self.universe_for(debruijn);
+ let p = ty::PlaceholderRegion { universe, name: br.kind };
+ self.mapped_regions.insert(p.clone(), br);
+ self.infcx.tcx.mk_region(ty::RePlaceholder(p))
+ }
+ _ => r,
+ }
+ }
+
+ fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+ match *t.kind() {
+ ty::Bound(debruijn, _)
+ if debruijn.as_usize() + 1
+ > self.current_index.as_usize() + self.universe_indices.len() =>
+ {
+ bug!("Bound vars outside of `self.universe_indices`");
+ }
+ ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => {
+ let universe = self.universe_for(debruijn);
+ let p = ty::PlaceholderType { universe, name: bound_ty.var };
+ self.mapped_types.insert(p.clone(), bound_ty);
+ self.infcx.tcx.mk_ty(ty::Placeholder(p))
+ }
+ _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self),
+ _ => t,
+ }
+ }
+
+ fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
+ match *ct {
+ ty::Const { val: ty::ConstKind::Bound(debruijn, _), ty: _ }
+ if debruijn.as_usize() + 1
+ > self.current_index.as_usize() + self.universe_indices.len() =>
+ {
+ bug!("Bound vars outside of `self.universe_indices`");
+ }
+ ty::Const { val: ty::ConstKind::Bound(debruijn, bound_const), ty }
+ if debruijn >= self.current_index =>
+ {
+ let universe = self.universe_for(debruijn);
+ let p = ty::PlaceholderConst {
+ universe,
+ name: ty::BoundConst { var: bound_const, ty },
+ };
+ self.mapped_consts.insert(p.clone(), bound_const);
+ self.infcx.tcx.mk_const(ty::Const { val: ty::ConstKind::Placeholder(p), ty })
+ }
+ _ if ct.has_vars_bound_at_or_above(self.current_index) => ct.super_fold_with(self),
+ _ => ct,
+ }
+ }
+}
+
+// The inverse of `BoundVarReplacer`: replaces placeholders with the bound vars from which they came.
+pub struct PlaceholderReplacer<'me, 'tcx> {
+ infcx: &'me InferCtxt<'me, 'tcx>,
+ mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
+ mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
+ mapped_consts: BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar>,
+ universe_indices: &'me Vec<Option<ty::UniverseIndex>>,
+ current_index: ty::DebruijnIndex,
+}
+
+impl<'me, 'tcx> PlaceholderReplacer<'me, 'tcx> {
+ pub fn replace_placeholders<T: TypeFoldable<'tcx>>(
+ infcx: &'me InferCtxt<'me, 'tcx>,
+ mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
+ mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
+ mapped_consts: BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar>,
+ universe_indices: &'me Vec<Option<ty::UniverseIndex>>,
+ value: T,
+ ) -> T {
+ let mut replacer = PlaceholderReplacer {
+ infcx,
+ mapped_regions,
+ mapped_types,
+ mapped_consts,
+ universe_indices,
+ current_index: ty::INNERMOST,
+ };
+ value.super_fold_with(&mut replacer)
+ }
+}
+
+impl TypeFolder<'tcx> for PlaceholderReplacer<'_, 'tcx> {
+ fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
+ self.infcx.tcx
+ }
+
+ fn fold_binder<T: TypeFoldable<'tcx>>(
+ &mut self,
+ t: ty::Binder<'tcx, T>,
+ ) -> ty::Binder<'tcx, T> {
+ if !t.has_placeholders() && !t.has_infer_regions() {
+ return t;
+ }
+ self.current_index.shift_in(1);
+ let t = t.super_fold_with(self);
+ self.current_index.shift_out(1);
+ t
+ }
+
+ fn fold_region(&mut self, r0: ty::Region<'tcx>) -> ty::Region<'tcx> {
+ let r1 = match r0 {
+ ty::ReVar(_) => self
+ .infcx
+ .inner
+ .borrow_mut()
+ .unwrap_region_constraints()
+ .opportunistic_resolve_region(self.infcx.tcx, r0),
+ _ => r0,
+ };
+
+ let r2 = match *r1 {
+ ty::RePlaceholder(p) => {
+ let replace_var = self.mapped_regions.get(&p);
+ match replace_var {
+ Some(replace_var) => {
+ let index = self
+ .universe_indices
+ .iter()
+ .position(|u| matches!(u, Some(pu) if *pu == p.universe))
+ .unwrap_or_else(|| bug!("Unexpected placeholder universe."));
+ let db = ty::DebruijnIndex::from_usize(
+ self.universe_indices.len() - index + self.current_index.as_usize() - 1,
+ );
+ self.tcx().mk_region(ty::ReLateBound(db, *replace_var))
+ }
+ None => r1,
+ }
+ }
+ _ => r1,
+ };
+
+ debug!(?r0, ?r1, ?r2, "fold_region");
+
+ r2
+ }
+
+ fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+ match *ty.kind() {
+ ty::Placeholder(p) => {
+ let replace_var = self.mapped_types.get(&p);
+ match replace_var {
+ Some(replace_var) => {
+ let index = self
+ .universe_indices
+ .iter()
+ .position(|u| matches!(u, Some(pu) if *pu == p.universe))
+ .unwrap_or_else(|| bug!("Unexpected placeholder universe."));
+ let db = ty::DebruijnIndex::from_usize(
+ self.universe_indices.len() - index + self.current_index.as_usize() - 1,
+ );
+ self.tcx().mk_ty(ty::Bound(db, *replace_var))
+ }
+ None => ty,
+ }
+ }
+
+ _ if ty.has_placeholders() || ty.has_infer_regions() => ty.super_fold_with(self),
+ _ => ty,
+ }
+ }
+
+ fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
+ if let ty::Const { val: ty::ConstKind::Placeholder(p), ty } = *ct {
+ let replace_var = self.mapped_consts.get(&p);
+ match replace_var {
+ Some(replace_var) => {
+ let index = self
+ .universe_indices
+ .iter()
+ .position(|u| matches!(u, Some(pu) if *pu == p.universe))
+ .unwrap_or_else(|| bug!("Unexpected placeholder universe."));
+ let db = ty::DebruijnIndex::from_usize(
+ self.universe_indices.len() - index + self.current_index.as_usize() - 1,
+ );
+ self.tcx()
+ .mk_const(ty::Const { val: ty::ConstKind::Bound(db, *replace_var), ty })
+ }
+ None => ct,
+ }
+ } else {
+ ct.super_fold_with(self)
+ }
+ }
+}
+
/// The guts of `normalize`: normalize a specific projection like `<T
/// as Trait>::Item`. The result is always a type (and possibly
/// additional obligations). If ambiguity arises, which implies that
error: false,
cache: SsoHashMap::new(),
anon_depth: 0,
+ universes: vec![],
};
let result = value.fold_with(&mut normalizer);
cache: SsoHashMap<Ty<'tcx>, Ty<'tcx>>,
error: bool,
anon_depth: usize,
+ universes: Vec<Option<ty::UniverseIndex>>,
}
impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
self.infcx.tcx
}
+ fn fold_binder<T: TypeFoldable<'tcx>>(
+ &mut self,
+ t: ty::Binder<'tcx, T>,
+ ) -> ty::Binder<'tcx, T> {
+ self.universes.push(None);
+ let t = t.super_fold_with(self);
+ self.universes.pop();
+ t
+ }
+
#[instrument(level = "debug", skip(self))]
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
if !ty.has_projections() {
}
}
}
+ ty::Projection(data) if !data.trait_ref(self.infcx.tcx).has_escaping_bound_vars() => {
+ // See note in `rustc_trait_selection::traits::project`
+
+ // One other point mentioning: In `traits::project`, if a
+ // projection can't be normalized, we return an inference variable
+ // and register an obligation to later resolve that. Here, the query
+ // will just return ambiguity. In both cases, the effect is the same: we only want
+ // to return `ty` because there are bound vars that we aren't yet handling in a more
+ // complete way.
+
+ // `BoundVarReplacer` can't handle escaping bound vars. Ideally, we want this before even calling
+ // `QueryNormalizer`, but some const-generics tests pass escaping bound vars.
+ // Also, use `ty` so we get that sweet `outer_exclusive_binder` optimization
+ assert!(!ty.has_vars_bound_at_or_above(ty::DebruijnIndex::from_usize(
+ self.universes.len()
+ )));
+
+ let tcx = self.infcx.tcx;
+ let infcx = self.infcx;
+ let (data, mapped_regions, mapped_types, mapped_consts) =
+ crate::traits::project::BoundVarReplacer::replace_bound_vars(
+ infcx,
+ &mut self.universes,
+ data,
+ );
+ let data = data.super_fold_with(self);
+
+ let mut orig_values = OriginalQueryValues::default();
+ // HACK(matthewjasper) `'static` is special-cased in selection,
+ // so we cannot canonicalize it.
+ let c_data = self
+ .infcx
+ .canonicalize_hr_query_hack(self.param_env.and(data), &mut orig_values);
+ debug!("QueryNormalizer: c_data = {:#?}", c_data);
+ debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
+ let normalized_ty = match tcx.normalize_projection_ty(c_data) {
+ Ok(result) => {
+ // We don't expect ambiguity.
+ if result.is_ambiguous() {
+ self.error = true;
+ return ty;
+ }
+ match self.infcx.instantiate_query_response_and_region_obligations(
+ self.cause,
+ self.param_env,
+ &orig_values,
+ result,
+ ) {
+ Ok(InferOk { value: result, obligations }) => {
+ debug!("QueryNormalizer: result = {:#?}", result);
+ debug!("QueryNormalizer: obligations = {:#?}", obligations);
+ self.obligations.extend(obligations);
+ result.normalized_ty
+ }
+ Err(_) => {
+ self.error = true;
+ ty
+ }
+ }
+ }
+ Err(NoSolution) => {
+ self.error = true;
+ ty
+ }
+ };
+ crate::traits::project::PlaceholderReplacer::replace_placeholders(
+ infcx,
+ mapped_regions,
+ mapped_types,
+ mapped_consts,
+ &self.universes,
+ normalized_ty,
+ )
+ }
_ => ty,
})();
use rustc_hir::{is_range_literal, Node};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::adjustment::AllowTwoPhase;
+use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, AssocItem, Ty, TypeAndMut};
use rustc_span::symbol::sym;
use rustc_span::Span;
let sole_field = &variant.fields[0];
let sole_field_ty = sole_field.ty(self.tcx, substs);
if self.can_coerce(expr_ty, sole_field_ty) {
- let variant_path = self.tcx.def_path_str(variant.def_id);
+ let variant_path =
+ with_no_trimmed_paths(|| self.tcx.def_path_str(variant.def_id));
// FIXME #56861: DRYer prelude filtering
if let Some(path) = variant_path.strip_prefix("std::prelude::") {
if let Some((_, path)) = path.split_once("::") {
&self,
pat: &'tcx Pat<'tcx>,
qpath: &'tcx hir::QPath<'tcx>,
- subpats: &'tcx [&'tcx Pat<'tcx>],
+ subpats: &'tcx [Pat<'tcx>],
ddpos: Option<usize>,
expected: Ty<'tcx>,
def_bm: BindingMode,
pat_span: Span,
res: Res,
qpath: &hir::QPath<'_>,
- subpats: &'tcx [&'tcx Pat<'tcx>],
+ subpats: &'tcx [Pat<'tcx>],
fields: &'tcx [ty::FieldDef],
expected: Ty<'tcx>,
had_err: bool,
fn check_pat_tuple(
&self,
span: Span,
- elements: &'tcx [&'tcx Pat<'tcx>],
+ elements: &'tcx [Pat<'tcx>],
ddpos: Option<usize>,
expected: Ty<'tcx>,
def_bm: BindingMode,
fn check_pat_slice(
&self,
span: Span,
- before: &'tcx [&'tcx Pat<'tcx>],
+ before: &'tcx [Pat<'tcx>],
slice: Option<&'tcx Pat<'tcx>>,
- after: &'tcx [&'tcx Pat<'tcx>],
+ after: &'tcx [Pat<'tcx>],
expected: Ty<'tcx>,
def_bm: BindingMode,
ti: TopInfo<'tcx>,
&mut self,
place_with_id: &PlaceWithHirId<'tcx>,
diag_expr_id: hir::HirId,
- mode: euv::ConsumeMode,
) {
debug!(
- "adjust_upvar_borrow_kind_for_consume(place_with_id={:?}, diag_expr_id={:?}, mode={:?})",
- place_with_id, diag_expr_id, mode
+ "adjust_upvar_borrow_kind_for_consume(place_with_id={:?}, diag_expr_id={:?})",
+ place_with_id, diag_expr_id
);
-
- // Copy type being used as ByValue are equivalent to ImmBorrow and don't require any
- // escalation.
- match mode {
- euv::ConsumeMode::Copy => return,
- euv::ConsumeMode::Move => {}
- };
-
let tcx = self.fcx.tcx;
let upvar_id = if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base {
upvar_id
}
}
- fn consume(
- &mut self,
- place_with_id: &PlaceWithHirId<'tcx>,
- diag_expr_id: hir::HirId,
- mode: euv::ConsumeMode,
- ) {
- debug!(
- "consume(place_with_id={:?}, diag_expr_id={:?}, mode={:?})",
- place_with_id, diag_expr_id, mode
- );
+ fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) {
+ debug!("consume(place_with_id={:?}, diag_expr_id={:?})", place_with_id, diag_expr_id);
if !self.capture_information.contains_key(&place_with_id.place) {
self.init_capture_info_for_place(&place_with_id, diag_expr_id);
}
- self.adjust_upvar_borrow_kind_for_consume(&place_with_id, diag_expr_id, mode);
+ self.adjust_upvar_borrow_kind_for_consume(&place_with_id, diag_expr_id);
}
fn borrow(
}
ty::AssocKind::Fn => {
let sig = fcx.tcx.fn_sig(item.def_id);
- let sig = fcx.normalize_associated_types_in(span, sig);
let hir_sig = sig_if_method.expect("bad signature for method");
check_fn_or_method(
fcx,
for_id(tcx, item_id, span).with_fcx(|fcx| {
let def_id = tcx.hir().local_def_id(item_id);
let sig = tcx.fn_sig(def_id);
- let sig = fcx.normalize_associated_types_in(span, sig);
let mut implied_bounds = vec![];
check_fn_or_method(fcx, ident.span, sig, decl, def_id.to_def_id(), &mut implied_bounds);
implied_bounds
def_id: DefId,
implied_bounds: &mut Vec<Ty<'tcx>>,
) {
- let sig = fcx.normalize_associated_types_in(span, sig);
let sig = fcx.tcx.liberate_late_bound_regions(def_id, sig);
+ let sig = fcx.normalize_associated_types_in(span, sig);
for (&input_ty, ty) in iter::zip(sig.inputs(), hir_decl.inputs) {
fcx.register_wf_obligation(input_ty.into(), ty.span, ObligationCauseCode::MiscObligation);
let span = fn_sig.decl.inputs[0].span;
let sig = fcx.tcx.fn_sig(method.def_id);
- let sig = fcx.normalize_associated_types_in(span, sig);
let sig = fcx.tcx.liberate_late_bound_regions(method.def_id, sig);
+ let sig = fcx.normalize_associated_types_in(span, sig);
debug!("check_method_receiver: sig={:?}", sig);
}
}
+#[instrument(skip(tcx), level = "debug")]
fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> {
use rustc_hir::{Expr, ImplItem, Item, TraitItem};
- debug!("find_opaque_ty_constraints({:?})", def_id);
-
struct ConstraintLocator<'tcx> {
tcx: TyCtxt<'tcx>,
def_id: DefId,
}
impl ConstraintLocator<'_> {
+ #[instrument(skip(self), level = "debug")]
fn check(&mut self, def_id: LocalDefId) {
// Don't try to check items that cannot possibly constrain the type.
if !self.tcx.has_typeck_results(def_id) {
- debug!(
- "find_opaque_ty_constraints: no constraint for `{:?}` at `{:?}`: no typeck results",
- self.def_id, def_id,
- );
+ debug!("no constraint: no typeck results");
return;
}
// Calling `mir_borrowck` can lead to cycle errors through
.get_by(|(key, _)| key.def_id == self.def_id)
.is_none()
{
- debug!(
- "find_opaque_ty_constraints: no constraint for `{:?}` at `{:?}`",
- self.def_id, def_id,
- );
+ debug!("no constraints in typeck results");
return;
}
// Use borrowck to get the type with unerased regions.
let concrete_opaque_types = &self.tcx.mir_borrowck(def_id).concrete_opaque_types;
- if let Some((opaque_type_key, concrete_type)) =
- concrete_opaque_types.iter().find(|(key, _)| key.def_id == self.def_id)
- {
- debug!(
- "find_opaque_ty_constraints: found constraint for `{:?}` at `{:?}`: {:?}",
- self.def_id, def_id, concrete_type,
- );
+ debug!(?concrete_opaque_types);
+ for (opaque_type_key, concrete_type) in concrete_opaque_types {
+ if opaque_type_key.def_id != self.def_id {
+ // Ignore constraints for other opaque types.
+ continue;
+ }
+
+ debug!(?concrete_type, ?opaque_type_key.substs, "found constraint");
// FIXME(oli-obk): trace the actual span from inference to improve errors.
let span = self.tcx.def_span(def_id);
if let Some((prev_span, prev_ty)) = self.found {
if *concrete_type != prev_ty {
- debug!("find_opaque_ty_constraints: span={:?}", span);
+ debug!(?span);
// Found different concrete types for the opaque type.
let mut err = self.tcx.sess.struct_span_err(
span,
} else {
self.found = Some((span, concrete_type));
}
- } else {
- debug!(
- "find_opaque_ty_constraints: no constraint for `{:?}` at `{:?}`",
- self.def_id, def_id,
- );
}
}
}
//! normal visitor, which just walks the entire body in one shot, the
//! `ExprUseVisitor` determines how expressions are being used.
-pub use self::ConsumeMode::*;
-
// Export these here so that Clippy can use them.
pub use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection};
/// This trait defines the callbacks you can expect to receive when
/// employing the ExprUseVisitor.
pub trait Delegate<'tcx> {
- // The value found at `place` is either copied or moved, depending
+ // The value found at `place` is moved, depending
// on `mode`. Where `diag_expr_id` is the id used for diagnostics for `place`.
//
+ // Use of a `Copy` type in a ByValue context is considered a use
+ // by `ImmBorrow` and `borrow` is called instead. This is because
+ // a shared borrow is the "minimum access" that would be needed
+ // to perform a copy.
+ //
+ //
// The parameter `diag_expr_id` indicates the HIR id that ought to be used for
// diagnostics. Around pattern matching such as `let pat = expr`, the diagnostic
// id will be the id of the expression `expr` but the place itself will have
// the id of the binding in the pattern `pat`.
- fn consume(
- &mut self,
- place_with_id: &PlaceWithHirId<'tcx>,
- diag_expr_id: hir::HirId,
- mode: ConsumeMode,
- );
+ fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId);
// The value found at `place` is being borrowed with kind `bk`.
// `diag_expr_id` is the id used for diagnostics (see `consume` for more details).
}
#[derive(Copy, Clone, PartialEq, Debug)]
-pub enum ConsumeMode {
+enum ConsumeMode {
Copy, // reference to x where x has a type that copies
Move, // reference to x where x has a type that moves
}
}
fn delegate_consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) {
- debug!("delegate_consume(place_with_id={:?})", place_with_id);
-
- let mode = copy_or_move(&self.mc, place_with_id);
- self.delegate.consume(place_with_id, diag_expr_id, mode);
+ delegate_consume(&self.mc, self.delegate, place_with_id, diag_expr_id)
}
fn consume_exprs(&mut self, exprs: &[hir::Expr<'_>]) {
| PatKind::Path(..)
| PatKind::Struct(..)
| PatKind::Tuple(..) => {
- // If the PatKind is a TupleStruct, Struct or Tuple then we want to check
+ // If the PatKind is a TupleStruct, Path, Struct or Tuple then we want to check
// whether the Variant is a MultiVariant or a SingleVariant. We only want
// to borrow discr if it is a MultiVariant.
// If it is a SingleVariant and creates a binding we will handle that when
// this callback gets called again.
- if let ty::Adt(def, _) = place.place.base_ty.kind() {
+
+ // Get the type of the Place after all projections have been applied
+ let place_ty = place.place.ty();
+
+ if let ty::Adt(def, _) = place_ty.kind() {
if def.variants.len() > 1 {
needs_to_be_read = true;
}
delegate.borrow(place, discr_place.hir_id, bk);
}
ty::BindByValue(..) => {
- let mode = copy_or_move(mc, &place);
debug!("walk_pat binding consuming pat");
- delegate.consume(place, discr_place.hir_id, mode);
+ delegate_consume(mc, *delegate, place, discr_place.hir_id);
}
}
}
match capture_info.capture_kind {
ty::UpvarCapture::ByValue(_) => {
- let mode = copy_or_move(&self.mc, &place_with_id);
- self.delegate.consume(&place_with_id, place_with_id.hir_id, mode);
+ self.delegate_consume(&place_with_id, place_with_id.hir_id);
}
ty::UpvarCapture::ByRef(upvar_borrow) => {
self.delegate.borrow(
place_with_id.place.ty(),
mc.tcx().hir().span(place_with_id.hir_id),
) {
- Move
+ ConsumeMode::Move
} else {
- Copy
+ ConsumeMode::Copy
+ }
+}
+
+// - If a place is used in a `ByValue` context then move it if it's not a `Copy` type.
+// - If the place that is a `Copy` type consider it a `ImmBorrow`.
+fn delegate_consume<'a, 'tcx>(
+ mc: &mc::MemCategorizationContext<'a, 'tcx>,
+ delegate: &mut (dyn Delegate<'tcx> + 'a),
+ place_with_id: &PlaceWithHirId<'tcx>,
+ diag_expr_id: hir::HirId,
+) {
+ debug!("delegate_consume(place_with_id={:?})", place_with_id);
+
+ let mode = copy_or_move(&mc, place_with_id);
+
+ match mode {
+ ConsumeMode::Move => delegate.consume(place_with_id, diag_expr_id),
+ ConsumeMode::Copy => {
+ delegate.borrow(place_with_id, diag_expr_id, ty::BorrowKind::ImmBorrow)
+ }
}
}
use core::mem::size_of_val;
use core::mem::{self, align_of_val_raw};
use core::ops::{CoerceUnsized, Deref, DispatchFromDyn, Receiver};
-#[cfg(not(no_global_oom_handling))]
use core::pin::Pin;
use core::ptr::{self, NonNull};
#[cfg(not(no_global_oom_handling))]
unsafe { Pin::new_unchecked(Arc::new(data)) }
}
+ /// Constructs a new `Pin<Arc<T>>`, return an error if allocation fails.
+ #[unstable(feature = "allocator_api", issue = "32838")]
+ #[inline]
+ pub fn try_pin(data: T) -> Result<Pin<Arc<T>>, AllocError> {
+ unsafe { Ok(Pin::new_unchecked(Arc::try_new(data)?)) }
+ }
+
/// Constructs a new `Arc<T>`, returning an error if allocation fails.
///
/// # Examples
/// # Examples
///
/// ```
- /// #![feature(array_map)]
/// let x = [1, 2, 3];
/// let y = x.map(|v| v + 1);
/// assert_eq!(y, [2, 3, 4]);
/// let y = x.map(|v| v.len());
/// assert_eq!(y, [6, 9, 3, 3]);
/// ```
- #[unstable(feature = "array_map", issue = "75243")]
+ #[stable(feature = "array_map", since = "1.55.0")]
pub fn map<F, U>(self, f: F) -> [U; N]
where
F: FnMut(T) -> U,
/// array if its elements are not `Copy`.
///
/// ```
- /// #![feature(array_methods, array_map)]
+ /// #![feature(array_methods)]
///
/// let strings = ["Ferris".to_string(), "♥".to_string(), "Rust".to_string()];
/// let is_ascii = strings.each_ref().map(|s| s.is_ascii());
/// For any `a`, `b`, and `n`:
///
/// * `steps_between(&a, &b) == Some(n)` if and only if `Step::forward_checked(&a, n) == Some(b)`
- /// * `steps_between(&a, &b) == Some(n)` if and only if `Step::backward_checked(&a, n) == Some(a)`
+ /// * `steps_between(&a, &b) == Some(n)` if and only if `Step::backward_checked(&b, n) == Some(a)`
/// * `steps_between(&a, &b) == Some(n)` only if `a <= b`
/// * Corollary: `steps_between(&a, &b) == Some(0)` if and only if `a == b`
/// * Note that `a <= b` does _not_ imply `steps_between(&a, &b) != None`;
#[macro_use]
mod macros;
+// We don't export this through #[macro_export] for now, to avoid breakage.
+// See https://github.com/rust-lang/rust/issues/82913
+#[cfg(not(test))]
+#[unstable(feature = "assert_matches", issue = "82775")]
+/// Unstable module containing the unstable `assert_matches` macro.
+pub mod assert {
+ #[unstable(feature = "assert_matches", issue = "82775")]
+ pub use crate::macros::{assert_matches, debug_assert_matches};
+}
+
#[macro_use]
mod internal_macros;
/// ```
/// #![feature(assert_matches)]
///
+/// use std::assert::assert_matches;
+///
/// let a = 1u32.checked_add(2);
/// let b = 1u32.checked_sub(2);
/// assert_matches!(a, Some(_));
/// let c = Ok("abc".to_string());
/// assert_matches!(c, Ok(x) | Err(x) if x.len() < 100);
/// ```
-#[macro_export]
#[unstable(feature = "assert_matches", issue = "82775")]
#[allow_internal_unstable(core_panic)]
-macro_rules! assert_matches {
+#[rustc_macro_transparency = "semitransparent"]
+pub macro assert_matches {
($left:expr, $( $pattern:pat_param )|+ $( if $guard: expr )? $(,)?) => ({
match $left {
$( $pattern )|+ $( if $guard )? => {}
);
}
}
- });
+ }),
($left:expr, $( $pattern:pat_param )|+ $( if $guard: expr )?, $($arg:tt)+) => ({
match $left {
$( $pattern )|+ $( if $guard )? => {}
);
}
}
- });
+ }),
}
/// Asserts that a boolean expression is `true` at runtime.
/// ```
/// #![feature(assert_matches)]
///
+/// use std::assert::debug_assert_matches;
+///
/// let a = 1u32.checked_add(2);
/// let b = 1u32.checked_sub(2);
/// debug_assert_matches!(a, Some(_));
#[macro_export]
#[unstable(feature = "assert_matches", issue = "82775")]
#[allow_internal_unstable(assert_matches)]
-macro_rules! debug_assert_matches {
- ($($arg:tt)*) => (if $crate::cfg!(debug_assertions) { $crate::assert_matches!($($arg)*); })
+#[rustc_macro_transparency = "semitransparent"]
+pub macro debug_assert_matches($($arg:tt)*) {
+ if $crate::cfg!(debug_assertions) { $crate::assert::assert_matches!($($arg)*); }
}
/// Returns whether the given expression matches any of the given patterns.
-// ignore-tidy-undocumented-unsafe
-
use crate::cmp;
use crate::mem::{self, MaybeUninit};
use crate::ptr;
// the way until about `left + right == 32`, but the worst case performance breaks even
// around 16. 24 was chosen as middle ground. If the size of `T` is larger than 4
// `usize`s, this algorithm also outperforms other algorithms.
+ // SAFETY: callers must ensure `mid - left` is valid for reading and writing.
let x = unsafe { mid.sub(left) };
// beginning of first round
+ // SAFETY: see previous comment.
let mut tmp: T = unsafe { x.read() };
let mut i = right;
// `gcd` can be found before hand by calculating `gcd(left + right, right)`,
// the very end. This is possibly due to the fact that swapping or replacing temporaries
// uses only one memory address in the loop instead of needing to manage two.
loop {
+ // [long-safety-expl]
+ // SAFETY: callers must ensure `[left, left+mid+right)` are all valid for reading and
+ // writing.
+ //
+ // - `i` start with `right` so `mid-left <= x+i = x+right = mid-left+right < mid+right`
+ // - `i <= left+right-1` is always true
+ // - if `i < left`, `right` is added so `i < left+right` and on the next
+ // iteration `left` is removed from `i` so it doesn't go further
+ // - if `i >= left`, `left` is removed immediately and so it doesn't go further.
+ // - overflows cannot happen for `i` since the function's safety contract ask for
+ // `mid+right-1 = x+left+right` to be valid for writing
+ // - underflows cannot happen because `i` must be bigger or equal to `left` for
+ // a substraction of `left` to happen.
+ //
+ // So `x+i` is valid for reading and writing if the caller respected the contract
tmp = unsafe { x.add(i).replace(tmp) };
// instead of incrementing `i` and then checking if it is outside the bounds, we
// check if `i` will go outside the bounds on the next increment. This prevents
i -= left;
if i == 0 {
// end of first round
+ // SAFETY: tmp has been read from a valid source and x is valid for writing
+ // according to the caller.
unsafe { x.write(tmp) };
break;
}
}
// finish the chunk with more rounds
for start in 1..gcd {
+ // SAFETY: `gcd` is at most equal to `right` so all values in `1..gcd` are valid for
+ // reading and writing as per the function's safety contract, see [long-safety-expl]
+ // above
tmp = unsafe { x.add(start).read() };
+ // [safety-expl-addition]
+ //
+ // Here `start < gcd` so `start < right` so `i < right+right`: `right` being the
+ // greatest common divisor of `(left+right, right)` means that `left = right` so
+ // `i < left+right` so `x+i = mid-left+i` is always valid for reading and writing
+ // according to the function's safety contract.
i = start + right;
loop {
+ // SAFETY: see [long-safety-expl] and [safety-expl-addition]
tmp = unsafe { x.add(i).replace(tmp) };
if i >= left {
i -= left;
if i == start {
+ // SAFETY: see [long-safety-expl] and [safety-expl-addition]
unsafe { x.add(start).write(tmp) };
break;
}
// The `[T; 0]` here is to ensure this is appropriately aligned for T
let mut rawarray = MaybeUninit::<(BufType, [T; 0])>::uninit();
let buf = rawarray.as_mut_ptr() as *mut T;
+ // SAFETY: `mid-left <= mid-left+right < mid+right`
let dim = unsafe { mid.sub(left).add(right) };
if left <= right {
+ // SAFETY:
+ //
+ // 1) The `else if` condition about the sizes ensures `[mid-left; left]` will fit in
+ // `buf` without overflow and `buf` was created just above and so cannot be
+ // overlapped with any value of `[mid-left; left]`
+ // 2) [mid-left, mid+right) are all valid for reading and writing and we don't care
+ // about overlaps here.
+ // 3) The `if` condition about `left <= right` ensures writing `left` elements to
+ // `dim = mid-left+right` is valid because:
+ // - `buf` is valid and `left` elements were written in it in 1)
+ // - `dim+left = mid-left+right+left = mid+right` and we write `[dim, dim+left)`
unsafe {
+ // 1)
ptr::copy_nonoverlapping(mid.sub(left), buf, left);
+ // 2)
ptr::copy(mid, mid.sub(left), right);
+ // 3)
ptr::copy_nonoverlapping(buf, dim, left);
}
} else {
+ // SAFETY: same reasoning as above but with `left` and `right` reversed
unsafe {
ptr::copy_nonoverlapping(mid, buf, right);
ptr::copy(mid.sub(left), dim, left);
// of this algorithm would be, and swapping using that last chunk instead of swapping
// adjacent chunks like this algorithm is doing, but this way is still faster.
loop {
+ // SAFETY:
+ // `left >= right` so `[mid-right, mid+right)` is valid for reading and writing
+ // Substracting `right` from `mid` each turn is counterbalanced by the addition and
+ // check after it.
unsafe {
ptr::swap_nonoverlapping(mid.sub(right), mid, right);
mid = mid.sub(right);
} else {
// Algorithm 3, `left < right`
loop {
+ // SAFETY: `[mid-left, mid+left)` is valid for reading and writing because
+ // `left < right` so `mid+left < mid+right`.
+ // Adding `left` to `mid` each turn is counterbalanced by the substraction and check
+ // after it.
unsafe {
ptr::swap_nonoverlapping(mid.sub(left), mid, left);
mid = mid.add(left);
#![feature(alloc_layout_extra)]
#![feature(array_chunks)]
#![feature(array_methods)]
-#![feature(array_map)]
#![feature(array_windows)]
#![feature(bool_to_option)]
#![feature(box_syntax)]
#[stable(feature = "rust1", since = "1.0.0")]
#[allow(deprecated, deprecated_in_future)]
pub use core::{
- assert_eq, assert_matches, assert_ne, debug_assert, debug_assert_eq, debug_assert_matches,
- debug_assert_ne, matches, r#try, todo, unimplemented, unreachable, write, writeln,
+ assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, matches, r#try, todo,
+ unimplemented, unreachable, write, writeln,
};
// Re-export built-in macros defined through libcore.
//! WASI-specific extensions to primitives in the `std::fs` module.
#![deny(unsafe_op_in_unsafe_fn)]
-#![unstable(feature = "wasi_ext", issue = "none")]
+#![unstable(feature = "wasi_ext", issue = "71213")]
use crate::ffi::OsStr;
use crate::fs::{self, File, Metadata, OpenOptions};
//! WASI-specific extensions to general I/O primitives
#![deny(unsafe_op_in_unsafe_fn)]
-#![unstable(feature = "wasi_ext", issue = "none")]
+#![unstable(feature = "wasi_ext", issue = "71213")]
use crate::fs;
use crate::io;
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="str">
- <DisplayString>{data_ptr,[length]s8}</DisplayString>
- <StringView>data_ptr,[length]s8</StringView>
+ <DisplayString>{(char*)data_ptr,[length]s8}</DisplayString>
+ <StringView>(char*)data_ptr,[length]s8</StringView>
<Expand>
<Item Name="[len]" ExcludeView="simple">length</Item>
<Synthetic Name="[chars]">
<Item Name="[len]" ExcludeView="simple">vec.len</Item>
<Item Name="[capacity]" ExcludeView="simple">vec.buf.cap</Item>
<Synthetic Name="[chars]">
+ <DisplayString>{(char*)vec.buf.ptr.pointer,[vec.len]s8}</DisplayString>
<Expand>
<ArrayItems>
<Size>vec.len</Size>
</Synthetic>
</Expand>
</Type>
+
<Type Name="alloc::rc::Rc<*>">
<DisplayString>{ptr.pointer->value}</DisplayString>
<Expand>
<ExpandedItem>ptr.pointer->value</ExpandedItem>
+ <Item Name="[Reference count]">ptr.pointer->strong</Item>
+ <Item Name="[Weak reference count]">ptr.pointer->weak</Item>
</Expand>
</Type>
+ <Type Name="alloc::rc::Weak<*>">
+ <DisplayString>{ptr.pointer->value}</DisplayString>
+ <Expand>
+ <ExpandedItem>ptr.pointer->value</ExpandedItem>
+ <Item Name="[Reference count]">ptr.pointer->strong</Item>
+ <Item Name="[Weak reference count]">ptr.pointer->weak</Item>
+ </Expand>
+ </Type>
+
<Type Name="alloc::sync::Arc<*>">
<DisplayString>{ptr.pointer->data}</DisplayString>
<Expand>
<ExpandedItem>ptr.pointer->data</ExpandedItem>
+ <Item Name="[Reference count]">ptr.pointer->strong</Item>
+ <Item Name="[Weak reference count]">ptr.pointer->weak</Item>
</Expand>
</Type>
<Type Name="alloc::sync::Weak<*>">
<DisplayString>{ptr.pointer->data}</DisplayString>
<Expand>
<ExpandedItem>ptr.pointer->data</ExpandedItem>
+ <Item Name="[Reference count]">ptr.pointer->strong</Item>
+ <Item Name="[Weak reference count]">ptr.pointer->weak</Item>
</Expand>
</Type>
<Type Name="alloc::borrow::Cow<*>">
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
- <Type Name="core::ptr::Unique<*>">
- <DisplayString>{{ Unique {pointer} }}</DisplayString>
+ <Type Name="core::cell::Cell<*>">
+ <DisplayString>{value.value}</DisplayString>
<Expand>
- <Item Name="[ptr]">pointer</Item>
+ <ExpandedItem>value.value</ExpandedItem>
+ </Expand>
+ </Type>
+ <Type Name="core::cell::Ref<*>">
+ <DisplayString>{value}</DisplayString>
+ <Expand>
+ <ExpandedItem>value</ExpandedItem>
+ </Expand>
+ </Type>
+ <Type Name="core::cell::RefMut<*>">
+ <DisplayString>{value}</DisplayString>
+ <Expand>
+ <ExpandedItem>value</ExpandedItem>
+ </Expand>
+ </Type>
+ <Type Name="core::cell::RefCell<*>">
+ <DisplayString>{value.value}</DisplayString>
+ <Expand>
+ <Item Name="[Borrow state]" Condition="borrow.value.value == 0">"Unborrowed",sb</Item>
+ <Item Name="[Borrow state]" Condition="borrow.value.value > 0">"Immutably borrowed",sb</Item>
+ <Item Name="[Borrow state]" Condition="borrow.value.value < 0">"Mutably borrowed",sb</Item>
+ <ExpandedItem>value.value</ExpandedItem>
+ </Expand>
+ </Type>
+ <Type Name="core::cell::UnsafeCell<*>">
+ <DisplayString>{value}</DisplayString>
+ <Expand>
+ <ExpandedItem>value</ExpandedItem>
</Expand>
</Type>
- <Type Name="core::ptr::Shared<*>">
- <DisplayString>{{ Shared {pointer} }}</DisplayString>
+ <Type Name="core::mem::manually_drop::ManuallyDrop<*>">
+ <DisplayString>{value}</DisplayString>
<Expand>
- <Item Name="[ptr]">pointer</Item>
+ <ExpandedItem>value</ExpandedItem>
+ </Expand>
+ </Type>
+
+ <Type Name="core::num::nonzero::NonZeroI8">
+ <DisplayString>{__0}</DisplayString>
+ </Type>
+ <Type Name="core::num::nonzero::NonZeroI16">
+ <DisplayString>{__0}</DisplayString>
+ </Type>
+ <Type Name="core::num::nonzero::NonZeroI32">
+ <DisplayString>{__0}</DisplayString>
+ </Type>
+ <Type Name="core::num::nonzero::NonZeroI64">
+ <DisplayString>{__0}</DisplayString>
+ </Type>
+ <Type Name="core::num::nonzero::NonZeroI128">
+ <DisplayString>{__0}</DisplayString>
+ </Type>
+ <Type Name="core::num::nonzero::NonZeroIsize">
+ <DisplayString>{__0}</DisplayString>
+ </Type>
+ <Type Name="core::num::nonzero::NonZeroU8">
+ <DisplayString>{__0}</DisplayString>
+ </Type>
+ <Type Name="core::num::nonzero::NonZeroU16">
+ <DisplayString>{__0}</DisplayString>
+ </Type>
+ <Type Name="core::num::nonzero::NonZeroU32">
+ <DisplayString>{__0}</DisplayString>
+ </Type>
+ <Type Name="core::num::nonzero::NonZeroU64">
+ <DisplayString>{__0}</DisplayString>
+ </Type>
+ <Type Name="core::num::nonzero::NonZeroU128">
+ <DisplayString>{__0}</DisplayString>
+ </Type>
+ <Type Name="core::num::nonzero::NonZeroUsize">
+ <DisplayString>{__0}</DisplayString>
+ </Type>
+
+ <Type Name="core::num::wrapping::Wrapping<*>">
+ <DisplayString>{__0}</DisplayString>
+ </Type>
+
+ <Type Name="core::ops::range::Range<*>">
+ <DisplayString>({start}..{end})</DisplayString>
+ </Type>
+ <Type Name="core::ops::range::RangeFrom<*>">
+ <DisplayString>({start}..)</DisplayString>
+ </Type>
+ <Type Name="core::ops::range::RangeInclusive<*>">
+ <DisplayString>({start}..={end})</DisplayString>
+ </Type>
+ <Type Name="core::ops::range::RangeTo<*>">
+ <DisplayString>(..{end})</DisplayString>
+ </Type>
+ <Type Name="core::ops::range::RangeToInclusive<*>">
+ <DisplayString>(..={end})</DisplayString>
+ </Type>
+
+ <Type Name="core::pin::Pin<*>">
+ <DisplayString>Pin({(void*)pointer}: {pointer})</DisplayString>
+ <Expand>
+ <ExpandedItem>pointer</ExpandedItem>
</Expand>
</Type>
<Type Name="core::ptr::non_null::NonNull<*>">
- <DisplayString>{(void*) pointer}</DisplayString>
+ <DisplayString>NonNull({(void*) pointer}: {pointer})</DisplayString>
+ <Expand>
+ <ExpandedItem>pointer</ExpandedItem>
+ </Expand>
+ </Type>
+
+ <Type Name="core::ptr::unique::Unique<*>">
+ <DisplayString>Unique({(void*)pointer}: {pointer})</DisplayString>
+ <Expand>
+ <ExpandedItem>pointer</ExpandedItem>
+ </Expand>
+ </Type>
+
+ <Type Name="core::sync::atomic::AtomicBool">
+ <DisplayString>{(bool)v.value}</DisplayString>
+ </Type>
+ <Type Name="core::sync::atomic::AtomicI8">
+ <DisplayString>{v.value}</DisplayString>
+ </Type>
+ <Type Name="core::sync::atomic::AtomicI16">
+ <DisplayString>{v.value}</DisplayString>
+ </Type>
+ <Type Name="core::sync::atomic::AtomicI32">
+ <DisplayString>{v.value}</DisplayString>
+ </Type>
+ <Type Name="core::sync::atomic::AtomicI64">
+ <DisplayString>{v.value}</DisplayString>
+ </Type>
+ <Type Name="core::sync::atomic::AtomicIsize">
+ <DisplayString>{v.value}</DisplayString>
+ </Type>
+ <Type Name="core::sync::atomic::AtomicU8">
+ <DisplayString>{v.value}</DisplayString>
+ </Type>
+ <Type Name="core::sync::atomic::AtomicU16">
+ <DisplayString>{v.value}</DisplayString>
+ </Type>
+ <Type Name="core::sync::atomic::AtomicU32">
+ <DisplayString>{v.value}</DisplayString>
+ </Type>
+ <Type Name="core::sync::atomic::AtomicU64">
+ <DisplayString>{v.value}</DisplayString>
+ </Type>
+ <Type Name="core::sync::atomic::AtomicUsize">
+ <DisplayString>{v.value}</DisplayString>
+ </Type>
+
+ <Type Name="core::time::Duration">
+ <DisplayString>{secs,d}s {nanos,d}ns</DisplayString>
<Expand>
- <Item Name="[value]">*pointer</Item>
+ <Item Name="seconds">secs,d</Item>
+ <Item Name="nanoseconds">nanos,d</Item>
</Expand>
</Type>
-</AutoVisualizer>
\ No newline at end of file
+</AutoVisualizer>
</Type>
<Type Name="std::ffi::c_str::CString">
- <DisplayString>{inner.data_ptr,s}</DisplayString>
+ <DisplayString>{(char*)inner.data_ptr}</DisplayString>
<Expand>
<Synthetic Name="[chars]">
+ <DisplayString>{(char*)inner.data_ptr}</DisplayString>
<Expand>
<ArrayItems>
<Size>inner.length</Size>
</Synthetic>
</Expand>
</Type>
+
+ <Type Name="std::ffi::os_str::OsString">
+ <DisplayString>{(char*)inner.inner.bytes.buf.ptr.pointer,[inner.inner.bytes.len]}</DisplayString>
+ <Expand>
+ <Synthetic Name="[chars]">
+ <DisplayString>{(char*)inner.inner.bytes.buf.ptr.pointer,[inner.inner.bytes.len]}</DisplayString>
+ <Expand>
+ <ArrayItems>
+ <Size>inner.inner.bytes.len</Size>
+ <ValuePointer>(char*)inner.inner.bytes.buf.ptr.pointer</ValuePointer>
+ </ArrayItems>
+ </Expand>
+ </Synthetic>
+ </Expand>
+ </Type>
</AutoVisualizer>
.filter_map(|ItemLink { link: s, link_text, did, ref fragment }| {
match did {
Some(did) => {
- if let Some((mut href, ..)) = href(did.clone(), cx) {
+ if let Ok((mut href, ..)) = href(did.clone(), cx) {
if let Some(ref fragment) = *fragment {
href.push('#');
href.push_str(fragment);
PatKind::Wild | PatKind::Struct(..) => return kw::Underscore,
PatKind::Binding(_, _, ident, _) => return ident.name,
PatKind::TupleStruct(ref p, ..) | PatKind::Path(ref p) => qpath_to_string(p),
- PatKind::Or(ref pats) => pats
- .iter()
- .map(|p| name_from_pat(&**p).to_string())
- .collect::<Vec<String>>()
- .join(" | "),
+ PatKind::Or(ref pats) => {
+ pats.iter().map(|p| name_from_pat(p).to_string()).collect::<Vec<String>>().join(" | ")
+ }
PatKind::Tuple(ref elts, _) => format!(
"({})",
- elts.iter()
- .map(|p| name_from_pat(&**p).to_string())
- .collect::<Vec<String>>()
- .join(", ")
+ elts.iter().map(|p| name_from_pat(p).to_string()).collect::<Vec<String>>().join(", ")
),
PatKind::Box(ref p) => return name_from_pat(&**p),
PatKind::Ref(ref p, _) => return name_from_pat(&**p),
}
PatKind::Range(..) => return kw::Underscore,
PatKind::Slice(ref begin, ref mid, ref end) => {
- let begin = begin.iter().map(|p| name_from_pat(&**p).to_string());
+ let begin = begin.iter().map(|p| name_from_pat(p).to_string());
let mid = mid.as_ref().map(|p| format!("..{}", name_from_pat(&**p))).into_iter();
- let end = end.iter().map(|p| name_from_pat(&**p).to_string());
+ let end = end.iter().map(|p| name_from_pat(p).to_string());
format!("[{}]", begin.chain(mid).chain(end).collect::<Vec<_>>().join(", "))
}
})
}
}
-crate fn href(did: DefId, cx: &Context<'_>) -> Option<(String, ItemType, Vec<String>)> {
+// Possible errors when computing href link source for a `DefId`
+crate enum HrefError {
+ /// This item is known to rustdoc, but from a crate that does not have documentation generated.
+ ///
+ /// This can only happen for non-local items.
+ DocumentationNotBuilt,
+ /// This can only happen for non-local items when `--document-private-items` is not passed.
+ Private,
+ // Not in external cache, href link should be in same page
+ NotInExternalCache,
+}
+
+crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<String>), HrefError> {
let cache = &cx.cache();
let relative_to = &cx.current;
fn to_module_fqp(shortty: ItemType, fqp: &[String]) -> &[String] {
}
if !did.is_local() && !cache.access_levels.is_public(did) && !cache.document_private {
- return None;
+ return Err(HrefError::Private);
}
let (fqp, shortty, mut url_parts) = match cache.paths.get(&did) {
href_relative_parts(module_fqp, relative_to)
}),
None => {
- let &(ref fqp, shortty) = cache.external_paths.get(&did)?;
- let module_fqp = to_module_fqp(shortty, fqp);
- (
- fqp,
- shortty,
- match cache.extern_locations[&did.krate] {
- ExternalLocation::Remote(ref s) => {
- let s = s.trim_end_matches('/');
- let mut s = vec![&s[..]];
- s.extend(module_fqp[..].iter().map(String::as_str));
- s
- }
- ExternalLocation::Local => href_relative_parts(module_fqp, relative_to),
- ExternalLocation::Unknown => return None,
- },
- )
+ if let Some(&(ref fqp, shortty)) = cache.external_paths.get(&did) {
+ let module_fqp = to_module_fqp(shortty, fqp);
+ (
+ fqp,
+ shortty,
+ match cache.extern_locations[&did.krate] {
+ ExternalLocation::Remote(ref s) => {
+ let s = s.trim_end_matches('/');
+ let mut s = vec![&s[..]];
+ s.extend(module_fqp[..].iter().map(String::as_str));
+ s
+ }
+ ExternalLocation::Local => href_relative_parts(module_fqp, relative_to),
+ ExternalLocation::Unknown => return Err(HrefError::DocumentationNotBuilt),
+ },
+ )
+ } else {
+ return Err(HrefError::NotInExternalCache);
+ }
}
};
let last = &fqp.last().unwrap()[..];
url_parts.push(&filename);
}
}
- Some((url_parts.join("/"), shortty, fqp.to_vec()))
+ Ok((url_parts.join("/"), shortty, fqp.to_vec()))
}
/// Both paths should only be modules.
write!(w, "{}{:#}", &last.name, last.args.print(cx))?;
} else {
let path = if use_absolute {
- if let Some((_, _, fqp)) = href(did, cx) {
+ if let Ok((_, _, fqp)) = href(did, cx) {
format!(
"{}::{}",
fqp[..fqp.len() - 1].join("::"),
) -> impl fmt::Display + 'a {
let parts = href(did.into(), cx);
display_fn(move |f| {
- if let Some((url, short_ty, fqp)) = parts {
+ if let Ok((url, short_ty, fqp)) = parts {
write!(
f,
r#"<a class="{}" href="{}" title="{} {}">{}</a>"#,
// look at).
box clean::ResolvedPath { did, .. } => {
match href(did.into(), cx) {
- Some((ref url, _, ref path)) if !f.alternate() => {
+ Ok((ref url, _, ref path)) if !f.alternate() => {
write!(
f,
"<a class=\"type\" href=\"{url}#{shortty}.{name}\" \
is_paragraph = true;
}
html::push_html(&mut ret, content.into_iter());
- write!(ret, " <a href=\"#fnref{}\" rev=\"footnote\">↩</a>", id)
- .unwrap();
+ write!(ret, " <a href=\"#fnref{}\">↩</a>", id).unwrap();
if is_paragraph {
ret.push_str("</p>");
}
use crate::html::escape::Escape;
use crate::html::format::{
href, print_abi_with_space, print_constness_with_space, print_default_space,
- print_generic_bounds, print_where_clause, Buffer, PrintWithSpace,
+ print_generic_bounds, print_where_clause, Buffer, HrefError, PrintWithSpace,
};
use crate::html::markdown::{Markdown, MarkdownHtml, MarkdownSummaryLine};
) {
let name = meth.name.as_ref().unwrap();
let href = match link {
- AssocItemLink::Anchor(Some(ref id)) => format!("#{}", id),
- AssocItemLink::Anchor(None) => format!("#{}.{}", meth.type_(), name),
+ AssocItemLink::Anchor(Some(ref id)) => Some(format!("#{}", id)),
+ AssocItemLink::Anchor(None) => Some(format!("#{}.{}", meth.type_(), name)),
AssocItemLink::GotoSource(did, provided_methods) => {
// We're creating a link from an impl-item to the corresponding
// trait-item and need to map the anchored type accordingly.
ItemType::TyMethod
};
- href(did.expect_def_id(), cx)
- .map(|p| format!("{}#{}.{}", p.0, ty, name))
- .unwrap_or_else(|| format!("#{}.{}", ty, name))
+ match (href(did.expect_def_id(), cx), ty) {
+ (Ok(p), ty) => Some(format!("{}#{}.{}", p.0, ty, name)),
+ (Err(HrefError::DocumentationNotBuilt), ItemType::TyMethod) => None,
+ (Err(_), ty) => Some(format!("#{}.{}", ty, name)),
+ }
}
};
let vis = meth.visibility.print_with_space(meth.def_id, cx).to_string();
w.reserve(header_len + "<a href=\"\" class=\"fnname\">{".len() + "</a>".len());
write!(
w,
- "{indent}{vis}{constness}{asyncness}{unsafety}{defaultness}{abi}fn <a href=\"{href}\" class=\"fnname\">{name}</a>\
+ "{indent}{vis}{constness}{asyncness}{unsafety}{defaultness}{abi}fn <a {href} class=\"fnname\">{name}</a>\
{generics}{decl}{notable_traits}{where_clause}",
indent = indent_str,
vis = vis,
unsafety = unsafety,
defaultness = defaultness,
abi = abi,
- href = href,
+ // links without a href are valid - https://www.w3schools.com/tags/att_a_href.asp
+ href = href.map(|href| format!("href=\"{}\"", href)).unwrap_or_else(|| "".to_string()),
name = name,
generics = g.print(cx),
decl = d.full_print(header_len, indent, header.asyncness, cx),
pre {
padding: 14px;
}
+.type-decl pre {
+ overflow-x: auto;
+}
.source .content pre {
padding: 20px;
--- /dev/null
+// only-cdb
+// compile-flags:-g
+
+// === CDB TESTS ==================================================================================
+
+// cdb-command: g
+
+// cdb-command: dx duration
+// cdb-check:duration : 5s 12ns [Type: core::time::Duration]
+// cdb-check: [<Raw View>] [Type: core::time::Duration]
+// cdb-check: seconds : 5 [Type: unsigned __int64]
+// cdb-check: nanoseconds : 12 [Type: unsigned int]
+
+use std::time::Duration;
+
+fn main() {
+ let duration = Duration::new(5, 12);
+
+ zzz(); // #break
+}
+
+fn zzz() { }
--- /dev/null
+// only-cdb
+// compile-flags:-g
+
+// === CDB TESTS ==================================================================================
+
+// cdb-command: g
+
+// cdb-command: dx nonnull
+// cdb-check:nonnull : NonNull(0x[...]: 0xc) [Type: core::ptr::non_null::NonNull<u32>]
+// cdb-check: [<Raw View>] [Type: core::ptr::non_null::NonNull<u32>]
+// cdb-check: 0xc [Type: unsigned int]
+
+// cdb-command: dx manuallydrop
+// cdb-check:manuallydrop : 12345 [Type: core::mem::manually_drop::ManuallyDrop<i32>]
+// cdb-check: [<Raw View>] [Type: core::mem::manually_drop::ManuallyDrop<i32>]
+
+// cdb-command: dx pin
+// cdb-check:pin : Pin(0x[...]: "this") [Type: core::pin::Pin<ref_mut$<alloc::string::String> >]
+// cdb-check: [<Raw View>] [Type: core::pin::Pin<ref_mut$<alloc::string::String> >]
+// cdb-check: [len] : 0x4 [Type: unsigned [...]]
+// cdb-check: [capacity] : 0x4 [Type: unsigned [...]]
+// cdb-check: [chars] : "this"
+
+// cdb-command: dx unique
+// cdb-check:unique : Unique(0x[...]: (0x2a, 4321)) [Type: core::ptr::unique::Unique<tuple$<u64,i32> >]
+// cdb-check: [<Raw View>] [Type: core::ptr::unique::Unique<tuple$<u64,i32> >]
+// cdb-check: [0] : 0x2a [Type: unsigned __int64]
+// cdb-check: [1] : 4321 [Type: int]
+
+#![feature(ptr_internals)]
+
+use std::mem::ManuallyDrop;
+use std::pin::Pin;
+use std::ptr::{NonNull, Unique};
+
+fn main() {
+ let nonnull: NonNull<_> = (&12u32).into();
+
+ let manuallydrop = ManuallyDrop::new(12345i32);
+
+ let mut s = "this".to_string();
+ let pin = Pin::new(&mut s);
+
+ let unique: Unique<_> = (&mut (42u64, 4321i32)).into();
+
+ zzz(); // #break
+}
+
+fn zzz() { }
// cdb-command: g
// cdb-command:dx static_c,d
-// cdb-check:static_c,d [Type: core::cell::Cell<i32>]
-// cdb-check: [...] value [Type: core::cell::UnsafeCell<i32>]
+// cdb-check:static_c,d : 10 [Type: core::cell::Cell<i32>]
+// cdb-check: [<Raw View>] [Type: core::cell::Cell<i32>]
// cdb-command: dx static_c.value,d
-// cdb-check:static_c.value,d [Type: core::cell::UnsafeCell<i32>]
-// cdb-check: [...] value : 10 [Type: int]
+// cdb-check:static_c.value,d : 10 [Type: core::cell::UnsafeCell<i32>]
+// cdb-check: [<Raw View>] [Type: core::cell::UnsafeCell<i32>]
// cdb-command: dx dynamic_c,d
-// cdb-check:dynamic_c,d [Type: core::cell::RefCell<i32>]
-// cdb-check: [...] borrow [Type: core::cell::Cell<isize>]
-// cdb-check: [...] value [Type: core::cell::UnsafeCell<i32>]
+// cdb-check:dynamic_c,d : 15 [Type: core::cell::RefCell<i32>]
+// cdb-check: [<Raw View>] [Type: core::cell::RefCell<i32>]
+// cdb-check: [Borrow state] : Unborrowed
// cdb-command: dx dynamic_c.value,d
-// cdb-check:dynamic_c.value,d [Type: core::cell::UnsafeCell<i32>]
-// cdb-check: [...] value : 15 [Type: int]
+// cdb-check:dynamic_c.value,d : 15 [Type: core::cell::UnsafeCell<i32>]
+// cdb-check: [<Raw View>] [Type: core::cell::UnsafeCell<i32>]
// cdb-command: dx b,d
-// cdb-check:b,d [Type: core::cell::RefMut<i32>]
-// cdb-check: [...] value : [...] : 42 [Type: int *]
-// cdb-check: [...] borrow [Type: core::cell::BorrowRefMut]
+// cdb-check:b,d : 42 [Type: core::cell::RefMut<i32>]
+// cdb-check: [<Raw View>] [Type: core::cell::RefMut<i32>]
+// cdb-check: 42 [Type: int]
+
+// cdb-command: g
+
+// cdb-command: dx dynamic_c,d
+// cdb-check:dynamic_c,d : 15 [Type: core::cell::RefCell<i32>]
+// cdb-check: [<Raw View>] [Type: core::cell::RefCell<i32>]
+// cdb-check: [Borrow state] : Immutably borrowed
+
+// cdb-command: dx r_borrow,d
+// cdb-check:r_borrow,d : 15 [Type: core::cell::Ref<i32>]
+// cdb-check: [<Raw View>] [Type: core::cell::Ref<i32>]
+// cdb-check: 15 [Type: int]
+
+// cdb-command: g
+
+// cdb-command: dx dynamic_c,d
+// cdb-check:dynamic_c,d : 15 [Type: core::cell::RefCell<i32>]
+// cdb-check: [<Raw View>] [Type: core::cell::RefCell<i32>]
+// cdb-check: [Borrow state] : Unborrowed
+
+// cdb-command: g
+
+// cdb-command: dx dynamic_c,d
+// cdb-check:dynamic_c,d : 15 [Type: core::cell::RefCell<i32>]
+// cdb-check: [<Raw View>] [Type: core::cell::RefCell<i32>]
+// cdb-check: [Borrow state] : Mutably borrowed
+
+// cdb-command: dx r_borrow_mut,d
+// cdb-check:r_borrow_mut,d : 15 [Type: core::cell::RefMut<i32>]
+// cdb-check: [<Raw View>] [Type: core::cell::RefMut<i32>]
+// cdb-check: 15 [Type: int]
+
+// cdb-command: g
+
+// cdb-command: dx dynamic_c,d
+// cdb-check:dynamic_c,d : 15 [Type: core::cell::RefCell<i32>]
+// cdb-check: [<Raw View>] [Type: core::cell::RefCell<i32>]
+// cdb-check: [Borrow state] : Unborrowed
#![allow(unused_variables)]
*b = 42;
zzz(); // #break
+
+ // Check that `RefCell`'s borrow state visualizes correctly
+ {
+ let r_borrow = dynamic_c.borrow();
+ zzz(); // #break
+ }
+
+ zzz(); // #break
+
+ {
+ let r_borrow_mut = dynamic_c.borrow_mut();
+ zzz(); // #break
+ }
+
+ zzz(); // #break
}
fn zzz() {()}
--- /dev/null
+// only-cdb
+// compile-flags:-g
+
+// Tests the visualizations for `NonZero{I,U}{8,16,32,64,128,size}`, `Wrapping<T>` and
+// `Atomic{Bool,I8,I16,I32,I64,Isize,U8,U16,U32,U64,Usize}` located in `libcore.natvis`.
+
+// === CDB TESTS ==================================================================================
+// cdb-command: g
+
+// cdb-command: dx nz_i8
+// cdb-check:nz_i8 : 11 [Type: core::num::nonzero::NonZeroI8]
+// cdb-check: [<Raw View>] [Type: core::num::nonzero::NonZeroI8]
+
+// cdb-command: dx nz_i16
+// cdb-check:nz_i16 : 22 [Type: core::num::nonzero::NonZeroI16]
+// cdb-check: [<Raw View>] [Type: core::num::nonzero::NonZeroI16]
+
+// cdb-command: dx nz_i32
+// cdb-check:nz_i32 : 33 [Type: core::num::nonzero::NonZeroI32]
+// cdb-check: [<Raw View>] [Type: core::num::nonzero::NonZeroI32]
+
+// cdb-command: dx nz_i64
+// cdb-check:nz_i64 : 44 [Type: core::num::nonzero::NonZeroI64]
+// cdb-check: [<Raw View>] [Type: core::num::nonzero::NonZeroI64]
+
+// 128-bit integers don't seem to work in CDB
+// cdb-command: dx nz_i128
+// cdb-check: [<Raw View>] [Type: core::num::nonzero::NonZeroI128]
+
+// cdb-command: dx nz_isize
+// cdb-check:nz_isize : 66 [Type: core::num::nonzero::NonZeroIsize]
+// cdb-check: [<Raw View>] [Type: core::num::nonzero::NonZeroIsize]
+
+// cdb-command: dx nz_u8
+// cdb-check:nz_u8 : 0x4d [Type: core::num::nonzero::NonZeroU8]
+// cdb-check: [<Raw View>] [Type: core::num::nonzero::NonZeroU8]
+
+// cdb-command: dx nz_u16
+// cdb-check:nz_u16 : 0x58 [Type: core::num::nonzero::NonZeroU16]
+// cdb-check: [<Raw View>] [Type: core::num::nonzero::NonZeroU16]
+
+// cdb-command: dx nz_u32
+// cdb-check:nz_u32 : 0x63 [Type: core::num::nonzero::NonZeroU32]
+// cdb-check: [<Raw View>] [Type: core::num::nonzero::NonZeroU32]
+
+// cdb-command: dx nz_u64
+// cdb-check:nz_u64 : 0x64 [Type: core::num::nonzero::NonZeroU64]
+// cdb-check: [<Raw View>] [Type: core::num::nonzero::NonZeroU64]
+
+// 128-bit integers don't seem to work in CDB
+// cdb-command: dx nz_u128
+// cdb-check: [<Raw View>] [Type: core::num::nonzero::NonZeroU128]
+
+// cdb-command: dx nz_usize
+// cdb-check:nz_usize : 0x7a [Type: core::num::nonzero::NonZeroUsize]
+// cdb-check: [<Raw View>] [Type: core::num::nonzero::NonZeroUsize]
+
+// cdb-command: dx w_i8
+// cdb-check:w_i8 : 10 [Type: core::num::wrapping::Wrapping<i8>]
+// cdb-check: [<Raw View>] [Type: core::num::wrapping::Wrapping<i8>]
+
+// cdb-command: dx w_i16
+// cdb-check:w_i16 : 20 [Type: core::num::wrapping::Wrapping<i16>]
+// cdb-check: [<Raw View>] [Type: core::num::wrapping::Wrapping<i16>]
+
+// cdb-command: dx w_i32
+// cdb-check:w_i32 : 30 [Type: core::num::wrapping::Wrapping<i32>]
+// cdb-check: [<Raw View>] [Type: core::num::wrapping::Wrapping<i32>]
+
+// cdb-command: dx w_i64
+// cdb-check:w_i64 : 40 [Type: core::num::wrapping::Wrapping<i64>]
+// cdb-check: [<Raw View>] [Type: core::num::wrapping::Wrapping<i64>]
+
+// 128-bit integers don't seem to work in CDB
+// cdb-command: dx w_i128
+// cdb-check:w_i128 [Type: core::num::wrapping::Wrapping<i128>]
+// cdb-check: [<Raw View>] [Type: core::num::wrapping::Wrapping<i128>]
+
+// cdb-command: dx w_isize
+// cdb-check:w_isize : 60 [Type: core::num::wrapping::Wrapping<isize>]
+// cdb-check: [<Raw View>] [Type: core::num::wrapping::Wrapping<isize>]
+
+// cdb-command: dx w_u8
+// cdb-check:w_u8 : 0x46 [Type: core::num::wrapping::Wrapping<u8>]
+// cdb-check: [<Raw View>] [Type: core::num::wrapping::Wrapping<u8>]
+
+// cdb-command: dx w_u16
+// cdb-check:w_u16 : 0x50 [Type: core::num::wrapping::Wrapping<u16>]
+// cdb-check: [<Raw View>] [Type: core::num::wrapping::Wrapping<u16>]
+
+// cdb-command: dx w_u32
+// cdb-check:w_u32 : 0x5a [Type: core::num::wrapping::Wrapping<u32>]
+// cdb-check: [<Raw View>] [Type: core::num::wrapping::Wrapping<u32>]
+
+// cdb-command: dx w_u64
+// cdb-check:w_u64 : 0x64 [Type: core::num::wrapping::Wrapping<u64>]
+// cdb-check: [<Raw View>] [Type: core::num::wrapping::Wrapping<u64>]
+
+// 128-bit integers don't seem to work in CDB
+// cdb-command: dx w_u128
+// cdb-check:w_u128 [Type: core::num::wrapping::Wrapping<u128>]
+// cdb-check: [<Raw View>] [Type: core::num::wrapping::Wrapping<u128>]
+
+// cdb-command: dx w_usize
+// cdb-check:w_usize : 0x78 [Type: core::num::wrapping::Wrapping<usize>]
+// cdb-check: [<Raw View>] [Type: core::num::wrapping::Wrapping<usize>]
+
+// cdb-command: dx a_bool_t
+// cdb-check:a_bool_t : true [Type: core::sync::atomic::AtomicBool]
+// cdb-check: [<Raw View>] [Type: core::sync::atomic::AtomicBool]
+
+// cdb-command: dx a_bool_f
+// cdb-check:a_bool_f : false [Type: core::sync::atomic::AtomicBool]
+// cdb-check: [<Raw View>] [Type: core::sync::atomic::AtomicBool]
+
+// cdb-command: dx a_i8
+// cdb-check:a_i8 : 2 [Type: core::sync::atomic::AtomicI8]
+// cdb-check: [<Raw View>] [Type: core::sync::atomic::AtomicI8]
+
+// cdb-command: dx a_i16
+// cdb-check:a_i16 : 4 [Type: core::sync::atomic::AtomicI16]
+// cdb-check: [<Raw View>] [Type: core::sync::atomic::AtomicI16]
+
+// cdb-command: dx a_i32
+// cdb-check:a_i32 : 8 [Type: core::sync::atomic::AtomicI32]
+// cdb-check: [<Raw View>] [Type: core::sync::atomic::AtomicI32]
+
+// cdb-command: dx a_i64
+// cdb-check:a_i64 : 16 [Type: core::sync::atomic::AtomicI64]
+// cdb-check: [<Raw View>] [Type: core::sync::atomic::AtomicI64]
+
+// cdb-command: dx a_isize
+// cdb-check:a_isize : 32 [Type: core::sync::atomic::AtomicIsize]
+// cdb-check: [<Raw View>] [Type: core::sync::atomic::AtomicIsize]
+
+// cdb-command: dx a_u8
+// cdb-check:a_u8 : 0x40 [Type: core::sync::atomic::AtomicU8]
+// cdb-check: [<Raw View>] [Type: core::sync::atomic::AtomicU8]
+
+// cdb-command: dx a_u16
+// cdb-check:a_u16 : 0x80 [Type: core::sync::atomic::AtomicU16]
+// cdb-check: [<Raw View>] [Type: core::sync::atomic::AtomicU16]
+
+// cdb-command: dx a_u32
+// cdb-check:a_u32 : 0x100 [Type: core::sync::atomic::AtomicU32]
+// cdb-check: [<Raw View>] [Type: core::sync::atomic::AtomicU32]
+
+// cdb-command: dx a_u64
+// cdb-check:a_u64 : 0x200 [Type: core::sync::atomic::AtomicU64]
+// cdb-check: [<Raw View>] [Type: core::sync::atomic::AtomicU64]
+
+// cdb-command: dx a_usize
+// cdb-check:a_usize : 0x400 [Type: core::sync::atomic::AtomicUsize]
+// cdb-check: [<Raw View>] [Type: core::sync::atomic::AtomicUsize]
+
+use std::num::*;
+use std::sync::atomic::*;
+
+fn main() {
+ let nz_i8 = NonZeroI8::new(11).unwrap();
+ let nz_i16 = NonZeroI16::new(22).unwrap();
+ let nz_i32 = NonZeroI32::new(33).unwrap();
+ let nz_i64 = NonZeroI64::new(44).unwrap();
+ let nz_i128 = NonZeroI128::new(55).unwrap();
+ let nz_isize = NonZeroIsize::new(66).unwrap();
+
+ let nz_u8 = NonZeroU8::new(77).unwrap();
+ let nz_u16 = NonZeroU16::new(88).unwrap();
+ let nz_u32 = NonZeroU32::new(99).unwrap();
+ let nz_u64 = NonZeroU64::new(100).unwrap();
+ let nz_u128 = NonZeroU128::new(111).unwrap();
+ let nz_usize = NonZeroUsize::new(122).unwrap();
+
+ let w_i8 = Wrapping(10i8);
+ let w_i16 = Wrapping(20i16);
+ let w_i32 = Wrapping(30i32);
+ let w_i64 = Wrapping(40i64);
+ let w_i128 = Wrapping(50i128);
+ let w_isize = Wrapping(60isize);
+
+ let w_u8 = Wrapping(70u8);
+ let w_u16 = Wrapping(80u16);
+ let w_u32 = Wrapping(90u32);
+ let w_u64 = Wrapping(100u64);
+ let w_u128 = Wrapping(110u128);
+ let w_usize = Wrapping(120usize);
+
+ let a_bool_t = AtomicBool::new(true);
+ let a_bool_f = AtomicBool::new(false);
+
+ let a_i8 = AtomicI8::new(2);
+ let a_i16 = AtomicI16::new(4);
+ let a_i32 = AtomicI32::new(8);
+ let a_i64 = AtomicI64::new(16);
+ let a_isize = AtomicIsize::new(32);
+
+ let a_u8 = AtomicU8::new(64);
+ let a_u16 = AtomicU16::new(128);
+ let a_u32 = AtomicU32::new(256);
+ let a_u64 = AtomicU64::new(512);
+ let a_usize = AtomicUsize::new(1024);
+
+ zzz(); // #break
+}
+
+fn zzz() { }
// cdb-check: [11] : 33 '!' [Type: char]
// cdb-command: dx os_string
-// cdb-check:os_string [Type: [...]::OsString]
-// NOTE: OsString doesn't have a .natvis entry yet.
+// cdb-check:os_string : "IAMA OS string 😃" [Type: std::ffi::os_str::OsString]
+// cdb-check: [<Raw View>] [Type: std::ffi::os_str::OsString]
+// cdb-check: [chars] : "IAMA OS string 😃"
// cdb-command: dx some
// cdb-check:some : Some [Type: enum$<core::option::Option<i16> >]
// NOTE: cdb fails to interpret debug info of Option enums on i686.
// cdb-check:some_string [Type: enum$<core::option::Option<alloc::string::String>, 1, [...], Some>]
+// cdb-command: dx linkedlist
+// cdb-check:linkedlist : { len=0x2 } [Type: alloc::collections::linked_list::LinkedList<i32>]
+// cdb-check: [<Raw View>] [Type: alloc::collections::linked_list::LinkedList<i32>]
+// cdb-check: [0x0] : 128 [Type: int]
+// cdb-check: [0x1] : 42 [Type: int]
+
+// cdb-command: dx vecdeque
+// cdb-check:vecdeque : { len=0x2 } [Type: alloc::collections::vec_deque::VecDeque<i32>]
+// cdb-check: [<Raw View>] [Type: alloc::collections::vec_deque::VecDeque<i32>]
+// cdb-check: [len] : 0x2
+// cdb-check: [capacity] : 0x8 [Type: unsigned [...]]
+// cdb-check: [0x0] : 90 [Type: int]
+// cdb-check: [0x1] : 20 [Type: int]
+
#![allow(unused_variables)]
+use std::collections::{LinkedList, VecDeque};
use std::ffi::OsString;
-
fn main() {
// &[]
let some_string = Some("IAMA optional string!".to_owned());
+ // LinkedList
+ let mut linkedlist = LinkedList::new();
+ linkedlist.push_back(42);
+ linkedlist.push_front(128);
+
+ // VecDeque
+ let mut vecdeque = VecDeque::new();
+ vecdeque.push_back(20);
+ vecdeque.push_front(90);
+
zzz(); // #break
}
// cdb-command: g
// cdb-command: dx r1,d
-// cdb-check:r1,d [Type: core::ops::range::Range<i32>]
-// cdb-check: [...] start : 3 [Type: int]
-// cdb-check: [...] end : 5 [Type: int]
+// cdb-check:r1,d : (3..5) [Type: core::ops::range::Range<i32>]
+// cdb-check: [<Raw View>] [Type: core::ops::range::Range<i32>]
// cdb-command: dx r2,d
-// cdb-check:r2,d [Type: core::ops::range::RangeFrom<i32>]
-// cdb-check: [...] start : 2 [Type: int]
+// cdb-check:r2,d : (2..) [Type: core::ops::range::RangeFrom<i32>]
+// cdb-check: [<Raw View>] [Type: core::ops::range::RangeFrom<i32>]
// cdb-command: dx r3,d
-// cdb-check:r3,d [Type: core::ops::range::RangeInclusive<i32>]
-// cdb-check: [...] start : 1 [Type: int]
-// cdb-check: [...] end : 4 [Type: int]
-// cdb-check: [...] exhausted : false [Type: bool]
+// cdb-check:r3,d : (1..=4) [Type: core::ops::range::RangeInclusive<i32>]
+// cdb-check: [<Raw View>] [Type: core::ops::range::RangeInclusive<i32>]
// cdb-command: dx r4,d
-// cdb-check:r4,d [Type: core::ops::range::RangeToInclusive<i32>]
-// cdb-check: [...] end : 3 [Type: int]
+// cdb-check:r4,d : (..10) [Type: core::ops::range::RangeTo<i32>]
+// cdb-check: [<Raw View>] [Type: core::ops::range::RangeTo<i32>]
// cdb-command: dx r5,d
-// cdb-check:r5,d [Type: core::ops::range::RangeFull]
+// cdb-check:r5,d : (..=3) [Type: core::ops::range::RangeToInclusive<i32>]
+// cdb-check: [<Raw View>] [Type: core::ops::range::RangeToInclusive<i32>]
+
+// cdb-command: dx r6,d
+// cdb-check:r6,d [Type: core::ops::range::RangeFull]
#[allow(unused_variables)]
fn main()
{
- let r1 = Range{start: 3, end: 5};
- let r2 = RangeFrom{start: 2};
- let r3 = RangeInclusive::new(1, 4);
- let r4 = RangeToInclusive{end: 3};
- let r5 = RangeFull{};
+ let r1 = (3..5);
+ let r2 = (2..);
+ let r3 = (1..=4);
+ let r4 = (..10);
+ let r5 = (..=3);
+ let r6 = (..);
zzz(); // #break
}
// cdb-command:dx r,d
// cdb-check:r,d : 42 [Type: alloc::rc::Rc<i32>]
+// cdb-check: [<Raw View>] [Type: alloc::rc::Rc<i32>]
+// cdb-check: [Reference count] : 2 [Type: core::cell::Cell<usize>]
+// cdb-check: [Weak reference count] : 2 [Type: core::cell::Cell<usize>]
// cdb-command:dx r1,d
// cdb-check:r1,d : 42 [Type: alloc::rc::Rc<i32>]
+// cdb-check: [<Raw View>] [Type: alloc::rc::Rc<i32>]
+// cdb-check: [Reference count] : 2 [Type: core::cell::Cell<usize>]
+// cdb-check: [Weak reference count] : 2 [Type: core::cell::Cell<usize>]
// cdb-command:dx w1,d
-// cdb-check:w1,d [Type: alloc::rc::Weak<i32>]
-// cdb-check: [...] ptr : [...] [Type: core::ptr::non_null::NonNull<alloc::rc::RcBox<i32> >]
+// cdb-check:w1,d : 42 [Type: alloc::rc::Weak<i32>]
+// cdb-check: [<Raw View>] [Type: alloc::rc::Weak<i32>]
+// cdb-check: [Reference count] : 2 [Type: core::cell::Cell<usize>]
+// cdb-check: [Weak reference count] : 2 [Type: core::cell::Cell<usize>]
// cdb-command:dx a,d
// cdb-check:a,d : 42 [Type: alloc::sync::Arc<i32>]
+// cdb-check: [<Raw View>] [Type: alloc::sync::Arc<i32>]
+// cdb-check: [Reference count] : 2 [Type: core::sync::atomic::AtomicUsize]
+// cdb-check: [Weak reference count] : 2 [Type: core::sync::atomic::AtomicUsize]
// cdb-command:dx a1,d
// cdb-check:a1,d : 42 [Type: alloc::sync::Arc<i32>]
+// cdb-check: [<Raw View>] [Type: alloc::sync::Arc<i32>]
+// cdb-check: [Reference count] : 2 [Type: core::sync::atomic::AtomicUsize]
+// cdb-check: [Weak reference count] : 2 [Type: core::sync::atomic::AtomicUsize]
// cdb-command:dx w2,d
// cdb-check:w2,d : 42 [Type: alloc::sync::Weak<i32>]
+// cdb-check: [<Raw View>] [Type: alloc::sync::Weak<i32>]
+// cdb-check: [Reference count] : 2 [Type: core::sync::atomic::AtomicUsize]
+// cdb-check: [Weak reference count] : 2 [Type: core::sync::atomic::AtomicUsize]
use std::rc::Rc;
use std::sync::Arc;
// cdb-command:dx l
// cdb-check:l [Type: std::sync::rwlock::RwLock<i32>]
// cdb-check: [...] poison [Type: std::sync::poison::Flag]
-// cdb-check: [...] data [Type: core::cell::UnsafeCell<i32>]
+// cdb-check: [...] data : 0 [Type: core::cell::UnsafeCell<i32>]
//
// cdb-command:dx r
// cdb-check:r [Type: std::sync::rwlock::RwLockReadGuard<i32>]
// cdb-check: [...] lock : [...] [Type: std::sync::rwlock::RwLock<i32> *]
//
// cdb-command:dx r.lock->data,d
-// cdb-check:r.lock->data,d [Type: core::cell::UnsafeCell<i32>]
-// cdb-check: [...] value : 0 [Type: int]
+// cdb-check:r.lock->data,d : 0 [Type: core::cell::UnsafeCell<i32>]
+// cdb-check: [<Raw View>] [Type: core::cell::UnsafeCell<i32>]
#[allow(unused_variables)]
/// ```
pub struct Foo;
}
+
+pub mod long_trait {
+ use std::ops::DerefMut;
+
+ pub trait ALongNameBecauseItHelpsTestingTheCurrentProblem: DerefMut<Target = u32>
+ + From<u128> + Send + Sync + AsRef<str> + 'static {}
+}
--- /dev/null
+// This test ensures that the type declaration content overflow is handled inside the <pre> directly.
+goto: file://|DOC_PATH|/lib2/long_trait/trait.ALongNameBecauseItHelpsTestingTheCurrentProblem.html
+// We set a fixed size so there is no chance of "random" resize.
+size: (1100, 800)
+// Logically, the <body> scroll width should be the width of the window.
+assert-property: ("body", {"scrollWidth": "1100"})
+// However, since there is overflow in the type declaration, its scroll width is bigger.
+assert-property: (".type-decl pre", {"scrollWidth": "1324"})
--- /dev/null
+#![crate_name = "issue_86620_1"]
+
+pub trait VZip {
+ fn vzip() -> usize;
+}
+
+impl<T> VZip for T {
+ fn vzip() -> usize {
+ 0
+ }
+}
extern crate rustdoc_extern_default_method as ext;
// @count extern_default_method/struct.Struct.html '//*[@id="method.provided"]' 1
+// @has extern_default_method/struct.Struct.html '//div[@id="method.provided"]//a[@class="fnname"]/@href' #method.provided
+// @has extern_default_method/struct.Struct.html '//div[@id="method.provided"]//a[@class="anchor"]/@href' #method.provided
pub use ext::Struct;
--- /dev/null
+// aux-build:issue-86620-1.rs
+
+extern crate issue_86620_1;
+
+use issue_86620_1::*;
+
+// @!has issue_86620/struct.S.html '//div[@id="method.vzip"]//a[@class="fnname"]/@href' #tymethod.vzip
+// @has issue_86620/struct.S.html '//div[@id="method.vzip"]//a[@class="anchor"]/@href' #method.vzip
+pub struct S;
error[E0277]: the trait bound `Self: Get` is not satisfied
- --> $DIR/associated-types-for-unimpl-trait.rs:10:5
+ --> $DIR/associated-types-for-unimpl-trait.rs:10:8
|
LL | fn uhoh<U:Get>(&self, foo: U, bar: <Self as Get>::Value) {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self`
+ | ^^^^ the trait `Get` is not implemented for `Self`
|
help: consider further restricting `Self`
|
error[E0277]: the trait bound `T: Get` is not satisfied
- --> $DIR/associated-types-no-suitable-bound.rs:11:5
+ --> $DIR/associated-types-no-suitable-bound.rs:11:8
|
LL | fn uhoh<T>(foo: <T as Get>::Value) {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `T`
+ | ^^^^ the trait `Get` is not implemented for `T`
|
help: consider restricting type parameter `T`
|
error[E0277]: the trait bound `Self: Get` is not satisfied
- --> $DIR/associated-types-no-suitable-supertrait-2.rs:17:5
+ --> $DIR/associated-types-no-suitable-supertrait-2.rs:17:8
|
LL | fn uhoh<U:Get>(&self, foo: U, bar: <Self as Get>::Value) {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self`
+ | ^^^^ the trait `Get` is not implemented for `Self`
|
help: consider further restricting `Self`
|
error[E0277]: the trait bound `Self: Get` is not satisfied
- --> $DIR/associated-types-no-suitable-supertrait.rs:17:5
+ --> $DIR/associated-types-no-suitable-supertrait.rs:17:8
|
LL | fn uhoh<U:Get>(&self, foo: U, bar: <Self as Get>::Value) {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self`
+ | ^^^^ the trait `Get` is not implemented for `Self`
|
help: consider further restricting `Self`
|
| ^^^^^^^^^^^^^^^
error[E0277]: the trait bound `(T, U): Get` is not satisfied
- --> $DIR/associated-types-no-suitable-supertrait.rs:22:5
+ --> $DIR/associated-types-no-suitable-supertrait.rs:22:8
|
LL | fn uhoh<U:Get>(&self, foo: U, bar: <(T, U) as Get>::Value) {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `(T, U)`
+ | ^^^^ the trait `Get` is not implemented for `(T, U)`
error: aborting due to 2 previous errors
error[E0277]: the trait bound `Self: Get` is not satisfied
- --> $DIR/associated-types-projection-to-unrelated-trait-in-method-without-default.rs:10:5
+ --> $DIR/associated-types-projection-to-unrelated-trait-in-method-without-default.rs:10:8
|
LL | fn okay<U:Get>(&self, foo: U, bar: <Self as Get>::Value);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self`
+ | ^^^^ the trait `Get` is not implemented for `Self`
|
help: consider further restricting `Self`
|
--- /dev/null
+// build-pass
+// edition:2018
+
+// Regression test to ensure we handle debruijn indices correctly in projection
+// normalization under binders. Found in crater run for #85499
+
+use std::future::Future;
+use std::pin::Pin;
+pub enum Outcome<S, E> {
+ Success((S, E)),
+}
+pub struct Request<'r> {
+ _marker: std::marker::PhantomData<&'r ()>,
+}
+pub trait FromRequest<'r>: Sized {
+ type Error;
+ fn from_request<'life0>(
+ request: &'r Request<'life0>,
+ ) -> Pin<Box<dyn Future<Output = Outcome<Self, Self::Error>>>>;
+}
+impl<'r, T: FromRequest<'r>> FromRequest<'r> for Option<T> {
+ type Error = ();
+ fn from_request<'life0>(
+ request: &'r Request<'life0>,
+ ) -> Pin<Box<dyn Future<Output = Outcome<Self, Self::Error>>>> {
+ Box::pin(async move {
+ let request = request;
+ match T::from_request(request).await {
+ _ => todo!(),
+ }
+ });
+ todo!()
+ }
+}
+
+fn main() {}
--- /dev/null
+// build-pass
+// edition:2018
+
+// Regression test to ensure we handle debruijn indices correctly in projection
+// normalization under binders. Found in crater run for #85499
+
+use std::future::Future;
+use std::pin::Pin;
+pub enum Outcome<S, E> {
+ Success(S),
+ Failure(E),
+}
+pub struct Request<'r> {
+ _marker: std::marker::PhantomData<&'r ()>,
+}
+pub trait FromRequest<'r>: Sized {
+ type Error;
+ fn from_request<'life0>(
+ request: &'r Request<'life0>,
+ ) -> Pin<Box<dyn Future<Output = Outcome<Self, Self::Error>>>>;
+}
+pub struct S<T> {
+ _marker: std::marker::PhantomData<T>,
+}
+impl<'r, T: FromRequest<'r>> S<T> {
+ pub async fn from_request(request: &'r Request<'_>) {
+ let _ = T::from_request(request).await;
+ }
+}
+
+fn main() {}
--- /dev/null
+// build-pass
+// edition:2018
+
+// Regression test to ensure we handle debruijn indices correctly in projection
+// normalization under binders. Found in crater run for #85499
+
+use std::future::{Future, Ready};
+async fn read() {
+ let _ = connect(&()).await;
+}
+async fn connect<A: ToSocketAddr>(addr: A) {
+ let _ = addr.to_socket_addr().await;
+}
+pub trait ToSocketAddr {
+ type Future: Future<Output = ()>;
+ fn to_socket_addr(&self) -> Self::Future;
+}
+impl ToSocketAddr for &() {
+ type Future = Ready<()>;
+ fn to_socket_addr(&self) -> Self::Future {
+ unimplemented!()
+ }
+}
+struct Server;
+impl Server {
+ fn and_then<F>(self, _fun: F) -> AndThen<F> {
+ unimplemented!()
+ }
+}
+struct AndThen<F> {
+ _marker: std::marker::PhantomData<F>,
+}
+pub async fn run<F>(_: F) {
+}
+fn main() {
+ let _ = async {
+ let server = Server;
+ let verification_route = server.and_then(read);
+ run(verification_route).await;
+ };
+}
--- /dev/null
+// build-pass
+
+// Ensures that we don't regress on "implementation is not general enough" when
+// normalizating under binders.
+
+#![feature(no_core)]
+
+pub trait Yokeable<'a> {
+ type Output: 'a;
+}
+
+pub struct Yoke<Y: for<'a> Yokeable<'a>> {
+ _yokeable: Y,
+}
+
+impl<Y: for<'a> Yokeable<'a>> Yoke<Y> {
+ pub fn project<'this, P>(
+ &'this self,
+ _f: for<'a> fn(<Y as Yokeable<'a>>::Output, &'a ()) -> <P as Yokeable<'a>>::Output,
+ ) -> Yoke<P>
+ where
+ P: for<'a> Yokeable<'a>,
+ {
+ unimplemented!()
+ }
+}
+
+pub fn slice(y: Yoke<&'static ()>) -> Yoke<&'static ()> {
+ y.project(move |yk, _| yk)
+}
+
+impl<'a, T> Yokeable<'a> for &'static T {
+ type Output = &'a T;
+}
+
+fn main() {}
--- /dev/null
+// run-pass
+// edition:2021
+
+enum Variant {
+ A,
+ B, //~ WARNING: variant is never constructed: `B`
+}
+
+struct A {
+ field: Variant,
+}
+
+fn discriminant_is_a_ref() {
+ let here = A { field: Variant::A };
+ let out_ref = &here.field;
+
+ || match out_ref { //~ WARNING: unused closure that must be used
+ Variant::A => (),
+ Variant::B => (),
+ };
+}
+
+fn discriminant_is_a_field() {
+ let here = A { field: Variant::A };
+
+ || match here.field { //~ WARNING: unused closure that must be used
+ Variant::A => (),
+ Variant::B => (),
+ };
+}
+
+fn main() {
+ discriminant_is_a_ref();
+ discriminant_is_a_field();
+}
--- /dev/null
+warning: variant is never constructed: `B`
+ --> $DIR/issue-87097.rs:6:5
+ |
+LL | B,
+ | ^
+ |
+ = note: `#[warn(dead_code)]` on by default
+
+warning: unused closure that must be used
+ --> $DIR/issue-87097.rs:17:5
+ |
+LL | / || match out_ref {
+LL | | Variant::A => (),
+LL | | Variant::B => (),
+LL | | };
+ | |______^
+ |
+ = note: `#[warn(unused_must_use)]` on by default
+ = note: closures are lazy and do nothing unless called
+
+warning: unused closure that must be used
+ --> $DIR/issue-87097.rs:26:5
+ |
+LL | / || match here.field {
+LL | | Variant::A => (),
+LL | | Variant::B => (),
+LL | | };
+ | |______^
+ |
+ = note: closures are lazy and do nothing unless called
+
+warning: 3 warnings emitted
+
//~| NOTE: Min Capture p_foo[Deref,Deref,(0, 0)] -> UniqueImmBorrow
}
+// Test that move closures can take ownership of Copy type
+fn returned_closure_owns_copy_type_data() -> impl Fn() -> i32 {
+ let x = 10;
+
+ let c = #[rustc_capture_analysis] move || x;
+ //~^ ERROR: attributes on expressions are experimental
+ //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+ //~| First Pass analysis includes:
+ //~| NOTE: Capturing x[] -> ImmBorrow
+ //~| Min Capture analysis includes:
+ //~| NOTE: Min Capture x[] -> ByValue
+
+ c
+}
+
fn main() {
simple_move_closure();
simple_ref();
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/move_closure.rs:202:13
+ |
+LL | let c = #[rustc_capture_analysis] move || x;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+ = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+ --> $DIR/move_closure.rs:202:39
+ |
+LL | let c = #[rustc_capture_analysis] move || x;
+ | ^^^^^^^^^
+ |
+note: Capturing x[] -> ImmBorrow
+ --> $DIR/move_closure.rs:202:47
+ |
+LL | let c = #[rustc_capture_analysis] move || x;
+ | ^
+
+error: Min Capture analysis includes:
+ --> $DIR/move_closure.rs:202:39
+ |
+LL | let c = #[rustc_capture_analysis] move || x;
+ | ^^^^^^^^^
+ |
+note: Min Capture x[] -> ByValue
+ --> $DIR/move_closure.rs:202:47
+ |
+LL | let c = #[rustc_capture_analysis] move || x;
+ | ^
+
error: First Pass analysis includes:
--> $DIR/move_closure.rs:15:5
|
LL | let c = #[rustc_capture_analysis] move || p_foo.x += 10;
| ^^^^^^^
-error: aborting due to 30 previous errors
+error: aborting due to 33 previous errors
For more information about this error, try `rustc --explain E0658`.
// Test that move closures compile properly with `capture_disjoint_fields` enabled.
+#![allow(unused)]
+
fn simple_ref() {
let mut s = 10;
let ref_s = &mut s;
c();
}
+// Test that move closures can take ownership of Copy type
+fn returned_closure_owns_copy_type_data() -> impl Fn() -> i32 {
+ let x = 10;
+
+ let c = move || x;
+
+ c
+}
+
fn main() {
simple_ref();
struct_contains_ref_to_another_struct();
disjoint_via_ref();
data_moved_but_not_fn_once();
+
+ returned_closure_owns_copy_type_data();
}
i: i32,
}
+#[repr(packed, align(0x100))]
+pub struct S(u16); //~ ERROR type has conflicting packed and align representation hints
+
+#[repr(packed, align(0x100))]
+pub union U { //~ ERROR type has conflicting packed and align representation hints
+ u: u16
+}
+
+static B: U = U { u: 0 };
+static A: S = S(0);
+
fn main() {}
LL | | }
| |_^
-error: aborting due to 10 previous errors
+error[E0587]: type has conflicting packed and align representation hints
+ --> $DIR/conflicting-repr-hints.rs:70:1
+ |
+LL | pub struct S(u16);
+ | ^^^^^^^^^^^^^^^^^^
+
+error[E0587]: type has conflicting packed and align representation hints
+ --> $DIR/conflicting-repr-hints.rs:73:1
+ |
+LL | / pub union U {
+LL | | u: u16
+LL | | }
+ | |_^
+
+error: aborting due to 12 previous errors
Some errors have detailed explanations: E0566, E0587, E0634.
For more information about an error, try `rustc --explain E0566`.
--- /dev/null
+#![feature(const_generics_defaults)]
+
+struct Struct<const N: usize = { Self; 10 }>;
+//~^ ERROR generic parameters cannot use `Self` in their defaults [E0735]
+
+enum Enum<const N: usize = { Self; 10 }> { }
+//~^ ERROR generic parameters cannot use `Self` in their defaults [E0735]
+
+union Union<const N: usize = { Self; 10 }> { not_empty: () }
+//~^ ERROR generic parameters cannot use `Self` in their defaults [E0735]
+
+fn main() {
+ let _: Struct;
+ let _: Enum;
+ let _: Union;
+}
--- /dev/null
+error[E0735]: generic parameters cannot use `Self` in their defaults
+ --> $DIR/default-const-param-cannot-reference-self.rs:3:34
+ |
+LL | struct Struct<const N: usize = { Self; 10 }>;
+ | ^^^^ `Self` in generic parameter default
+
+error[E0735]: generic parameters cannot use `Self` in their defaults
+ --> $DIR/default-const-param-cannot-reference-self.rs:6:30
+ |
+LL | enum Enum<const N: usize = { Self; 10 }> { }
+ | ^^^^ `Self` in generic parameter default
+
+error[E0735]: generic parameters cannot use `Self` in their defaults
+ --> $DIR/default-const-param-cannot-reference-self.rs:9:32
+ |
+LL | union Union<const N: usize = { Self; 10 }> { not_empty: () }
+ | ^^^^ `Self` in generic parameter default
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0735`.
#![feature(const_float_bits_conv)]
#![feature(const_float_classify)]
#![feature(const_trait_impl)]
-#![allow(incomplete_features)]
// Don't promote
const fn nop<T>(x: T) -> T { x }
// build-pass
#![crate_type = "lib"]
-#![allow(incomplete_features)]
#![feature(staged_api)]
#![feature(const_trait_impl)]
#![stable(feature = "foo", since = "1.0.0")]
--- /dev/null
+// check-pass
+
+#![feature(generic_associated_types)]
+#![allow(incomplete_features)]
+
+trait Marker {}
+
+impl Marker for u32 {}
+
+trait MyTrait {
+ type Item<'a>;
+}
+
+struct MyStruct;
+
+impl MyTrait for MyStruct {
+ type Item<'a> = u32;
+}
+
+fn ty_check<T>()
+where
+ T: MyTrait,
+ for<'a> T::Item<'a>: Marker
+{}
+
+fn main() {
+ ty_check::<MyStruct>();
+}
--- /dev/null
+// run-pass
+
+#![feature(generic_associated_types)]
+#![allow(incomplete_features)]
+
+pub trait Iter {
+ type Item<'a> where Self: 'a;
+
+ fn next<'a>(&'a mut self) -> Option<Self::Item<'a>>;
+
+ fn for_each<F>(mut self, mut f: F)
+ where Self: Sized, F: for<'a> FnMut(Self::Item<'a>)
+ {
+ while let Some(item) = self.next() {
+ f(item);
+ }
+ }
+}
+
+pub struct Windows<T> {
+ items: Vec<T>,
+ start: usize,
+ len: usize,
+}
+
+impl<T> Windows<T> {
+ pub fn new(items: Vec<T>, len: usize) -> Self {
+ Self { items, start: 0, len }
+ }
+}
+
+impl<T> Iter for Windows<T> {
+ type Item<'a> where T: 'a = &'a mut [T];
+
+ fn next<'a>(&'a mut self) -> Option<Self::Item<'a>> {
+ let slice = self.items.get_mut(self.start..self.start + self.len)?;
+ self.start += 1;
+ Some(slice)
+ }
+}
+
+fn main() {
+ Windows::new(vec![1, 2, 3, 4, 5], 3)
+ .for_each(|slice| println!("{:?}", slice));
+}
// compatibility concern.
struct Snobound<'a, P = Self> { x: Option<&'a P> }
-//~^ ERROR type parameters cannot use `Self` in their defaults [E0735]
+//~^ ERROR generic parameters cannot use `Self` in their defaults [E0735]
enum Enobound<'a, P = Self> { A, B(Option<&'a P>) }
-//~^ ERROR type parameters cannot use `Self` in their defaults [E0735]
+//~^ ERROR generic parameters cannot use `Self` in their defaults [E0735]
union Unobound<'a, P = Self> { x: i32, y: Option<&'a P> }
-//~^ ERROR type parameters cannot use `Self` in their defaults [E0735]
+//~^ ERROR generic parameters cannot use `Self` in their defaults [E0735]
// Disallowing `Self` in defaults sidesteps need to check the bounds
// on the defaults in cases like these.
struct Ssized<'a, P: Sized = [Self]> { x: Option<&'a P> }
-//~^ ERROR type parameters cannot use `Self` in their defaults [E0735]
+//~^ ERROR generic parameters cannot use `Self` in their defaults [E0735]
enum Esized<'a, P: Sized = [Self]> { A, B(Option<&'a P>) }
-//~^ ERROR type parameters cannot use `Self` in their defaults [E0735]
+//~^ ERROR generic parameters cannot use `Self` in their defaults [E0735]
union Usized<'a, P: Sized = [Self]> { x: i32, y: Option<&'a P> }
-//~^ ERROR type parameters cannot use `Self` in their defaults [E0735]
+//~^ ERROR generic parameters cannot use `Self` in their defaults [E0735]
fn demo_usages() {
// An ICE means you only get the error from the first line of the
-error[E0735]: type parameters cannot use `Self` in their defaults
+error[E0735]: generic parameters cannot use `Self` in their defaults
--> $DIR/issue-61631-default-type-param-cannot-reference-self.rs:13:25
|
LL | struct Snobound<'a, P = Self> { x: Option<&'a P> }
- | ^^^^ `Self` in type parameter default
+ | ^^^^ `Self` in generic parameter default
-error[E0735]: type parameters cannot use `Self` in their defaults
+error[E0735]: generic parameters cannot use `Self` in their defaults
--> $DIR/issue-61631-default-type-param-cannot-reference-self.rs:16:23
|
LL | enum Enobound<'a, P = Self> { A, B(Option<&'a P>) }
- | ^^^^ `Self` in type parameter default
+ | ^^^^ `Self` in generic parameter default
-error[E0735]: type parameters cannot use `Self` in their defaults
+error[E0735]: generic parameters cannot use `Self` in their defaults
--> $DIR/issue-61631-default-type-param-cannot-reference-self.rs:19:24
|
LL | union Unobound<'a, P = Self> { x: i32, y: Option<&'a P> }
- | ^^^^ `Self` in type parameter default
+ | ^^^^ `Self` in generic parameter default
-error[E0735]: type parameters cannot use `Self` in their defaults
+error[E0735]: generic parameters cannot use `Self` in their defaults
--> $DIR/issue-61631-default-type-param-cannot-reference-self.rs:25:31
|
LL | struct Ssized<'a, P: Sized = [Self]> { x: Option<&'a P> }
- | ^^^^ `Self` in type parameter default
+ | ^^^^ `Self` in generic parameter default
-error[E0735]: type parameters cannot use `Self` in their defaults
+error[E0735]: generic parameters cannot use `Self` in their defaults
--> $DIR/issue-61631-default-type-param-cannot-reference-self.rs:28:29
|
LL | enum Esized<'a, P: Sized = [Self]> { A, B(Option<&'a P>) }
- | ^^^^ `Self` in type parameter default
+ | ^^^^ `Self` in generic parameter default
-error[E0735]: type parameters cannot use `Self` in their defaults
+error[E0735]: generic parameters cannot use `Self` in their defaults
--> $DIR/issue-61631-default-type-param-cannot-reference-self.rs:31:30
|
LL | union Usized<'a, P: Sized = [Self]> { x: i32, y: Option<&'a P> }
- | ^^^^ `Self` in type parameter default
+ | ^^^^ `Self` in generic parameter default
error: aborting due to 6 previous errors
error[E0277]: the trait bound `isize: HasState` is not satisfied
- --> $DIR/issue-18611.rs:1:1
+ --> $DIR/issue-18611.rs:1:4
|
-LL | / fn add_state(op: <isize as HasState>::State) {
-LL | |
-LL | | }
- | |_^ the trait `HasState` is not implemented for `isize`
+LL | fn add_state(op: <isize as HasState>::State) {
+ | ^^^^^^^^^ the trait `HasState` is not implemented for `isize`
error: aborting due to previous error
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
- --> $DIR/issue-20831-debruijn.rs:28:33
+ --> $DIR/issue-20831-debruijn.rs:28:8
|
LL | fn subscribe(&mut self, t : Box<dyn Subscriber<Input=<Self as Publisher>::Output> + 'a>) {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime defined on the method body at 28:58...
--> $DIR/issue-20831-debruijn.rs:28:58
LL | impl<'a> Publisher<'a> for MyStruct<'a> {
| ^^
note: ...so that the types are compatible
- --> $DIR/issue-20831-debruijn.rs:28:33
+ --> $DIR/issue-20831-debruijn.rs:28:8
|
LL | fn subscribe(&mut self, t : Box<dyn Subscriber<Input=<Self as Publisher>::Output> + 'a>) {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^
= note: expected `Publisher<'_>`
found `Publisher<'_>`
--- /dev/null
+// compile-flags: -Zfuture-incompat-test -Zemit-future-incompat-report
+// check-pass
+
+// The `-Zfuture-incompat-test flag causes any normal warning to be included
+// in the future-incompatible report. The stderr output here should mention
+// the future incompatible report (as extracted by compiletest).
+
+fn main() {
+ let x = 1;
+}
--- /dev/null
+Future incompatibility report: Future breakage diagnostic:
+warning: unused variable: `x`
+ --> $DIR/future-incompat-test.rs:9:9
+ |
+LL | let x = 1;
+ | ^ help: if this is intentional, prefix it with an underscore: `_x`
+ |
+ = note: `-A unused-variables` implied by `-A unused`
+
#![feature(assert_matches)]
+use std::assert::assert_matches;
+
fn main() {
assert_matches!(1 + 1, 3, "1 + 1 definitely should be 3");
}
#![feature(assert_matches)]
+use std::assert::assert_matches;
+
fn main() {
assert!(matches!((), ()));
assert_matches!((), ());
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'d` due to conflicting requirements
- --> $DIR/normalization-bounds-error.rs:12:1
+ --> $DIR/normalization-bounds-error.rs:12:4
|
LL | fn visit_seq<'d, 'a: 'd>() -> <&'a () as Visitor<'d>>::Value {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^
|
note: first, the lifetime cannot outlive the lifetime `'d` as defined on the function body at 12:14...
--> $DIR/normalization-bounds-error.rs:12:14
LL | fn visit_seq<'d, 'a: 'd>() -> <&'a () as Visitor<'d>>::Value {}
| ^^
note: ...so that the types are compatible
- --> $DIR/normalization-bounds-error.rs:12:1
+ --> $DIR/normalization-bounds-error.rs:12:4
|
LL | fn visit_seq<'d, 'a: 'd>() -> <&'a () as Visitor<'d>>::Value {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^
= note: expected `Visitor<'d>`
found `Visitor<'_>`
warning: 5 warnings emitted
-Future incompatibility report: Future breakage date: None, diagnostic:
+Future incompatibility report: Future breakage diagnostic:
warning: using an old version of `time-macros-impl`
--> $DIR/time-macros-impl/src/lib.rs:5:32
|
= note: the `time-macros-impl` crate will stop compiling in futures version of Rust. Please update to the latest version of the `time` crate to avoid breakage
= note: this warning originates in the macro `impl_macros` (in Nightly builds, run with -Z macro-backtrace for more info)
-Future breakage date: None, diagnostic:
+Future breakage diagnostic:
warning: using an old version of `time-macros-impl`
--> $DIR/time-macros-impl-0.1.0/src/lib.rs:5:32
|
= note: the `time-macros-impl` crate will stop compiling in futures version of Rust. Please update to the latest version of the `time` crate to avoid breakage
= note: this warning originates in the macro `impl_macros` (in Nightly builds, run with -Z macro-backtrace for more info)
-Future breakage date: None, diagnostic:
+Future breakage diagnostic:
warning: using an old version of `js-sys`
--> $DIR/js-sys-0.3.17/src/lib.rs:5:32
|
= note: older versions of the `js-sys` crate will stop compiling in future versions of Rust; please update to `js-sys` v0.3.40 or above
= note: this warning originates in the macro `arrays` (in Nightly builds, run with -Z macro-backtrace for more info)
-Future breakage date: None, diagnostic:
+Future breakage diagnostic:
warning: using an old version of `actix-web`
--> $DIR/actix-web/src/extract.rs:5:34
|
= note: the version of `actix-web` you are using might stop compiling in future versions of Rust; please update to the latest version of the `actix-web` crate to avoid breakage
= note: this warning originates in the macro `tuple_from_req` (in Nightly builds, run with -Z macro-backtrace for more info)
-Future breakage date: None, diagnostic:
+Future breakage diagnostic:
warning: using an old version of `actix-web`
--> $DIR/actix-web-2.0.0/src/extract.rs:5:34
|
warning: 1 warning emitted
-Future incompatibility report: Future breakage date: None, diagnostic:
+Future incompatibility report: Future breakage diagnostic:
warning: using `procedural-masquerade` crate
--> $DIR/issue-73933-procedural-masquerade.rs:8:6
|
fn main() {
- assert(true);
- //~^ ERROR expected function, found macro `assert`
+ assert_eq(1, 1);
+ //~^ ERROR expected function, found macro `assert_eq`
}
-error[E0423]: expected function, found macro `assert`
+error[E0423]: expected function, found macro `assert_eq`
--> $DIR/resolve-hint-macro.rs:2:5
|
-LL | assert(true);
- | ^^^^^^ not a function
+LL | assert_eq(1, 1);
+ | ^^^^^^^^^ not a function
|
help: use `!` to invoke the macro
|
-LL | assert!(true);
- | ^
+LL | assert_eq!(1, 1);
+ | ^
error: aborting due to previous error
// FIXME: This test should fail since, within a const impl of `Foo`, the bound on `Foo::Bar` should
// require a const impl of `Add` for the associated type.
-#![allow(incomplete_features)]
#![feature(const_trait_impl)]
struct NonConstAdd(i32);
#![feature(const_trait_impl)]
-#![allow(incomplete_features)]
#[default_method_body_is_const] //~ ERROR attribute should be applied
trait A {
error: attribute should be applied to a trait method with body
- --> $DIR/attr-misuse.rs:4:1
+ --> $DIR/attr-misuse.rs:3:1
|
LL | #[default_method_body_is_const]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |_- not a trait method or missing a body
error: attribute should be applied to a trait method with body
- --> $DIR/attr-misuse.rs:13:1
+ --> $DIR/attr-misuse.rs:12:1
|
LL | #[default_method_body_is_const]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| ------------ not a trait method or missing a body
error: attribute should be applied to a trait method with body
- --> $DIR/attr-misuse.rs:6:5
+ --> $DIR/attr-misuse.rs:5:5
|
LL | #[default_method_body_is_const]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#![feature(const_trait_impl)]
-#![allow(incomplete_features)]
pub trait MyTrait {
fn func(self);
#![feature(const_trait_impl)]
-#![allow(incomplete_features)]
#![feature(staged_api)]
#![stable(feature = "rust1", since = "1.0.0")]
-#![allow(incomplete_features)]
#![feature(const_trait_impl)]
pub trait Plus {
error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
- --> $DIR/call-const-trait-method-fail.rs:25:5
+ --> $DIR/call-const-trait-method-fail.rs:24:5
|
LL | a.plus(b)
| ^^^^^^^^^
// run-pass
-#![allow(incomplete_features)]
#![feature(const_trait_impl)]
struct Int(i32);
#![feature(const_trait_impl)]
#![feature(const_fn_trait_bound)]
-#![allow(incomplete_features)]
struct S;
// ignore-test
#![feature(const_trait_impl)]
-#![allow(incomplete_features)]
struct S;
#![feature(const_trait_impl)]
#![feature(const_fn_trait_bound)]
-#![allow(incomplete_features)]
struct S;
-#![allow(incomplete_features)]
#![feature(const_trait_impl)]
pub struct Int(i32);
error[E0119]: conflicting implementations of trait `std::ops::Add` for type `i32`
- --> $DIR/const-and-non-const-impl.rs:6:1
+ --> $DIR/const-and-non-const-impl.rs:5:1
|
LL | impl const std::ops::Add for i32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- impl Add for i32;
error[E0119]: conflicting implementations of trait `std::ops::Add` for type `Int`
- --> $DIR/const-and-non-const-impl.rs:24:1
+ --> $DIR/const-and-non-const-impl.rs:23:1
|
LL | impl std::ops::Add for Int {
| -------------------------- first implementation here
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Int`
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
- --> $DIR/const-and-non-const-impl.rs:6:1
+ --> $DIR/const-and-non-const-impl.rs:5:1
|
LL | impl const std::ops::Add for i32 {
| ^^^^^^^^^^^-------------^^^^^---
-#![allow(incomplete_features)]
#![feature(const_trait_impl)]
struct S;
error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
- --> $DIR/const-check-fns-in-const-impl.rs:12:16
+ --> $DIR/const-check-fns-in-const-impl.rs:11:16
|
LL | fn foo() { non_const() }
| ^^^^^^^^^^^
#![feature(const_trait_impl)]
#![feature(const_fn_trait_bound)] // FIXME is this needed?
-#![allow(incomplete_features)]
trait ConstDefaultFn: Sized {
fn b(self);
error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
- --> $DIR/const-default-method-bodies.rs:26:5
+ --> $DIR/const-default-method-bodies.rs:25:5
|
LL | NonConstImpl.a();
| ^^^^^^^^^^^^^^^^
#![feature(const_trait_impl)]
-#![allow(incomplete_features)]
struct Foo;
error: expected identifier, found keyword `impl`
- --> $DIR/const-impl-norecover.rs:6:7
+ --> $DIR/const-impl-norecover.rs:5:7
|
LL | const impl Foo {
| ^^^^ expected identifier, found keyword
#![feature(const_trait_impl)]
-#![allow(incomplete_features)]
trait Foo {}
error: expected identifier, found keyword `impl`
- --> $DIR/const-impl-recovery.rs:6:7
+ --> $DIR/const-impl-recovery.rs:5:7
|
LL | const impl Foo for i32 {}
| ^^^^ expected identifier, found keyword
|-- ^^^^^
error: expected identifier, found keyword `impl`
- --> $DIR/const-impl-recovery.rs:10:7
+ --> $DIR/const-impl-recovery.rs:9:7
|
LL | const impl<T: Foo> Bar for T {}
| ^^^^ expected identifier, found keyword
error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
- --> $DIR/cross-crate.rs:16:5
+ --> $DIR/cross-crate.rs:15:5
|
LL | NonConst.func();
| ^^^^^^^^^^^^^^^
// revisions: stock gated
#![cfg_attr(gated, feature(const_trait_impl))]
-#![allow(incomplete_features)]
// aux-build: cross-crate.rs
extern crate cross_crate;
error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
- --> $DIR/cross-crate.rs:16:5
+ --> $DIR/cross-crate.rs:15:5
|
LL | NonConst.func();
| ^^^^^^^^^^^^^^^
error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
- --> $DIR/cross-crate.rs:18:5
+ --> $DIR/cross-crate.rs:17:5
|
LL | Const.func();
| ^^^^^^^^^^^^
error: fatal error triggered by #[rustc_error]
- --> $DIR/feature-gate.rs:14:1
+ --> $DIR/feature-gate.rs:13:1
|
LL | fn main() {}
| ^^^^^^^^^
// gate-test-const_trait_impl
#![cfg_attr(gated, feature(const_trait_impl))]
-#![allow(incomplete_features)]
#![feature(rustc_attrs)]
struct S;
error[E0658]: const trait impls are experimental
- --> $DIR/feature-gate.rs:10:6
+ --> $DIR/feature-gate.rs:9:6
|
LL | impl const T for S {}
| ^^^^^
// run-pass
-#![allow(incomplete_features)]
#![feature(const_trait_impl)]
#![feature(const_fn_trait_bound)]
// Regression test for #69615.
#![feature(const_trait_impl)]
-#![allow(incomplete_features)]
pub trait MyTrait {
fn method(&self) -> Option<()>;
error[E0744]: `?` is not allowed in a `const fn`
- --> $DIR/hir-const-check.rs:12:9
+ --> $DIR/hir-const-check.rs:11:9
|
LL | Some(())?;
| ^^^^^^^^^
#![feature(const_trait_impl)]
-#![allow(incomplete_features)]
trait Tr {
fn req(&self);
error: const trait implementations may not use non-const default functions
- --> $DIR/impl-with-default-fn.rs:18:1
+ --> $DIR/impl-with-default-fn.rs:17:1
|
LL | / impl const Tr for S {
LL | | fn req(&self) {}
= note: `prov` not implemented
error: const trait implementations may not use non-const default functions
- --> $DIR/impl-with-default-fn.rs:33:1
+ --> $DIR/impl-with-default-fn.rs:32:1
|
LL | / impl const Tr for u32 {
LL | | fn req(&self) {}
= note: `prov` not implemented
error[E0046]: not all trait items implemented, missing: `req`
- --> $DIR/impl-with-default-fn.rs:27:1
+ --> $DIR/impl-with-default-fn.rs:26:1
|
LL | fn req(&self);
| -------------- `req` from trait
-#![allow(incomplete_features)]
#![feature(allow_internal_unstable)]
#![feature(const_add)]
#![feature(const_trait_impl)]
error: trait methods cannot be stable const fn
- --> $DIR/stability.rs:14:5
+ --> $DIR/stability.rs:13:5
|
LL | / fn sub(self, rhs: Self) -> Self {
LL | |
| |_____^
error: `<Int as Add>::add` is not yet stable as a const fn
- --> $DIR/stability.rs:32:5
+ --> $DIR/stability.rs:31:5
|
LL | Int(1i32) + Int(2i32)
| ^^^^^^^^^^^^^^^^^^^^^
#![feature(const_trait_bound_opt_out)]
#![feature(const_trait_impl)]
-#![allow(incomplete_features)]
// For now, this parses since an error does not occur until AST lowering.
impl ?const T {}
--- /dev/null
+mod option {
+ pub enum O<T> {
+ Some(T),
+ None,
+ }
+}
+
+fn main() {
+ let _: option::O<()> = (); //~ ERROR 9:28: 9:30: mismatched types [E0308]
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/suggest-full-enum-variant-for-local-module.rs:9:28
+ |
+LL | let _: option::O<()> = ();
+ | ------------- ^^
+ | | |
+ | | expected enum `O`, found `()`
+ | | help: try using a variant of the expected enum: `option::O::Some(())`
+ | expected due to this
+ |
+ = note: expected enum `O<()>`
+ found unit type `()`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
impl<'a> Output<'a> for &'a str {}
fn cool_fn<'a>(arg: &'a str) -> OpaqueOutputImpl<'a> {
+ //~^ ERROR: concrete type differs from previous defining opaque type use
let out: OpaqueOutputImpl<'a> = arg;
arg
}
LL | type OpaqueOutputImpl<'a> = impl Output<'a> + 'a;
| ^^^^^^^^^^^^^^^^^^^^
+error: concrete type differs from previous defining opaque type use
+ --> $DIR/issue-85113.rs:14:1
+ |
+LL | fn cool_fn<'a>(arg: &'a str) -> OpaqueOutputImpl<'a> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&'<empty> str`, got `&'a str`
+ |
+note: previous use here
+ --> $DIR/issue-85113.rs:14:1
+ |
+LL | fn cool_fn<'a>(arg: &'a str) -> OpaqueOutputImpl<'a> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
error[E0477]: the type `&'<empty> str` does not fulfill the required lifetime
--> $DIR/issue-85113.rs:5:29
|
= note: expected `Output<'a>`
found `Output<'_>`
-error: aborting due to 3 previous errors
+error: aborting due to 4 previous errors
Some errors have detailed explanations: E0477, E0495, E0700.
For more information about an error, try `rustc --explain E0477`.
error[E0277]: the trait bound `(): Foo` is not satisfied
- --> $DIR/wf-foreign-fn-decl-ret.rs:11:5
+ --> $DIR/wf-foreign-fn-decl-ret.rs:11:12
|
LL | pub fn lint_me() -> <() as Foo>::Assoc;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `()`
+ | ^^^^^^^ the trait `Foo` is not implemented for `()`
error[E0277]: the trait bound `u32: Unsatisfied` is not satisfied
--> $DIR/wf-foreign-fn-decl-ret.rs:14:32
-Subproject commit 3ebb5f15a940810f250b68821149387af583a79e
+Subproject commit 27277d966b3cfa454d6dea7f724cb961c036251c
[`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero
[`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len
[`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer
+[`rc_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex
[`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
[`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
[`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
[`string_from_utf8_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_from_utf8_as_bytes
[`string_lit_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_lit_as_bytes
[`string_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_to_string
+[`strlen_on_c_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#strlen_on_c_strings
[`struct_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools
[`suboptimal_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#suboptimal_flops
[`suspicious_arithmetic_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl
use std::path::PathBuf;
-use clippy_utils::diagnostics::span_lint;
-use clippy_utils::run_lints;
+use clippy_utils::{diagnostics::span_lint, is_lint_allowed};
use rustc_hir::{hir_id::CRATE_HIR_ID, Crate};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
impl LateLintPass<'_> for CargoCommonMetadata {
fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
- if !run_lints(cx, &[CARGO_COMMON_METADATA], CRATE_HIR_ID) {
+ if is_lint_allowed(cx, CARGO_COMMON_METADATA, CRATE_HIR_ID) {
return;
}
use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
use clippy_utils::{
both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, in_macro, is_else_clause,
- run_lints, search_same, ContainsName, SpanlessEq, SpanlessHash,
+ is_lint_allowed, search_same, ContainsName, SpanlessEq, SpanlessHash,
};
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashSet;
///
/// **Why is this bad?** Duplicate code is less maintainable.
///
- /// **Known problems:** Hopefully none.
+ /// **Known problems:**
+ /// * The lint doesn't check if the moved expressions modify values that are beeing used in
+ /// the if condition. The suggestion can in that case modify the behavior of the program.
+ /// See [rust-clippy#7452](https://github.com/rust-lang/rust-clippy/issues/7452)
///
/// **Example:**
/// ```ignore
if block_expr_eq;
if l_stmts.len() == r_stmts.len();
if l_stmts.len() == current_start_eq;
- if run_lints(cx, &[IF_SAME_THEN_ELSE], win[0].hir_id);
- if run_lints(cx, &[IF_SAME_THEN_ELSE], win[1].hir_id);
+ if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, win[0].hir_id);
+ if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, win[1].hir_id);
then {
span_lint_and_note(
cx,
expr_eq &= block_expr_eq;
}
- let has_expr = blocks[0].expr.is_some();
- if has_expr && !expr_eq {
+ if !expr_eq {
end_eq = 0;
}
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet;
+use clippy_utils::numeric_literal;
+use clippy_utils::source::snippet_opt;
use if_chain::if_chain;
use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
use rustc_errors::Applicability;
if let Some(ty_bound) = self.ty_bounds.last();
if matches!(lit.node,
LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed));
- if !ty_bound.is_integral();
+ if !ty_bound.is_numeric();
then {
- let suffix = match lit_ty.kind() {
- ty::Int(IntTy::I32) => "i32",
- ty::Float(FloatTy::F64) => "f64",
+ let (suffix, is_float) = match lit_ty.kind() {
+ ty::Int(IntTy::I32) => ("i32", false),
+ ty::Float(FloatTy::F64) => ("f64", true),
// Default numeric fallback never results in other types.
_ => return,
};
- let sugg = format!("{}_{}", snippet(self.cx, lit.span, ""), suffix);
+ let src = if let Some(src) = snippet_opt(self.cx, lit.span) {
+ src
+ } else {
+ match lit.node {
+ LitKind::Int(src, _) => format!("{}", src),
+ LitKind::Float(src, _) => format!("{}", src),
+ _ => return,
+ }
+ };
+ let sugg = numeric_literal::format(&src, Some(suffix), is_float);
span_lint_and_sugg(
self.cx,
DEFAULT_NUMERIC_FALLBACK,
}
impl<'tcx> TyBound<'tcx> {
- fn is_integral(self) -> bool {
+ fn is_numeric(self) -> bool {
match self {
TyBound::Any => true,
- TyBound::Ty(t) => t.is_integral(),
+ TyBound::Ty(t) => t.is_numeric(),
TyBound::Nothing => false,
}
}
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::peel_mid_ty_refs;
-use clippy_utils::{get_parent_node, in_macro, is_allowed};
+use clippy_utils::{get_parent_node, in_macro, is_lint_allowed};
use rustc_ast::util::parser::PREC_PREFIX;
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, UnOp};
match kind {
RefOp::Method(target_mut)
- if !is_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
+ if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
&& is_linted_explicit_deref_position(parent, expr.hir_id, expr.span) =>
{
self.state = Some((
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_then};
use clippy_utils::paths;
use clippy_utils::ty::{implements_trait, is_copy};
-use clippy_utils::{get_trait_def_id, is_allowed, is_automatically_derived, match_def_path};
+use clippy_utils::{get_trait_def_id, is_automatically_derived, is_lint_allowed, match_def_path};
use if_chain::if_chain;
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, NestedVisitorMap, Visitor};
if let ty::Adt(def, _) = ty.kind();
if let Some(local_def_id) = def.did.as_local();
let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
- if !is_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id);
+ if !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id);
if cx.tcx.inherent_impls(def.did)
.iter()
.map(|imp_did| item_from_def_id(cx, *imp_did))
FootnoteReference(text) | Text(text) => {
let (begin, span) = get_current_span(spans, range.start);
paragraph_span = paragraph_span.with_hi(span.hi());
- ticks_unbalanced |= text.contains('`');
+ ticks_unbalanced |= text.contains('`') && !in_code;
if Some(&text) == in_link.as_ref() || ticks_unbalanced {
// Probably a link of the form `<http://example.com>`
// Which are represented as a link to "http://example.com" with
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()) {
+ let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) {
Ok(p) => p,
Err(errs) => {
for mut err in errs {
// Because of the global session, we need to create a new session in a different thread with
// the edition we need.
let text = text.to_owned();
- if thread::spawn(move || has_needless_main(text, edition)).join().expect("thread::spawn failed") {
+ if thread::spawn(move || has_needless_main(text, edition))
+ .join()
+ .expect("thread::spawn failed")
+ {
span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
}
}
use rustc_span::symbol::kw;
use rustc_target::abi::LayoutOf;
use rustc_target::spec::abi::Abi;
-use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
+use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
#[derive(Copy, Clone)]
pub struct BoxedLocal {
}
impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
- fn consume(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, mode: ConsumeMode) {
+ fn consume(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) {
if cmt.place.projections.is_empty() {
if let PlaceBase::Local(lid) = cmt.place.base {
- if let ConsumeMode::Move = mode {
- // moved out or in. clearly can't be localized
- self.set.remove(&lid);
- }
+ self.set.remove(&lid);
let map = &self.cx.tcx.hir();
if let Some(Node::Binding(_)) = map.find(cmt.hir_id) {
if self.set.contains(&lid) {
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::higher;
use clippy_utils::higher::VecArgs;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{implements_trait, type_is_unsafe_function};
+use clippy_utils::usage::UsedAfterExprVisitor;
+use clippy_utils::{get_enclosing_loop_or_closure, higher};
use clippy_utils::{is_adjusted, iter_input_pats};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{def_id, Expr, ExprKind, Param, PatKind, QPath};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{self, ClosureKind, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
}
}
-fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) {
+fn check_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Closure(_, decl, eid, _, _) = expr.kind {
let body = cx.tcx.hir().body(eid);
let ex = &body.value;
then {
span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
- if let Some(snippet) = snippet_opt(cx, caller.span) {
+ if let Some(mut snippet) = snippet_opt(cx, caller.span) {
+ if_chain! {
+ if let ty::Closure(_, substs) = fn_ty.kind();
+ if let ClosureKind::FnMut = substs.as_closure().kind();
+ if UsedAfterExprVisitor::is_found(cx, caller)
+ || get_enclosing_loop_or_closure(cx.tcx, expr).is_some();
+
+ then {
+ // Mutable closure is used after current expr; we cannot consume it.
+ snippet = format!("&mut {}", snippet);
+ }
+ }
diag.span_suggestion(
expr.span,
"replace the closure with the function itself",
-use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::higher::FormatArgsExpn;
use clippy_utils::{is_expn_of, match_function_call, paths};
use if_chain::if_chain;
-use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
-use rustc_hir::{BorrowKind, Expr, ExprKind};
+use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if_chain! {
// match call to unwrap
- if let ExprKind::MethodCall(unwrap_fun, _, unwrap_args, _) = expr.kind;
+ if let ExprKind::MethodCall(unwrap_fun, _, [write_call], _) = expr.kind;
if unwrap_fun.ident.name == sym::unwrap;
// match call to write_fmt
- if !unwrap_args.is_empty();
- if let ExprKind::MethodCall(write_fun, _, write_args, _) =
- unwrap_args[0].kind;
+ if let ExprKind::MethodCall(write_fun, _, [write_recv, write_arg], _) = write_call.kind;
if write_fun.ident.name == sym!(write_fmt);
// match calls to std::io::stdout() / std::io::stderr ()
- if !write_args.is_empty();
- if let Some(dest_name) = if match_function_call(cx, &write_args[0], &paths::STDOUT).is_some() {
+ if let Some(dest_name) = if match_function_call(cx, write_recv, &paths::STDOUT).is_some() {
Some("stdout")
- } else if match_function_call(cx, &write_args[0], &paths::STDERR).is_some() {
+ } else if match_function_call(cx, write_recv, &paths::STDERR).is_some() {
Some("stderr")
} else {
None
};
+ if let Some(format_args) = FormatArgsExpn::parse(write_arg);
then {
- let write_span = unwrap_args[0].span;
let calling_macro =
// ordering is important here, since `writeln!` uses `write!` internally
- if is_expn_of(write_span, "writeln").is_some() {
+ if is_expn_of(write_call.span, "writeln").is_some() {
Some("writeln")
- } else if is_expn_of(write_span, "write").is_some() {
+ } else if is_expn_of(write_call.span, "write").is_some() {
Some("write")
} else {
None
// We need to remove the last trailing newline from the string because the
// underlying `fmt::write` function doesn't know whether `println!` or `print!` was
// used.
- if let Some(mut write_output) = write_output_string(write_args) {
+ let (used, sugg_mac) = if let Some(macro_name) = calling_macro {
+ (
+ format!("{}!({}(), ...)", macro_name, dest_name),
+ macro_name.replace("write", "print"),
+ )
+ } else {
+ (
+ format!("{}().write_fmt(...)", dest_name),
+ "print".into(),
+ )
+ };
+ let msg = format!("use of `{}.unwrap()`", used);
+ if let [write_output] = *format_args.format_string_symbols {
+ let mut write_output = write_output.to_string();
if write_output.ends_with('\n') {
write_output.pop();
}
- if let Some(macro_name) = calling_macro {
- span_lint_and_sugg(
- cx,
- EXPLICIT_WRITE,
- expr.span,
- &format!(
- "use of `{}!({}(), ...).unwrap()`",
- macro_name,
- dest_name
- ),
- "try this",
- format!("{}{}!(\"{}\")", prefix, macro_name.replace("write", "print"), write_output.escape_default()),
- Applicability::MachineApplicable
- );
- } else {
- span_lint_and_sugg(
- cx,
- EXPLICIT_WRITE,
- expr.span,
- &format!("use of `{}().write_fmt(...).unwrap()`", dest_name),
- "try this",
- format!("{}print!(\"{}\")", prefix, write_output.escape_default()),
- Applicability::MachineApplicable
- );
- }
+ let sugg = format!("{}{}!(\"{}\")", prefix, sugg_mac, write_output.escape_default());
+ span_lint_and_sugg(
+ cx,
+ EXPLICIT_WRITE,
+ expr.span,
+ &msg,
+ "try this",
+ sugg,
+ Applicability::MachineApplicable
+ );
} else {
// We don't have a proper suggestion
- if let Some(macro_name) = calling_macro {
- span_lint(
- cx,
- EXPLICIT_WRITE,
- expr.span,
- &format!(
- "use of `{}!({}(), ...).unwrap()`. Consider using `{}{}!` instead",
- macro_name,
- dest_name,
- prefix,
- macro_name.replace("write", "print")
- )
- );
- } else {
- span_lint(
- cx,
- EXPLICIT_WRITE,
- expr.span,
- &format!("use of `{}().write_fmt(...).unwrap()`. Consider using `{}print!` instead", dest_name, prefix),
- );
- }
+ let help = format!("consider using `{}{}!` instead", prefix, sugg_mac);
+ span_lint_and_help(cx, EXPLICIT_WRITE, expr.span, &msg, None, &help);
}
-
}
}
}
}
-
-// Extract the output string from the given `write_args`.
-fn write_output_string(write_args: &[Expr<'_>]) -> Option<String> {
- if_chain! {
- // Obtain the string that should be printed
- if write_args.len() > 1;
- if let ExprKind::Call(_, output_args) = write_args[1].kind;
- if !output_args.is_empty();
- if let ExprKind::AddrOf(BorrowKind::Ref, _, output_string_expr) = output_args[0].kind;
- if let ExprKind::Array(string_exprs) = output_string_expr.kind;
- // we only want to provide an automatic suggestion for simple (non-format) strings
- if string_exprs.len() == 1;
- if let ExprKind::Lit(ref lit) = string_exprs[0].kind;
- if let LitKind::Str(ref write_output, _) = lit.node;
- then {
- return Some(write_output.to_string())
- }
- }
- None
-}
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::paths;
-use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher::FormatExpn;
+use clippy_utils::last_path_segment;
+use clippy_utils::source::{snippet_opt, snippet_with_applicability};
use clippy_utils::sugg::Sugg;
-use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_expn_of, last_path_segment, match_def_path, match_function_call};
use if_chain::if_chain;
-use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
-use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, MatchSource, PatKind};
-use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_hir::{BorrowKind, Expr, ExprKind, QPath};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
-use rustc_span::sym;
+use rustc_span::symbol::kw;
+use rustc_span::{sym, Span};
declare_clippy_lint! {
/// **What it does:** Checks for the use of `format!("string literal with no
impl<'tcx> LateLintPass<'tcx> for UselessFormat {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- let span = match is_expn_of(expr.span, "format") {
- Some(s) if !s.from_expansion() => s,
+ let FormatExpn { call_site, format_args } = match FormatExpn::parse(expr) {
+ Some(e) if !e.call_site.from_expansion() => e,
_ => return,
};
- // Operate on the only argument of `alloc::fmt::format`.
- if let Some(sugg) = on_new_v1(cx, expr) {
- span_useless_format(cx, span, "consider using `.to_string()`", sugg);
- } else if let Some(sugg) = on_new_v1_fmt(cx, expr) {
- span_useless_format(cx, span, "consider using `.to_string()`", sugg);
- }
- }
-}
-
-fn span_useless_format<T: LintContext>(cx: &T, span: Span, help: &str, mut sugg: String) {
- let to_replace = span.source_callsite();
-
- // The callsite span contains the statement semicolon for some reason.
- let snippet = snippet(cx, to_replace, "..");
- if snippet.ends_with(';') {
- sugg.push(';');
- }
-
- span_lint_and_then(cx, USELESS_FORMAT, span, "useless use of `format!`", |diag| {
- diag.span_suggestion(
- to_replace,
- help,
- sugg,
- Applicability::MachineApplicable, // snippet
- );
- });
-}
-
-fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) -> Option<String> {
- if_chain! {
- if let ExprKind::AddrOf(BorrowKind::Ref, _, format_args) = expr.kind;
- if let ExprKind::Array(elems) = arms[0].body.kind;
- if elems.len() == 1;
- if let Some(args) = match_function_call(cx, &elems[0], &paths::FMT_ARGUMENTV1_NEW);
- // matches `core::fmt::Display::fmt`
- if args.len() == 2;
- if let ExprKind::Path(ref qpath) = args[1].kind;
- if let Some(did) = cx.qpath_res(qpath, args[1].hir_id).opt_def_id();
- if match_def_path(cx, did, &paths::DISPLAY_FMT_METHOD);
- // check `(arg0,)` in match block
- if let PatKind::Tuple(pats, None) = arms[0].pat.kind;
- if pats.len() == 1;
- then {
- let ty = cx.typeck_results().pat_ty(pats[0]).peel_refs();
- if *ty.kind() != rustc_middle::ty::Str && !is_type_diagnostic_item(cx, ty, sym::string_type) {
- return None;
- }
- if let ExprKind::Lit(ref lit) = format_args.kind {
- if let LitKind::Str(ref s, _) = lit.node {
- return Some(format!("{:?}.to_string()", s.as_str()));
+ let mut applicability = Applicability::MachineApplicable;
+ if format_args.value_args.is_empty() {
+ if_chain! {
+ if let [e] = &*format_args.format_string_parts;
+ if let ExprKind::Lit(lit) = &e.kind;
+ if let Some(s_src) = snippet_opt(cx, lit.span);
+ then {
+ // Simulate macro expansion, converting {{ and }} to { and }.
+ let s_expand = s_src.replace("{{", "{").replace("}}", "}");
+ let sugg = format!("{}.to_string()", s_expand);
+ span_useless_format(cx, call_site, sugg, applicability);
}
- } else {
- let sugg = Sugg::hir(cx, format_args, "<arg>");
- if let ExprKind::MethodCall(path, _, _, _) = format_args.kind {
- if path.ident.name == sym!(to_string) {
- return Some(format!("{}", sugg));
- }
- } else if let ExprKind::Binary(..) = format_args.kind {
- return Some(format!("{}", sugg));
+ }
+ } else if let [value] = *format_args.value_args {
+ if_chain! {
+ if format_args.format_string_symbols == [kw::Empty];
+ if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
+ ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::string_type, adt.did),
+ ty::Str => true,
+ _ => false,
+ };
+ if format_args.args.iter().all(|e| is_display_arg(e));
+ if format_args.fmt_expr.map_or(true, |e| check_unformatted(e));
+ then {
+ let is_new_string = match value.kind {
+ ExprKind::Binary(..) => true,
+ ExprKind::MethodCall(path, ..) => path.ident.name.as_str() == "to_string",
+ _ => false,
+ };
+ let sugg = if is_new_string {
+ snippet_with_applicability(cx, value.span, "..", &mut applicability).into_owned()
+ } else {
+ let sugg = Sugg::hir_with_applicability(cx, value, "<arg>", &mut applicability);
+ format!("{}.to_string()", sugg.maybe_par())
+ };
+ span_useless_format(cx, call_site, sugg, applicability);
}
- return Some(format!("{}.to_string()", sugg.maybe_par()));
}
- }
+ };
}
- None
}
-fn on_new_v1<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<String> {
- if_chain! {
- if let Some(args) = match_function_call(cx, expr, &paths::FMT_ARGUMENTS_NEW_V1);
- if args.len() == 2;
- // Argument 1 in `new_v1()`
- if let ExprKind::AddrOf(BorrowKind::Ref, _, arr) = args[0].kind;
- if let ExprKind::Array(pieces) = arr.kind;
- if pieces.len() == 1;
- if let ExprKind::Lit(ref lit) = pieces[0].kind;
- if let LitKind::Str(ref s, _) = lit.node;
- // Argument 2 in `new_v1()`
- if let ExprKind::AddrOf(BorrowKind::Ref, _, arg1) = args[1].kind;
- if let ExprKind::Match(matchee, arms, MatchSource::Normal) = arg1.kind;
- if arms.len() == 1;
- if let ExprKind::Tup(tup) = matchee.kind;
- then {
- // `format!("foo")` expansion contains `match () { () => [], }`
- if tup.is_empty() {
- if let Some(s_src) = snippet_opt(cx, lit.span) {
- // Simulate macro expansion, converting {{ and }} to { and }.
- let s_expand = s_src.replace("{{", "{").replace("}}", "}");
- return Some(format!("{}.to_string()", s_expand));
- }
- } else if s.as_str().is_empty() {
- return on_argumentv1_new(cx, &tup[0], arms);
- }
- }
+fn span_useless_format(cx: &LateContext<'_>, span: Span, mut sugg: String, mut applicability: Applicability) {
+ // The callsite span contains the statement semicolon for some reason.
+ if snippet_with_applicability(cx, span, "..", &mut applicability).ends_with(';') {
+ sugg.push(';');
}
- None
+
+ span_lint_and_sugg(
+ cx,
+ USELESS_FORMAT,
+ span,
+ "useless use of `format!`",
+ "consider using `.to_string()`",
+ sugg,
+ applicability,
+ );
}
-fn on_new_v1_fmt<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<String> {
+fn is_display_arg(expr: &Expr<'_>) -> bool {
if_chain! {
- if let Some(args) = match_function_call(cx, expr, &paths::FMT_ARGUMENTS_NEW_V1_FORMATTED);
- if args.len() == 3;
- if check_unformatted(&args[2]);
- // Argument 1 in `new_v1_formatted()`
- if let ExprKind::AddrOf(BorrowKind::Ref, _, arr) = args[0].kind;
- if let ExprKind::Array(pieces) = arr.kind;
- if pieces.len() == 1;
- if let ExprKind::Lit(ref lit) = pieces[0].kind;
- if let LitKind::Str(..) = lit.node;
- // Argument 2 in `new_v1_formatted()`
- if let ExprKind::AddrOf(BorrowKind::Ref, _, arg1) = args[1].kind;
- if let ExprKind::Match(matchee, arms, MatchSource::Normal) = arg1.kind;
- if arms.len() == 1;
- if let ExprKind::Tup(tup) = matchee.kind;
- then {
- return on_argumentv1_new(cx, &tup[0], arms);
- }
+ if let ExprKind::Call(_, [_, fmt]) = expr.kind;
+ if let ExprKind::Path(QPath::Resolved(_, path)) = fmt.kind;
+ if let [.., t, _] = path.segments;
+ if t.ident.name.as_str() == "Display";
+ then { true } else { false }
}
- None
}
/// Checks if the expression matches
fn check_unformatted(expr: &Expr<'_>) -> bool {
if_chain! {
if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind;
- if let ExprKind::Array(exprs) = expr.kind;
- if exprs.len() == 1;
+ if let ExprKind::Array([expr]) = expr.kind;
// struct `core::fmt::rt::v1::Argument`
- if let ExprKind::Struct(_, fields, _) = exprs[0].kind;
+ if let ExprKind::Struct(_, fields, _) = expr.kind;
if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format);
// struct `core::fmt::rt::v1::FormatSpec`
if let ExprKind::Struct(_, fields, _) = format_field.expr.kind;
//! lint on inherent implementations
use clippy_utils::diagnostics::span_lint_and_note;
-use clippy_utils::{in_macro, is_allowed};
+use clippy_utils::{in_macro, is_lint_allowed};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::{def_id::LocalDefId, Crate, Item, ItemKind, Node};
use rustc_lint::{LateContext, LateLintPass};
.filter(|(&id, impls)| {
impls.len() > 1
// Check for `#[allow]` on the type definition
- && !is_allowed(
+ && !is_lint_allowed(
cx,
MULTIPLE_INHERENT_IMPL,
cx.tcx.hir().local_def_id_to_hir_id(id),
..
}) = cx.tcx.hir().get(id)
{
- (!in_macro(span) && impl_item.generics.params.is_empty() && !is_allowed(cx, MULTIPLE_INHERENT_IMPL, id))
+ (!in_macro(span) && impl_item.generics.params.is_empty() && !is_lint_allowed(cx, MULTIPLE_INHERENT_IMPL, id))
.then(|| span)
} else {
None
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{get_item_name, get_parent_as_impl, is_allowed};
+use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
if let Some(ty_id) = cx.qpath_res(ty_path, imp.self_ty.hir_id).opt_def_id();
if let Some(local_id) = ty_id.as_local();
let ty_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id);
- if !is_allowed(cx, LEN_WITHOUT_IS_EMPTY, ty_hir_id);
+ if !is_lint_allowed(cx, LEN_WITHOUT_IS_EMPTY, ty_hir_id);
if let Some(output) = parse_len_output(cx, cx.tcx.fn_sig(item.def_id).skip_binder());
then {
let (name, kind) = match cx.tcx.hir().find(ty_hir_id) {
mod slow_vector_initialization;
mod stable_sort_primitive;
mod strings;
+mod strlen_on_c_strings;
mod suspicious_operation_groupings;
mod suspicious_trait_impl;
mod swap;
strings::STRING_LIT_AS_BYTES,
strings::STRING_TO_STRING,
strings::STR_TO_STRING,
+ strlen_on_c_strings::STRLEN_ON_C_STRINGS,
suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS,
suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL,
suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL,
types::LINKEDLIST,
types::OPTION_OPTION,
types::RC_BUFFER,
+ types::RC_MUTEX,
types::REDUNDANT_ALLOCATION,
types::TYPE_COMPLEXITY,
types::VEC_BOX,
LintId::of(strings::STRING_TO_STRING),
LintId::of(strings::STR_TO_STRING),
LintId::of(types::RC_BUFFER),
+ LintId::of(types::RC_MUTEX),
LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS),
LintId::of(unwrap_in_result::UNWRAP_IN_RESULT),
LintId::of(verbose_file_reads::VERBOSE_FILE_READS),
LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
- LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
+ LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
LintId::of(swap::ALMOST_SWAPPED),
LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
- LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
LintId::of(ptr::CMP_NULL),
LintId::of(ptr::PTR_ARG),
LintId::of(ptr_eq::PTR_EQ),
LintId::of(reference::REF_IN_DEREF),
LintId::of(repeat_once::REPEAT_ONCE),
LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
+ LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
LintId::of(swap::MANUAL_SWAP),
LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT),
LintId::of(transmute::CROSSPOINTER_TRANSMUTE),
LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
LintId::of(mutex_atomic::MUTEX_INTEGER),
+ LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE),
LintId::of(regex::TRIVIAL_REGEX),
store.register_late_pass(move || box missing_enforced_import_rename::ImportRename::new(import_renames.clone()));
let scripts = conf.allowed_scripts.clone();
store.register_early_pass(move || box disallowed_script_idents::DisallowedScriptIdents::new(&scripts));
+ store.register_late_pass(|| box strlen_on_c_strings::StrlenOnCStrings);
}
#[rustfmt::skip]
use rustc_lint::LateContext;
use rustc_middle::{mir::FakeReadCause, ty};
use rustc_span::source_map::Span;
-use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
+use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) {
if let Some(higher::Range {
}
impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> {
- fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId, _: ConsumeMode) {}
+ fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) {
if let ty::BorrowKind::MutBorrow = bk {
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
use clippy_utils::{
- can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, path_to_local_id,
+ can_move_expr_to_closure, in_constant, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id,
peel_hir_expr_refs,
};
use rustc_ast::util::parser::PREC_POSTFIX;
// These two lints will go back and forth with each other.
if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit
- && !is_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id)
+ && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id)
{
return;
}
},
_ => {
if path_to_local_id(some_expr, id)
- && !is_allowed(cx, MATCH_AS_REF, expr.hir_id)
+ && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id)
&& binding_ref.is_some()
{
return;
if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| {
match arm.pat.kind {
PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
- PatKind::TupleStruct(ref qpath, &[pat], _) =>
+ PatKind::TupleStruct(ref qpath, [pat], _) =>
matches!(pat.kind, PatKind::Wild) && is_lang_ctor(cx, qpath, ResultErr),
_ => false,
}
});
let unwrap_arm = &arms[1 - idx];
- if let PatKind::TupleStruct(ref qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind;
+ if let PatKind::TupleStruct(ref qpath, [unwrap_pat], _) = unwrap_arm.pat.kind;
if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk);
if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind;
if path_to_local_id(unwrap_arm.body, binding_hir_id);
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
use clippy_utils::visitors::LocalUsedVisitor;
use clippy_utils::{
- get_parent_expr, in_macro, is_allowed, is_expn_of, is_lang_ctor, is_refutable, is_wild, meets_msrv, msrvs,
+ get_parent_expr, in_macro, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_wild, meets_msrv, msrvs,
path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks,
strip_pat_refs,
};
if let PatKind::TupleStruct(
QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind;
if args.len() == 1;
- if let PatKind::Binding(_, arg, ..) = strip_pat_refs(args[0]).kind;
+ if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind;
let body = remove_blocks(arms[0].body);
if path_to_local_id(body, arg);
};
let ty = cx.typeck_results().expr_ty(ex);
- if *ty.kind() != ty::Bool || is_allowed(cx, MATCH_BOOL, ex.hir_id) {
+ if *ty.kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id) {
check_single_match_single_pattern(cx, ex, arms, expr, els);
check_single_match_opt_like(cx, ex, arms, expr, ty, els);
}
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_expn_of;
-use clippy_utils::source::{snippet, snippet_with_applicability};
+use clippy_utils::higher::FormatExpn;
+use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item;
-use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
}
}
- fn generate_format_arg_snippet(
- cx: &LateContext<'_>,
- a: &hir::Expr<'_>,
- applicability: &mut Applicability,
- ) -> Vec<String> {
- if_chain! {
- if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, format_arg) = a.kind;
- if let hir::ExprKind::Match(format_arg_expr, _, _) = format_arg.kind;
- if let hir::ExprKind::Tup(format_arg_expr_tup) = format_arg_expr.kind;
-
- then {
- format_arg_expr_tup
- .iter()
- .map(|a| snippet_with_applicability(cx, a.span, "..", applicability).into_owned())
- .collect()
- } else {
- unreachable!()
- }
- }
- }
-
fn is_call(node: &hir::ExprKind<'_>) -> bool {
match node {
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => {
let mut applicability = Applicability::MachineApplicable;
//Special handling for `format!` as arg_root
- if_chain! {
- if let hir::ExprKind::Block(block, None) = &arg_root.kind;
- if block.stmts.len() == 1;
- if let hir::StmtKind::Local(local) = &block.stmts[0].kind;
- if let Some(arg_root) = &local.init;
- if let hir::ExprKind::Call(inner_fun, inner_args) = arg_root.kind;
- if is_expn_of(inner_fun.span, "format").is_some() && inner_args.len() == 1;
- if let hir::ExprKind::Call(_, format_args) = &inner_args[0].kind;
- then {
- let fmt_spec = &format_args[0];
- let fmt_args = &format_args[1];
-
- let mut args = vec![snippet(cx, fmt_spec.span, "..").into_owned()];
-
- args.extend(generate_format_arg_snippet(cx, fmt_args, &mut applicability));
-
- let sugg = args.join(", ");
-
- span_lint_and_sugg(
- cx,
- EXPECT_FUN_CALL,
- span_replace_word,
- &format!("use of `{}` followed by a function call", name),
- "try this",
- format!("unwrap_or_else({} panic!({}))", closure_args, sugg),
- applicability,
- );
-
- return;
- }
+ if let Some(format_expn) = FormatExpn::parse(arg_root) {
+ let span = match *format_expn.format_args.value_args {
+ [] => format_expn.format_args.format_string_span,
+ [.., last] => format_expn.format_args.format_string_span.to(last.span),
+ };
+ let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
+ span_lint_and_sugg(
+ cx,
+ EXPECT_FUN_CALL,
+ span_replace_word,
+ &format!("use of `{}` followed by a function call", name),
+ "try this",
+ format!("unwrap_or_else({} panic!({}))", closure_args, sugg),
+ applicability,
+ );
+ return;
}
let mut arg_root_snippet: Cow<'_, _> = snippet_with_applicability(cx, arg_root.span, "..", &mut applicability);
//! lint on multiple versions of a crate being used
use clippy_utils::diagnostics::span_lint;
-use clippy_utils::run_lints;
+use clippy_utils::is_lint_allowed;
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_hir::{Crate, CRATE_HIR_ID};
use rustc_lint::{LateContext, LateLintPass};
impl LateLintPass<'_> for MultipleCrateVersions {
fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
- if !run_lints(cx, &[MULTIPLE_CRATE_VERSIONS], CRATE_HIR_ID) {
+ if is_lint_allowed(cx, MULTIPLE_CRATE_VERSIONS, CRATE_HIR_ID) {
return;
}
},
Tuple(..) => ty.tuple_fields().any(|ty| is_mutable_type(cx, ty, span)),
Adt(..) => {
- cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
- && !ty.has_escaping_bound_vars()
+ !ty.has_escaping_bound_vars()
+ && cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
&& !ty.is_freeze(cx.tcx.at(span), cx.param_env)
},
_ => false,
impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
use self::Expression::{Bool, RetBool};
+ if e.span.from_expansion() {
+ return;
+ }
if let ExprKind::If(pred, then_block, Some(else_expr)) = e.kind {
let reduce = |ret, not| {
let mut applicability = Applicability::MachineApplicable;
}
impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt {
- fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _: HirId, mode: euv::ConsumeMode) {
- if let euv::ConsumeMode::Move = mode {
- self.move_common(cmt);
- }
+ fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _: HirId) {
+ self.move_common(cmt);
}
fn borrow(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {}
/// vec![1, 2, 3];
/// ```
pub NONSTANDARD_MACRO_BRACES,
- style,
+ nursery,
"check consistent use of braces in macro"
}
}
}
-fn is_offending_macro<'a>(cx: &EarlyContext<'_>, span: Span, this: &'a MacroBraces) -> Option<MacroInfo<'a>> {
+fn is_offending_macro<'a>(cx: &EarlyContext<'_>, span: Span, mac_braces: &'a MacroBraces) -> Option<MacroInfo<'a>> {
if_chain! {
if in_macro(span);
- if let Some((name, braces)) = find_matching_macro(span, &this.macro_braces);
+ if let Some((name, braces)) = find_matching_macro(span, &mac_braces.macro_braces);
if let Some(snip) = snippet_opt(cx, span.ctxt().outer_expn_data().call_site);
- let c = snip.replace(" ", ""); // make formatting consistent
+ // we must check only invocation sites
+ // https://github.com/rust-lang/rust-clippy/issues/7422
+ if snip.starts_with(name);
+ // make formatting consistent
+ let c = snip.replace(" ", "");
if !c.starts_with(&format!("{}!{}", name, braces.0));
- if !this.done.contains(&span.ctxt().outer_expn_data().call_site);
+ if !mac_braces.done.contains(&span.ctxt().outer_expn_data().call_site);
then {
Some((name, braces, snip))
} else {
if !is_else_clause(cx.tcx, expr);
if arms.len() == 2;
if !is_result_ok(cx, cond_expr); // Don't lint on Result::ok because a different lint does it already
- if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind;
+ if let PatKind::TupleStruct(struct_qpath, [inner_pat], _) = &arms[0].pat.kind;
if is_lang_ctor(cx, struct_qpath, OptionSome);
if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind;
if !contains_return_break_continue_macro(arms[0].body);
impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if match_panic_call(cx, expr).is_some() && is_expn_of(expr.span, "debug_assert").is_none() {
+ if match_panic_call(cx, expr).is_some()
+ && (is_expn_of(expr.span, "debug_assert").is_none() && is_expn_of(expr.span, "assert").is_none())
+ {
let span = get_outer_span(expr);
if is_expn_of(expr.span, "unimplemented").is_some() {
span_lint(
fn find_first_mismatch_in_tuple<'tcx, I>(
cx: &LateContext<'tcx>,
- pats: &[&Pat<'_>],
+ pats: &[Pat<'_>],
ty_iter_src: I,
) -> Option<(Span, Mutability, Level)>
where
use clippy_utils::ptr::get_spans;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{is_type_diagnostic_item, match_type, walk_ptrs_hir_ty};
-use clippy_utils::{expr_path_res, is_allowed, match_any_def_paths, paths};
+use clippy_utils::{expr_path_res, is_lint_allowed, match_any_def_paths, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{
for (idx, (arg, ty)) in decl.inputs.iter().zip(fn_ty.inputs()).enumerate() {
// Honor the allow attribute on parameters. See issue 5644.
if let Some(body) = &body {
- if is_allowed(cx, PTR_ARG, body.params[idx].hir_id) {
+ if is_lint_allowed(cx, PTR_ARG, body.params[idx].hir_id) {
continue;
}
}
use rustc_middle::mir::{
self, traversal,
visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor as _},
+ Mutability,
};
use rustc_middle::ty::{self, fold::TypeVisitor, Ty};
use rustc_mir::dataflow::{Analysis, AnalysisDomain, GenKill, GenKillAnalysis, ResultsCursor};
let mir = cx.tcx.optimized_mir(def_id.to_def_id());
+ let possible_origin = {
+ let mut vis = PossibleOriginVisitor::new(mir);
+ vis.visit_body(mir);
+ vis.into_map(cx)
+ };
let maybe_storage_live_result = MaybeStorageLive
.into_engine(cx.tcx, mir)
.pass_name("redundant_clone")
.iterate_to_fixpoint()
.into_results_cursor(mir);
let mut possible_borrower = {
- let mut vis = PossibleBorrowerVisitor::new(cx, mir);
+ let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin);
vis.visit_body(mir);
vis.into_map(cx, maybe_storage_live_result)
};
possible_borrower: TransitiveRelation<mir::Local>,
body: &'a mir::Body<'tcx>,
cx: &'a LateContext<'tcx>,
+ possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
}
impl<'a, 'tcx> PossibleBorrowerVisitor<'a, 'tcx> {
- fn new(cx: &'a LateContext<'tcx>, body: &'a mir::Body<'tcx>) -> Self {
+ fn new(
+ cx: &'a LateContext<'tcx>,
+ body: &'a mir::Body<'tcx>,
+ possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
+ ) -> Self {
Self {
possible_borrower: TransitiveRelation::default(),
cx,
body,
+ possible_origin,
}
}
..
} = &terminator.kind
{
+ // TODO add doc
// If the call returns something with lifetimes,
// let's conservatively assume the returned value contains lifetime of all the arguments.
// For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`.
- if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_continue() {
- return;
- }
+
+ let mut immutable_borrowers = vec![];
+ let mut mutable_borrowers = vec![];
for op in args {
match op {
mir::Operand::Copy(p) | mir::Operand::Move(p) => {
- self.possible_borrower.add(p.local, *dest);
+ if let ty::Ref(_, _, Mutability::Mut) = self.body.local_decls[p.local].ty.kind() {
+ mutable_borrowers.push(p.local);
+ } else {
+ immutable_borrowers.push(p.local);
+ }
},
mir::Operand::Constant(..) => (),
}
}
+
+ let mut mutable_variables: Vec<mir::Local> = mutable_borrowers
+ .iter()
+ .filter_map(|r| self.possible_origin.get(r))
+ .flat_map(HybridBitSet::iter)
+ .collect();
+
+ if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() {
+ mutable_variables.push(*dest);
+ }
+
+ for y in mutable_variables {
+ for x in &immutable_borrowers {
+ self.possible_borrower.add(*x, y);
+ }
+ for x in &mutable_borrowers {
+ self.possible_borrower.add(*x, y);
+ }
+ }
+ }
+ }
+}
+
+/// Collect possible borrowed for every `&mut` local.
+/// For exampel, `_1 = &mut _2` generate _1: {_2,...}
+/// Known Problems: not sure all borrowed are tracked
+struct PossibleOriginVisitor<'a, 'tcx> {
+ possible_origin: TransitiveRelation<mir::Local>,
+ body: &'a mir::Body<'tcx>,
+}
+
+impl<'a, 'tcx> PossibleOriginVisitor<'a, 'tcx> {
+ fn new(body: &'a mir::Body<'tcx>) -> Self {
+ Self {
+ possible_origin: TransitiveRelation::default(),
+ body,
+ }
+ }
+
+ fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap<mir::Local, HybridBitSet<mir::Local>> {
+ let mut map = FxHashMap::default();
+ for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) {
+ if is_copy(cx, self.body.local_decls[row].ty) {
+ continue;
+ }
+
+ let borrowers = self.possible_origin.reachable_from(&row);
+ if !borrowers.is_empty() {
+ let mut bs = HybridBitSet::new_empty(self.body.local_decls.len());
+ for &c in borrowers {
+ if c != mir::Local::from_usize(0) {
+ bs.insert(c);
+ }
+ }
+
+ if !bs.is_empty() {
+ map.insert(row, bs);
+ }
+ }
+ }
+ map
+ }
+}
+
+impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleOriginVisitor<'a, 'tcx> {
+ fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) {
+ let lhs = place.local;
+ match rvalue {
+ // Only consider `&mut`, which can modify origin place
+ mir::Rvalue::Ref(_, rustc_middle::mir::BorrowKind::Mut { .. }, borrowed) |
+ // _2: &mut _;
+ // _3 = move _2
+ mir::Rvalue::Use(mir::Operand::Move(borrowed)) |
+ // _3 = move _2 as &mut _;
+ mir::Rvalue::Cast(_, mir::Operand::Move(borrowed), _)
+ => {
+ self.possible_origin.add(lhs, borrowed.local);
+ },
+ _ => {},
}
}
}
use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::SpanlessEq;
-use clippy_utils::{get_parent_expr, is_allowed, match_function_call, method_calls, paths};
+use clippy_utils::{get_parent_expr, is_lint_allowed, match_function_call, method_calls, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath};
) = e.kind
{
if is_string(cx, left) {
- if !is_allowed(cx, STRING_ADD_ASSIGN, e.hir_id) {
+ if !is_lint_allowed(cx, STRING_ADD_ASSIGN, e.hir_id) {
let parent = get_parent_expr(cx, e);
if let Some(p) = parent {
if let ExprKind::Assign(target, _, _) = p.kind {
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_macro;
+use clippy_utils::paths;
+use clippy_utils::source::snippet_with_macro_callsite;
+use clippy_utils::ty::{is_type_diagnostic_item, is_type_ref_to_diagnostic_item};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::{sym, Symbol};
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for usage of `libc::strlen` on a `CString` or `CStr` value,
+ /// and suggest calling `as_bytes().len()` or `to_bytes().len()` respectively instead.
+ ///
+ /// **Why is this bad?** This avoids calling an unsafe `libc` function.
+ /// Currently, it also avoids calculating the length.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust, ignore
+ /// use std::ffi::CString;
+ /// let cstring = CString::new("foo").expect("CString::new failed");
+ /// let len = unsafe { libc::strlen(cstring.as_ptr()) };
+ /// ```
+ /// Use instead:
+ /// ```rust, no_run
+ /// use std::ffi::CString;
+ /// let cstring = CString::new("foo").expect("CString::new failed");
+ /// let len = cstring.as_bytes().len();
+ /// ```
+ pub STRLEN_ON_C_STRINGS,
+ complexity,
+ "using `libc::strlen` on a `CString` or `CStr` value, while `as_bytes().len()` or `to_bytes().len()` respectively can be used instead"
+}
+
+declare_lint_pass!(StrlenOnCStrings => [STRLEN_ON_C_STRINGS]);
+
+impl LateLintPass<'tcx> for StrlenOnCStrings {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
+ if in_macro(expr.span) {
+ return;
+ }
+
+ if_chain! {
+ if let hir::ExprKind::Call(func, [recv]) = expr.kind;
+ if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = func.kind;
+
+ if (&paths::LIBC_STRLEN).iter().map(|x| Symbol::intern(x)).eq(
+ path.segments.iter().map(|seg| seg.ident.name));
+ if let hir::ExprKind::MethodCall(path, _, args, _) = recv.kind;
+ if args.len() == 1;
+ if !args.iter().any(|e| e.span.from_expansion());
+ if path.ident.name == sym::as_ptr;
+ then {
+ let cstring = &args[0];
+ let ty = cx.typeck_results().expr_ty(cstring);
+ let val_name = snippet_with_macro_callsite(cx, cstring.span, "..");
+ let sugg = if is_type_diagnostic_item(cx, ty, sym::cstring_type){
+ format!("{}.as_bytes().len()", val_name)
+ } else if is_type_ref_to_diagnostic_item(cx, ty, sym::CStr){
+ format!("{}.to_bytes().len()", val_name)
+ } else {
+ return;
+ };
+
+ span_lint_and_sugg(
+ cx,
+ STRLEN_ON_C_STRINGS,
+ expr.span,
+ "using `libc::strlen` on a `CString` or `CStr` value",
+ "try this (you might also need to get rid of `unsafe` block in some cases):",
+ sugg,
+ Applicability::Unspecified // Sometimes unnecessary `unsafe` block
+ );
+ }
+ }
+ }
+}
mod linked_list;
mod option_option;
mod rc_buffer;
+mod rc_mutex;
mod redundant_allocation;
mod type_complexity;
mod utils;
declare_clippy_lint! {
/// **What it does:** Checks for use of redundant allocations anywhere in the code.
///
- /// **Why is this bad?** Expressions such as `Rc<&T>`, `Rc<Rc<T>>`, `Rc<Box<T>>`, `Box<&T>`
- /// add an unnecessary level of indirection.
+ /// **Why is this bad?** Expressions such as `Rc<&T>`, `Rc<Rc<T>>`, `Rc<Arc<T>>`, `Rc<Box<T>>`, Arc<&T>`, `Arc<Rc<T>>`,
+ /// `Arc<Arc<T>>`, `Arc<Box<T>>`, `Box<&T>`, `Box<Rc<T>>`, `Box<Arc<T>>`, `Box<Box<T>>`, add an unnecessary level of indirection.
///
/// **Known problems:** None.
///
"usage of very complex types that might be better factored into `type` definitions"
}
+declare_clippy_lint! {
+ /// **What it does:** Checks for `Rc<Mutex<T>>`.
+ ///
+ /// **Why is this bad?** `Rc` is used in single thread and `Mutex` is used in multi thread.
+ /// Consider using `Rc<RefCell<T>>` in single thread or `Arc<Mutex<T>>` in multi thread.
+ ///
+ /// **Known problems:** Sometimes combining generic types can lead to the requirement that a
+ /// type use Rc in conjunction with Mutex. We must consider those cases false positives, but
+ /// alas they are quite hard to rule out. Luckily they are also rare.
+ ///
+ /// **Example:**
+ /// ```rust,ignore
+ /// use std::rc::Rc;
+ /// use std::sync::Mutex;
+ /// fn foo(interned: Rc<Mutex<i32>>) { ... }
+ /// ```
+ ///
+ /// Better:
+ ///
+ /// ```rust,ignore
+ /// use std::rc::Rc;
+ /// use std::cell::RefCell
+ /// fn foo(interned: Rc<RefCell<i32>>) { ... }
+ /// ```
+ pub RC_MUTEX,
+ restriction,
+ "usage of `Rc<Mutex<T>>`"
+}
+
pub struct Types {
vec_box_size_threshold: u64,
type_complexity_threshold: u64,
}
-impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, TYPE_COMPLEXITY]);
+impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, RC_MUTEX, TYPE_COMPLEXITY]);
impl<'tcx> LateLintPass<'tcx> for Types {
fn check_fn(&mut self, cx: &LateContext<'_>, _: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, id: HirId) {
triggered |= vec_box::check(cx, hir_ty, qpath, def_id, self.vec_box_size_threshold);
triggered |= option_option::check(cx, hir_ty, qpath, def_id);
triggered |= linked_list::check(cx, hir_ty, def_id);
+ triggered |= rc_mutex::check(cx, hir_ty, qpath, def_id);
if triggered {
return;
--- /dev/null
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::is_ty_param_diagnostic_item;
+use if_chain::if_chain;
+use rustc_hir::{self as hir, def_id::DefId, QPath};
+use rustc_lint::LateContext;
+use rustc_span::symbol::sym;
+
+use super::RC_MUTEX;
+
+pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
+ if_chain! {
+ if cx.tcx.is_diagnostic_item(sym::Rc, def_id) ;
+ if let Some(_) = is_ty_param_diagnostic_item(cx, qpath, sym!(mutex_type)) ;
+
+ then{
+ span_lint(
+ cx,
+ RC_MUTEX,
+ hir_ty.span,
+ "found `Rc<Mutex<_>>`. Consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead",
+ );
+ return true;
+ }
+ }
+
+ false
+}
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item, is_ty_param_lang_item};
use rustc_errors::Applicability;
use rustc_hir::{self as hir, def_id::DefId, LangItem, QPath, TyKind};
use super::{utils, REDUNDANT_ALLOCATION};
pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
- if Some(def_id) == cx.tcx.lang_items().owned_box() {
- if let Some(span) = utils::match_borrows_parameter(cx, qpath) {
- let mut applicability = Applicability::MachineApplicable;
- span_lint_and_sugg(
- cx,
- REDUNDANT_ALLOCATION,
- hir_ty.span,
- "usage of `Box<&T>`",
- "try",
- snippet_with_applicability(cx, span, "..", &mut applicability).to_string(),
- applicability,
- );
- return true;
- }
+ let outer_sym = if Some(def_id) == cx.tcx.lang_items().owned_box() {
+ "Box"
+ } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
+ "Rc"
+ } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) {
+ "Arc"
+ } else {
+ return false;
+ };
+
+ if let Some(span) = utils::match_borrows_parameter(cx, qpath) {
+ let mut applicability = Applicability::MaybeIncorrect;
+ let generic_snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
+ span_lint_and_then(
+ cx,
+ REDUNDANT_ALLOCATION,
+ hir_ty.span,
+ &format!("usage of `{}<{}>`", outer_sym, generic_snippet),
+ |diag| {
+ diag.span_suggestion(hir_ty.span, "try", format!("{}", generic_snippet), applicability);
+ diag.note(&format!(
+ "`{generic}` is already a pointer, `{outer}<{generic}>` allocates a pointer on the heap",
+ outer = outer_sym,
+ generic = generic_snippet
+ ));
+ },
+ );
+ return true;
}
- if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
- if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Rc) {
- let mut applicability = Applicability::MachineApplicable;
- span_lint_and_sugg(
- cx,
- REDUNDANT_ALLOCATION,
- hir_ty.span,
- "usage of `Rc<Rc<T>>`",
- "try",
- snippet_with_applicability(cx, ty.span, "..", &mut applicability).to_string(),
- applicability,
- );
- true
- } else if let Some(ty) = is_ty_param_lang_item(cx, qpath, LangItem::OwnedBox) {
- let qpath = match &ty.kind {
- TyKind::Path(qpath) => qpath,
- _ => return false,
- };
- let inner_span = match get_qpath_generic_tys(qpath).next() {
- Some(ty) => ty.span,
- None => return false,
- };
- let mut applicability = Applicability::MachineApplicable;
- span_lint_and_sugg(
- cx,
- REDUNDANT_ALLOCATION,
- hir_ty.span,
- "usage of `Rc<Box<T>>`",
- "try",
- format!(
- "Rc<{}>",
- snippet_with_applicability(cx, inner_span, "..", &mut applicability)
- ),
- applicability,
- );
- true
- } else {
- utils::match_borrows_parameter(cx, qpath).map_or(false, |span| {
- let mut applicability = Applicability::MachineApplicable;
- span_lint_and_sugg(
- cx,
- REDUNDANT_ALLOCATION,
+ let (inner_sym, ty) = if let Some(ty) = is_ty_param_lang_item(cx, qpath, LangItem::OwnedBox) {
+ ("Box", ty)
+ } else if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Rc) {
+ ("Rc", ty)
+ } else if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Arc) {
+ ("Arc", ty)
+ } else {
+ return false;
+ };
+
+ let inner_qpath = match &ty.kind {
+ TyKind::Path(inner_qpath) => inner_qpath,
+ _ => return false,
+ };
+ let inner_span = match get_qpath_generic_tys(inner_qpath).next() {
+ Some(ty) => ty.span,
+ None => return false,
+ };
+ if inner_sym == outer_sym {
+ let mut applicability = Applicability::MaybeIncorrect;
+ let generic_snippet = snippet_with_applicability(cx, inner_span, "..", &mut applicability);
+ span_lint_and_then(
+ cx,
+ REDUNDANT_ALLOCATION,
+ hir_ty.span,
+ &format!("usage of `{}<{}<{}>>`", outer_sym, inner_sym, generic_snippet),
+ |diag| {
+ diag.span_suggestion(
hir_ty.span,
- "usage of `Rc<&T>`",
"try",
- snippet_with_applicability(cx, span, "..", &mut applicability).to_string(),
+ format!("{}<{}>", outer_sym, generic_snippet),
applicability,
);
- true
- })
- }
+ diag.note(&format!(
+ "`{inner}<{generic}>` is already on the heap, `{outer}<{inner}<{generic}>>` makes an extra allocation",
+ outer = outer_sym,
+ inner = inner_sym,
+ generic = generic_snippet
+ ));
+ },
+ );
} else {
- false
+ let generic_snippet = snippet(cx, inner_span, "..");
+ span_lint_and_then(
+ cx,
+ REDUNDANT_ALLOCATION,
+ hir_ty.span,
+ &format!("usage of `{}<{}<{}>>`", outer_sym, inner_sym, generic_snippet),
+ |diag| {
+ diag.note(&format!(
+ "`{inner}<{generic}>` is already on the heap, `{outer}<{inner}<{generic}>>` makes an extra allocation",
+ outer = outer_sym,
+ inner = inner_sym,
+ generic = generic_snippet
+ ));
+ diag.help(&format!(
+ "consider using just `{outer}<{generic}>` or `{inner}<{generic}>`",
+ outer = outer_sym,
+ inner = inner_sym,
+ generic = generic_snippet
+ ));
+ },
+ );
}
+ true
}
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_allowed;
+use clippy_utils::is_lint_allowed;
use clippy_utils::source::snippet;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
span,
"literal non-ASCII character detected",
"consider replacing the string with",
- if is_allowed(cx, UNICODE_NOT_NFC, id) {
+ if is_lint_allowed(cx, UNICODE_NOT_NFC, id) {
escape(string.chars())
} else {
escape(string.nfc())
Applicability::MachineApplicable,
);
}
- if is_allowed(cx, NON_ASCII_LITERAL, id) && string.chars().zip(string.nfc()).any(|(a, b)| a != b) {
+ if is_lint_allowed(cx, NON_ASCII_LITERAL, id) && string.chars().zip(string.nfc()).any(|(a, b)| a != b) {
span_lint_and_sugg(
cx,
UNICODE_NOT_NFC,
/// ()
/// }
/// ```
+ /// is equivalent to
+ /// ```rust
+ /// fn return_unit() {}
+ /// ```
pub UNUSED_UNIT,
style,
"needless unit expression"
impl<'tcx> LateLintPass<'tcx> for UseSelf {
fn check_item(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) {
- if !is_item_interesting(item) {
- // This does two things:
- // 1) Reduce needless churn on `self.stack`
- // 2) Don't push `StackItem::NoCheck` when entering `ItemKind::OpaqueTy`,
- // in order to lint `foo() -> impl <..>`
+ if matches!(item.kind, ItemKind::OpaqueTy(_)) {
+ // skip over `ItemKind::OpaqueTy` in order to lint `foo() -> impl <..>`
return;
}
// We push the self types of `impl`s on a stack here. Only the top type on the stack is
}
fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) {
- if is_item_interesting(item) {
+ if !matches!(item.kind, ItemKind::OpaqueTy(_)) {
self.stack.pop();
}
}
span_lint(cx, span);
}
}
-
-fn is_item_interesting(item: &Item<'_>) -> bool {
- use rustc_hir::ItemKind::{Const, Enum, Fn, Impl, Static, Struct, Trait, Union};
- matches!(
- item.kind,
- Impl { .. } | Static(..) | Const(..) | Fn(..) | Enum(..) | Struct(..) | Union(..) | Trait(..)
- )
-}
use clippy_utils::source::snippet;
use clippy_utils::ty::match_type;
use clippy_utils::{
- is_else_clause, is_expn_of, is_expr_path_def_path, match_def_path, method_calls, path_to_res, paths, run_lints,
- SpanlessEq,
+ is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path, method_calls, path_to_res,
+ paths, SpanlessEq,
};
use if_chain::if_chain;
use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, ModKind, NodeId};
impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
- if !run_lints(cx, &[DEFAULT_LINT], item.hir_id()) {
+ if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) {
return;
}
}
fn check_crate_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx Crate<'_>) {
- if !run_lints(cx, &[LINT_WITHOUT_LINT_PASS], CRATE_HIR_ID) {
+ if is_lint_allowed(cx, LINT_WITHOUT_LINT_PASS, CRATE_HIR_ID) {
return;
}
impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if !run_lints(cx, &[COMPILER_LINT_FUNCTIONS], expr.hir_id) {
+ if is_lint_allowed(cx, COMPILER_LINT_FUNCTIONS, expr.hir_id) {
return;
}
impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
- if !run_lints(cx, &[OUTER_EXPN_EXPN_DATA], expr.hir_id) {
+ if is_lint_allowed(cx, OUTER_EXPN_EXPN_DATA, expr.hir_id) {
return;
}
impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
- if !run_lints(cx, &[COLLAPSIBLE_SPAN_LINT_CALLS], expr.hir_id) {
+ if is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) {
return;
}
impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
- if !run_lints(cx, &[MATCH_TYPE_ON_DIAGNOSTIC_ITEM], expr.hir_id) {
+ if is_lint_allowed(cx, MATCH_TYPE_ON_DIAGNOSTIC_ITEM, expr.hir_id) {
return;
}
lint_name: &str,
item: &'hir Item<'_>,
) -> Option<(String, &'static str)> {
- let result = cx.lint_store.check_lint_name(lint_name, Some(sym::clippy));
+ let result = cx
+ .lint_store
+ .check_lint_name(cx.sess(), lint_name, Some(sym::clippy), &[]);
if let CheckLintNameResult::Tool(Ok(lint_lst)) = result {
if let Some(group) = get_lint_group(cx, lint_lst[0]) {
if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) {
-use clippy_utils::diagnostics::span_lint;
-use clippy_utils::run_lints;
+use clippy_utils::{diagnostics::span_lint, is_lint_allowed};
use rustc_hir::{hir_id::CRATE_HIR_ID, Crate};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
impl LateLintPass<'_> for WildcardDependencies {
fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
- if !run_lints(cx, &[WILDCARD_DEPENDENCIES], CRATE_HIR_ID) {
+ if is_lint_allowed(cx, WILDCARD_DEPENDENCIES, CRATE_HIR_ID) {
return;
}
use crate::{is_expn_of, match_def_path, paths};
use if_chain::if_chain;
-use rustc_ast::ast;
+use rustc_ast::ast::{self, LitKind};
use rustc_hir as hir;
use rustc_hir::{BorrowKind, Expr, ExprKind, StmtKind, UnOp};
use rustc_lint::LateContext;
-use rustc_span::source_map::Span;
+use rustc_span::{sym, ExpnKind, Span, Symbol};
/// Converts a hir binary operator to the corresponding `ast` type.
#[must_use]
}
None
}
+
+/// A parsed `format!` expansion
+pub struct FormatExpn<'tcx> {
+ /// Span of `format!(..)`
+ pub call_site: Span,
+ /// Inner `format_args!` expansion
+ pub format_args: FormatArgsExpn<'tcx>,
+}
+
+impl FormatExpn<'tcx> {
+ /// Parses an expanded `format!` invocation
+ pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> {
+ if_chain! {
+ if let ExprKind::Block(block, _) = expr.kind;
+ if let [stmt] = block.stmts;
+ if let StmtKind::Local(local) = stmt.kind;
+ if let Some(init) = local.init;
+ if let ExprKind::Call(_, [format_args]) = init.kind;
+ let expn_data = expr.span.ctxt().outer_expn_data();
+ if let ExpnKind::Macro(_, sym::format) = expn_data.kind;
+ if let Some(format_args) = FormatArgsExpn::parse(format_args);
+ then {
+ Some(FormatExpn {
+ call_site: expn_data.call_site,
+ format_args,
+ })
+ } else {
+ None
+ }
+ }
+ }
+}
+
+/// A parsed `format_args!` expansion
+pub struct FormatArgsExpn<'tcx> {
+ /// Span of the first argument, the format string
+ pub format_string_span: Span,
+ /// Values passed after the format string
+ pub value_args: Vec<&'tcx Expr<'tcx>>,
+
+ /// String literal expressions which represent the format string split by "{}"
+ pub format_string_parts: &'tcx [Expr<'tcx>],
+ /// Symbols corresponding to [`format_string_parts`]
+ pub format_string_symbols: Vec<Symbol>,
+ /// Expressions like `ArgumentV1::new(arg0, Debug::fmt)`
+ pub args: &'tcx [Expr<'tcx>],
+ /// The final argument passed to `Arguments::new_v1_formatted`, if applicable
+ pub fmt_expr: Option<&'tcx Expr<'tcx>>,
+}
+
+impl FormatArgsExpn<'tcx> {
+ /// Parses an expanded `format_args!` or `format_args_nl!` invocation
+ pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> {
+ if_chain! {
+ if let ExpnKind::Macro(_, name) = expr.span.ctxt().outer_expn_data().kind;
+ let name = name.as_str();
+ if name.ends_with("format_args") || name.ends_with("format_args_nl");
+ if let ExprKind::Call(_, args) = expr.kind;
+ if let Some((strs_ref, args, fmt_expr)) = match args {
+ // Arguments::new_v1
+ [strs_ref, args] => Some((strs_ref, args, None)),
+ // Arguments::new_v1_formatted
+ [strs_ref, args, fmt_expr] => Some((strs_ref, args, Some(fmt_expr))),
+ _ => None,
+ };
+ if let ExprKind::AddrOf(BorrowKind::Ref, _, strs_arr) = strs_ref.kind;
+ if let ExprKind::Array(format_string_parts) = strs_arr.kind;
+ if let Some(format_string_symbols) = format_string_parts
+ .iter()
+ .map(|e| {
+ if let ExprKind::Lit(lit) = &e.kind {
+ if let LitKind::Str(symbol, _style) = lit.node {
+ return Some(symbol);
+ }
+ }
+ None
+ })
+ .collect();
+ if let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args.kind;
+ if let ExprKind::Match(args, [arm], _) = args.kind;
+ if let ExprKind::Tup(value_args) = args.kind;
+ if let Some(value_args) = value_args
+ .iter()
+ .map(|e| match e.kind {
+ ExprKind::AddrOf(_, _, e) => Some(e),
+ _ => None,
+ })
+ .collect();
+ if let ExprKind::Array(args) = arm.body.kind;
+ then {
+ Some(FormatArgsExpn {
+ format_string_span: strs_ref.span,
+ value_args,
+ format_string_parts,
+ format_string_symbols,
+ args,
+ fmt_expr,
+ })
+ } else {
+ None
+ }
+ }
+ }
+}
}
/// Checks if given pattern is a wildcard (`_`)
-pub fn is_wild<'tcx>(pat: &impl std::ops::Deref<Target = Pat<'tcx>>) -> bool {
+pub fn is_wild(pat: &Pat<'_>) -> bool {
matches!(pat.kind, PatKind::Wild)
}
)
}
- fn are_refutable<'a, I: Iterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, mut i: I) -> bool {
- i.any(|pat| is_refutable(cx, pat))
+ fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
+ i.into_iter().any(|pat| is_refutable(cx, pat))
}
match pat.kind {
PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
PatKind::Or(pats) => {
// TODO: should be the honest check, that pats is exhaustive set
- are_refutable(cx, pats.iter().map(|pat| &**pat))
+ are_refutable(cx, pats)
},
- PatKind::Tuple(pats, _) => are_refutable(cx, pats.iter().map(|pat| &**pat)),
+ PatKind::Tuple(pats, _) => are_refutable(cx, pats),
PatKind::Struct(ref qpath, fields, _) => {
is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| &*field.pat))
},
PatKind::TupleStruct(ref qpath, pats, _) => {
- is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats.iter().map(|pat| &**pat))
+ is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats)
},
- PatKind::Slice(head, ref middle, tail) => {
+ PatKind::Slice(head, middle, tail) => {
match &cx.typeck_results().node_type(pat.hir_id).kind() {
rustc_ty::Slice(..) => {
// [..] is the only irrefutable slice pattern.
!head.is_empty() || middle.is_none() || !tail.is_empty()
},
rustc_ty::Array(..) => {
- are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).map(|pat| &**pat))
+ are_refutable(cx, head.iter().chain(middle).chain(tail.iter()))
},
_ => {
// unreachable!()
/// the function once on the given pattern.
pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
if let PatKind::Or(pats) = pat.kind {
- pats.iter().copied().for_each(f);
+ pats.iter().for_each(f);
} else {
f(pat);
}
/// Returns `true` if the lint is allowed in the current context
///
/// Useful for skipping long running code when it's unnecessary
-pub fn is_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
+pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
}
}
}
-/// This function checks if any of the lints in the slice is enabled for the provided `HirId`.
-/// A lint counts as enabled with any of the levels: `Level::Forbid` | `Level::Deny` | `Level::Warn`
-///
-/// ```ignore
-/// #[deny(clippy::YOUR_AWESOME_LINT)]
-/// println!("Hello, World!"); // <- Clippy code: run_lints(cx, &[YOUR_AWESOME_LINT], id) == true
-///
-/// #[allow(clippy::YOUR_AWESOME_LINT)]
-/// println!("See you soon!"); // <- Clippy code: run_lints(cx, &[YOUR_AWESOME_LINT], id) == false
-/// ```
-pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bool {
- lints.iter().any(|lint| {
- matches!(
- cx.tcx.lint_level_at_node(lint, id),
- (Level::Forbid | Level::Deny | Level::Warn, _)
- )
- })
-}
-
/// Returns Option<String> where String is a textual representation of the type encapsulated in the
/// slice iff the given expression is a slice of primitives (as defined in the
/// `is_recursively_primitive_type` function) and None otherwise.
}
if let Some(suffix) = self.suffix {
+ if output.ends_with('.') {
+ output.push('0');
+ }
output.push('_');
output.push_str(suffix);
}
pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"];
pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
pub const DIR_BUILDER: [&str; 3] = ["std", "fs", "DirBuilder"];
-pub const DISPLAY_FMT_METHOD: [&str; 4] = ["core", "fmt", "Display", "fmt"];
pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"];
pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
pub const DROP: [&str; 3] = ["core", "mem", "drop"];
pub const F64_EPSILON: [&str; 4] = ["core", "f64", "<impl f64>", "EPSILON"];
pub const FILE: [&str; 3] = ["std", "fs", "File"];
pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
-pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
-pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"];
-pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"];
pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"];
pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"];
pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
#[cfg(feature = "internal-lints")]
pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
+pub const LIBC_STRLEN: [&str; 2] = ["libc", "strlen"];
pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"];
#[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))]
pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
use rustc_span::sym;
use rustc_span::symbol::{Ident, Symbol};
use rustc_span::DUMMY_SP;
-use rustc_trait_selection::traits::query::normalize::AtExt;
use rustc_trait_selection::infer::InferCtxtExt;
+use rustc_trait_selection::traits::query::normalize::AtExt;
use crate::{match_def_path, must_use_attr};
return false;
}
let ty_params = cx.tcx.mk_substs(ty_params.iter());
- cx.tcx.infer_ctxt().enter(|infcx|
- infcx.type_implements_trait(trait_id, ty, ty_params, cx.param_env)
- .must_apply_modulo_regions()
- )
+ cx.tcx.infer_ctxt().enter(|infcx| {
+ infcx
+ .type_implements_trait(trait_id, ty, ty_params, cx.param_env)
+ .must_apply_modulo_regions()
+ })
}
/// Checks whether this type implements `Drop`.
}
}
+/// Checks if the type is a reference equals to a diagnostic item
+pub fn is_type_ref_to_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
+ match ty.kind() {
+ ty::Ref(_, ref_ty, _) => match ref_ty.kind() {
+ ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did),
+ _ => false,
+ },
+ _ => false,
+ }
+}
+
/// Checks if the type is equal to a diagnostic item
///
/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
use rustc_middle::hir::map::Map;
use rustc_middle::mir::FakeReadCause;
use rustc_middle::ty;
-use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
+use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
/// Returns a set of mutated local variable IDs, or `None` if mutations could not be determined.
pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> Option<HirIdSet> {
}
impl<'tcx> Delegate<'tcx> for MutVarsDelegate {
- fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId, _: ConsumeMode) {}
+ fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, bk: ty::BorrowKind) {
if let ty::BorrowKind::MutBorrow = bk {
recursive_visitor.visit_expr(expression);
recursive_visitor.seen_return_break_continue
}
+
+pub struct UsedAfterExprVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ expr: &'tcx Expr<'tcx>,
+ definition: HirId,
+ past_expr: bool,
+ used_after_expr: bool,
+}
+impl<'a, 'tcx> UsedAfterExprVisitor<'a, 'tcx> {
+ pub fn is_found(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+ utils::path_to_local(expr).map_or(false, |definition| {
+ let mut visitor = UsedAfterExprVisitor {
+ cx,
+ expr,
+ definition,
+ past_expr: false,
+ used_after_expr: false,
+ };
+ utils::get_enclosing_block(cx, definition).map_or(false, |block| {
+ visitor.visit_block(block);
+ visitor.used_after_expr
+ })
+ })
+ }
+}
+
+impl<'a, 'tcx> intravisit::Visitor<'tcx> for UsedAfterExprVisitor<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
+ }
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+ if self.used_after_expr {
+ return;
+ }
+
+ if expr.hir_id == self.expr.hir_id {
+ self.past_expr = true;
+ } else if self.past_expr && utils::path_to_local_id(expr, self.definition) {
+ self.used_after_expr = true;
+ } else {
+ intravisit::walk_expr(self, expr);
+ }
+ }
+}
[toolchain]
-channel = "nightly-2021-07-01"
+channel = "nightly-2021-07-15"
components = ["llvm-tools-preview", "rustc-dev", "rust-src"]
--- /dev/null
+// compile-flags: --emit=link
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+
+#[proc_macro_derive(DeriveSomething)]
+pub fn derive(_: TokenStream) -> TokenStream {
+ "fn _f() -> Vec<u8> { vec![] }".parse().unwrap()
+}
+
+#[proc_macro]
+pub fn foo_bar(_: TokenStream) -> TokenStream {
+ "fn issue_7422() { eprintln!(); }".parse().unwrap()
+}
-// #![warn(clippy::nonstandard_macro_braces)]
+// aux-build:proc_macro_derive.rs
+#![warn(clippy::nonstandard_macro_braces)]
+
+extern crate proc_macro_derive;
extern crate quote;
use quote::quote;
+#[derive(proc_macro_derive::DeriveSomething)]
+pub struct S;
+
+proc_macro_derive::foo_bar!();
+
#[rustfmt::skip]
macro_rules! test {
() => {
error: use of irregular braces for `vec!` macro
- --> $DIR/conf_nonstandard_macro_braces.rs:29:13
+ --> $DIR/conf_nonstandard_macro_braces.rs:37:13
|
LL | let _ = vec! {1, 2, 3};
| ^^^^^^^^^^^^^^
|
= note: `-D clippy::nonstandard-macro-braces` implied by `-D warnings`
help: consider writing `vec![1, 2, 3]`
- --> $DIR/conf_nonstandard_macro_braces.rs:29:13
+ --> $DIR/conf_nonstandard_macro_braces.rs:37:13
|
LL | let _ = vec! {1, 2, 3};
| ^^^^^^^^^^^^^^
error: use of irregular braces for `format!` macro
- --> $DIR/conf_nonstandard_macro_braces.rs:30:13
+ --> $DIR/conf_nonstandard_macro_braces.rs:38:13
|
LL | let _ = format!["ugh {} stop being such a good compiler", "hello"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: consider writing `format!("ugh () stop being such a good compiler", "hello")`
- --> $DIR/conf_nonstandard_macro_braces.rs:30:13
+ --> $DIR/conf_nonstandard_macro_braces.rs:38:13
|
LL | let _ = format!["ugh {} stop being such a good compiler", "hello"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: use of irregular braces for `quote!` macro
- --> $DIR/conf_nonstandard_macro_braces.rs:31:13
+ --> $DIR/conf_nonstandard_macro_braces.rs:39:13
|
LL | let _ = quote!(let x = 1;);
| ^^^^^^^^^^^^^^^^^^
|
help: consider writing `quote! {let x = 1;}`
- --> $DIR/conf_nonstandard_macro_braces.rs:31:13
+ --> $DIR/conf_nonstandard_macro_braces.rs:39:13
|
LL | let _ = quote!(let x = 1;);
| ^^^^^^^^^^^^^^^^^^
error: use of irregular braces for `quote::quote!` macro
- --> $DIR/conf_nonstandard_macro_braces.rs:32:13
+ --> $DIR/conf_nonstandard_macro_braces.rs:40:13
|
LL | let _ = quote::quote!(match match match);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: consider writing `quote::quote! {match match match}`
- --> $DIR/conf_nonstandard_macro_braces.rs:32:13
+ --> $DIR/conf_nonstandard_macro_braces.rs:40:13
|
LL | let _ = quote::quote!(match match match);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: use of irregular braces for `vec!` macro
- --> $DIR/conf_nonstandard_macro_braces.rs:10:9
+ --> $DIR/conf_nonstandard_macro_braces.rs:18:9
|
LL | vec!{0, 0, 0}
| ^^^^^^^^^^^^^
| ------- in this macro invocation
|
help: consider writing `vec![0, 0, 0]`
- --> $DIR/conf_nonstandard_macro_braces.rs:10:9
+ --> $DIR/conf_nonstandard_macro_braces.rs:18:9
|
LL | vec!{0, 0, 0}
| ^^^^^^^^^^^^^
= note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info)
error: use of irregular braces for `type_pos!` macro
- --> $DIR/conf_nonstandard_macro_braces.rs:41:12
+ --> $DIR/conf_nonstandard_macro_braces.rs:49:12
|
LL | let _: type_pos!(usize) = vec![];
| ^^^^^^^^^^^^^^^^
|
help: consider writing `type_pos![usize]`
- --> $DIR/conf_nonstandard_macro_braces.rs:41:12
+ --> $DIR/conf_nonstandard_macro_braces.rs:49:12
|
LL | let _: type_pos!(usize) = vec![];
| ^^^^^^^^^^^^^^^^
error: use of irregular braces for `eprint!` macro
- --> $DIR/conf_nonstandard_macro_braces.rs:43:5
+ --> $DIR/conf_nonstandard_macro_braces.rs:51:5
|
LL | eprint!("test if user config overrides defaults");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: consider writing `eprint!["test if user config overrides defaults"];`
- --> $DIR/conf_nonstandard_macro_braces.rs:43:5
+ --> $DIR/conf_nonstandard_macro_braces.rs:51:5
|
LL | eprint!("test if user config overrides defaults");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
--- /dev/null
+#![allow(dead_code)]
+#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)]
+
+// ##################################
+// # Issue clippy#7369
+// ##################################
+#[derive(Debug)]
+pub struct FooBar {
+ foo: Vec<u32>,
+}
+
+impl FooBar {
+ pub fn bar(&mut self) {
+ if true {
+ self.foo.pop();
+ } else {
+ self.baz();
+
+ self.foo.pop();
+
+ self.baz()
+ }
+ }
+
+ fn baz(&mut self) {}
+}
+
+fn main() {}
--- /dev/null
+pub trait Trait {
+ fn f();
+}
+
+impl Trait for usize {
+ fn f() {
+ extern "C" {
+ fn g() -> usize;
+ }
+ }
+}
+
+fn main() {}
+++ /dev/null
-// aux-build:macro_rules.rs
-
-#![warn(clippy::default_numeric_fallback)]
-#![allow(unused)]
-#![allow(clippy::never_loop)]
-#![allow(clippy::no_effect)]
-#![allow(clippy::unnecessary_operation)]
-#![allow(clippy::branches_sharing_code)]
-
-#[macro_use]
-extern crate macro_rules;
-
-mod basic_expr {
- fn test() {
- // Should lint unsuffixed literals typed `i32`.
- let x = 22;
- let x = [1, 2, 3];
- let x = if true { (1, 2) } else { (3, 4) };
- let x = match 1 {
- 1 => 1,
- _ => 2,
- };
-
- // Should lint unsuffixed literals typed `f64`.
- let x = 0.12;
-
- // Should NOT lint suffixed literals.
- let x = 22_i32;
- let x = 0.12_f64;
-
- // Should NOT lint literals in init expr if `Local` has a type annotation.
- let x: f64 = 0.1;
- let x: [i32; 3] = [1, 2, 3];
- let x: (i32, i32) = if true { (1, 2) } else { (3, 4) };
- let x: _ = 1;
- }
-}
-
-mod nested_local {
- fn test() {
- let x: _ = {
- // Should lint this because this literal is not bound to any types.
- let y = 1;
-
- // Should NOT lint this because this literal is bound to `_` of outer `Local`.
- 1
- };
-
- let x: _ = if true {
- // Should lint this because this literal is not bound to any types.
- let y = 1;
-
- // Should NOT lint this because this literal is bound to `_` of outer `Local`.
- 1
- } else {
- // Should lint this because this literal is not bound to any types.
- let y = 1;
-
- // Should NOT lint this because this literal is bound to `_` of outer `Local`.
- 2
- };
- }
-}
-
-mod function_def {
- fn ret_i32() -> i32 {
- // Even though the output type is specified,
- // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
- 1
- }
-
- fn test() {
- // Should lint this because return type is inferred to `i32` and NOT bound to a concrete
- // type.
- let f = || -> _ { 1 };
-
- // Even though the output type is specified,
- // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
- let f = || -> i32 { 1 };
- }
-}
-
-mod function_calls {
- fn concrete_arg(x: i32) {}
-
- fn generic_arg<T>(t: T) {}
-
- fn test() {
- // Should NOT lint this because the argument type is bound to a concrete type.
- concrete_arg(1);
-
- // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
- generic_arg(1);
-
- // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
- let x: _ = generic_arg(1);
- }
-}
-
-mod struct_ctor {
- struct ConcreteStruct {
- x: i32,
- }
-
- struct GenericStruct<T> {
- x: T,
- }
-
- fn test() {
- // Should NOT lint this because the field type is bound to a concrete type.
- ConcreteStruct { x: 1 };
-
- // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
- GenericStruct { x: 1 };
-
- // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
- let _ = GenericStruct { x: 1 };
- }
-}
-
-mod method_calls {
- struct StructForMethodCallTest {}
-
- impl StructForMethodCallTest {
- fn concrete_arg(&self, x: i32) {}
-
- fn generic_arg<T>(&self, t: T) {}
- }
-
- fn test() {
- let s = StructForMethodCallTest {};
-
- // Should NOT lint this because the argument type is bound to a concrete type.
- s.concrete_arg(1);
-
- // Should lint this because the argument type is bound to a concrete type.
- s.generic_arg(1);
- }
-}
-
-mod in_macro {
- macro_rules! internal_macro {
- () => {
- let x = 22;
- };
- }
-
- // Should lint in internal macro.
- fn internal() {
- internal_macro!();
- }
-
- // Should NOT lint in external macro.
- fn external() {
- default_numeric_fallback!();
- }
-}
-
-fn main() {}
+++ /dev/null
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:16:17
- |
-LL | let x = 22;
- | ^^ help: consider adding suffix: `22_i32`
- |
- = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:17:18
- |
-LL | let x = [1, 2, 3];
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:17:21
- |
-LL | let x = [1, 2, 3];
- | ^ help: consider adding suffix: `2_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:17:24
- |
-LL | let x = [1, 2, 3];
- | ^ help: consider adding suffix: `3_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:18:28
- |
-LL | let x = if true { (1, 2) } else { (3, 4) };
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:18:31
- |
-LL | let x = if true { (1, 2) } else { (3, 4) };
- | ^ help: consider adding suffix: `2_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:18:44
- |
-LL | let x = if true { (1, 2) } else { (3, 4) };
- | ^ help: consider adding suffix: `3_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:18:47
- |
-LL | let x = if true { (1, 2) } else { (3, 4) };
- | ^ help: consider adding suffix: `4_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:19:23
- |
-LL | let x = match 1 {
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:20:13
- |
-LL | 1 => 1,
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:20:18
- |
-LL | 1 => 1,
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:21:18
- |
-LL | _ => 2,
- | ^ help: consider adding suffix: `2_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:25:17
- |
-LL | let x = 0.12;
- | ^^^^ help: consider adding suffix: `0.12_f64`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:43:21
- |
-LL | let y = 1;
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:51:21
- |
-LL | let y = 1;
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:57:21
- |
-LL | let y = 1;
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:69:9
- |
-LL | 1
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:75:27
- |
-LL | let f = || -> _ { 1 };
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:79:29
- |
-LL | let f = || -> i32 { 1 };
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:93:21
- |
-LL | generic_arg(1);
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:96:32
- |
-LL | let x: _ = generic_arg(1);
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:114:28
- |
-LL | GenericStruct { x: 1 };
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:117:36
- |
-LL | let _ = GenericStruct { x: 1 };
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:137:23
- |
-LL | s.generic_arg(1);
- | ^ help: consider adding suffix: `1_i32`
-
-error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback.rs:144:21
- |
-LL | let x = 22;
- | ^^ help: consider adding suffix: `22_i32`
-...
-LL | internal_macro!();
- | ------------------ in this macro invocation
- |
- = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: aborting due to 25 previous errors
-
--- /dev/null
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::default_numeric_fallback)]
+#![allow(unused)]
+#![allow(clippy::never_loop)]
+#![allow(clippy::no_effect)]
+#![allow(clippy::unnecessary_operation)]
+#![allow(clippy::branches_sharing_code)]
+#![allow(clippy::match_single_binding)]
+
+#[macro_use]
+extern crate macro_rules;
+
+mod basic_expr {
+ fn test() {
+ // Should lint unsuffixed literals typed `f64`.
+ let x = 0.12_f64;
+ let x = [1.0_f64, 2.0_f64, 3.0_f64];
+ let x = if true { (1.0_f64, 2.0_f64) } else { (3.0_f64, 4.0_f64) };
+ let x = match 1.0_f64 {
+ _ => 1.0_f64,
+ };
+
+ // Should NOT lint suffixed literals.
+ let x = 0.12_f64;
+
+ // Should NOT lint literals in init expr if `Local` has a type annotation.
+ let x: f64 = 0.1;
+ let x: [f64; 3] = [1., 2., 3.];
+ let x: (f64, f64) = if true { (1., 2.) } else { (3., 4.) };
+ let x: _ = 1.;
+ }
+}
+
+mod nested_local {
+ fn test() {
+ let x: _ = {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1.0_f64;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 1.
+ };
+
+ let x: _ = if true {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1.0_f64;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 1.
+ } else {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1.0_f64;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 2.
+ };
+ }
+}
+
+mod function_def {
+ fn ret_f64() -> f64 {
+ // Even though the output type is specified,
+ // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+ 1.0_f64
+ }
+
+ fn test() {
+ // Should lint this because return type is inferred to `f64` and NOT bound to a concrete
+ // type.
+ let f = || -> _ { 1.0_f64 };
+
+ // Even though the output type is specified,
+ // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+ let f = || -> f64 { 1.0_f64 };
+ }
+}
+
+mod function_calls {
+ fn concrete_arg(f: f64) {}
+
+ fn generic_arg<T>(t: T) {}
+
+ fn test() {
+ // Should NOT lint this because the argument type is bound to a concrete type.
+ concrete_arg(1.);
+
+ // Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
+ generic_arg(1.0_f64);
+
+ // Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
+ let x: _ = generic_arg(1.0_f64);
+ }
+}
+
+mod struct_ctor {
+ struct ConcreteStruct {
+ x: f64,
+ }
+
+ struct GenericStruct<T> {
+ x: T,
+ }
+
+ fn test() {
+ // Should NOT lint this because the field type is bound to a concrete type.
+ ConcreteStruct { x: 1. };
+
+ // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+ GenericStruct { x: 1.0_f64 };
+
+ // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+ let _ = GenericStruct { x: 1.0_f64 };
+ }
+}
+
+mod enum_ctor {
+ enum ConcreteEnum {
+ X(f64),
+ }
+
+ enum GenericEnum<T> {
+ X(T),
+ }
+
+ fn test() {
+ // Should NOT lint this because the field type is bound to a concrete type.
+ ConcreteEnum::X(1.);
+
+ // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+ GenericEnum::X(1.0_f64);
+ }
+}
+
+mod method_calls {
+ struct StructForMethodCallTest {}
+
+ impl StructForMethodCallTest {
+ fn concrete_arg(&self, f: f64) {}
+
+ fn generic_arg<T>(&self, t: T) {}
+ }
+
+ fn test() {
+ let s = StructForMethodCallTest {};
+
+ // Should NOT lint this because the argument type is bound to a concrete type.
+ s.concrete_arg(1.);
+
+ // Should lint this because the argument type is bound to a concrete type.
+ s.generic_arg(1.0_f64);
+ }
+}
+
+mod in_macro {
+ macro_rules! internal_macro {
+ () => {
+ let x = 22.0_f64;
+ };
+ }
+
+ // Should lint in internal macro.
+ fn internal() {
+ internal_macro!();
+ }
+
+ // Should NOT lint in external macro.
+ fn external() {
+ default_numeric_fallback!();
+ }
+}
+
+fn main() {}
--- /dev/null
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::default_numeric_fallback)]
+#![allow(unused)]
+#![allow(clippy::never_loop)]
+#![allow(clippy::no_effect)]
+#![allow(clippy::unnecessary_operation)]
+#![allow(clippy::branches_sharing_code)]
+#![allow(clippy::match_single_binding)]
+
+#[macro_use]
+extern crate macro_rules;
+
+mod basic_expr {
+ fn test() {
+ // Should lint unsuffixed literals typed `f64`.
+ let x = 0.12;
+ let x = [1., 2., 3.];
+ let x = if true { (1., 2.) } else { (3., 4.) };
+ let x = match 1. {
+ _ => 1.,
+ };
+
+ // Should NOT lint suffixed literals.
+ let x = 0.12_f64;
+
+ // Should NOT lint literals in init expr if `Local` has a type annotation.
+ let x: f64 = 0.1;
+ let x: [f64; 3] = [1., 2., 3.];
+ let x: (f64, f64) = if true { (1., 2.) } else { (3., 4.) };
+ let x: _ = 1.;
+ }
+}
+
+mod nested_local {
+ fn test() {
+ let x: _ = {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1.;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 1.
+ };
+
+ let x: _ = if true {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1.;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 1.
+ } else {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1.;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 2.
+ };
+ }
+}
+
+mod function_def {
+ fn ret_f64() -> f64 {
+ // Even though the output type is specified,
+ // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+ 1.
+ }
+
+ fn test() {
+ // Should lint this because return type is inferred to `f64` and NOT bound to a concrete
+ // type.
+ let f = || -> _ { 1. };
+
+ // Even though the output type is specified,
+ // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+ let f = || -> f64 { 1. };
+ }
+}
+
+mod function_calls {
+ fn concrete_arg(f: f64) {}
+
+ fn generic_arg<T>(t: T) {}
+
+ fn test() {
+ // Should NOT lint this because the argument type is bound to a concrete type.
+ concrete_arg(1.);
+
+ // Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
+ generic_arg(1.);
+
+ // Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
+ let x: _ = generic_arg(1.);
+ }
+}
+
+mod struct_ctor {
+ struct ConcreteStruct {
+ x: f64,
+ }
+
+ struct GenericStruct<T> {
+ x: T,
+ }
+
+ fn test() {
+ // Should NOT lint this because the field type is bound to a concrete type.
+ ConcreteStruct { x: 1. };
+
+ // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+ GenericStruct { x: 1. };
+
+ // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+ let _ = GenericStruct { x: 1. };
+ }
+}
+
+mod enum_ctor {
+ enum ConcreteEnum {
+ X(f64),
+ }
+
+ enum GenericEnum<T> {
+ X(T),
+ }
+
+ fn test() {
+ // Should NOT lint this because the field type is bound to a concrete type.
+ ConcreteEnum::X(1.);
+
+ // Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
+ GenericEnum::X(1.);
+ }
+}
+
+mod method_calls {
+ struct StructForMethodCallTest {}
+
+ impl StructForMethodCallTest {
+ fn concrete_arg(&self, f: f64) {}
+
+ fn generic_arg<T>(&self, t: T) {}
+ }
+
+ fn test() {
+ let s = StructForMethodCallTest {};
+
+ // Should NOT lint this because the argument type is bound to a concrete type.
+ s.concrete_arg(1.);
+
+ // Should lint this because the argument type is bound to a concrete type.
+ s.generic_arg(1.);
+ }
+}
+
+mod in_macro {
+ macro_rules! internal_macro {
+ () => {
+ let x = 22.;
+ };
+ }
+
+ // Should lint in internal macro.
+ fn internal() {
+ internal_macro!();
+ }
+
+ // Should NOT lint in external macro.
+ fn external() {
+ default_numeric_fallback!();
+ }
+}
+
+fn main() {}
--- /dev/null
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:18:17
+ |
+LL | let x = 0.12;
+ | ^^^^ help: consider adding suffix: `0.12_f64`
+ |
+ = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:19:18
+ |
+LL | let x = [1., 2., 3.];
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:19:22
+ |
+LL | let x = [1., 2., 3.];
+ | ^^ help: consider adding suffix: `2.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:19:26
+ |
+LL | let x = [1., 2., 3.];
+ | ^^ help: consider adding suffix: `3.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:20:28
+ |
+LL | let x = if true { (1., 2.) } else { (3., 4.) };
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:20:32
+ |
+LL | let x = if true { (1., 2.) } else { (3., 4.) };
+ | ^^ help: consider adding suffix: `2.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:20:46
+ |
+LL | let x = if true { (1., 2.) } else { (3., 4.) };
+ | ^^ help: consider adding suffix: `3.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:20:50
+ |
+LL | let x = if true { (1., 2.) } else { (3., 4.) };
+ | ^^ help: consider adding suffix: `4.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:21:23
+ |
+LL | let x = match 1. {
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:22:18
+ |
+LL | _ => 1.,
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:40:21
+ |
+LL | let y = 1.;
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:48:21
+ |
+LL | let y = 1.;
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:54:21
+ |
+LL | let y = 1.;
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:66:9
+ |
+LL | 1.
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:72:27
+ |
+LL | let f = || -> _ { 1. };
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:76:29
+ |
+LL | let f = || -> f64 { 1. };
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:90:21
+ |
+LL | generic_arg(1.);
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:93:32
+ |
+LL | let x: _ = generic_arg(1.);
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:111:28
+ |
+LL | GenericStruct { x: 1. };
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:114:36
+ |
+LL | let _ = GenericStruct { x: 1. };
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:132:24
+ |
+LL | GenericEnum::X(1.);
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:152:23
+ |
+LL | s.generic_arg(1.);
+ | ^^ help: consider adding suffix: `1.0_f64`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_f64.rs:159:21
+ |
+LL | let x = 22.;
+ | ^^^ help: consider adding suffix: `22.0_f64`
+...
+LL | internal_macro!();
+ | ------------------ in this macro invocation
+ |
+ = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 23 previous errors
+
--- /dev/null
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::default_numeric_fallback)]
+#![allow(unused)]
+#![allow(clippy::never_loop)]
+#![allow(clippy::no_effect)]
+#![allow(clippy::unnecessary_operation)]
+#![allow(clippy::branches_sharing_code)]
+
+#[macro_use]
+extern crate macro_rules;
+
+mod basic_expr {
+ fn test() {
+ // Should lint unsuffixed literals typed `i32`.
+ let x = 22_i32;
+ let x = [1_i32, 2_i32, 3_i32];
+ let x = if true { (1_i32, 2_i32) } else { (3_i32, 4_i32) };
+ let x = match 1_i32 {
+ 1_i32 => 1_i32,
+ _ => 2_i32,
+ };
+
+ // Should NOT lint suffixed literals.
+ let x = 22_i32;
+
+ // Should NOT lint literals in init expr if `Local` has a type annotation.
+ let x: [i32; 3] = [1, 2, 3];
+ let x: (i32, i32) = if true { (1, 2) } else { (3, 4) };
+ let x: _ = 1;
+ }
+}
+
+mod nested_local {
+ fn test() {
+ let x: _ = {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1_i32;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 1
+ };
+
+ let x: _ = if true {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1_i32;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 1
+ } else {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1_i32;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 2
+ };
+ }
+}
+
+mod function_def {
+ fn ret_i32() -> i32 {
+ // Even though the output type is specified,
+ // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+ 1_i32
+ }
+
+ fn test() {
+ // Should lint this because return type is inferred to `i32` and NOT bound to a concrete
+ // type.
+ let f = || -> _ { 1_i32 };
+
+ // Even though the output type is specified,
+ // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+ let f = || -> i32 { 1_i32 };
+ }
+}
+
+mod function_calls {
+ fn concrete_arg(x: i32) {}
+
+ fn generic_arg<T>(t: T) {}
+
+ fn test() {
+ // Should NOT lint this because the argument type is bound to a concrete type.
+ concrete_arg(1);
+
+ // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
+ generic_arg(1_i32);
+
+ // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
+ let x: _ = generic_arg(1_i32);
+ }
+}
+
+mod struct_ctor {
+ struct ConcreteStruct {
+ x: i32,
+ }
+
+ struct GenericStruct<T> {
+ x: T,
+ }
+
+ fn test() {
+ // Should NOT lint this because the field type is bound to a concrete type.
+ ConcreteStruct { x: 1 };
+
+ // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+ GenericStruct { x: 1_i32 };
+
+ // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+ let _ = GenericStruct { x: 1_i32 };
+ }
+}
+
+mod enum_ctor {
+ enum ConcreteEnum {
+ X(i32),
+ }
+
+ enum GenericEnum<T> {
+ X(T),
+ }
+
+ fn test() {
+ // Should NOT lint this because the field type is bound to a concrete type.
+ ConcreteEnum::X(1);
+
+ // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+ GenericEnum::X(1_i32);
+ }
+}
+
+mod method_calls {
+ struct StructForMethodCallTest {}
+
+ impl StructForMethodCallTest {
+ fn concrete_arg(&self, x: i32) {}
+
+ fn generic_arg<T>(&self, t: T) {}
+ }
+
+ fn test() {
+ let s = StructForMethodCallTest {};
+
+ // Should NOT lint this because the argument type is bound to a concrete type.
+ s.concrete_arg(1);
+
+ // Should lint this because the argument type is bound to a concrete type.
+ s.generic_arg(1_i32);
+ }
+}
+
+mod in_macro {
+ macro_rules! internal_macro {
+ () => {
+ let x = 22_i32;
+ };
+ }
+
+ // Should lint in internal macro.
+ fn internal() {
+ internal_macro!();
+ }
+
+ // Should NOT lint in external macro.
+ fn external() {
+ default_numeric_fallback!();
+ }
+}
+
+fn main() {}
--- /dev/null
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::default_numeric_fallback)]
+#![allow(unused)]
+#![allow(clippy::never_loop)]
+#![allow(clippy::no_effect)]
+#![allow(clippy::unnecessary_operation)]
+#![allow(clippy::branches_sharing_code)]
+
+#[macro_use]
+extern crate macro_rules;
+
+mod basic_expr {
+ fn test() {
+ // Should lint unsuffixed literals typed `i32`.
+ let x = 22;
+ let x = [1, 2, 3];
+ let x = if true { (1, 2) } else { (3, 4) };
+ let x = match 1 {
+ 1 => 1,
+ _ => 2,
+ };
+
+ // Should NOT lint suffixed literals.
+ let x = 22_i32;
+
+ // Should NOT lint literals in init expr if `Local` has a type annotation.
+ let x: [i32; 3] = [1, 2, 3];
+ let x: (i32, i32) = if true { (1, 2) } else { (3, 4) };
+ let x: _ = 1;
+ }
+}
+
+mod nested_local {
+ fn test() {
+ let x: _ = {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 1
+ };
+
+ let x: _ = if true {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 1
+ } else {
+ // Should lint this because this literal is not bound to any types.
+ let y = 1;
+
+ // Should NOT lint this because this literal is bound to `_` of outer `Local`.
+ 2
+ };
+ }
+}
+
+mod function_def {
+ fn ret_i32() -> i32 {
+ // Even though the output type is specified,
+ // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+ 1
+ }
+
+ fn test() {
+ // Should lint this because return type is inferred to `i32` and NOT bound to a concrete
+ // type.
+ let f = || -> _ { 1 };
+
+ // Even though the output type is specified,
+ // this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
+ let f = || -> i32 { 1 };
+ }
+}
+
+mod function_calls {
+ fn concrete_arg(x: i32) {}
+
+ fn generic_arg<T>(t: T) {}
+
+ fn test() {
+ // Should NOT lint this because the argument type is bound to a concrete type.
+ concrete_arg(1);
+
+ // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
+ generic_arg(1);
+
+ // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type.
+ let x: _ = generic_arg(1);
+ }
+}
+
+mod struct_ctor {
+ struct ConcreteStruct {
+ x: i32,
+ }
+
+ struct GenericStruct<T> {
+ x: T,
+ }
+
+ fn test() {
+ // Should NOT lint this because the field type is bound to a concrete type.
+ ConcreteStruct { x: 1 };
+
+ // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+ GenericStruct { x: 1 };
+
+ // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+ let _ = GenericStruct { x: 1 };
+ }
+}
+
+mod enum_ctor {
+ enum ConcreteEnum {
+ X(i32),
+ }
+
+ enum GenericEnum<T> {
+ X(T),
+ }
+
+ fn test() {
+ // Should NOT lint this because the field type is bound to a concrete type.
+ ConcreteEnum::X(1);
+
+ // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type.
+ GenericEnum::X(1);
+ }
+}
+
+mod method_calls {
+ struct StructForMethodCallTest {}
+
+ impl StructForMethodCallTest {
+ fn concrete_arg(&self, x: i32) {}
+
+ fn generic_arg<T>(&self, t: T) {}
+ }
+
+ fn test() {
+ let s = StructForMethodCallTest {};
+
+ // Should NOT lint this because the argument type is bound to a concrete type.
+ s.concrete_arg(1);
+
+ // Should lint this because the argument type is bound to a concrete type.
+ s.generic_arg(1);
+ }
+}
+
+mod in_macro {
+ macro_rules! internal_macro {
+ () => {
+ let x = 22;
+ };
+ }
+
+ // Should lint in internal macro.
+ fn internal() {
+ internal_macro!();
+ }
+
+ // Should NOT lint in external macro.
+ fn external() {
+ default_numeric_fallback!();
+ }
+}
+
+fn main() {}
--- /dev/null
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:17:17
+ |
+LL | let x = 22;
+ | ^^ help: consider adding suffix: `22_i32`
+ |
+ = note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:18:18
+ |
+LL | let x = [1, 2, 3];
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:18:21
+ |
+LL | let x = [1, 2, 3];
+ | ^ help: consider adding suffix: `2_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:18:24
+ |
+LL | let x = [1, 2, 3];
+ | ^ help: consider adding suffix: `3_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:19:28
+ |
+LL | let x = if true { (1, 2) } else { (3, 4) };
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:19:31
+ |
+LL | let x = if true { (1, 2) } else { (3, 4) };
+ | ^ help: consider adding suffix: `2_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:19:44
+ |
+LL | let x = if true { (1, 2) } else { (3, 4) };
+ | ^ help: consider adding suffix: `3_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:19:47
+ |
+LL | let x = if true { (1, 2) } else { (3, 4) };
+ | ^ help: consider adding suffix: `4_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:20:23
+ |
+LL | let x = match 1 {
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:21:13
+ |
+LL | 1 => 1,
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:21:18
+ |
+LL | 1 => 1,
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:22:18
+ |
+LL | _ => 2,
+ | ^ help: consider adding suffix: `2_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:39:21
+ |
+LL | let y = 1;
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:47:21
+ |
+LL | let y = 1;
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:53:21
+ |
+LL | let y = 1;
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:65:9
+ |
+LL | 1
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:71:27
+ |
+LL | let f = || -> _ { 1 };
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:75:29
+ |
+LL | let f = || -> i32 { 1 };
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:89:21
+ |
+LL | generic_arg(1);
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:92:32
+ |
+LL | let x: _ = generic_arg(1);
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:110:28
+ |
+LL | GenericStruct { x: 1 };
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:113:36
+ |
+LL | let _ = GenericStruct { x: 1 };
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:131:24
+ |
+LL | GenericEnum::X(1);
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:151:23
+ |
+LL | s.generic_arg(1);
+ | ^ help: consider adding suffix: `1_i32`
+
+error: default numeric fallback might occur
+ --> $DIR/default_numeric_fallback_i32.rs:158:21
+ |
+LL | let x = 22;
+ | ^^ help: consider adding suffix: `22_i32`
+...
+LL | internal_macro!();
+ | ------------------ in this macro invocation
+ |
+ = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 25 previous errors
+
/// - This `item has unbalanced tick marks
/// - This item needs backticks_here
fn other_markdown() {}
+
+#[rustfmt::skip]
+/// - ```rust
+/// /// `lol`
+/// pub struct Struct;
+/// ```
+fn iss_7421() {}
fn test_deref_with_trait_method() {
let _ = [Bar].iter().map(|s| s.to_string()).collect::<Vec<_>>();
}
+
+fn mutable_closure_used_again(x: Vec<i32>, y: Vec<i32>, z: Vec<i32>) {
+ let mut res = Vec::new();
+ let mut add_to_res = |n| res.push(n);
+ x.into_iter().for_each(&mut add_to_res);
+ y.into_iter().for_each(&mut add_to_res);
+ z.into_iter().for_each(add_to_res);
+}
+
+fn mutable_closure_in_loop() {
+ let mut value = 0;
+ let mut closure = |n| value += n;
+ for _ in 0..5 {
+ Some(1).map(&mut closure);
+ }
+}
fn test_deref_with_trait_method() {
let _ = [Bar].iter().map(|s| s.to_string()).collect::<Vec<_>>();
}
+
+fn mutable_closure_used_again(x: Vec<i32>, y: Vec<i32>, z: Vec<i32>) {
+ let mut res = Vec::new();
+ let mut add_to_res = |n| res.push(n);
+ x.into_iter().for_each(|x| add_to_res(x));
+ y.into_iter().for_each(|x| add_to_res(x));
+ z.into_iter().for_each(|x| add_to_res(x));
+}
+
+fn mutable_closure_in_loop() {
+ let mut value = 0;
+ let mut closure = |n| value += n;
+ for _ in 0..5 {
+ Some(1).map(|n| closure(n));
+ }
+}
LL | let a = Some(1u8).map(|a| closure(a));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure`
-error: aborting due to 13 previous errors
+error: redundant closure
+ --> $DIR/eta.rs:227:28
+ |
+LL | x.into_iter().for_each(|x| add_to_res(x));
+ | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
+
+error: redundant closure
+ --> $DIR/eta.rs:228:28
+ |
+LL | y.into_iter().for_each(|x| add_to_res(x));
+ | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
+
+error: redundant closure
+ --> $DIR/eta.rs:229:28
+ |
+LL | z.into_iter().for_each(|x| add_to_res(x));
+ | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res`
+
+error: redundant closure
+ --> $DIR/eta.rs:236:21
+ |
+LL | Some(1).map(|n| closure(n));
+ | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure`
+
+error: aborting due to 17 previous errors
-error: use of `writeln!(stderr(), ...).unwrap()`. Consider using `eprintln!` instead
+error: use of `writeln!(stderr(), ...).unwrap()`
--> $DIR/explicit_write_non_rustfix.rs:7:5
|
LL | writeln!(std::io::stderr(), "foo {}", bar).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::explicit-write` implied by `-D warnings`
+ = help: consider using `eprintln!` instead
error: aborting due to previous error
// Wrap it with braces
let v: Vec<String> = vec!["foo".to_string(), "bar".to_string()];
let _s: String = (&*v.join("\n")).to_string();
+
+ format!("prepend {:+}", "s");
}
// Wrap it with braces
let v: Vec<String> = vec!["foo".to_string(), "bar".to_string()];
let _s: String = format!("{}", &*v.join("\n"));
+
+ format!("prepend {:+}", "s");
}
unreachable!();
}
+fn assert() {
+ assert!(true);
+ assert_eq!(true, true);
+ assert_ne!(true, false);
+}
+
+fn assert_msg() {
+ assert!(true, "this should not panic");
+ assert_eq!(true, true, "this should not panic");
+ assert_ne!(true, false, "this should not panic");
+}
+
fn debug_assert() {
debug_assert!(true);
debug_assert_eq!(true, true);
unimplemented();
unreachable();
core_versions();
+ assert();
+ assert_msg();
+ debug_assert();
+ debug_assert_msg();
}
--- /dev/null
+#![warn(clippy::rc_mutex)]
+#![allow(clippy::blacklisted_name)]
+
+use std::rc::Rc;
+use std::sync::Mutex;
+
+pub struct MyStruct {
+ foo: Rc<Mutex<i32>>,
+}
+
+pub struct SubT<T> {
+ foo: T,
+}
+
+pub enum MyEnum {
+ One,
+ Two,
+}
+
+pub fn test1<T>(foo: Rc<Mutex<T>>) {}
+
+pub fn test2(foo: Rc<Mutex<MyEnum>>) {}
+
+pub fn test3(foo: Rc<Mutex<SubT<usize>>>) {}
+
+fn main() {
+ test1(Rc::new(Mutex::new(1)));
+ test2(Rc::new(Mutex::new(MyEnum::One)));
+ test3(Rc::new(Mutex::new(SubT { foo: 1 })));
+
+ let _my_struct = MyStruct {
+ foo: Rc::new(Mutex::new(1)),
+ };
+}
--- /dev/null
+error: found `Rc<Mutex<_>>`. Consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
+ --> $DIR/rc_mutex.rs:8:10
+ |
+LL | foo: Rc<Mutex<i32>>,
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::rc-mutex` implied by `-D warnings`
+
+error: found `Rc<Mutex<_>>`. Consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
+ --> $DIR/rc_mutex.rs:20:22
+ |
+LL | pub fn test1<T>(foo: Rc<Mutex<T>>) {}
+ | ^^^^^^^^^^^^
+
+error: found `Rc<Mutex<_>>`. Consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
+ --> $DIR/rc_mutex.rs:22:19
+ |
+LL | pub fn test2(foo: Rc<Mutex<MyEnum>>) {}
+ | ^^^^^^^^^^^^^^^^^
+
+error: found `Rc<Mutex<_>>`. Consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
+ --> $DIR/rc_mutex.rs:24:19
+ |
+LL | pub fn test3(foo: Rc<Mutex<SubT<usize>>>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 4 previous errors
+
+++ /dev/null
-// run-rustfix
-#![warn(clippy::all)]
-#![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
-#![allow(clippy::blacklisted_name, unused_variables, dead_code)]
-
-use std::boxed::Box;
-use std::rc::Rc;
-
-pub struct MyStruct {}
-
-pub struct SubT<T> {
- foo: T,
-}
-
-pub enum MyEnum {
- One,
- Two,
-}
-
-// Rc<&T>
-
-pub fn test1<T>(foo: &T) {}
-
-pub fn test2(foo: &MyStruct) {}
-
-pub fn test3(foo: &MyEnum) {}
-
-pub fn test4_neg(foo: Rc<SubT<&usize>>) {}
-
-// Rc<Rc<T>>
-
-pub fn test5(a: Rc<bool>) {}
-
-// Rc<Box<T>>
-
-pub fn test6(a: Rc<bool>) {}
-
-// Box<&T>
-
-pub fn test7<T>(foo: &T) {}
-
-pub fn test8(foo: &MyStruct) {}
-
-pub fn test9(foo: &MyEnum) {}
-
-pub fn test10_neg(foo: Box<SubT<&usize>>) {}
-
-fn main() {}
-// run-rustfix
#![warn(clippy::all)]
#![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
#![allow(clippy::blacklisted_name, unused_variables, dead_code)]
-
-use std::boxed::Box;
-use std::rc::Rc;
+#![allow(unused_imports)]
pub struct MyStruct {}
Two,
}
-// Rc<&T>
+mod outer_box {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
-pub fn test1<T>(foo: Rc<&T>) {}
+ pub fn box_test6<T>(foo: Box<Rc<T>>) {}
-pub fn test2(foo: Rc<&MyStruct>) {}
+ pub fn box_test7<T>(foo: Box<Arc<T>>) {}
-pub fn test3(foo: Rc<&MyEnum>) {}
+ pub fn box_test8() -> Box<Rc<SubT<usize>>> {
+ unimplemented!();
+ }
-pub fn test4_neg(foo: Rc<SubT<&usize>>) {}
+ pub fn box_test9<T>(foo: Box<Arc<T>>) -> Box<Arc<SubT<T>>> {
+ unimplemented!();
+ }
+}
-// Rc<Rc<T>>
+mod outer_rc {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
-pub fn test5(a: Rc<Rc<bool>>) {}
+ pub fn rc_test5(a: Rc<Box<bool>>) {}
-// Rc<Box<T>>
+ pub fn rc_test7(a: Rc<Arc<bool>>) {}
-pub fn test6(a: Rc<Box<bool>>) {}
+ pub fn rc_test8() -> Rc<Box<SubT<usize>>> {
+ unimplemented!();
+ }
-// Box<&T>
+ pub fn rc_test9<T>(foo: Rc<Arc<T>>) -> Rc<Arc<SubT<T>>> {
+ unimplemented!();
+ }
+}
-pub fn test7<T>(foo: Box<&T>) {}
+mod outer_arc {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
-pub fn test8(foo: Box<&MyStruct>) {}
+ pub fn arc_test5(a: Arc<Box<bool>>) {}
-pub fn test9(foo: Box<&MyEnum>) {}
+ pub fn arc_test6(a: Arc<Rc<bool>>) {}
-pub fn test10_neg(foo: Box<SubT<&usize>>) {}
+ pub fn arc_test8() -> Arc<Box<SubT<usize>>> {
+ unimplemented!();
+ }
+
+ pub fn arc_test9<T>(foo: Arc<Rc<T>>) -> Arc<Rc<SubT<T>>> {
+ unimplemented!();
+ }
+}
fn main() {}
-error: usage of `Rc<&T>`
- --> $DIR/redundant_allocation.rs:22:22
+error: usage of `Box<Rc<T>>`
+ --> $DIR/redundant_allocation.rs:25:30
|
-LL | pub fn test1<T>(foo: Rc<&T>) {}
- | ^^^^^^ help: try: `&T`
+LL | pub fn box_test6<T>(foo: Box<Rc<T>>) {}
+ | ^^^^^^^^^^
|
= note: `-D clippy::redundant-allocation` implied by `-D warnings`
+ = note: `Rc<T>` is already on the heap, `Box<Rc<T>>` makes an extra allocation
+ = help: consider using just `Box<T>` or `Rc<T>`
-error: usage of `Rc<&T>`
- --> $DIR/redundant_allocation.rs:24:19
+error: usage of `Box<Arc<T>>`
+ --> $DIR/redundant_allocation.rs:27:30
|
-LL | pub fn test2(foo: Rc<&MyStruct>) {}
- | ^^^^^^^^^^^^^ help: try: `&MyStruct`
+LL | pub fn box_test7<T>(foo: Box<Arc<T>>) {}
+ | ^^^^^^^^^^^
+ |
+ = note: `Arc<T>` is already on the heap, `Box<Arc<T>>` makes an extra allocation
+ = help: consider using just `Box<T>` or `Arc<T>`
+
+error: usage of `Box<Rc<SubT<usize>>>`
+ --> $DIR/redundant_allocation.rs:29:27
+ |
+LL | pub fn box_test8() -> Box<Rc<SubT<usize>>> {
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `Rc<SubT<usize>>` is already on the heap, `Box<Rc<SubT<usize>>>` makes an extra allocation
+ = help: consider using just `Box<SubT<usize>>` or `Rc<SubT<usize>>`
+
+error: usage of `Box<Arc<T>>`
+ --> $DIR/redundant_allocation.rs:33:30
+ |
+LL | pub fn box_test9<T>(foo: Box<Arc<T>>) -> Box<Arc<SubT<T>>> {
+ | ^^^^^^^^^^^
+ |
+ = note: `Arc<T>` is already on the heap, `Box<Arc<T>>` makes an extra allocation
+ = help: consider using just `Box<T>` or `Arc<T>`
+
+error: usage of `Box<Arc<SubT<T>>>`
+ --> $DIR/redundant_allocation.rs:33:46
+ |
+LL | pub fn box_test9<T>(foo: Box<Arc<T>>) -> Box<Arc<SubT<T>>> {
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: `Arc<SubT<T>>` is already on the heap, `Box<Arc<SubT<T>>>` makes an extra allocation
+ = help: consider using just `Box<SubT<T>>` or `Arc<SubT<T>>`
+
+error: usage of `Rc<Box<bool>>`
+ --> $DIR/redundant_allocation.rs:46:24
+ |
+LL | pub fn rc_test5(a: Rc<Box<bool>>) {}
+ | ^^^^^^^^^^^^^
+ |
+ = note: `Box<bool>` is already on the heap, `Rc<Box<bool>>` makes an extra allocation
+ = help: consider using just `Rc<bool>` or `Box<bool>`
+
+error: usage of `Rc<Arc<bool>>`
+ --> $DIR/redundant_allocation.rs:48:24
+ |
+LL | pub fn rc_test7(a: Rc<Arc<bool>>) {}
+ | ^^^^^^^^^^^^^
+ |
+ = note: `Arc<bool>` is already on the heap, `Rc<Arc<bool>>` makes an extra allocation
+ = help: consider using just `Rc<bool>` or `Arc<bool>`
+
+error: usage of `Rc<Box<SubT<usize>>>`
+ --> $DIR/redundant_allocation.rs:50:26
+ |
+LL | pub fn rc_test8() -> Rc<Box<SubT<usize>>> {
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `Box<SubT<usize>>` is already on the heap, `Rc<Box<SubT<usize>>>` makes an extra allocation
+ = help: consider using just `Rc<SubT<usize>>` or `Box<SubT<usize>>`
+
+error: usage of `Rc<Arc<T>>`
+ --> $DIR/redundant_allocation.rs:54:29
+ |
+LL | pub fn rc_test9<T>(foo: Rc<Arc<T>>) -> Rc<Arc<SubT<T>>> {
+ | ^^^^^^^^^^
+ |
+ = note: `Arc<T>` is already on the heap, `Rc<Arc<T>>` makes an extra allocation
+ = help: consider using just `Rc<T>` or `Arc<T>`
-error: usage of `Rc<&T>`
- --> $DIR/redundant_allocation.rs:26:19
+error: usage of `Rc<Arc<SubT<T>>>`
+ --> $DIR/redundant_allocation.rs:54:44
+ |
+LL | pub fn rc_test9<T>(foo: Rc<Arc<T>>) -> Rc<Arc<SubT<T>>> {
+ | ^^^^^^^^^^^^^^^^
|
-LL | pub fn test3(foo: Rc<&MyEnum>) {}
- | ^^^^^^^^^^^ help: try: `&MyEnum`
+ = note: `Arc<SubT<T>>` is already on the heap, `Rc<Arc<SubT<T>>>` makes an extra allocation
+ = help: consider using just `Rc<SubT<T>>` or `Arc<SubT<T>>`
-error: usage of `Rc<Rc<T>>`
- --> $DIR/redundant_allocation.rs:32:17
+error: usage of `Arc<Box<bool>>`
+ --> $DIR/redundant_allocation.rs:67:25
|
-LL | pub fn test5(a: Rc<Rc<bool>>) {}
- | ^^^^^^^^^^^^ help: try: `Rc<bool>`
+LL | pub fn arc_test5(a: Arc<Box<bool>>) {}
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `Box<bool>` is already on the heap, `Arc<Box<bool>>` makes an extra allocation
+ = help: consider using just `Arc<bool>` or `Box<bool>`
-error: usage of `Rc<Box<T>>`
- --> $DIR/redundant_allocation.rs:36:17
+error: usage of `Arc<Rc<bool>>`
+ --> $DIR/redundant_allocation.rs:69:25
+ |
+LL | pub fn arc_test6(a: Arc<Rc<bool>>) {}
+ | ^^^^^^^^^^^^^
|
-LL | pub fn test6(a: Rc<Box<bool>>) {}
- | ^^^^^^^^^^^^^ help: try: `Rc<bool>`
+ = note: `Rc<bool>` is already on the heap, `Arc<Rc<bool>>` makes an extra allocation
+ = help: consider using just `Arc<bool>` or `Rc<bool>`
-error: usage of `Box<&T>`
- --> $DIR/redundant_allocation.rs:40:22
+error: usage of `Arc<Box<SubT<usize>>>`
+ --> $DIR/redundant_allocation.rs:71:27
|
-LL | pub fn test7<T>(foo: Box<&T>) {}
- | ^^^^^^^ help: try: `&T`
+LL | pub fn arc_test8() -> Arc<Box<SubT<usize>>> {
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `Box<SubT<usize>>` is already on the heap, `Arc<Box<SubT<usize>>>` makes an extra allocation
+ = help: consider using just `Arc<SubT<usize>>` or `Box<SubT<usize>>`
-error: usage of `Box<&T>`
- --> $DIR/redundant_allocation.rs:42:19
+error: usage of `Arc<Rc<T>>`
+ --> $DIR/redundant_allocation.rs:75:30
+ |
+LL | pub fn arc_test9<T>(foo: Arc<Rc<T>>) -> Arc<Rc<SubT<T>>> {
+ | ^^^^^^^^^^
|
-LL | pub fn test8(foo: Box<&MyStruct>) {}
- | ^^^^^^^^^^^^^^ help: try: `&MyStruct`
+ = note: `Rc<T>` is already on the heap, `Arc<Rc<T>>` makes an extra allocation
+ = help: consider using just `Arc<T>` or `Rc<T>`
-error: usage of `Box<&T>`
- --> $DIR/redundant_allocation.rs:44:19
+error: usage of `Arc<Rc<SubT<T>>>`
+ --> $DIR/redundant_allocation.rs:75:45
+ |
+LL | pub fn arc_test9<T>(foo: Arc<Rc<T>>) -> Arc<Rc<SubT<T>>> {
+ | ^^^^^^^^^^^^^^^^
|
-LL | pub fn test9(foo: Box<&MyEnum>) {}
- | ^^^^^^^^^^^^ help: try: `&MyEnum`
+ = note: `Rc<SubT<T>>` is already on the heap, `Arc<Rc<SubT<T>>>` makes an extra allocation
+ = help: consider using just `Arc<SubT<T>>` or `Rc<SubT<T>>`
-error: aborting due to 8 previous errors
+error: aborting due to 15 previous errors
--- /dev/null
+// run-rustfix
+#![warn(clippy::all)]
+#![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
+#![allow(clippy::blacklisted_name, unused_variables, dead_code)]
+#![allow(unused_imports)]
+
+pub struct MyStruct {}
+
+pub struct SubT<T> {
+ foo: T,
+}
+
+pub enum MyEnum {
+ One,
+ Two,
+}
+
+mod outer_box {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
+
+ pub fn box_test1<T>(foo: &T) {}
+
+ pub fn box_test2(foo: &MyStruct) {}
+
+ pub fn box_test3(foo: &MyEnum) {}
+
+ pub fn box_test4_neg(foo: Box<SubT<&usize>>) {}
+
+ pub fn box_test5<T>(foo: Box<T>) {}
+}
+
+mod outer_rc {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
+
+ pub fn rc_test1<T>(foo: &T) {}
+
+ pub fn rc_test2(foo: &MyStruct) {}
+
+ pub fn rc_test3(foo: &MyEnum) {}
+
+ pub fn rc_test4_neg(foo: Rc<SubT<&usize>>) {}
+
+ pub fn rc_test6(a: Rc<bool>) {}
+}
+
+mod outer_arc {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
+
+ pub fn arc_test1<T>(foo: &T) {}
+
+ pub fn arc_test2(foo: &MyStruct) {}
+
+ pub fn arc_test3(foo: &MyEnum) {}
+
+ pub fn arc_test4_neg(foo: Arc<SubT<&usize>>) {}
+
+ pub fn arc_test7(a: Arc<bool>) {}
+}
+
+fn main() {}
--- /dev/null
+// run-rustfix
+#![warn(clippy::all)]
+#![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
+#![allow(clippy::blacklisted_name, unused_variables, dead_code)]
+#![allow(unused_imports)]
+
+pub struct MyStruct {}
+
+pub struct SubT<T> {
+ foo: T,
+}
+
+pub enum MyEnum {
+ One,
+ Two,
+}
+
+mod outer_box {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
+
+ pub fn box_test1<T>(foo: Box<&T>) {}
+
+ pub fn box_test2(foo: Box<&MyStruct>) {}
+
+ pub fn box_test3(foo: Box<&MyEnum>) {}
+
+ pub fn box_test4_neg(foo: Box<SubT<&usize>>) {}
+
+ pub fn box_test5<T>(foo: Box<Box<T>>) {}
+}
+
+mod outer_rc {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
+
+ pub fn rc_test1<T>(foo: Rc<&T>) {}
+
+ pub fn rc_test2(foo: Rc<&MyStruct>) {}
+
+ pub fn rc_test3(foo: Rc<&MyEnum>) {}
+
+ pub fn rc_test4_neg(foo: Rc<SubT<&usize>>) {}
+
+ pub fn rc_test6(a: Rc<Rc<bool>>) {}
+}
+
+mod outer_arc {
+ use crate::MyEnum;
+ use crate::MyStruct;
+ use crate::SubT;
+ use std::boxed::Box;
+ use std::rc::Rc;
+ use std::sync::Arc;
+
+ pub fn arc_test1<T>(foo: Arc<&T>) {}
+
+ pub fn arc_test2(foo: Arc<&MyStruct>) {}
+
+ pub fn arc_test3(foo: Arc<&MyEnum>) {}
+
+ pub fn arc_test4_neg(foo: Arc<SubT<&usize>>) {}
+
+ pub fn arc_test7(a: Arc<Arc<bool>>) {}
+}
+
+fn main() {}
--- /dev/null
+error: usage of `Box<&T>`
+ --> $DIR/redundant_allocation_fixable.rs:26:30
+ |
+LL | pub fn box_test1<T>(foo: Box<&T>) {}
+ | ^^^^^^^ help: try: `&T`
+ |
+ = note: `-D clippy::redundant-allocation` implied by `-D warnings`
+ = note: `&T` is already a pointer, `Box<&T>` allocates a pointer on the heap
+
+error: usage of `Box<&MyStruct>`
+ --> $DIR/redundant_allocation_fixable.rs:28:27
+ |
+LL | pub fn box_test2(foo: Box<&MyStruct>) {}
+ | ^^^^^^^^^^^^^^ help: try: `&MyStruct`
+ |
+ = note: `&MyStruct` is already a pointer, `Box<&MyStruct>` allocates a pointer on the heap
+
+error: usage of `Box<&MyEnum>`
+ --> $DIR/redundant_allocation_fixable.rs:30:27
+ |
+LL | pub fn box_test3(foo: Box<&MyEnum>) {}
+ | ^^^^^^^^^^^^ help: try: `&MyEnum`
+ |
+ = note: `&MyEnum` is already a pointer, `Box<&MyEnum>` allocates a pointer on the heap
+
+error: usage of `Box<Box<T>>`
+ --> $DIR/redundant_allocation_fixable.rs:34:30
+ |
+LL | pub fn box_test5<T>(foo: Box<Box<T>>) {}
+ | ^^^^^^^^^^^ help: try: `Box<T>`
+ |
+ = note: `Box<T>` is already on the heap, `Box<Box<T>>` makes an extra allocation
+
+error: usage of `Rc<&T>`
+ --> $DIR/redundant_allocation_fixable.rs:45:29
+ |
+LL | pub fn rc_test1<T>(foo: Rc<&T>) {}
+ | ^^^^^^ help: try: `&T`
+ |
+ = note: `&T` is already a pointer, `Rc<&T>` allocates a pointer on the heap
+
+error: usage of `Rc<&MyStruct>`
+ --> $DIR/redundant_allocation_fixable.rs:47:26
+ |
+LL | pub fn rc_test2(foo: Rc<&MyStruct>) {}
+ | ^^^^^^^^^^^^^ help: try: `&MyStruct`
+ |
+ = note: `&MyStruct` is already a pointer, `Rc<&MyStruct>` allocates a pointer on the heap
+
+error: usage of `Rc<&MyEnum>`
+ --> $DIR/redundant_allocation_fixable.rs:49:26
+ |
+LL | pub fn rc_test3(foo: Rc<&MyEnum>) {}
+ | ^^^^^^^^^^^ help: try: `&MyEnum`
+ |
+ = note: `&MyEnum` is already a pointer, `Rc<&MyEnum>` allocates a pointer on the heap
+
+error: usage of `Rc<Rc<bool>>`
+ --> $DIR/redundant_allocation_fixable.rs:53:24
+ |
+LL | pub fn rc_test6(a: Rc<Rc<bool>>) {}
+ | ^^^^^^^^^^^^ help: try: `Rc<bool>`
+ |
+ = note: `Rc<bool>` is already on the heap, `Rc<Rc<bool>>` makes an extra allocation
+
+error: usage of `Arc<&T>`
+ --> $DIR/redundant_allocation_fixable.rs:64:30
+ |
+LL | pub fn arc_test1<T>(foo: Arc<&T>) {}
+ | ^^^^^^^ help: try: `&T`
+ |
+ = note: `&T` is already a pointer, `Arc<&T>` allocates a pointer on the heap
+
+error: usage of `Arc<&MyStruct>`
+ --> $DIR/redundant_allocation_fixable.rs:66:27
+ |
+LL | pub fn arc_test2(foo: Arc<&MyStruct>) {}
+ | ^^^^^^^^^^^^^^ help: try: `&MyStruct`
+ |
+ = note: `&MyStruct` is already a pointer, `Arc<&MyStruct>` allocates a pointer on the heap
+
+error: usage of `Arc<&MyEnum>`
+ --> $DIR/redundant_allocation_fixable.rs:68:27
+ |
+LL | pub fn arc_test3(foo: Arc<&MyEnum>) {}
+ | ^^^^^^^^^^^^ help: try: `&MyEnum`
+ |
+ = note: `&MyEnum` is already a pointer, `Arc<&MyEnum>` allocates a pointer on the heap
+
+error: usage of `Arc<Arc<bool>>`
+ --> $DIR/redundant_allocation_fixable.rs:72:25
+ |
+LL | pub fn arc_test7(a: Arc<Arc<bool>>) {}
+ | ^^^^^^^^^^^^^^ help: try: `Arc<bool>`
+ |
+ = note: `Arc<bool>` is already on the heap, `Arc<Arc<bool>>` makes an extra allocation
+
+error: aborting due to 12 previous errors
+
issue_5405();
manually_drop();
clone_then_move_cloned();
+ hashmap_neg();
+ false_negative_5707();
}
#[derive(Clone)]
let mut x = S(String::new());
x.0.clone().chars().for_each(|_| x.m());
}
+
+fn hashmap_neg() {
+ // issue 5707
+ use std::collections::HashMap;
+ use std::path::PathBuf;
+
+ let p = PathBuf::from("/");
+
+ let mut h: HashMap<&str, &str> = HashMap::new();
+ h.insert("orig-p", p.to_str().unwrap());
+
+ let mut q = p.clone();
+ q.push("foo");
+
+ println!("{:?} {}", h, q.display());
+}
+
+fn false_negative_5707() {
+ fn foo(_x: &Alpha, _y: &mut Alpha) {}
+
+ let x = Alpha;
+ let mut y = Alpha;
+ foo(&x, &mut y);
+ let _z = x.clone(); // pr 7346 can't lint on `x`
+ drop(y);
+}
issue_5405();
manually_drop();
clone_then_move_cloned();
+ hashmap_neg();
+ false_negative_5707();
}
#[derive(Clone)]
let mut x = S(String::new());
x.0.clone().chars().for_each(|_| x.m());
}
+
+fn hashmap_neg() {
+ // issue 5707
+ use std::collections::HashMap;
+ use std::path::PathBuf;
+
+ let p = PathBuf::from("/");
+
+ let mut h: HashMap<&str, &str> = HashMap::new();
+ h.insert("orig-p", p.to_str().unwrap());
+
+ let mut q = p.clone();
+ q.push("foo");
+
+ println!("{:?} {}", h, q.display());
+}
+
+fn false_negative_5707() {
+ fn foo(_x: &Alpha, _y: &mut Alpha) {}
+
+ let x = Alpha;
+ let mut y = Alpha;
+ foo(&x, &mut y);
+ let _z = x.clone(); // pr 7346 can't lint on `x`
+ drop(y);
+}
| ^^^^^
error: redundant clone
- --> $DIR/redundant_clone.rs:63:25
+ --> $DIR/redundant_clone.rs:65:25
|
LL | if b { (a.clone(), a.clone()) } else { (Alpha, a) }
| ^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/redundant_clone.rs:63:24
+ --> $DIR/redundant_clone.rs:65:24
|
LL | if b { (a.clone(), a.clone()) } else { (Alpha, a) }
| ^
error: redundant clone
- --> $DIR/redundant_clone.rs:120:15
+ --> $DIR/redundant_clone.rs:122:15
|
LL | let _s = s.clone();
| ^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/redundant_clone.rs:120:14
+ --> $DIR/redundant_clone.rs:122:14
|
LL | let _s = s.clone();
| ^
error: redundant clone
- --> $DIR/redundant_clone.rs:121:15
+ --> $DIR/redundant_clone.rs:123:15
|
LL | let _t = t.clone();
| ^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/redundant_clone.rs:121:14
+ --> $DIR/redundant_clone.rs:123:14
|
LL | let _t = t.clone();
| ^
error: redundant clone
- --> $DIR/redundant_clone.rs:131:19
+ --> $DIR/redundant_clone.rs:133:19
|
LL | let _f = f.clone();
| ^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/redundant_clone.rs:131:18
+ --> $DIR/redundant_clone.rs:133:18
|
LL | let _f = f.clone();
| ^
error: redundant clone
- --> $DIR/redundant_clone.rs:143:14
+ --> $DIR/redundant_clone.rs:145:14
|
LL | let y = x.clone().join("matthias");
| ^^^^^^^^ help: remove this
|
note: cloned value is neither consumed nor mutated
- --> $DIR/redundant_clone.rs:143:13
+ --> $DIR/redundant_clone.rs:145:13
|
LL | let y = x.clone().join("matthias");
| ^^^^^^^^^
--- /dev/null
+#![warn(clippy::strlen_on_c_strings)]
+#![allow(dead_code)]
+#![feature(rustc_private)]
+extern crate libc;
+
+use std::ffi::{CStr, CString};
+
+fn main() {
+ // CString
+ let cstring = CString::new("foo").expect("CString::new failed");
+ let len = unsafe { libc::strlen(cstring.as_ptr()) };
+
+ // CStr
+ let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
+ let len = unsafe { libc::strlen(cstr.as_ptr()) };
+}
--- /dev/null
+error: using `libc::strlen` on a `CString` or `CStr` value
+ --> $DIR/strlen_on_c_strings.rs:11:24
+ |
+LL | let len = unsafe { libc::strlen(cstring.as_ptr()) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::strlen-on-c-strings` implied by `-D warnings`
+help: try this (you might also need to get rid of `unsafe` block in some cases):
+ |
+LL | let len = unsafe { cstring.as_bytes().len() };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+ --> $DIR/strlen_on_c_strings.rs:15:24
+ |
+LL | let len = unsafe { libc::strlen(cstr.as_ptr()) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try this (you might also need to get rid of `unsafe` block in some cases):
+ |
+LL | let len = unsafe { cstr.to_bytes().len() };
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
#[derive(Deserialize)]
struct FutureBreakageItem {
- future_breakage_date: Option<String>,
diagnostic: Diagnostic,
}
.into_iter()
.map(|item| {
format!(
- "Future breakage date: {}, diagnostic:\n{}",
- item.future_breakage_date
- .unwrap_or_else(|| "None".to_string()),
+ "Future breakage diagnostic:\n{}",
item.diagnostic
.rendered
.unwrap_or_else(|| "Not rendered".to_string())