Skip to content
23 changes: 23 additions & 0 deletions compiler/rustc_lint/src/builtin.rs
Original file line number Diff line number Diff line change
@@ -58,6 +58,7 @@ use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::GenericArgKind;
use rustc_middle::ty::ToPredicate;
use rustc_middle::ty::TypeVisitableExt;
use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef};
use rustc_session::config::ExpectedValues;
@@ -68,6 +69,7 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{BytePos, InnerSpan, Span};
use rustc_target::abi::Abi;
use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt};
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
use rustc_trait_selection::traits::{self, misc::type_allowed_to_implement_copy};

use crate::nonstandard_style::{method_context, MethodLateContext};
@@ -673,6 +675,9 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
if ty.is_copy_modulo_regions(cx.tcx, param_env) {
return;
}
if type_implements_negative_copy_modulo_regions(cx.tcx, ty, param_env) {
return;
}

// We shouldn't recommend implementing `Copy` on stateful things,
// such as iterators.
@@ -708,6 +713,24 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
}
}

/// Check whether a `ty` has a negative `Copy` implementation, ignoring outlives constraints.
fn type_implements_negative_copy_modulo_regions<'tcx>(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> bool {
let trait_ref = ty::TraitRef::new(tcx, tcx.require_lang_item(hir::LangItem::Copy, None), [ty]);
let pred = ty::TraitPredicate { trait_ref, polarity: ty::ImplPolarity::Negative };
let obligation = traits::Obligation {
cause: traits::ObligationCause::dummy(),
param_env,
recursion_depth: 0,
predicate: ty::Binder::dummy(pred).to_predicate(tcx),
};

tcx.infer_ctxt().build().predicate_must_hold_modulo_regions(&obligation)
}

declare_lint! {
/// The `missing_debug_implementations` lint detects missing
/// implementations of [`fmt::Debug`] for public types.
40 changes: 40 additions & 0 deletions library/core/src/clone.rs
Original file line number Diff line number Diff line change
@@ -86,6 +86,46 @@
/// }
/// ```
///
/// If we `derive`:
///
/// ```
/// #[derive(Copy, Clone)]
/// struct Generate<T>(fn() -> T);
/// ```
///
/// the auto-derived implementations will have unnecessary `T: Copy` and `T: Clone` bounds:
///
/// ```
/// # struct Generate<T>(fn() -> T);
///
/// // Automatically derived
/// impl<T: Copy> Copy for Generate<T> { }
///
/// // Automatically derived
/// impl<T: Clone> Clone for Generate<T> {
/// fn clone(&self) -> Generate<T> {
/// Generate(Clone::clone(&self.0))
/// }
/// }
/// ```
///
/// The bounds are unnecessary because clearly the function itself should be
/// copy- and cloneable even if its return type is not:
///
/// ```compile_fail,E0599
/// #[derive(Copy, Clone)]
/// struct Generate<T>(fn() -> T);
///
/// struct NotCloneable;
///
/// fn generate_not_cloneable() -> NotCloneable {
/// NotCloneable
/// }
///
/// Generate(generate_not_cloneable).clone(); // error: trait bounds were not satisfied
/// // Note: With the manual implementations the above line will compile.
/// ```
///
/// ## Additional implementors
///
/// In addition to the [implementors listed below][impls],
12 changes: 8 additions & 4 deletions library/std/src/env.rs
Original file line number Diff line number Diff line change
@@ -178,7 +178,8 @@ impl Iterator for Vars {
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for Vars {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Vars").finish_non_exhaustive()
let Self { inner: VarsOs { inner } } = self;
f.debug_struct("Vars").field("inner", inner).finish()
}
}

@@ -196,7 +197,8 @@ impl Iterator for VarsOs {
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for VarsOs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("VarOs").finish_non_exhaustive()
let Self { inner } = self;
f.debug_struct("VarsOs").field("inner", inner).finish()
}
}

@@ -829,7 +831,8 @@ impl DoubleEndedIterator for Args {
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for Args {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Args").field("inner", &self.inner.inner).finish()
let Self { inner: ArgsOs { inner } } = self;
f.debug_struct("Args").field("inner", inner).finish()
}
}

@@ -870,7 +873,8 @@ impl DoubleEndedIterator for ArgsOs {
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for ArgsOs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ArgsOs").field("inner", &self.inner).finish()
let Self { inner } = self;
f.debug_struct("ArgsOs").field("inner", inner).finish()
}
}

20 changes: 20 additions & 0 deletions library/std/src/env/tests.rs
Original file line number Diff line number Diff line change
@@ -95,8 +95,28 @@ fn args_debug() {
format!("Args {{ inner: {:?} }}", args().collect::<Vec<_>>()),
format!("{:?}", args())
);
}

#[test]
fn args_os_debug() {
assert_eq!(
format!("ArgsOs {{ inner: {:?} }}", args_os().collect::<Vec<_>>()),
format!("{:?}", args_os())
);
}

