-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
red-knot[salsa part 2]: Setup semantic DB and Jar (#11837)
Co-authored-by: Alex Waygood <[email protected]>
- Loading branch information
1 parent
9dc226b
commit efbf7b1
Showing
7 changed files
with
267 additions
and
15 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
use std::ops::Deref; | ||
use std::sync::Arc; | ||
|
||
use ruff_source_file::LineIndex; | ||
|
||
use crate::vfs::VfsFile; | ||
use crate::Db; | ||
|
||
/// Reads the content of file. | ||
#[salsa::tracked] | ||
pub fn source_text(db: &dyn Db, file: VfsFile) -> SourceText { | ||
let content = file.read(db); | ||
|
||
SourceText { | ||
inner: Arc::from(content), | ||
} | ||
} | ||
|
||
/// Computes the [`LineIndex`] for `file`. | ||
#[salsa::tracked] | ||
pub fn line_index(db: &dyn Db, file: VfsFile) -> LineIndex { | ||
let source = source_text(db, file); | ||
|
||
LineIndex::from_source_text(&source) | ||
} | ||
|
||
/// The source text of a [`VfsFile`]. | ||
/// | ||
/// Cheap cloneable in `O(1)`. | ||
#[derive(Clone, Eq, PartialEq)] | ||
pub struct SourceText { | ||
inner: Arc<str>, | ||
} | ||
|
||
impl SourceText { | ||
pub fn as_str(&self) -> &str { | ||
&self.inner | ||
} | ||
} | ||
|
||
impl Deref for SourceText { | ||
type Target = str; | ||
|
||
fn deref(&self) -> &str { | ||
self.as_str() | ||
} | ||
} | ||
|
||
impl std::fmt::Debug for SourceText { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
f.debug_tuple("SourceText").field(&self.inner).finish() | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use filetime::FileTime; | ||
use salsa::EventKind; | ||
|
||
use ruff_source_file::OneIndexed; | ||
use ruff_text_size::TextSize; | ||
|
||
use crate::file_system::FileSystemPath; | ||
use crate::source::{line_index, source_text}; | ||
use crate::tests::TestDb; | ||
use crate::Db; | ||
|
||
#[test] | ||
fn re_runs_query_when_file_revision_changes() { | ||
let mut db = TestDb::new(); | ||
let path = FileSystemPath::new("test.py"); | ||
|
||
db.file_system_mut().write_file(path, "x = 10".to_string()); | ||
|
||
let file = db.file(path); | ||
|
||
assert_eq!(&*source_text(&db, file), "x = 10"); | ||
|
||
db.file_system_mut().write_file(path, "x = 20".to_string()); | ||
file.set_revision(&mut db).to(FileTime::now().into()); | ||
|
||
assert_eq!(&*source_text(&db, file), "x = 20"); | ||
} | ||
|
||
#[test] | ||
fn text_is_cached_if_revision_is_unchanged() { | ||
let mut db = TestDb::new(); | ||
let path = FileSystemPath::new("test.py"); | ||
|
||
db.file_system_mut().write_file(path, "x = 10".to_string()); | ||
|
||
let file = db.file(path); | ||
|
||
assert_eq!(&*source_text(&db, file), "x = 10"); | ||
|
||
// Change the file permission only | ||
file.set_permissions(&mut db).to(Some(0o777)); | ||
|
||
db.events().lock().unwrap().clear(); | ||
assert_eq!(&*source_text(&db, file), "x = 10"); | ||
|
||
let events = db.events(); | ||
let events = events.lock().unwrap(); | ||
|
||
assert!(!events | ||
.iter() | ||
.any(|event| matches!(event.kind, EventKind::WillExecute { .. }))); | ||
} | ||
|
||
#[test] | ||
fn line_index_for_source() { | ||
let mut db = TestDb::new(); | ||
let path = FileSystemPath::new("test.py"); | ||
|
||
db.file_system_mut() | ||
.write_file(path, "x = 10\ny = 20".to_string()); | ||
|
||
let file = db.file(path); | ||
let index = line_index(&db, file); | ||
let text = source_text(&db, file); | ||
|
||
assert_eq!(index.line_count(), 2); | ||
assert_eq!( | ||
index.line_start(OneIndexed::from_zero_indexed(0), &text), | ||
TextSize::new(0) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
use ruff_db::{Db as SourceDb, Upcast}; | ||
use salsa::DbWithJar; | ||
|
||
// Salsa doesn't support a struct without fields, so allow the clippy lint for now. | ||
#[allow(clippy::empty_structs_with_brackets)] | ||
#[salsa::jar(db=Db)] | ||
pub struct Jar(); | ||
|
||
/// Database giving access to semantic information about a Python program. | ||
pub trait Db: SourceDb + DbWithJar<Jar> + Upcast<dyn SourceDb> {} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::{Db, Jar}; | ||
use ruff_db::file_system::{FileSystem, MemoryFileSystem}; | ||
use ruff_db::vfs::Vfs; | ||
use ruff_db::{Db as SourceDb, Jar as SourceJar, Upcast}; | ||
use salsa::DebugWithDb; | ||
|
||
#[salsa::db(Jar, SourceJar)] | ||
pub(crate) struct TestDb { | ||
storage: salsa::Storage<Self>, | ||
vfs: Vfs, | ||
file_system: MemoryFileSystem, | ||
events: std::sync::Arc<std::sync::Mutex<Vec<salsa::Event>>>, | ||
} | ||
|
||
impl TestDb { | ||
#[allow(unused)] | ||
pub(crate) fn new() -> Self { | ||
Self { | ||
storage: salsa::Storage::default(), | ||
file_system: MemoryFileSystem::default(), | ||
events: std::sync::Arc::default(), | ||
vfs: Vfs::with_stubbed_vendored(), | ||
} | ||
} | ||
|
||
#[allow(unused)] | ||
pub(crate) fn memory_file_system(&self) -> &MemoryFileSystem { | ||
&self.file_system | ||
} | ||
|
||
#[allow(unused)] | ||
pub(crate) fn memory_file_system_mut(&mut self) -> &mut MemoryFileSystem { | ||
&mut self.file_system | ||
} | ||
|
||
#[allow(unused)] | ||
pub(crate) fn vfs_mut(&mut self) -> &mut Vfs { | ||
&mut self.vfs | ||
} | ||
} | ||
|
||
impl SourceDb for TestDb { | ||
fn file_system(&self) -> &dyn FileSystem { | ||
&self.file_system | ||
} | ||
|
||
fn vfs(&self) -> &Vfs { | ||
&self.vfs | ||
} | ||
} | ||
|
||
impl Upcast<dyn SourceDb> for TestDb { | ||
fn upcast(&self) -> &(dyn SourceDb + 'static) { | ||
self | ||
} | ||
} | ||
|
||
impl Db for TestDb {} | ||
|
||
impl salsa::Database for TestDb { | ||
fn salsa_event(&self, event: salsa::Event) { | ||
tracing::trace!("event: {:?}", event.debug(self)); | ||
let mut events = self.events.lock().unwrap(); | ||
events.push(event); | ||
} | ||
} | ||
|
||
impl salsa::ParallelDatabase for TestDb { | ||
fn snapshot(&self) -> salsa::Snapshot<Self> { | ||
salsa::Snapshot::new(Self { | ||
storage: self.storage.snapshot(), | ||
vfs: self.vfs.snapshot(), | ||
file_system: self.file_system.snapshot(), | ||
events: self.events.clone(), | ||
}) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters