Skip to content

Commit 9602bda

Browse files
committed
Auto merge of #154842 - JonathanBrouwer:rollup-nuHAPsT, r=JonathanBrouwer
Rollup of 2 pull requests Successful merges: - #154555 (Make sure we run the full suite, even when more specific filters are given) - #154674 (borrowck: Don't mention unused vars in closure outlive errors)
2 parents e73c56a + 9b2b299 commit 9602bda

File tree

5 files changed

+152
-37
lines changed

5 files changed

+152
-37
lines changed

compiler/rustc_borrowck/src/diagnostics/var_name.rs

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
use rustc_index::IndexSlice;
2-
use rustc_middle::mir::{Body, Local};
2+
use rustc_middle::mir::visit::{PlaceContext, VisitPlacesWith, Visitor};
3+
use rustc_middle::mir::{Body, Local, Place};
34
use rustc_middle::ty::{self, RegionVid, TyCtxt};
45
use rustc_span::{Span, Symbol};
56
use tracing::debug;
67

78
use crate::region_infer::RegionInferenceContext;
89

910
impl<'tcx> RegionInferenceContext<'tcx> {
11+
/// Find the the name and span of the variable corresponding to the given region.
12+
/// The returned var will also be ensured to actually be used in `body`.
1013
pub(crate) fn get_var_name_and_span_for_region(
1114
&self,
1215
tcx: TyCtxt<'tcx>,
@@ -22,13 +25,20 @@ impl<'tcx> RegionInferenceContext<'tcx> {
2225
self.get_upvar_index_for_region(tcx, fr)
2326
.map(|index| {
2427
// FIXME(project-rfc-2229#8): Use place span for diagnostics
28+
// We know our upvars are used thanks to `fn compute_min_captures()` in `upvar.rs`.
2529
let (name, span) = self.get_upvar_name_and_span_for_region(tcx, upvars, index);
2630
(Some(name), span)
2731
})
2832
.or_else(|| {
2933
debug!("get_var_name_and_span_for_region: attempting argument");
30-
self.get_argument_index_for_region(tcx, fr).map(|index| {
31-
self.get_argument_name_and_span_for_region(body, local_names, index)
34+
self.get_argument_index_for_region(tcx, fr).and_then(|index| {
35+
let local = self.user_arg_index_to_local(body, index);
36+
if body_uses_local(body, local) {
37+
Some(self.get_argument_name_and_span_for_region(body, local_names, index))
38+
} else {
39+
debug!("get_var_name_and_span_for_region: skipping unused local {local:?}");
40+
None
41+
}
3242
})
3343
})
3444
}
@@ -105,6 +115,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
105115
Some(argument_index)
106116
}
107117

118+
/// Given the index of an argument as seen from the user (i.e. excluding
119+
/// implicit inputs), returns the corresponding MIR local.
120+
fn user_arg_index_to_local(&self, body: &Body<'tcx>, user_arg_index: usize) -> Local {
121+
let implicit_inputs = self.universal_regions().defining_ty.implicit_inputs();
122+
body.args_iter().nth(implicit_inputs + user_arg_index).unwrap()
123+
}
124+
108125
/// Given the index of an argument, finds its name (if any) and the span from where it was
109126
/// declared.
110127
pub(crate) fn get_argument_name_and_span_for_region(
@@ -113,8 +130,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
113130
local_names: &IndexSlice<Local, Option<Symbol>>,
114131
argument_index: usize,
115132
) -> (Option<Symbol>, Span) {
116-
let implicit_inputs = self.universal_regions().defining_ty.implicit_inputs();
117-
let argument_local = Local::from_usize(implicit_inputs + argument_index + 1);
133+
let argument_local = self.user_arg_index_to_local(body, argument_index);
118134
debug!("get_argument_name_and_span_for_region: argument_local={argument_local:?}");
119135

120136
let argument_name = local_names[argument_local];
@@ -126,3 +142,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
126142
(argument_name, argument_span)
127143
}
128144
}
145+
146+
fn body_uses_local<'tcx>(body: &Body<'tcx>, target: Local) -> bool {
147+
let mut used = false;
148+
VisitPlacesWith(|place: Place<'_>, context: PlaceContext| {
149+
if !matches!(context, PlaceContext::NonUse(_)) && place.local == target {
150+
used = true;
151+
}
152+
})
153+
.visit_body(body);
154+
used
155+
}

src/bootstrap/src/core/build_steps/test.rs

