Remove salsa::report_untracked_read when finding the dynamic module resolution paths (#12509)

This commit is contained in:
Micha Reiser 2024-07-29 11:31:29 +02:00 committed by GitHub
parent e18b4e42d3
commit 2f54d05d97
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 242 additions and 67 deletions

View file

@ -24,7 +24,9 @@ countme = { workspace = true }
dashmap = { workspace = true }
filetime = { workspace = true }
ignore = { workspace = true, optional = true }
matchit = { workspace = true }
salsa = { workspace = true }
path-slash = { workspace = true }
tracing = { workspace = true }
rustc-hash = { workspace = true }
zip = { workspace = true }

View file

@ -15,6 +15,10 @@ impl FileRevision {
Self(value)
}
pub fn now() -> Self {
Self::from(filetime::FileTime::now())
}
pub const fn zero() -> Self {
Self(0)
}

View file

@ -4,15 +4,18 @@ use countme::Count;
use dashmap::mapref::entry::Entry;
use salsa::Setter;
pub use file_root::{FileRoot, FileRootKind};
pub use path::FilePath;
use ruff_notebook::{Notebook, NotebookError};
use crate::file_revision::FileRevision;
use crate::files::file_root::FileRoots;
use crate::files::private::FileStatus;
use crate::system::{Metadata, SystemPath, SystemPathBuf, SystemVirtualPath, SystemVirtualPathBuf};
use crate::vendored::{VendoredPath, VendoredPathBuf};
use crate::{Db, FxDashMap};
mod file_root;
mod path;
/// Interns a file system path and returns a salsa `File` ingredient.
@ -54,6 +57,9 @@ struct FilesInner {
/// Lookup table that maps vendored files to the salsa [`File`] ingredients.
vendored_by_path: FxDashMap<VendoredPathBuf, File>,
/// Lookup table that maps file paths to their [`FileRoot`].
roots: std::sync::RwLock<FileRoots>,
}
impl Files {
@ -72,6 +78,7 @@ impl Files {
.system_by_path
.entry(absolute.clone())
.or_insert_with(|| {
// TODO: Set correct durability according to source root.
let metadata = db.system().path_metadata(path);
match metadata {
@ -161,6 +168,33 @@ impl Files {
Some(file)
}
/// Looks up the closest root for `path`. Returns `None` if `path` isn't enclosed by any source root.
///
/// Roots can be nested, in which case the closest root is returned.
pub fn root(&self, db: &dyn Db, path: &SystemPath) -> Option<FileRoot> {
let roots = self.inner.roots.read().unwrap();
let absolute = SystemPath::absolute(path, db.system().current_directory());
roots.at(&absolute)
}
/// Adds a new root for `path` and returns the root.
///
/// The root isn't added nor is the file root's kind updated if a root for `path` already exists.
pub fn try_add_root(&self, db: &dyn Db, path: &SystemPath, kind: FileRootKind) -> FileRoot {
let mut roots = self.inner.roots.write().unwrap();
let absolute = SystemPath::absolute(path, db.system().current_directory());
roots.try_add(db, absolute, kind)
}
/// Updates the revision of the root for `path`.
pub fn touch_root(db: &mut dyn Db, path: &SystemPath) {
if let Some(root) = db.files().root(db, path) {
root.set_revision(db).to(FileRevision::now());
}
}
/// Refreshes the state of all known files under `path` recursively.
///
/// The most common use case is to update the [`Files`] state after removing or moving a directory.
@ -180,6 +214,14 @@ impl Files {
file.sync(db);
}
}
let roots = inner.roots.read().unwrap();
for root in roots.all() {
if root.path(db).starts_with(&path) {
root.set_revision(db).to(FileRevision::now());
}
}
}
/// Refreshes the state of all known files.
@ -197,6 +239,12 @@ impl Files {
let file = entry.value();
file.sync(db);
}
let roots = inner.roots.read().unwrap();
for root in roots.all() {
root.set_revision(db).to(FileRevision::now());
}
}
}
@ -309,6 +357,7 @@ impl File {
}
fn sync_system_path(db: &mut dyn Db, path: &SystemPath, file: Option<File>) {
Files::touch_root(db, path);
let Some(file) = file.or_else(|| db.files().try_system(db, path)) else {
return;
};

View file

@ -0,0 +1,125 @@
use std::fmt::Formatter;
use path_slash::PathExt;
use crate::file_revision::FileRevision;
use crate::system::{SystemPath, SystemPathBuf};
use crate::Db;
/// A root path for files tracked by the database.
///
/// We currently create roots for:
/// * static module resolution paths
/// * the workspace root
///
/// The main usage of file roots is to determine a file's durability. But it can also be used
/// to make a salsa query dependent on whether a file in a root has changed without writing any
/// manual invalidation logic.
#[salsa::input]
pub struct FileRoot {
/// The path of a root is guaranteed to never change.
#[return_ref]
path_buf: SystemPathBuf,
/// The kind of the root at the time of its creation.
kind_at_time_of_creation: FileRootKind,
/// A revision that changes when the contents of the source root change.
///
/// The revision changes when a new file was added, removed, or changed inside this source root.
pub revision: FileRevision,
}
impl FileRoot {
pub fn path(self, db: &dyn Db) -> &SystemPath {
self.path_buf(db)
}
pub fn durability(self, db: &dyn Db) -> salsa::Durability {
match self.kind_at_time_of_creation(db) {
FileRootKind::Workspace => salsa::Durability::LOW,
FileRootKind::LibrarySearchPath => salsa::Durability::HIGH,
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum FileRootKind {
/// The root of a workspace.
Workspace,
/// A non-workspace module resolution search path.
LibrarySearchPath,
}
#[derive(Default)]
pub(super) struct FileRoots {
by_path: matchit::Router<FileRoot>,
roots: Vec<FileRoot>,
}
impl FileRoots {
/// Tries to add a new root for `path` and returns the root.
///
/// The root isn't added nor is the file root's kind updated if a root for `path` already exists.
pub(super) fn try_add(
&mut self,
db: &dyn Db,
path: SystemPathBuf,
kind: FileRootKind,
) -> FileRoot {
// SAFETY: Guaranteed to succeed because `path` is a UTF-8 that only contains Unicode characters.
let normalized_path = path.as_std_path().to_slash().unwrap();
if let Ok(existing) = self.by_path.at(&normalized_path) {
// Only if it is an exact match
if existing.value.path(db) == &*path {
return *existing.value;
}
}
// normalize the path to use `/` separators and escape the '{' and '}' characters,
// which matchit uses for routing parameters
let mut route = normalized_path.replace('{', "{{").replace('}', "}}");
// Insert a new source root
let root = FileRoot::new(db, path, kind, FileRevision::now());
// Insert a path that matches the root itself
self.by_path.insert(route.clone(), root).unwrap();
// Insert a path that matches all subdirectories and files
route.push_str("/{*filepath}");
self.by_path.insert(route, root).unwrap();
self.roots.push(root);
root
}
/// Returns the closest root for `path` or `None` if no root contains `path`.
pub(super) fn at(&self, path: &SystemPath) -> Option<FileRoot> {
// SAFETY: Guaranteed to succeed because `path` is a UTF-8 that only contains Unicode characters.
let normalized_path = path.as_std_path().to_slash().unwrap();
dbg!(&normalized_path);
dbg!(&self.roots);
let entry = self.by_path.at(&normalized_path).ok()?;
Some(*entry.value)
}
pub(super) fn all(&self) -> impl Iterator<Item = FileRoot> + '_ {
self.roots.iter().copied()
}
}
impl std::fmt::Debug for FileRoots {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("FileRoots").field(&self.roots).finish()
}
}
impl PartialEq for FileRoots {
fn eq(&self, other: &Self) -> bool {
self.roots.eq(&other.roots)
}
}

View file

@ -76,9 +76,8 @@ where
let event = events.iter().find(|event| {
if let salsa::EventKind::WillExecute { database_key } = event.kind {
dbg!(db
.lookup_ingredient(database_key.ingredient_index())
.debug_name())
db.lookup_ingredient(database_key.ingredient_index())
.debug_name()
== query_name
&& database_key.key_index() == input.as_id()
} else {
@ -190,7 +189,6 @@ fn const_query_was_not_run_fails_if_query_was_run() {
assert_eq!(len(&db), 5);
let events = db.take_salsa_events();
dbg!(&events);
assert_const_function_query_was_not_run(&db, len, &events);
}