diff --git a/test/rust_analyzer/generated_srcs_test/rust_project_json_test.rs b/test/rust_analyzer/generated_srcs_test/rust_project_json_test.rs index d6a2c5bb6a..2c6e351aa1 100644 --- a/test/rust_analyzer/generated_srcs_test/rust_project_json_test.rs +++ b/test/rust_analyzer/generated_srcs_test/rust_project_json_test.rs @@ -22,23 +22,23 @@ mod tests { include_dirs: Vec, } + /// `{output_base}/external/.../library` → `{output_base}` + fn output_base(p: &Project) -> &str { + p.sysroot_src + .rsplitn(2, "/external/") + .last() + .expect("sysroot_src should contain /external/") + } + #[test] fn test_generated_srcs() { let rust_project_path = PathBuf::from(env::var("RUST_PROJECT_JSON").unwrap()); - let content = std::fs::read_to_string(&rust_project_path) - .unwrap_or_else(|_| panic!("couldn't open {:?}", rust_project_path)); + let content = std::fs::read_to_string(&rust_project_path).unwrap(); let project: Project = - serde_json::from_str(&content).expect("Failed to deserialize project JSON"); + serde_json::from_str(&content).expect("project JSON should deserialize correctly"); - // /tmp/_bazel/12345678/external/tools/rustlib/library => /tmp/_bazel - let output_base = project - .sysroot_src - .rsplitn(2, "/external/") - .last() - .unwrap() - .rsplitn(2, '/') - .last() - .unwrap(); + // /tmp/_bazel/12345678/external/tools/rustlib/library => /tmp/_bazel/12345678 + let output_base = output_base(&project); println!("output_base: {output_base}"); let with_gen = project @@ -58,4 +58,28 @@ mod tests { // The second entry is the output base, where the generated files are located. assert!(include_dirs[1].starts_with(output_base)); } + + #[test] + fn test_external_root_paths() { + let rust_project_path = PathBuf::from(env::var("RUST_PROJECT_JSON").unwrap()); + let content = std::fs::read_to_string(&rust_project_path).unwrap(); + let project: Project = + serde_json::from_str(&content).expect("project JSON should deserialize correctly"); + let output_base = output_base(&project); + let mut external_roots = project + .crates + .iter() + .map(|c| &c.root_module) + .filter(|p| p.contains("/external/")) + .peekable(); + + assert!(external_roots.peek().is_some()); + + let output_base_external = format!("{output_base}/external/"); + + for root in external_roots { + assert!(root.starts_with(&output_base_external)); + assert!(!root.contains("execroot")); + } + } } diff --git a/tools/rust_analyzer/lib.rs b/tools/rust_analyzer/lib.rs index 6733caedf3..092a2e7df2 100644 --- a/tools/rust_analyzer/lib.rs +++ b/tools/rust_analyzer/lib.rs @@ -496,7 +496,14 @@ where .replace("__WORKSPACE__", workspace.as_str()) .replace("${pwd}", execution_root.as_str()) .replace("__EXEC_ROOT__", execution_root.as_str()) - .replace("__OUTPUT_BASE__", output_base.as_str()); + .replace("__OUTPUT_BASE__", output_base.as_str()) + // exec_root/external contains symlinks into the output_base/external + // but those symlinks are ephemeral. if we do not replace here, goto + // definition can fail because those symlinks would no longer be found. + .replace( + &format!("{execution_root}/external/"), + &format!("{output_base}/external/"), + ); serde_json::from_str(&content).context("failed to deserialize after template substitution") }