Skip to content
Merged
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
27 changes: 23 additions & 4 deletions crates/oak_db/src/file_imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,14 @@ impl File {
Some(package) if is_testthat_file(self, db) => {
testthat_imports(self, db, package, None)
},
Some(package) => package_imports(self, db, package),
None => script_imports(self, db),
Some(package) if is_package_source(self, db, package) => {
package_imports(self, db, package)
},
// A file with a package back-pointer that isn't a loadable `R/`
// file (`data-raw/`, `inst/`, a non-collated `R/` file) lives in
// the package but isn't loaded with it. Resolve it as a standalone
// script, same as a file with no package at all.
_ => script_imports(self, db),
}
}

Expand Down Expand Up @@ -113,12 +119,25 @@ impl File {
Some(package) if is_testthat_file(self, db) => {
testthat_imports(self, db, package, Some(offset))
},
Some(package) => narrow_package_top_level(self, db, package),
None => narrow_script_top_level(self, db, offset),
Some(package) if is_package_source(self, db, package) => {
narrow_package_top_level(self, db, package)
},
// A packaged file that isn't a loadable `R/` file narrows like a
// standalone script.
_ => narrow_script_top_level(self, db, offset),
}
}
}

/// Whether `file` is one of its package's loadable `R/` files, the ones in
/// `package.files()`. A file can carry a package back-pointer without being
/// loadable: `data-raw/`, `inst/`, and `R/` files left out of a `Collate:`
/// directive all land in `package.scripts()` instead and resolve as
/// standalone scripts.
fn is_package_source(file: File, db: &dyn Db, package: Package) -> bool {
package.files(db).contains(&file)
}

fn narrow_script_top_level(file: File, db: &dyn Db, offset: TextSize) -> Vec<ImportLayer> {
let mut layers = attach_layers(file, db, Some(offset));
extend_with_default_search_path(db, &mut layers);
Expand Down
50 changes: 50 additions & 0 deletions crates/oak_db/src/tests/file_imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,56 @@ fn test_package_file_emits_namespace_and_collation_layers() {
]);
}

#[test]
fn test_package_script_resolves_as_standalone_script() {
// A `data-raw/` file carries a package back-pointer but lives in
// `scripts`, not `files`. It isn't loaded with the package, so its
// imports must be the standalone-script view, never the package view
// (no `R/` `File` layers, no namespace layers). See #1270.
let mut db = TestDb::new();
let installed = install_packages(&mut db, &["dplyr", "base"]);
let dplyr = installed[0];

let workspace = workspace_root(&db, "w");
let pkg = Package::new(
&db,
file_path("w/pkg/DESCRIPTION"),
"pkg".to_string(),
FileRevision::zero(),
FileRevision::zero(),
None,
Vec::new(),
Vec::new(),
);
let r_file = File::new(
&db,
file_path("w/pkg/R/a.R"),
FileRevision::zero(),
Some("internal <- 1\n".to_string()),
Some(pkg),
);
let data_raw = File::new(
&db,
file_path("w/pkg/data-raw/prep.R"),
FileRevision::zero(),
Some("library(dplyr)\n".to_string()),
Some(pkg),
);
pkg.set_files(&mut db).to(vec![r_file]);
pkg.set_scripts(&mut db).to(vec![data_raw]);
workspace.set_packages(&mut db).to(vec![pkg]);
db.workspace_roots().set_roots(&mut db).to(vec![workspace]);

let _ = dplyr;
// Only its own `library(dplyr)` and `base` from the default search path
// (the only other registered package). `R/a.R` and the namespace are
// absent because the script isn't part of the package's namespace.
assert_eq!(shape(&db, data_raw.imports(&db)), vec![
"Package(dplyr)".to_string(),
"Package(base)".to_string(),
]);
}

#[test]
fn test_testthat_file_sees_helpers_package_and_testthat() {
let mut db = TestDb::new();
Expand Down
28 changes: 28 additions & 0 deletions crates/oak_db/src/tests/file_imports_at.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,34 @@ fn test_package_namespace_and_base_layers_always_visible() {
assert!(attached_packages(&layers).contains(&base));
}

#[test]
fn test_package_script_top_level_resolves_as_standalone_script() {
// A `data-raw/` file carries a package back-pointer but lives in
// `scripts`, not `files`. At top level it must narrow like a standalone
// script and never take the package path, which would log the spurious
// "back-pointer but not in its files" warning from #1270.
let mut db = TestDb::new();
let dplyr = install_packages(&mut db, &["dplyr"])[0];
let pkg = install_workspace_package(&mut db, "pkg");

let r_file = make_package_file(&mut db, "/workspace/pkg/R/a.R", "internal <- 1\n", pkg);
let source = "library(dplyr)\nx <- 1\n";
let data_raw = make_package_file(&mut db, "/workspace/pkg/data-raw/prep.R", source, pkg);
pkg.set_files(&mut db).to(vec![r_file]);
pkg.set_scripts(&mut db).to(vec![data_raw]);

// Before the `library()` call: nothing attached, and no `R/` `File`
// layers (the script isn't part of the package's namespace).
let before = data_raw.imports_at(&db, TextSize::from(0));
assert_eq!(attached_packages(&before), Vec::<Package>::new());
assert_eq!(package_files(&before), Vec::<File>::new());

// After the call: dplyr attached, still no `R/` `File` layers.
let after = data_raw.imports_at(&db, TextSize::from(source.len() as u32));
assert_eq!(attached_packages(&after), vec![dplyr]);
assert_eq!(package_files(&after), Vec::<File>::new());
}

#[test]
fn test_testthat_top_level_library_narrows_by_offset() {
// A test file's own top-level `library()` call narrows like a script's:
Expand Down
Loading