Lines changed: 70 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ use crate::core::{android, debuggers};
3737
use crate::utils::build_stamp::{self, BuildStamp};
3838
use crate::utils::exec::{BootstrapCommand, command};
3939
use crate::utils::helpers::{
40-
self, LldThreads, add_dylib_path, add_rustdoc_cargo_linker_args, dylib_path, dylib_path_var,
41-
linker_args, linker_flags, t, target_supports_cranelift_backend, up_to_date,
40+
self, LldThreads, TestFilterCategory, add_dylib_path, add_rustdoc_cargo_linker_args,
41+
dylib_path, dylib_path_var, linker_args, linker_flags, t, target_supports_cranelift_backend,
42+
up_to_date,
4243
};
4344
use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests};
4445
use crate::{CLang, CodegenBackendKind, GitRepo, Mode, PathSet, TestTarget, envify};
@@ -969,13 +970,15 @@ impl Step for Clippy {
969970
let paths = &builder.config.paths[..];
970971
let mut test_names = Vec::new();
971972
for path in paths {
972-
if let Some(path) =
973-
helpers::is_valid_test_suite_arg(path, "src/tools/clippy/tests", builder)
974-
{
975-
test_names.push(path);
976-
} else if path.ends_with("src/tools/clippy") {
977-
// When src/tools/clippy is called directly, all tests should be run.
978-
break 'partially_test;
973+
match helpers::is_valid_test_suite_arg(path, "src/tools/clippy/tests", builder) {
974+
TestFilterCategory::Arg(path) => {
975+
test_names.push(path);
976+
}
977+
TestFilterCategory::Fullsuite => {
978+
// When src/tools/clippy is called directly, all tests should be run.
979+
break 'partially_test;
980+
}
981+
TestFilterCategory::Uninteresting => {}
979982
}
980983
}
981984
cargo.env("TESTNAME", test_names.join(","));
@@ -1104,16 +1107,30 @@ impl Step for RustdocJSStd {
11041107
.arg(builder.doc_out(self.target))
11051108
.arg("--test-folder")
11061109
.arg(builder.src.join("tests/rustdoc-js-std"));
1107-
for path in &builder.paths {
1108-
if let Some(p) = helpers::is_valid_test_suite_arg(path, "tests/rustdoc-js-std", builder)
1109-
{
1110-
if !p.ends_with(".js") {
1111-
eprintln!("A non-js file was given: `{}`", path.display());
1112-
panic!("Cannot run rustdoc-js-std tests");
1110+
1111+
let full_suite = builder.paths.iter().any(|path| {
1112+
matches!(
1113+
helpers::is_valid_test_suite_arg(path, "tests/rustdoc-js-std", builder),
1114+
TestFilterCategory::Fullsuite
1115+
)
1116+
});
1117+
1118+
// If we have to also run the full suite, don't worry about the individual arguments.
1119+
// They will be covered by running the entire suite
1120+
if !full_suite {
1121+
for path in &builder.paths {
1122+
if let TestFilterCategory::Arg(p) =
1123+
helpers::is_valid_test_suite_arg(path, "tests/rustdoc-js-std", builder)
1124+
{
1125+
if !p.ends_with(".js") {
1126+
eprintln!("A non-js file was given: `{}`", path.display());
1127+
panic!("Cannot run rustdoc-js-std tests");
1128+
}
1129+
command.arg("--test-file").arg(path);
11131130
}
1114-
command.arg("--test-file").arg(path);
11151131
}
11161132
}
1133+
11171134
builder.ensure(crate::core::build_steps::doc::Std::from_build_compiler(
11181135
self.build_compiler,
11191136
self.target,
@@ -1252,14 +1269,27 @@ impl Step for RustdocGUI {
12521269

12531270
add_rustdoc_cargo_linker_args(&mut cmd, builder, self.test_compiler.host, LldThreads::No);
12541271

1255-
for path in &builder.paths {
1256-
if let Some(p) = helpers::is_valid_test_suite_arg(path, "tests/rustdoc-gui", builder) {
1257-
if !p.ends_with(".goml") {
1258-
eprintln!("A non-goml file was given: `{}`", path.display());
1259-
panic!("Cannot run rustdoc-gui tests");
1260-
}
1261-
if let Some(name) = path.file_name().and_then(|f| f.to_str()) {
1262-
cmd.arg("--goml-file").arg(name);
1272+
let full_suite = builder.paths.iter().any(|path| {
1273+
matches!(
1274+
helpers::is_valid_test_suite_arg(path, "tests/rustdoc-js-std", builder),
1275+
TestFilterCategory::Fullsuite
1276+
)
1277+
});
1278+
1279+
// If we have to also run the full suite, don't worry about the individual arguments.
1280+
// They will be covered by running the entire suite
1281+
if !full_suite {
1282+
for path in &builder.paths {
1283+
if let TestFilterCategory::Arg(p) =
1284+
helpers::is_valid_test_suite_arg(path, "tests/rustdoc-gui", builder)
1285+
{
1286+
if !p.ends_with(".goml") {
1287+
eprintln!("A non-goml file was given: `{}`", path.display());
1288+
panic!("Cannot run rustdoc-gui tests");
1289+
}
1290+
if let Some(name) = path.file_name().and_then(|f| f.to_str()) {
1291+
cmd.arg("--goml-file").arg(name);
1292+
}
12631293
}
12641294
}
12651295
}
@@ -2270,11 +2300,23 @@ Please disable assertions with `rust.debug-assertions = false`.
22702300
}
22712301
paths = &paths_v;
22722302
}
2303+
22732304
// Get test-args by striping suite path
2274-
let mut test_args: Vec<&str> = paths
2275-
.iter()
2276-
.filter_map(|p| helpers::is_valid_test_suite_arg(p, suite_path, builder))
2277-
.collect();
2305+
let mut test_args = Vec::new();
2306+
for p in paths {
2307+
match helpers::is_valid_test_suite_arg(p, suite_path, builder) {
2308+
TestFilterCategory::Fullsuite => {
2309+
// If we also have to run the full suite, don't append _any_ test args here,
2310+
// clear the list instead and break out.
2311+
// That way none of the more specific paths make it into test_args,
2312+
// since running the whole suite will run the specific ones anyway.
2313+
test_args.clear();
2314+
break;
2315+
}
2316+
TestFilterCategory::Arg(a) => test_args.push(a),
2317+
TestFilterCategory::Uninteresting => {}
2318+
}
2319+
}
22782320

22792321
test_args.append(&mut builder.config.test_args());
22802322

src/bootstrap/src/utils/helpers.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -240,18 +240,30 @@ pub fn target_supports_cranelift_backend(target: TargetSelection) -> bool {
240240
}
241241
}
242242

243+
/// Value returned from [`is_valid_test_suite_arg`], which figures out which paths start with the
244+
/// suite name (and therefore which should be run).
245+
pub enum TestFilterCategory<'a> {
246+
/// If a path is equal to the name of the suite, this is returned.
247+
Fullsuite,
248+
/// If a path starts with the suite, the suite prefix is stripped and the rest is returned as
249+
/// this variant.
250+
Arg(&'a str),
251+
/// For paths that don't start with the suite.
252+
Uninteresting,
253+
}
254+
243255
pub fn is_valid_test_suite_arg<'a, P: AsRef<Path>>(
244256
path: &'a Path,
245257
suite_path: P,
246258
builder: &Builder<'_>,
247-
) -> Option<&'a str> {
259+
) -> TestFilterCategory<'a> {
248260
let suite_path = suite_path.as_ref();
249261
let path = match path.strip_prefix(".") {
250262
Ok(p) => p,
251263
Err(_) => path,
252264
};
253265
if !path.starts_with(suite_path) {
254-
return None;
266+
return TestFilterCategory::Uninteresting;
255267
}
256268
let abs_path = builder.src.join(path);
257269
let exists = abs_path.is_dir() || abs_path.is_file();
@@ -268,8 +280,8 @@ pub fn is_valid_test_suite_arg<'a, P: AsRef<Path>>(
268280
// flag is respected, so providing an empty --test-args conflicts with
269281
// any following it.
270282
match path.strip_prefix(suite_path).ok().and_then(|p| p.to_str()) {
271-
Some(s) if !s.is_empty() => Some(s),
272-
_ => None,
283+
Some(s) if !s.is_empty() => TestFilterCategory::Arg(s),
284+
_ => TestFilterCategory::Fullsuite,
273285
}
274286
}
275287

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//! Regression test for <https://github.com/rust-lang/rust/issues/113121>.
2+
3+
#![allow(unused_variables)]
4+
5+
fn consume<T: 'static>(_: T) {}
6+
7+
fn foo<'a>(
8+
used_arg: &'a u8,
9+
unused_arg: &'static u16, // Unused in closure. Must not appear in error.
10+
) {
11+
let unused_var: &'static u32 = &42; // Unused in closure. Must not appear in error.
12+
13+
let c = move || used_arg;
14+
consume(c); //~ ERROR: borrowed data escapes outside of function
15+
}
16+
17+
fn main() {}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0521]: borrowed data escapes outside of function
2+
--> $DIR/var-matching-lifetime-but-unused-not-mentioned.rs:14:5
3+
|
4+
LL | fn foo<'a>(
5+
| -- lifetime `'a` defined here
6+
LL | used_arg: &'a u8,
7+
| -------- `used_arg` is a reference that is only valid in the function body
8+
...
9+
LL | consume(c);
10+
| ^^^^^^^^^^
11+
| |
12+
| `used_arg` escapes the function body here
13+
| argument requires that `'a` must outlive `'static`
14+
15+
error: aborting due to 1 previous error
16+
17+
For more information about this error, try `rustc --explain E0521`.

0 commit comments

Comments
 (0)