Auto merge of #149572 - cuviper:beta-next, r=cuviper

[beta] backports

- rustdoc: Use configured target modifiers when collecting doctests rust-lang/rust#148068
- fix(rustdoc): Color doctest errors rust-lang/rust#148834
- Fix the issue of unused assignment from MIR liveness checking rust-lang/rust#149072
- Skip unused variables warning for unreachable code rust-lang/rust#149096
- In `BTreeMap::eq`, do not compare the elements if the sizes are different. rust-lang/rust#149125
- Handle cycles when checking impl candidates for `doc(hidden)` rust-lang/rust#149185
- Generalize branch references rust-lang/rust#148395
  - only the commit updating CI scripts
- Change default branch references rust-lang/rust#148564

r? cuviper
This commit is contained in:
bors 2025-12-03 19:24:47 +00:00
commit ac0aff2115
32 changed files with 506 additions and 75 deletions

View file

@ -6,7 +6,7 @@ name: Post merge analysis
on:
push:
branches:
- master
- main
jobs:
analysis:

View file

@ -3465,14 +3465,9 @@ impl Drop for Buffy {
pub fn stderr_destination(color: ColorConfig) -> Destination {
let buffer_writer = std::io::stderr();
let choice = color.to_color_choice();
// We need to resolve `ColorChoice::Auto` before `Box`ing since
// `ColorChoice::Auto` on `dyn Write` will always resolve to `Never`
let choice = if matches!(choice, ColorChoice::Auto) {
AutoStream::choice(&buffer_writer)
} else {
choice
};
let choice = get_stderr_color_choice(color, &buffer_writer);
// On Windows we'll be performing global synchronization on the entire
// system for emitting rustc errors, so there's no need to buffer
// anything.
@ -3487,6 +3482,11 @@ pub fn stderr_destination(color: ColorConfig) -> Destination {
}
}
pub fn get_stderr_color_choice(color: ColorConfig, stderr: &std::io::Stderr) -> ColorChoice {
let choice = color.to_color_choice();
if matches!(choice, ColorChoice::Auto) { AutoStream::choice(stderr) } else { choice }
}
/// On Windows, BRIGHT_BLUE is hard to read on black. Use cyan instead.
///
/// See #36178.

View file

@ -45,6 +45,9 @@ struct Access {
/// When we encounter multiple statements at the same location, we only increase the liveness,
/// in order to avoid false positives.
live: bool,
/// Is this a direct access to the place itself, no projections, or to a field?
/// This helps distinguish `x = ...` from `x.field = ...`
is_direct: bool,
}
#[tracing::instrument(level = "debug", skip(tcx), ret)]
@ -689,15 +692,17 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> {
|place: Place<'tcx>, kind, source_info: SourceInfo, live: &DenseBitSet<PlaceIndex>| {
if let Some((index, extra_projections)) = checked_places.get(place.as_ref()) {
if !is_indirect(extra_projections) {
let is_direct = extra_projections.is_empty();
match assignments[index].entry(source_info) {
IndexEntry::Vacant(v) => {
let access = Access { kind, live: live.contains(index) };
let access = Access { kind, live: live.contains(index), is_direct };
v.insert(access);
}
IndexEntry::Occupied(mut o) => {
// There were already a sighting. Mark this statement as live if it
// was, to avoid false positives.
o.get_mut().live |= live.contains(index);
o.get_mut().is_direct &= is_direct;
}
}
}
@ -781,7 +786,7 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> {
continue;
};
let source_info = body.local_decls[place.local].source_info;
let access = Access { kind, live: live.contains(index) };
let access = Access { kind, live: live.contains(index), is_direct: true };
assignments[index].insert(source_info, access);
}
}
@ -875,6 +880,33 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> {
dead_captures
}
/// Check if a local is referenced in any reachable basic block.
/// Variables in unreachable code (e.g., after `todo!()`) should not trigger unused warnings.
fn is_local_in_reachable_code(&self, local: Local) -> bool {
struct LocalVisitor {
target_local: Local,
found: bool,
}
impl<'tcx> Visitor<'tcx> for LocalVisitor {
fn visit_local(&mut self, local: Local, _context: PlaceContext, _location: Location) {
if local == self.target_local {
self.found = true;
}
}
}
let mut visitor = LocalVisitor { target_local: local, found: false };
for (bb, bb_data) in traversal::postorder(self.body) {
visitor.visit_basic_block_data(bb, bb_data);
if visitor.found {
return true;
}
}
false
}
/// Report fully unused locals, and forget the corresponding assignments.
fn report_fully_unused(&mut self) {
let tcx = self.tcx;
@ -928,6 +960,10 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> {
let statements = &mut self.assignments[index];
if statements.is_empty() {
if !self.is_local_in_reachable_code(local) {
continue;
}
let sugg = if from_macro {
errors::UnusedVariableSugg::NoSugg { span: def_span, name }
} else {
@ -977,8 +1013,10 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> {
self.checked_places,
self.body,
) {
statements.clear();
continue;
statements.retain(|_, access| access.is_direct);
if statements.is_empty() {
continue;
}
}
let typo = maybe_suggest_typo();
@ -1049,26 +1087,28 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> {
let Some((name, decl_span)) = self.checked_places.names[index] else { continue };
// We have outstanding assignments and with non-trivial drop.
// This is probably a drop-guard, so we do not issue a warning there.
if maybe_drop_guard(
let is_maybe_drop_guard = maybe_drop_guard(
tcx,
self.typing_env,
index,
&self.ever_dropped,
self.checked_places,
self.body,
) {
continue;
}
);
// We probed MIR in reverse order for dataflow.
// We revert the vector to give a consistent order to the user.
for (source_info, Access { live, kind }) in statements.into_iter().rev() {
for (source_info, Access { live, kind, is_direct }) in statements.into_iter().rev() {
if live {
continue;
}
// If this place was dropped and has non-trivial drop,
// skip reporting field assignments.
if !is_direct && is_maybe_drop_guard {
continue;
}
// Report the dead assignment.
let Some(hir_id) = source_info.scope.lint_root(&self.body.source_scopes) else {
continue;

View file

@ -1,11 +1,12 @@
use core::ops::ControlFlow;
use std::borrow::Cow;
use std::collections::hash_set;
use std::path::PathBuf;
use rustc_abi::ExternAbi;
use rustc_ast::ast::LitKind;
use rustc_ast::{LitIntType, TraitObjectSyntax};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::unord::UnordSet;
use rustc_errors::codes::*;
use rustc_errors::{
@ -1927,11 +1928,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
if self.tcx.visibility(did).is_accessible_from(body_def_id, self.tcx) {
// don't suggest foreign `#[doc(hidden)]` types
if !did.is_local() {
while let Some(parent) = parent_map.get(&did) {
let mut previously_seen_dids: FxHashSet<DefId> = Default::default();
previously_seen_dids.insert(did);
while let Some(&parent) = parent_map.get(&did)
&& let hash_set::Entry::Vacant(v) =
previously_seen_dids.entry(parent)
{
if self.tcx.is_doc_hidden(did) {
return false;
}
did = *parent;
v.insert();
did = parent;
}
}
true

View file

@ -20,6 +20,7 @@
#![feature(associated_type_defaults)]
#![feature(box_patterns)]
#![feature(default_field_values)]
#![feature(hash_set_entry)]
#![feature(if_let_guard)]
#![feature(iter_intersperse)]
#![feature(iterator_try_reduce)]

View file

@ -2414,7 +2414,7 @@ impl<K, V> Default for BTreeMap<K, V> {
#[stable(feature = "rust1", since = "1.0.0")]
impl<K: PartialEq, V: PartialEq, A: Allocator + Clone> PartialEq for BTreeMap<K, V, A> {
fn eq(&self, other: &BTreeMap<K, V, A>) -> bool {
self.iter().eq(other)
self.len() == other.len() && self.iter().zip(other).all(|(a, b)| a == b)
}
}

View file

@ -0,0 +1,96 @@
//! Regression tests which fail if some collections' `PartialEq::eq` impls compare
//! elements when the collections have different sizes.
//! This behavior is not guaranteed either way, so regressing these tests is fine
//! if it is done on purpose.
use std::cmp::Ordering;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, LinkedList};
/// This intentionally has a panicking `PartialEq` impl, to test that various
/// collections' `PartialEq` impls don't actually compare elements if their sizes
/// are unequal.
///
/// This is not advisable in normal code.
#[derive(Debug, Clone, Copy, Hash)]
struct Evil;
impl PartialEq for Evil {
fn eq(&self, _: &Self) -> bool {
panic!("Evil::eq is evil");
}
}
impl Eq for Evil {}
impl PartialOrd for Evil {
fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
Some(Ordering::Equal)
}
}
impl Ord for Evil {
fn cmp(&self, _: &Self) -> Ordering {
// Constructing a `BTreeSet`/`BTreeMap` uses `cmp` on the elements,
// but comparing it with with `==` uses `eq` on the elements,
// so Evil::cmp doesn't need to be evil.
Ordering::Equal
}
}
// check Evil works
#[test]
#[should_panic = "Evil::eq is evil"]
fn evil_eq_works() {
let v1 = vec![Evil];
let v2 = vec![Evil];
_ = v1 == v2;
}
// check various containers don't compare if their sizes are different
#[test]
fn vec_evil_eq() {
let v1 = vec![Evil];
let v2 = vec![Evil; 2];
assert_eq!(false, v1 == v2);
}
#[test]
fn hashset_evil_eq() {
let s1 = HashSet::from([(0, Evil)]);
let s2 = HashSet::from([(0, Evil), (1, Evil)]);
assert_eq!(false, s1 == s2);
}
#[test]
fn hashmap_evil_eq() {
let m1 = HashMap::from([(0, Evil)]);
let m2 = HashMap::from([(0, Evil), (1, Evil)]);
assert_eq!(false, m1 == m2);
}
#[test]
fn btreeset_evil_eq() {
let s1 = BTreeSet::from([(0, Evil)]);
let s2 = BTreeSet::from([(0, Evil), (1, Evil)]);
assert_eq!(false, s1 == s2);
}
#[test]
fn btreemap_evil_eq() {
let m1 = BTreeMap::from([(0, Evil)]);
let m2 = BTreeMap::from([(0, Evil), (1, Evil)]);
assert_eq!(false, m1 == m2);
}
#[test]
fn linkedlist_evil_eq() {
let m1 = LinkedList::from([Evil]);
let m2 = LinkedList::from([Evil; 2]);
assert_eq!(false, m1 == m2);
}

View file

@ -1 +1,2 @@
mod binary_heap;
mod eq_diff_len;

View file

@ -123,7 +123,7 @@ pub fn load_job_db(db: &str) -> anyhow::Result<JobDatabase> {
/// modulo certain carve-outs" in [`validate_job_database`].
///
/// This invariant is important to make sure that it's not easily possible (without modifying
/// `citool`) to have PRs with red PR-only CI jobs merged into `master`, causing all subsequent PR
/// `citool`) to have PRs with red PR-only CI jobs merged into `main`, causing all subsequent PR
/// CI runs to be red until the cause is fixed.
fn register_pr_jobs_as_auto_jobs(db: &mut JobDatabase) -> anyhow::Result<()> {
for pr_job in &db.pr_jobs {
@ -273,7 +273,7 @@ pub enum RunType {
/// Merge attempt workflow
AutoJob,
/// Fake job only used for sharing Github Actions cache.
MasterJob,
MainJob,
}
/// Maximum number of custom try jobs that can be requested in a single
@ -323,7 +323,7 @@ fn calculate_jobs(
(jobs, "try", &db.envs.try_env)
}
RunType::AutoJob => (db.auto_jobs.clone(), "auto", &db.envs.auto_env),
RunType::MasterJob => return Ok(vec![]),
RunType::MainJob => return Ok(vec![]),
};
let jobs = substitute_github_vars(jobs.clone())
.context("Failed to substitute GitHub context variables in jobs")?;
@ -376,7 +376,7 @@ pub fn calculate_job_matrix(
eprintln!("Run type: {run_type:?}");
let jobs = calculate_jobs(&run_type, &db, channel)?;
if jobs.is_empty() && !matches!(run_type, RunType::MasterJob) {
if jobs.is_empty() && !matches!(run_type, RunType::MainJob) {
return Err(anyhow::anyhow!("Computed job list is empty"));
}
@ -384,7 +384,7 @@ pub fn calculate_job_matrix(
RunType::PullRequest => "pr",
RunType::TryJob { .. } => "try",
RunType::AutoJob => "auto",
RunType::MasterJob => "master",
RunType::MainJob => "main",
};
eprintln!("Output");

View file

@ -47,7 +47,7 @@ impl GitHubContext {
Some(RunType::TryJob { job_patterns: patterns })
}
("push", "refs/heads/auto") => Some(RunType::AutoJob),
("push", "refs/heads/master") => Some(RunType::MasterJob),
("push", "refs/heads/main") => Some(RunType::MainJob),
_ => None,
}
}

View file

@ -46,11 +46,11 @@ fn pr_jobs() {
}
#[test]
fn master_jobs() {
let stdout = get_matrix("push", "commit", "refs/heads/master");
fn main_jobs() {
let stdout = get_matrix("push", "commit", "refs/heads/main");
insta::assert_snapshot!(stdout, @r#"
jobs=[]
run_type=master
run_type=main
"#);
}

View file

@ -17,7 +17,7 @@ ci_dir=$(cd $(dirname $0) && pwd)/..
# On the beta channel we'll be automatically calculating the prerelease version
# via the git history, so unshallow our shallow clone from CI.
if [ "$(releaseChannel)" = "beta" ]; then
git fetch origin --unshallow beta master
git fetch origin --unshallow beta main
fi
function fetch_github_commit_archive {

View file

@ -1,5 +1,6 @@
#!/bin/bash
# Ensure commits in beta are in master & commits in stable are in beta + master.
# Ensure commits in beta are in the default branch & commits in stable are in beta + the default
# branch.
set -euo pipefail
IFS=$'\n\t'
@ -19,11 +20,11 @@ verify_backported_commits_main() {
fi
if [[ $ci_base_branch == "beta" ]]; then
verify_cherries master "$BETA_LIMIT" \
verify_cherries HEAD "$BETA_LIMIT" \
|| exit 1
elif [[ $ci_base_branch == "stable" ]]; then
(verify_cherries master "$STABLE_LIMIT" \
(verify_cherries HEAD "$STABLE_LIMIT" \
& verify_cherries beta "$STABLE_LIMIT") \
|| exit 1
@ -64,7 +65,7 @@ verify_cherries() {
continue
fi
if ! is_in_master "$backport_sha"; then
if ! is_in_default_branch "$backport_sha"; then
bad_backports+=("$sha")
continue
fi
@ -85,7 +86,7 @@ verify_cherries() {
done
echo
echo "do not match any commits in \`$1\`. If this was intended, add the text"
echo '\`backport-of: <SHA of a commit already in master>\`'
echo '\`backport-of: <SHA of a commit already in the default branch>\`'
echo 'somewhere in the message of each of these commits.'
echo
failure=1
@ -101,7 +102,7 @@ verify_cherries() {
done
echo
echo 'have commit messages marked \`backport-of: <SHA>\`, but the SHA is not in'
echo '\`master\`.'
echo 'the default branch.'
echo
failure=1
fi
@ -132,11 +133,11 @@ get_backport() {
| sed -n '/^.*backport-of:\s\?\([a-f0-9]\+\|nothing\).*/{s//\1/p;q}'
}
# Check if a commit is in master.
# Check if a commit is in the default branch.
#
# $1 = <sha>
is_in_master() {
git merge-base --is-ancestor "$1" origin/master 2> /dev/null
is_in_default_branch() {
git merge-base --is-ancestor "$1" origin/HEAD 2> /dev/null
}
verify_backported_commits_main

View file

@ -1,7 +1,7 @@
#!/bin/bash
# We want to make sure all PRs are targeting the right branch when they're
# opened, otherwise we risk (for example) to land a beta-specific change to the
# master branch. This script ensures the branch of the PR matches the channel.
# default branch. This script ensures the branch of the PR matches the channel.
set -euo pipefail
IFS=$'\n\t'
@ -16,7 +16,7 @@ fi
channel=$(cat "$(ciCheckoutPath)/src/ci/channel")
case "${channel}" in
nightly)
channel_branch="master"
channel_branch="main"
;;
beta)
channel_branch="beta"

View file

@ -166,6 +166,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions
remap_path_prefix: options.remap_path_prefix.clone(),
unstable_opts: options.unstable_opts.clone(),
error_format: options.error_format.clone(),
target_modifiers: options.target_modifiers.clone(),
..config::Options::default()
};

View file

@ -8,8 +8,8 @@ use std::sync::Arc;
use rustc_ast::token::{Delimiter, TokenKind};
use rustc_ast::tokenstream::TokenTree;
use rustc_ast::{self as ast, AttrStyle, HasAttrs, StmtKind};
use rustc_errors::emitter::stderr_destination;
use rustc_errors::{AutoStream, ColorConfig, DiagCtxtHandle};
use rustc_errors::emitter::get_stderr_color_choice;
use rustc_errors::{AutoStream, ColorChoice, ColorConfig, DiagCtxtHandle};
use rustc_parse::lexer::StripTokens;
use rustc_parse::new_parser_from_source_str;
use rustc_session::parse::ParseSess;
@ -446,7 +446,7 @@ fn parse_source(
span: Span,
) -> Result<ParseSourceInfo, ()> {
use rustc_errors::DiagCtxt;
use rustc_errors::emitter::{Emitter, HumanEmitter};
use rustc_errors::emitter::HumanEmitter;
use rustc_span::source_map::FilePathMapping;
let mut info =
@ -458,9 +458,12 @@ fn parse_source(
let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
let translator = rustc_driver::default_translator();
info.supports_color =
HumanEmitter::new(stderr_destination(ColorConfig::Auto), translator.clone())
.supports_color();
let supports_color = match get_stderr_color_choice(ColorConfig::Auto, &std::io::stderr()) {
ColorChoice::Auto => unreachable!(),
ColorChoice::AlwaysAnsi | ColorChoice::Always => true,
ColorChoice::Never => false,
};
info.supports_color = supports_color;
// Any errors in parsing should also appear when the doctest is compiled for real, so just
// send all the errors that the parser emits directly into a `Sink` instead of stderr.
let emitter = HumanEmitter::new(AutoStream::never(Box::new(io::sink())), translator);

View file

@ -2,7 +2,7 @@ dist_server=https://static.rust-lang.org
artifacts_server=https://ci-artifacts.rust-lang.org/rustc-builds
artifacts_with_llvm_assertions_server=https://ci-artifacts.rust-lang.org/rustc-builds-alt
git_merge_commit_email=bors@rust-lang.org
nightly_branch=master
nightly_branch=main
# The configuration above this comment is editable, and can be changed
# by forks of the repository if they have alternate values.

View file

@ -25,4 +25,43 @@ fn main() {
.target("aarch64-unknown-none-softfloat")
.arg("-Zfixed-x18")
.run();
rustdoc()
.input("c.rs")
.crate_type("rlib")
.extern_("d", "libd.rmeta")
.target("aarch64-unknown-none-softfloat")
.arg("-Zfixed-x18")
.arg("--test")
.run();
rustdoc()
.input("c.rs")
.edition("2024")
.crate_type("rlib")
.extern_("d", "libd.rmeta")
.target("aarch64-unknown-none-softfloat")
.arg("-Zfixed-x18")
.arg("--test")
.run();
// rustdoc --test detects ABI mismatch
rustdoc()
.input("c.rs")
.crate_type("rlib")
.extern_("d", "libd.rmeta")
.target("aarch64-unknown-none-softfloat")
.arg("--test")
.run_fail()
.assert_stderr_contains("mixing `-Zfixed-x18` will cause an ABI mismatch");
// rustdoc --test -Cunsafe-allow-abi-mismatch=... ignores the mismatch
rustdoc()
.input("c.rs")
.crate_type("rlib")
.extern_("d", "libd.rmeta")
.target("aarch64-unknown-none-softfloat")
.arg("--test")
.arg("-Cunsafe-allow-abi-mismatch=fixed-x18")
.run();
}

View file

@ -33,15 +33,15 @@ fn main() {
// Drops are right-to-left: `z`, `y`, `x`.
let (x, Ok(y) | Err(y), z);
// Assignment order doesn't matter.
z = LogDrop(o, 1);
y = LogDrop(o, 2);
x = LogDrop(o, 3);
z = LogDrop(o, 1); //~ WARN value assigned to `z` is never read
y = LogDrop(o, 2); //~ WARN value assigned to `y` is never read
x = LogDrop(o, 3); //~ WARN value assigned to `x` is never read
});
assert_drop_order(1..=2, |o| {
// The first or-pattern alternative determines the bindings' drop order: `y`, `x`.
let ((true, x, y) | (false, y, x));
x = LogDrop(o, 2);
y = LogDrop(o, 1);
x = LogDrop(o, 2); //~ WARN value assigned to `x` is never read
y = LogDrop(o, 1); //~ WARN value assigned to `y` is never read
});
// `let pat = expr;` should have the same drop order.
@ -61,15 +61,21 @@ fn main() {
// `match` should have the same drop order.
assert_drop_order(1..=3, |o| {
// Drops are right-to-left: `z`, `y` `x`.
match (LogDrop(o, 3), Ok(LogDrop(o, 2)), LogDrop(o, 1)) { (x, Ok(y) | Err(y), z) => {} }
match (LogDrop(o, 3), Ok(LogDrop(o, 2)), LogDrop(o, 1)) {
(x, Ok(y) | Err(y), z) => {}
}
});
assert_drop_order(1..=2, |o| {
// The first or-pattern alternative determines the bindings' drop order: `y`, `x`.
match (true, LogDrop(o, 2), LogDrop(o, 1)) { (true, x, y) | (false, y, x) => {} }
match (true, LogDrop(o, 2), LogDrop(o, 1)) {
(true, x, y) | (false, y, x) => {}
}
});
assert_drop_order(1..=2, |o| {
// That drop order is used regardless of which or-pattern alternative matches: `y`, `x`.
match (false, LogDrop(o, 1), LogDrop(o, 2)) { (true, x, y) | (false, y, x) => {} }
match (false, LogDrop(o, 1), LogDrop(o, 2)) {
(true, x, y) | (false, y, x) => {}
}
});
// Function params are visited one-by-one, and the order of bindings within a param's pattern is

View file

@ -0,0 +1,43 @@
warning: value assigned to `x` is never read
--> $DIR/or-pattern-drop-order.rs:43:9
|
LL | x = LogDrop(o, 2);
| ^
|
= help: maybe it is overwritten before being read?
= note: `#[warn(unused_assignments)]` (part of `#[warn(unused)]`) on by default
warning: value assigned to `y` is never read
--> $DIR/or-pattern-drop-order.rs:44:9
|
LL | y = LogDrop(o, 1);
| ^
|
= help: maybe it is overwritten before being read?
warning: value assigned to `x` is never read
--> $DIR/or-pattern-drop-order.rs:38:9
|
LL | x = LogDrop(o, 3);
| ^
|
= help: maybe it is overwritten before being read?
warning: value assigned to `y` is never read
--> $DIR/or-pattern-drop-order.rs:37:9
|
LL | y = LogDrop(o, 2);
| ^
|
= help: maybe it is overwritten before being read?
warning: value assigned to `z` is never read
--> $DIR/or-pattern-drop-order.rs:36:9
|
LL | z = LogDrop(o, 1);
| ^
|
= help: maybe it is overwritten before being read?
warning: 5 warnings emitted

View file

@ -0,0 +1,42 @@
//@ check-fail
#![deny(unused)]
#![allow(dead_code)]
fn test_one_extra_assign() {
let mut value = b"0".to_vec(); //~ ERROR value assigned to `value` is never read
value = b"1".to_vec();
println!("{:?}", value);
}
fn test_two_extra_assign() {
let mut x = 1; //~ ERROR value assigned to `x` is never read
x = 2; //~ ERROR value assigned to `x` is never read
x = 3;
println!("{}", x);
}
struct Point {
x: i32,
y: i32,
}
fn test_indirect_assign() {
let mut p = Point { x: 1, y: 1 }; //~ ERROR value assigned to `p` is never read
p = Point { x: 2, y: 2 };
p.x = 3;
println!("{}", p.y);
}
struct Foo;
impl Drop for Foo {
fn drop(&mut self) {}
}
// testcase for issue #148418
fn test_unused_variable() {
let mut foo = Foo; //~ ERROR variable `foo` is assigned to, but never used
foo = Foo; //~ ERROR value assigned to `foo` is never read
}
fn main() {}

View file

@ -0,0 +1,62 @@
error: value assigned to `value` is never read
--> $DIR/unused-assign-148960.rs:6:21
|
LL | let mut value = b"0".to_vec();
| ^^^^^^^^^^^^^
|
= help: maybe it is overwritten before being read?
note: the lint level is defined here
--> $DIR/unused-assign-148960.rs:2:9
|
LL | #![deny(unused)]
| ^^^^^^
= note: `#[deny(unused_assignments)]` implied by `#[deny(unused)]`
error: value assigned to `x` is never read
--> $DIR/unused-assign-148960.rs:12:17
|
LL | let mut x = 1;
| ^
|
= help: maybe it is overwritten before being read?
error: value assigned to `x` is never read
--> $DIR/unused-assign-148960.rs:13:5
|
LL | x = 2;
| ^^^^^
|
= help: maybe it is overwritten before being read?
error: value assigned to `p` is never read
--> $DIR/unused-assign-148960.rs:24:17
|
LL | let mut p = Point { x: 1, y: 1 };
| ^^^^^^^^^^^^^^^^^^^^
|
= help: maybe it is overwritten before being read?
error: variable `foo` is assigned to, but never used
--> $DIR/unused-assign-148960.rs:38:9
|
LL | let mut foo = Foo;
| ^^^^^^^
|
= note: consider using `_foo` instead
= note: `#[deny(unused_variables)]` implied by `#[deny(unused)]`
help: you might have meant to pattern match on the similarly named struct `Foo`
|
LL - let mut foo = Foo;
LL + let Foo = Foo;
|
error: value assigned to `foo` is never read
--> $DIR/unused-assign-148960.rs:39:5
|
LL | foo = Foo;
| ^^^
|
= help: maybe it is overwritten before being read?
error: aborting due to 6 previous errors

View file

@ -0,0 +1,46 @@
//@ check-pass
#![allow(unreachable_code)]
#![allow(dead_code)]
#![warn(unused_variables)]
fn after_todo() {
todo!("not implemented");
// This should not warn - the code is unreachable
let a = 1;
if a < 2 {
eprintln!("a: {}", a);
}
}
fn after_panic() {
panic!("oops");
// This should not warn - the code is unreachable
let b = 2;
println!("{}", b);
}
fn after_unimplemented() {
unimplemented!();
// This should not warn - the code is unreachable
let c = 3;
println!("{}", c);
}
fn after_unreachable() {
unsafe { std::hint::unreachable_unchecked() }
// This should not warn - the code is unreachable
let d = 4;
println!("{}", d);
}
fn reachable_unused() {
// This SHOULD warn - the code is reachable
let e = 5; //~ WARN unused variable: `e`
}
fn main() {}

View file

@ -0,0 +1,14 @@
warning: unused variable: `e`
--> $DIR/unused-var-in-unreachable-code.rs:43:9
|
LL | let e = 5;
| ^ help: if this is intentional, prefix it with an underscore: `_e`
|
note: the lint level is defined here
--> $DIR/unused-var-in-unreachable-code.rs:5:9
|
LL | #![warn(unused_variables)]
| ^^^^^^^^^^^^^^^^
warning: 1 warning emitted

View file

@ -244,8 +244,8 @@ fn f10<T>(mut a: T, b: T) {
//~^ ERROR value assigned to `a` is never read
}
fn f10b<T>(mut a: Box<T>, b: Box<T>) {
a = b;
fn f10b<T>(mut a: Box<T>, b: Box<T>) { //~ ERROR variable `a` is assigned to, but never used
a = b; //~ ERROR value assigned to `a` is never read
}
// unused params warnings are not needed for intrinsic functions without bodies

View file

@ -283,5 +283,21 @@ LL | a = b;
|
= help: maybe it is overwritten before being read?
error: aborting due to 34 previous errors; 1 warning emitted
error: variable `a` is assigned to, but never used
--> $DIR/liveness-unused.rs:247:12
|
LL | fn f10b<T>(mut a: Box<T>, b: Box<T>) {
| ^^^^^
|
= note: consider using `_a` instead
error: value assigned to `a` is never read
--> $DIR/liveness-unused.rs:248:5
|
LL | a = b;
| ^
|
= help: maybe it is overwritten before being read?
error: aborting due to 36 previous errors; 1 warning emitted

View file

@ -4,7 +4,6 @@ fn main() {
return ();
let x = ();
//~^ WARN unused variable: `x`
x
}

View file

@ -1,10 +0,0 @@
warning: unused variable: `x`
--> $DIR/early-return-with-unreachable-code-24353.rs:6:9
|
LL | let x = ();
| ^ help: if this is intentional, prefix it with an underscore: `_x`
|
= note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default
warning: 1 warning emitted

View file

@ -33,3 +33,6 @@ impl Marker for hidden::Foo {}
impl Marker for hidden1::Bar {}
impl Marker for Baz {}
impl Marker for Quux {}
pub use crate as library;

View file

@ -21,7 +21,12 @@ pub fn test4(_: Quux) {}
fn test5<T: hidden_struct::Marker>() {}
fn test6<T: hidden_struct::library::Marker>() {}
fn main() {
test5::<i32>();
//~^ ERROR [E0277]
test6::<i32>();
//~^ ERROR [E0277]
}

View file

@ -38,7 +38,7 @@ LL + use hidden_struct::Quux;
|
error[E0277]: the trait bound `i32: Marker` is not satisfied
--> $DIR/dont-suggest-foreign-doc-hidden.rs:25:13
--> $DIR/dont-suggest-foreign-doc-hidden.rs:27:13
|
LL | test5::<i32>();
| ^^^ the trait `Marker` is not implemented for `i32`
@ -53,7 +53,23 @@ note: required by a bound in `test5`
LL | fn test5<T: hidden_struct::Marker>() {}
| ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `test5`
error: aborting due to 5 previous errors
error[E0277]: the trait bound `i32: Marker` is not satisfied
--> $DIR/dont-suggest-foreign-doc-hidden.rs:30:13
|
LL | test6::<i32>();
| ^^^ the trait `Marker` is not implemented for `i32`
|
= help: the following other types implement trait `Marker`:
Baz
Option<u32>
Quux
note: required by a bound in `test6`
--> $DIR/dont-suggest-foreign-doc-hidden.rs:24:13
|
LL | fn test6<T: hidden_struct::library::Marker>() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `test6`
error: aborting due to 6 previous errors
Some errors have detailed explanations: E0277, E0412.
For more information about an error, try `rustc --explain E0277`.

View file

@ -1605,7 +1605,7 @@ labels = ["has-merge-commits", "S-waiting-on-author"]
format = "rustc"
project-name = "Rust"
changelog-path = "RELEASES.md"
changelog-branch = "master"
changelog-branch = "main"
[shortcut]