diff --git a/rust/private/rust_analyzer.bzl b/rust/private/rust_analyzer.bzl index bc12306a82..2209c43d49 100644 --- a/rust/private/rust_analyzer.bzl +++ b/rust/private/rust_analyzer.bzl @@ -230,12 +230,24 @@ def _create_single_crate(ctx, attrs, info): crate["root_module"] = path_prefix + info.crate.root.path crate["source"] = {"exclude_dirs": [], "include_dirs": []} + # If some sources are generated and others are not, paths are complicated. + # + # bazel splits the files across bazel-out/package/* and workspace/package/*, + # but rustc requires the sources to all be in one directory. + # To address this, rules_rust adds symlinks: bazel-out/package/* => workspace/package/* + # + # If we specify one of the symlinks as our root_module, then rust-analyzer can index the crate, + # but won't recognize files in the workspace as being part of it, so features won't work. + # If we specify the path within the workspace, it won't manage to resolve the generated modules. + # + # There's no perfect solution here, for now we choose to try to find a workspace path. + # https://github.com/bazelbuild/rules_rust/issues/3126 if is_generated: - srcs = getattr(ctx.rule.files, "srcs", []) - src_map = {src.short_path: src for src in srcs if src.is_source} - if info.crate.root.short_path in src_map: - crate["root_module"] = _WORKSPACE_TEMPLATE + src_map[info.crate.root.short_path].path - crate["source"]["include_dirs"].append(path_prefix + info.crate.root.dirname) + # 'root' is a transformed source, likely a symlink. + # 'srcs' are what the user originally specified, likely workspace paths. + for src in getattr(ctx.rule.files, "srcs", []): + if src.is_source and info.crate.root.short_path == src.short_path: + crate["root_module"] = _WORKSPACE_TEMPLATE + src.path if info.build_info != None and info.build_info.out_dir != None: out_dir_path = info.build_info.out_dir.path 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 81fe369554..5febecfd39 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 @@ -6,7 +6,6 @@ mod tests { #[derive(Deserialize)] struct Project { - sysroot_src: String, crates: Vec, } @@ -14,12 +13,6 @@ mod tests { struct Crate { display_name: String, root_module: String, - source: Option, - } - - #[derive(Deserialize)] - struct Source { - include_dirs: Vec, } #[test] @@ -31,27 +24,20 @@ mod tests { let project: Project = serde_json::from_str(&content).expect("Failed to deserialize project JSON"); - // /tmp/_bazel/12345678/external/tools/rustlib/library => /tmp/_bazel - let output_base = project - .sysroot_src - .rsplitn(2, "/external/") - .last() - .unwrap() - .rsplitn(2, '/') - .last() - .unwrap(); - println!("output_base: {output_base}"); - let gen = project .crates .iter() .find(|c| &c.display_name == "generated_srcs") .unwrap(); - assert!(gen.root_module.starts_with("/")); - assert!(gen.root_module.ends_with("/lib.rs")); - let include_dirs = &gen.source.as_ref().unwrap().include_dirs; - assert!(include_dirs.len() == 1); - assert!(include_dirs[0].starts_with(output_base)); + // This target has mixed generated+plain sources, so rules_rust provides a + // directory where the root module is a symlink. + // However in the crate spec, we want the root_module to be a workspace path + // when possible. + let workspace_path = PathBuf::from(env::var("WORKSPACE").unwrap()); + assert_eq!( + gen.root_module, + workspace_path.join("lib.rs").to_string_lossy() + ); } } diff --git a/test/rust_analyzer/rust_analyzer_test_runner.sh b/test/rust_analyzer/rust_analyzer_test_runner.sh index 0e56d76bab..f8ee9b15f7 100755 --- a/test/rust_analyzer/rust_analyzer_test_runner.sh +++ b/test/rust_analyzer/rust_analyzer_test_runner.sh @@ -101,7 +101,7 @@ function rust_analyzer_test() { echo "Building..." bazel build //... echo "Testing..." - bazel test //... + bazel test --test_env=WORKSPACE="${workspace}" //... echo "Building with Aspects..." bazel build //... --config=strict popd &>/dev/null diff --git a/tools/rust_analyzer/aquery.rs b/tools/rust_analyzer/aquery.rs index bc98913b05..6aac54f71b 100644 --- a/tools/rust_analyzer/aquery.rs +++ b/tools/rust_analyzer/aquery.rs @@ -176,20 +176,23 @@ fn consolidate_crate_specs(crate_specs: Vec) -> anyhow::Result