#[test]
fn vars_debug() {
assert_eq!(
format!("Vars {{ inner: {:?} }}", vars().collect::<Vec<_>>()),
format!("{:?}", vars())
);
}

#[test]
fn vars_os_debug() {
assert_eq!(
format!("VarsOs {{ inner: {:?} }}", vars_os().collect::<Vec<_>>()),
format!("{:?}", vars_os())
);
}
7 changes: 7 additions & 0 deletions library/std/src/sys/hermit/os.rs
Original file line number Diff line number Diff line change
@@ -112,6 +112,13 @@ pub struct Env {
iter: vec::IntoIter<(OsString, OsString)>,
}

impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { iter } = self;
f.debug_list().entries(iter.as_slice()).finish()
}
}

impl !Send for Env {}
impl !Sync for Env {}

7 changes: 7 additions & 0 deletions library/std/src/sys/solid/os.rs
Original file line number Diff line number Diff line change
@@ -85,6 +85,13 @@ pub struct Env {
iter: vec::IntoIter<(OsString, OsString)>,
}

impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { iter } = self;
f.debug_list().entries(iter.as_slice()).finish()
}
}

impl !Send for Env {}
impl !Sync for Env {}

7 changes: 7 additions & 0 deletions library/std/src/sys/unix/os.rs
Original file line number Diff line number Diff line change
@@ -495,6 +495,13 @@ pub struct Env {
iter: vec::IntoIter<(OsString, OsString)>,
}

impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { iter } = self;
f.debug_list().entries(iter.as_slice()).finish()
}
}

impl !Send for Env {}
impl !Sync for Env {}

10 changes: 9 additions & 1 deletion library/std/src/sys/unsupported/os.rs
Original file line number Diff line number Diff line change
@@ -65,10 +65,18 @@ pub fn current_exe() -> io::Result<PathBuf> {

pub struct Env(!);

impl fmt::Debug for Env {
fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self(inner) = self;
match *inner {}
}
}

impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
self.0
let Self(inner) = self;
match *inner {}
}
}

8 changes: 8 additions & 0 deletions library/std/src/sys/wasi/os.rs
Original file line number Diff line number Diff line change
@@ -142,10 +142,18 @@ impl StdError for JoinPathsError {
pub fn current_exe() -> io::Result<PathBuf> {
unsupported()
}

pub struct Env {
iter: vec::IntoIter<(OsString, OsString)>,
}

impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { iter } = self;
f.debug_list().entries(iter.as_slice()).finish()
}
}

impl !Send for Env {}
impl !Sync for Env {}

30 changes: 25 additions & 5 deletions library/std/src/sys/windows/os.rs
Original file line number Diff line number Diff line change
@@ -85,25 +85,45 @@ pub fn error_string(mut errnum: i32) -> String {

pub struct Env {
base: c::LPWCH,
cur: c::LPWCH,
iter: EnvIterator,
}

impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { base: _, iter } = self;
f.debug_list().entries(iter.clone()).finish()
}
}

impl Iterator for Env {
type Item = (OsString, OsString);

fn next(&mut self) -> Option<(OsString, OsString)> {
let Self { base: _, iter } = self;
iter.next()
}
}

#[derive(Clone)]
struct EnvIterator(c::LPWCH);

impl Iterator for EnvIterator {
type Item = (OsString, OsString);

fn next(&mut self) -> Option<(OsString, OsString)> {
let Self(cur) = self;
loop {
unsafe {
if *self.cur == 0 {
if **cur == 0 {
return None;
}
let p = self.cur as *const u16;
let p = *cur as *const u16;
let mut len = 0;
while *p.add(len) != 0 {
len += 1;
}
let s = slice::from_raw_parts(p, len);
self.cur = self.cur.add(len + 1);
*cur = cur.add(len + 1);

// Windows allows environment variables to start with an equals
// symbol (in any other position, this is the separator between
@@ -137,7 +157,7 @@ pub fn env() -> Env {
if ch.is_null() {
panic!("failure getting env string from OS: {}", io::Error::last_os_error());
}
Env { base: ch, cur: ch }
Env { base: ch, iter: EnvIterator(ch) }
}
}

2 changes: 1 addition & 1 deletion src/tools/tidy/src/fluent_alphabetical.rs
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@ fn check_alphabetic(filename: &str, fluent: &str, bad: &mut bool) {
tidy_error!(
bad,
"{filename}: message `{}` appears before `{}`, but is alphabetically later than it
run tidy with `--bless` to sort the file correctly",
run `./x.py test tidy --bless` to sort the file correctly",
name.as_str(),
next.as_str()
);
15 changes: 15 additions & 0 deletions tests/ui/lint/missing-copy-implementations-negative-copy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Regression test for issue #101980.
// Ensure that we don't suggest impl'ing `Copy` for a type if it already impl's `!Copy`.

// check-pass

#![feature(negative_impls)]
#![deny(missing_copy_implementations)]

pub struct Struct {
pub field: i32,
}

impl !Copy for Struct {}

fn main() {}