Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 54 additions & 1 deletion compiler/rustc_interface/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use rustc_session::utils::{CanonicalizedPath, NativeLib};
use rustc_session::{CompilerIO, EarlyDiagCtxt, Session, build_session, getopts};
use rustc_span::edition::{DEFAULT_EDITION, Edition};
use rustc_span::source_map::{RealFileLoader, SourceMapInputs};
use rustc_span::{FileName, SourceFileHashAlgorithm, sym};
use rustc_span::{FileName, RealFileName, RemapPathScopeComponents, SourceFileHashAlgorithm, sym};
use rustc_target::spec::{
CodeModel, FramePointer, LinkerFlavorCli, MergeFunctions, OnBrokenPipe, PanicStrategy,
RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel,
Expand Down Expand Up @@ -175,6 +175,59 @@ fn test_can_print_warnings() {
});
}

// `--remap-path-prefix={cwd}=...` stores the `{cwd}` placeholder verbatim (so the absolute cwd
// never enters the tracked option and the incremental cache survives across build directories,
// see #132132) and expands it only when the mapping is applied.
#[test]
fn test_remap_path_prefix_cwd_placeholder() {
sess_and_cfg(&["--remap-path-prefix={cwd}/sub=mapped"], |sess, _cfg| {
// Stored verbatim as the placeholder text, not the resolved cwd.
assert_eq!(
sess.opts.remap_path_prefix,
vec![(PathBuf::from("{cwd}/sub"), PathBuf::from("mapped"))],
);
// ... but expanded + applied to real paths under the cwd.
let cwd = std::env::current_dir().unwrap();
let remapped = sess
.opts
.file_path_mapping()
.to_real_filename(&RealFileName::empty(), cwd.join("sub").join("foo.rs"))
.path(RemapPathScopeComponents::DEBUGINFO)
.to_path_buf();
assert_eq!(remapped, PathBuf::from("mapped/foo.rs"));
});
}

// `{{cwd}}` is an escaped literal `{cwd}` directory, NOT the placeholder, and is not expanded.
#[test]
fn test_remap_path_prefix_escaped_braces_are_literal() {
sess_and_cfg(&["--remap-path-prefix={{cwd}}/x=mapped"], |sess, _cfg| {
assert_eq!(
sess.opts.remap_path_prefix,
vec![(PathBuf::from("{{cwd}}/x"), PathBuf::from("mapped"))],
);
// Maps the literal prefix `{cwd}/x`, and does not touch the real cwd.
let remapped = sess
.opts
.file_path_mapping()
.to_real_filename(&RealFileName::empty(), PathBuf::from("{cwd}/x/foo.rs"))
.path(RemapPathScopeComponents::DEBUGINFO)
.to_path_buf();
assert_eq!(remapped, PathBuf::from("mapped/foo.rs"));
});
}

// `-Zremap-cwd-prefix` is sugar for `--remap-path-prefix={cwd}=VALUE`: same stored placeholder.
#[test]
fn test_remap_cwd_prefix_stores_placeholder() {
sess_and_cfg(&["-Zremap-cwd-prefix=mapped"], |sess, _cfg| {
assert_eq!(
sess.opts.remap_path_prefix,
vec![(PathBuf::from("{cwd}"), PathBuf::from("mapped"))],
);
});
}

#[test]
fn test_output_types_tracking_hash_different_paths() {
let mut v1 = Options::default();
Expand Down
60 changes: 51 additions & 9 deletions compiler/rustc_session/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::collections::btree_map::{
use std::collections::{BTreeMap, BTreeSet};
use std::ffi::OsStr;
use std::hash::Hash;
use std::path::{Path, PathBuf};
use std::path::{Component, Path, PathBuf};
use std::str::{self, FromStr};
use std::sync::LazyLock;
use std::{cmp, fs, iter};
Expand Down Expand Up @@ -1373,11 +1373,43 @@ pub fn host_tuple() -> &'static str {
(option_env!("CFG_COMPILER_HOST_TRIPLE")).expect("CFG_COMPILER_HOST_TRIPLE")
}

/// If `component` is a whole `{name}` placeholder, returns `Some(name)`. `{{`/`}}`-escaped
/// components (e.g. `{{cwd}}`) are not placeholders and return `None`.
fn remap_placeholder_name(component: &OsStr) -> Option<&str> {
let inner = component.to_str()?.strip_prefix('{')?.strip_suffix('}')?;
(!inner.is_empty() && !inner.contains(['{', '}'])).then_some(inner)
}

/// Expands a `--remap-path-prefix` FROM path: a `{cwd}` component becomes the working
/// directory, and `{{`/`}}` are unescaped to `{`/`}`. Expansion happens here, at apply time;
/// the FROM is stored with the placeholder intact, so the tracked option is stable across
/// build directories and the incremental cache survives. See #132132.
fn expand_remap_path_prefix(from: &Path) -> Option<PathBuf> {
let mut out = PathBuf::new();
for comp in from.components() {
match comp {
Component::Normal(seg) if remap_placeholder_name(seg) == Some("cwd") => {
out.push(std::env::current_dir().ok()?)
}
Component::Normal(seg) => match seg.to_str() {
Some(s) => out.push(s.replace("{{", "{").replace("}}", "}")),
None => out.push(seg),
},
other => out.push(other),
}
}
Some(out)
}

fn file_path_mapping(
remap_path_prefix: Vec<(PathBuf, PathBuf)>,
remap_path_scope: RemapPathScopeComponents,
) -> FilePathMapping {
FilePathMapping::new(remap_path_prefix.clone(), remap_path_scope)
let mapping = remap_path_prefix
.into_iter()
.filter_map(|(from, to)| Some((expand_remap_path_prefix(&from)?, to)))
.collect();
FilePathMapping::new(mapping, remap_path_scope)
}

impl Default for Options {
Expand Down Expand Up @@ -2396,13 +2428,23 @@ fn parse_remap_path_prefix(
Some((from, to)) => (PathBuf::from(from), PathBuf::from(to)),
})
.collect();
match &unstable_opts.remap_cwd_prefix {
Some(to) => match std::env::current_dir() {
Ok(cwd) => mapping.push((cwd, to.clone())),
Err(_) => (),
},
None => (),
};
// `-Zremap-cwd-prefix=VALUE` is sugar for `--remap-path-prefix={cwd}=VALUE`. See #132132.
if let Some(to) = &unstable_opts.remap_cwd_prefix {
mapping.push((PathBuf::from("{cwd}"), to.clone()));
}
// Only `{cwd}` is a recognised placeholder; reject typos like `{cdw}`.
for (from, _) in &mapping {
for comp in from.components() {
if let Component::Normal(seg) = comp
&& let Some(name) = remap_placeholder_name(seg)
&& name != "cwd"
{
early_dcx.early_fatal(format!(
"unknown placeholder `{{{name}}}` in `--remap-path-prefix`"
));
}
}
}
mapping
}

Expand Down
8 changes: 8 additions & 0 deletions tests/ui/errors/remap-path-prefix-unknown-placeholder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Unknown `{name}` placeholders in `--remap-path-prefix` are rejected.
// A literal `{foo}` directory would be written `{{foo}}`.
//
//@ compile-flags: --remap-path-prefix={foo}=bar

fn main() {}

//~? ERROR unknown placeholder `{foo}` in `--remap-path-prefix`
2 changes: 2 additions & 0 deletions tests/ui/errors/remap-path-prefix-unknown-placeholder.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
error: unknown placeholder `{foo}` in `--remap-path-prefix`

Loading