Compare commits
3 Commits
alex/subsc
...
ibraheem/p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
992e77e4d0 | ||
|
|
8d05367d60 | ||
|
|
2402831223 |
35
Cargo.lock
generated
35
Cargo.lock
generated
@@ -260,6 +260,9 @@ name = "bitflags"
|
||||
version = "2.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a65b545ab31d687cff52899d4890855fec459eb6afe0da6417b8a18da87aa29"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitvec"
|
||||
@@ -1028,6 +1031,16 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "erased-serde"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"typeid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.13"
|
||||
@@ -3451,12 +3464,13 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
[[package]]
|
||||
name = "salsa"
|
||||
version = "0.23.0"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=a3ffa22cb26756473d56f867aedec3fd907c4dd9#a3ffa22cb26756473d56f867aedec3fd907c4dd9"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=a0e7a06#a0e7a0660c93136f23bf08b4f1604eee3d1f6b11"
|
||||
dependencies = [
|
||||
"boxcar",
|
||||
"compact_str",
|
||||
"crossbeam-queue",
|
||||
"crossbeam-utils",
|
||||
"erased-serde",
|
||||
"hashbrown 0.15.5",
|
||||
"hashlink",
|
||||
"indexmap",
|
||||
@@ -3467,6 +3481,7 @@ dependencies = [
|
||||
"rustc-hash",
|
||||
"salsa-macro-rules",
|
||||
"salsa-macros",
|
||||
"serde",
|
||||
"smallvec",
|
||||
"thin-vec",
|
||||
"tracing",
|
||||
@@ -3475,12 +3490,12 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "salsa-macro-rules"
|
||||
version = "0.23.0"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=a3ffa22cb26756473d56f867aedec3fd907c4dd9#a3ffa22cb26756473d56f867aedec3fd907c4dd9"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=a0e7a06#a0e7a0660c93136f23bf08b4f1604eee3d1f6b11"
|
||||
|
||||
[[package]]
|
||||
name = "salsa-macros"
|
||||
version = "0.23.0"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=a3ffa22cb26756473d56f867aedec3fd907c4dd9#a3ffa22cb26756473d56f867aedec3fd907c4dd9"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=a0e7a06#a0e7a0660c93136f23bf08b4f1604eee3d1f6b11"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3700,6 +3715,9 @@ name = "smallvec"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "snapbox"
|
||||
@@ -3904,6 +3922,9 @@ name = "thin-vec"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "144f754d318415ac792f9d69fc87abbbfc043ce2ef041c60f16ad828f638717d"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
@@ -4261,6 +4282,7 @@ name = "ty_project"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode 2.0.1",
|
||||
"camino",
|
||||
"colored 3.0.0",
|
||||
"crossbeam",
|
||||
@@ -4290,6 +4312,7 @@ dependencies = [
|
||||
"tracing",
|
||||
"ty_combine",
|
||||
"ty_python_semantic",
|
||||
"ty_static",
|
||||
"ty_vendored",
|
||||
]
|
||||
|
||||
@@ -4461,6 +4484,12 @@ version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
|
||||
|
||||
[[package]]
|
||||
name = "typeid"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.18.0"
|
||||
|
||||
15
Cargo.toml
15
Cargo.toml
@@ -57,8 +57,8 @@ anyhow = { version = "1.0.80" }
|
||||
arc-swap = { version = "1.7.1" }
|
||||
assert_fs = { version = "1.1.0" }
|
||||
argfile = { version = "0.2.0" }
|
||||
bincode = { version = "2.0.0" }
|
||||
bitflags = { version = "2.5.0" }
|
||||
bincode = { version = "2.0.0", features = ["serde"] }
|
||||
bitflags = { version = "2.5.0", features = ["serde"] }
|
||||
bitvec = { version = "1.0.1", default-features = false, features = [
|
||||
"alloc",
|
||||
] }
|
||||
@@ -126,7 +126,7 @@ memchr = { version = "2.7.1" }
|
||||
mimalloc = { version = "0.1.39" }
|
||||
natord = { version = "1.0.9" }
|
||||
notify = { version = "8.0.0" }
|
||||
ordermap = { version = "0.5.0" }
|
||||
ordermap = { version = "0.5.0", features = ["serde"] }
|
||||
path-absolutize = { version = "3.1.1" }
|
||||
path-slash = { version = "0.2.1" }
|
||||
pathdiff = { version = "0.2.1" }
|
||||
@@ -143,24 +143,25 @@ regex-automata = { version = "0.4.9" }
|
||||
rustc-hash = { version = "2.0.0" }
|
||||
rustc-stable-hash = { version = "0.1.2" }
|
||||
# When updating salsa, make sure to also update the revision in `fuzz/Cargo.toml`
|
||||
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "a3ffa22cb26756473d56f867aedec3fd907c4dd9", default-features = false, features = [
|
||||
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "a0e7a06", default-features = false, features = [
|
||||
"compact_str",
|
||||
"macros",
|
||||
"salsa_unstable",
|
||||
"inventory",
|
||||
"persistence",
|
||||
] }
|
||||
schemars = { version = "0.8.16" }
|
||||
seahash = { version = "4.1.0" }
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
serde = { version = "1.0.197", features = ["derive", "rc"] }
|
||||
serde-wasm-bindgen = { version = "0.6.4" }
|
||||
serde_json = { version = "1.0.113" }
|
||||
serde_json = { version = "1.0.142" }
|
||||
serde_test = { version = "1.0.152" }
|
||||
serde_with = { version = "3.6.0", default-features = false, features = [
|
||||
"macros",
|
||||
] }
|
||||
shellexpand = { version = "3.0.0" }
|
||||
similar = { version = "2.4.0", features = ["inline"] }
|
||||
smallvec = { version = "1.13.2", features = ["union", "const_generics", "const_new"] }
|
||||
smallvec = { version = "1.13.2", features = ["union", "const_generics", "const_new", "serde"] }
|
||||
snapbox = { version = "0.6.0", features = [
|
||||
"diff",
|
||||
"term-svg",
|
||||
|
||||
@@ -31,7 +31,7 @@ ruff_workspace = { workspace = true }
|
||||
|
||||
anyhow = { workspace = true }
|
||||
argfile = { workspace = true }
|
||||
bincode = { workspace = true, features = ["serde"] }
|
||||
bincode = { workspace = true }
|
||||
bitflags = { workspace = true }
|
||||
cachedir = { workspace = true }
|
||||
clap = { workspace = true, features = ["derive", "env", "wrap_help"] }
|
||||
|
||||
@@ -22,6 +22,7 @@ mod stylesheet;
|
||||
/// a characteristic is a deficiency. An example of a characteristic that is
|
||||
/// _not_ a deficiency is the `reveal_type` diagnostic for our type checker.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, get_size2::GetSize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct Diagnostic {
|
||||
/// The actual diagnostic.
|
||||
///
|
||||
@@ -500,6 +501,7 @@ impl Diagnostic {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, get_size2::GetSize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
struct DiagnosticInner {
|
||||
id: DiagnosticId,
|
||||
severity: Severity,
|
||||
@@ -576,6 +578,7 @@ impl Eq for RenderingSortKey<'_> {}
|
||||
/// another (for a single parent diagnostic) is the order in which they were
|
||||
/// attached to the diagnostic.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, get_size2::GetSize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct SubDiagnostic {
|
||||
/// Like with `Diagnostic`, we box the `SubDiagnostic` to make it
|
||||
/// pointer-sized.
|
||||
@@ -685,6 +688,7 @@ impl SubDiagnostic {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, get_size2::GetSize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
struct SubDiagnosticInner {
|
||||
severity: SubDiagnosticSeverity,
|
||||
message: DiagnosticMessage,
|
||||
@@ -713,6 +717,7 @@ struct SubDiagnosticInner {
|
||||
/// Messages attached to annotations should also be as brief and specific as
|
||||
/// possible. Long messages could negative impact the quality of rendering.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, get_size2::GetSize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct Annotation {
|
||||
/// The span of this annotation, corresponding to some subsequence of the
|
||||
/// user's input that we want to highlight.
|
||||
@@ -855,6 +860,7 @@ impl Annotation {
|
||||
/// These tags are used to provide additional information about the annotation.
|
||||
/// and are passed through to the language server protocol.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, get_size2::GetSize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub enum DiagnosticTag {
|
||||
/// Unused or unnecessary code. Used for unused parameters, unreachable code, etc.
|
||||
Unnecessary,
|
||||
@@ -869,6 +875,7 @@ pub enum DiagnosticTag {
|
||||
///
|
||||
/// Rules use kebab case, e.g. `no-foo`.
|
||||
#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, get_size2::GetSize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(transparent))]
|
||||
pub struct LintName(&'static str);
|
||||
|
||||
impl LintName {
|
||||
@@ -881,6 +888,66 @@ impl LintName {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
pub use lint_name_serde::LintRegistryGuard;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
mod lint_name_serde {
|
||||
use super::LintName;
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
thread_local! {
|
||||
/// Serde doesn't provide any easy means to pass a value to a [`Deserialize`] implementation,
|
||||
/// but we need a way to retrieve static [`LintName`]s from the lint registry when deserializing.
|
||||
///
|
||||
/// Use the [`LintRegistryGuard`] to initialize the thread local before calling into any
|
||||
/// deserialization code. It ensures that the thread local variable gets cleaned up
|
||||
/// once deserialization is done (once the guard gets dropped).
|
||||
static LINT_REGISTRY: RefCell<Option<LintRegistry>> = const { RefCell::new(None) };
|
||||
}
|
||||
|
||||
type LintRegistry = fn(&str) -> Option<LintName>;
|
||||
|
||||
/// Guard to safely change the lint registry for the current thread.
|
||||
#[must_use]
|
||||
pub struct LintRegistryGuard {
|
||||
prev_value: Option<LintRegistry>,
|
||||
}
|
||||
|
||||
impl LintRegistryGuard {
|
||||
pub fn new(registry: LintRegistry) -> Self {
|
||||
let prev = LINT_REGISTRY.replace(Some(registry));
|
||||
Self { prev_value: prev }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for LintRegistryGuard {
|
||||
fn drop(&mut self) {
|
||||
LINT_REGISTRY.set(self.prev_value.take());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de> serde::Deserialize<'de> for LintName {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let name: &str = serde::Deserialize::deserialize(deserializer)?;
|
||||
|
||||
LINT_REGISTRY.with_borrow(|registry| {
|
||||
let registry = registry
|
||||
.expect("must set the `LintRegistryGuard` when deserializing a `LintName`");
|
||||
|
||||
registry(name).ok_or(serde::de::Error::custom(format!(
|
||||
"invalid `LintName` {name}"
|
||||
)))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for LintName {
|
||||
type Target = str;
|
||||
|
||||
@@ -909,6 +976,7 @@ impl PartialEq<&str> for LintName {
|
||||
|
||||
/// Uniquely identifies the kind of a diagnostic.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash, get_size2::GetSize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub enum DiagnosticId {
|
||||
Panic,
|
||||
|
||||
@@ -1097,6 +1165,30 @@ impl UnifiedFile {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl serde::Serialize for UnifiedFile {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
match self {
|
||||
UnifiedFile::Ty(file) => serde::Serialize::serialize(file, serializer),
|
||||
// Persistent caching is only used in ty.
|
||||
UnifiedFile::Ruff(..) => panic!("Ruff files are not persistable"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de> serde::Deserialize<'de> for UnifiedFile {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
serde::Deserialize::deserialize(deserializer).map(UnifiedFile::Ty)
|
||||
}
|
||||
}
|
||||
|
||||
/// A unified wrapper for types that can be converted to a [`SourceCode`].
|
||||
///
|
||||
/// As with [`UnifiedFile`], ruff and ty use slightly different representations for source code.
|
||||
@@ -1128,6 +1220,7 @@ impl DiagnosticSource {
|
||||
/// range isn't present, it semantically implies that the diagnostic refers to
|
||||
/// the entire file. For example, when the file should be executable but isn't.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct Span {
|
||||
file: UnifiedFile,
|
||||
range: Option<TextRange>,
|
||||
@@ -1206,6 +1299,7 @@ impl From<crate::files::FileRange> for Span {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash, get_size2::GetSize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub enum Severity {
|
||||
Info,
|
||||
Warning,
|
||||
@@ -1241,6 +1335,7 @@ impl Severity {
|
||||
/// used for main diagnostics. If we want to add `Severity::Help` in the future, this type could be
|
||||
/// deleted and the two combined again.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash, get_size2::GetSize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub enum SubDiagnosticSeverity {
|
||||
Help,
|
||||
Info,
|
||||
@@ -1489,6 +1584,7 @@ impl std::fmt::Display for ConciseMessage<'_> {
|
||||
/// a blanket trait implementation for `IntoDiagnosticMessage` for
|
||||
/// anything that implements `std::fmt::Display`.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash, get_size2::GetSize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct DiagnosticMessage(Box<str>);
|
||||
|
||||
impl DiagnosticMessage {
|
||||
@@ -1552,7 +1648,11 @@ impl<T: std::fmt::Display> IntoDiagnosticMessage for T {
|
||||
///
|
||||
/// For Ruff rules this means the noqa code.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Default, Hash, get_size2::GetSize)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(transparent))]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Serialize, serde::Deserialize),
|
||||
serde(transparent)
|
||||
)]
|
||||
pub struct SecondaryCode(String);
|
||||
|
||||
impl SecondaryCode {
|
||||
|
||||
@@ -9,7 +9,17 @@ use crate::system::file_time_now;
|
||||
/// * The last modification time of the file.
|
||||
/// * The hash of the file's content.
|
||||
/// * The revision as it comes from an external system, for example the LSP.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Copy,
|
||||
Clone,
|
||||
Debug,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Default,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub struct FileRevision(u128);
|
||||
|
||||
impl FileRevision {
|
||||
|
||||
@@ -14,7 +14,7 @@ use crate::diagnostic::{Span, UnifiedFile};
|
||||
use crate::file_revision::FileRevision;
|
||||
use crate::files::file_root::FileRoots;
|
||||
use crate::files::private::FileStatus;
|
||||
use crate::system::{SystemPath, SystemPathBuf, SystemVirtualPath, SystemVirtualPathBuf};
|
||||
use crate::system::{FileType, SystemPath, SystemPathBuf, SystemVirtualPath, SystemVirtualPathBuf};
|
||||
use crate::vendored::{VendoredPath, VendoredPathBuf};
|
||||
use crate::{Db, FxDashMap, vendored};
|
||||
|
||||
@@ -139,6 +139,7 @@ impl Files {
|
||||
};
|
||||
|
||||
tracing::trace!("Adding vendored file `{}`", path);
|
||||
|
||||
let file = File::builder(FilePath::Vendored(path.to_path_buf()))
|
||||
.permissions(Some(0o444))
|
||||
.revision(metadata.revision())
|
||||
@@ -200,7 +201,15 @@ impl Files {
|
||||
let mut roots = self.inner.roots.write().unwrap();
|
||||
|
||||
let absolute = SystemPath::absolute(path, db.system().current_directory());
|
||||
roots.try_add(db, absolute, kind)
|
||||
|
||||
let (Ok(root) | Err(root)) = roots.try_add(db, absolute, |absolute| {
|
||||
FileRoot::builder(absolute, kind, FileRevision::now())
|
||||
.durability(Durability::HIGH)
|
||||
.revision_durability(kind.durability())
|
||||
.new(db)
|
||||
});
|
||||
|
||||
root
|
||||
}
|
||||
|
||||
/// Updates the revision of the root for `path`.
|
||||
@@ -259,6 +268,51 @@ impl Files {
|
||||
root.set_revision(db).to(FileRevision::now());
|
||||
}
|
||||
}
|
||||
|
||||
/// Seed the files with an existing [`File`] instance.
|
||||
pub fn seed(&self, file: File, db: &dyn Db) {
|
||||
let seeded = match file.path(db) {
|
||||
FilePath::System(path) => self
|
||||
.inner
|
||||
.system_by_path
|
||||
.insert(path.clone(), file)
|
||||
.is_none(),
|
||||
FilePath::SystemVirtual(path) => self
|
||||
.inner
|
||||
.system_virtual_by_path
|
||||
.insert(path.clone(), VirtualFile(file))
|
||||
.is_none(),
|
||||
FilePath::Vendored(path) => self
|
||||
.inner
|
||||
.vendored_by_path
|
||||
.insert(path.clone(), file)
|
||||
.is_none(),
|
||||
};
|
||||
|
||||
// Recreating a `File` input means the persisted queries depending on that file
|
||||
// will be invalidated.
|
||||
assert!(
|
||||
seeded,
|
||||
"unexpected `File` input recreated for path `{}`",
|
||||
file.path(db)
|
||||
);
|
||||
}
|
||||
|
||||
/// Seed the files with an existing [`FileRoot`] instance.
|
||||
pub fn seed_root(&self, root: FileRoot, db: &dyn Db) {
|
||||
let mut roots = self.inner.roots.write().unwrap();
|
||||
let seeded = roots
|
||||
.try_add(db, root.path(db).to_path_buf(), |_| root)
|
||||
.is_ok();
|
||||
|
||||
// Recreating a `FileRoot` input means the persisted queries depending on that file
|
||||
// root will be invalidated.
|
||||
assert!(
|
||||
seeded,
|
||||
"unexpected `FileRoot` input recreated for path `{}`",
|
||||
root.path(db)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Files {
|
||||
@@ -290,7 +344,7 @@ impl std::panic::RefUnwindSafe for Files {}
|
||||
/// # Ordering
|
||||
/// Ordering is based on the file's salsa-assigned id and not on its values.
|
||||
/// The id may change between runs.
|
||||
#[salsa::input(heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::input(persist, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct File {
|
||||
/// The path of the file (immutable).
|
||||
@@ -414,6 +468,15 @@ impl File {
|
||||
}
|
||||
}
|
||||
|
||||
/// Loads all existing [`File`]s in the database.
|
||||
pub fn load_all(db: &dyn Db) -> Vec<File> {
|
||||
// TODO: Prune deleted paths.
|
||||
File::ingredient(db)
|
||||
.entries(db.zalsa())
|
||||
.map(|entry| entry.as_struct())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Private method providing the implementation for [`Self::sync_path`] and [`Self::sync`] for
|
||||
/// system paths.
|
||||
fn sync_system_path(db: &mut dyn Db, path: &SystemPath, file: Option<File>) {
|
||||
@@ -522,7 +585,17 @@ impl VirtualFile {
|
||||
// The types in here need to be public because they're salsa ingredients but we
|
||||
// don't want them to be publicly accessible. That's why we put them into a private module.
|
||||
mod private {
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Copy,
|
||||
Clone,
|
||||
Debug,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Default,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub enum FileStatus {
|
||||
/// The file exists.
|
||||
#[default]
|
||||
@@ -536,6 +609,16 @@ mod private {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FileType> for FileStatus {
|
||||
fn from(value: FileType) -> Self {
|
||||
match value {
|
||||
FileType::File => FileStatus::Exists,
|
||||
FileType::Symlink => FileStatus::Exists,
|
||||
FileType::Directory => FileStatus::IsADirectory,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum FileError {
|
||||
IsADirectory,
|
||||
|
||||
@@ -16,7 +16,7 @@ use crate::system::{SystemPath, SystemPathBuf};
|
||||
/// 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(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::input(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
pub struct FileRoot {
|
||||
/// The path of a root is guaranteed to never change.
|
||||
#[returns(deref)]
|
||||
@@ -35,9 +35,20 @@ impl FileRoot {
|
||||
pub fn durability(self, db: &dyn Db) -> salsa::Durability {
|
||||
self.kind_at_time_of_creation(db).durability()
|
||||
}
|
||||
|
||||
/// Loads all existing [`FileRoot`]s in the database.
|
||||
pub fn load_all(db: &dyn Db) -> Vec<FileRoot> {
|
||||
// TODO: Prune deleted paths.
|
||||
FileRoot::ingredient(db)
|
||||
.entries(db.zalsa())
|
||||
.map(|entry| entry.as_struct())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Eq, PartialEq, get_size2::GetSize, serde::Serialize, serde::Deserialize,
|
||||
)]
|
||||
pub enum FileRootKind {
|
||||
/// The root of a project.
|
||||
Project,
|
||||
@@ -47,7 +58,7 @@ pub enum FileRootKind {
|
||||
}
|
||||
|
||||
impl FileRootKind {
|
||||
const fn durability(self) -> Durability {
|
||||
pub const fn durability(self) -> Durability {
|
||||
match self {
|
||||
FileRootKind::Project => Durability::LOW,
|
||||
FileRootKind::LibrarySearchPath => Durability::HIGH,
|
||||
@@ -62,34 +73,34 @@ pub(super) struct FileRoots {
|
||||
}
|
||||
|
||||
impl FileRoots {
|
||||
/// Tries to add a new root for `path` and returns the root.
|
||||
/// Tries to add a new root for `path`.
|
||||
///
|
||||
/// The root isn't added nor is the file root's kind updated if a root for `path` already exists.
|
||||
///
|
||||
/// Returns `Ok(root)` if the `FileRoot` was successfully added, and returns `Err(root)` with
|
||||
/// the previous root if one already existed at that path.
|
||||
pub(super) fn try_add(
|
||||
&mut self,
|
||||
db: &dyn Db,
|
||||
path: SystemPathBuf,
|
||||
kind: FileRootKind,
|
||||
) -> FileRoot {
|
||||
create_root: impl FnOnce(SystemPathBuf) -> FileRoot,
|
||||
) -> Result<FileRoot, 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;
|
||||
return Err(*existing.value);
|
||||
}
|
||||
}
|
||||
|
||||
// normalize the path to use `/` separators and escape the '{' and '}' characters,
|
||||
// which matchit uses for routing parameters
|
||||
// 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::builder(path, kind, FileRevision::now())
|
||||
.durability(Durability::HIGH)
|
||||
.revision_durability(kind.durability())
|
||||
.new(db);
|
||||
let root = create_root(path);
|
||||
|
||||
// Insert a path that matches the root itself
|
||||
self.by_path.insert(route.clone(), root).unwrap();
|
||||
@@ -100,7 +111,7 @@ impl FileRoots {
|
||||
self.by_path.insert(route, root).unwrap();
|
||||
self.roots.push(root);
|
||||
|
||||
root
|
||||
Ok(root)
|
||||
}
|
||||
|
||||
/// Returns the closest root for `path` or `None` if no root contains `path`.
|
||||
|
||||
@@ -11,7 +11,9 @@ use std::fmt::{Display, Formatter};
|
||||
/// * a file stored on the [host system](crate::system::System).
|
||||
/// * a virtual file stored on the [host system](crate::system::System).
|
||||
/// * a vendored file stored in the [vendored file system](crate::vendored::VendoredFileSystem).
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Clone, Debug, Eq, PartialEq, Hash, get_size2::GetSize, serde::Serialize, serde::Deserialize,
|
||||
)]
|
||||
pub enum FilePath {
|
||||
/// Path to a file on the [host system](crate::system::System).
|
||||
System(SystemPathBuf),
|
||||
|
||||
@@ -148,7 +148,16 @@ impl From<Notebook> for SourceTextKind {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error, PartialEq, Eq, Clone, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Debug,
|
||||
thiserror::Error,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Clone,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub enum SourceTextError {
|
||||
#[error("Failed to read notebook: {0}`")]
|
||||
FailedToReadNotebook(String),
|
||||
|
||||
@@ -66,6 +66,9 @@ pub trait System: Debug + Sync + Send {
|
||||
/// See [dunce::canonicalize] for more information.
|
||||
fn canonicalize_path(&self, path: &SystemPath) -> Result<SystemPathBuf>;
|
||||
|
||||
/// Reads the content of the file at `path` into a bytes buffer.
|
||||
fn read_to_end(&self, path: &SystemPath) -> Result<Vec<u8>>;
|
||||
|
||||
/// Reads the content of the file at `path` into a [`String`].
|
||||
fn read_to_string(&self, path: &SystemPath) -> Result<String>;
|
||||
|
||||
@@ -242,7 +245,7 @@ pub trait WritableSystem: System {
|
||||
fn create_new_file(&self, path: &SystemPath) -> Result<()>;
|
||||
|
||||
/// Writes the given content to the file at the given path.
|
||||
fn write_file(&self, path: &SystemPath, content: &str) -> Result<()>;
|
||||
fn write_file(&self, path: &SystemPath, content: &[u8]) -> Result<()>;
|
||||
|
||||
/// Creates a directory at `path` as well as any intermediate directories.
|
||||
fn create_directory_all(&self, path: &SystemPath) -> Result<()>;
|
||||
@@ -278,7 +281,7 @@ pub trait WritableSystem: System {
|
||||
// ensures that only one thread/process ever attempts to write to it to avoid corrupting
|
||||
// the cache.
|
||||
self.create_new_file(&cache_path)?;
|
||||
self.write_file(&cache_path, &contents)?;
|
||||
self.write_file(&cache_path, contents.as_bytes())?;
|
||||
|
||||
Ok(Some(cache_path))
|
||||
}
|
||||
|
||||
@@ -114,8 +114,8 @@ impl MemoryFileSystem {
|
||||
matches!(by_path.get(&normalized), Some(Entry::Directory(_)))
|
||||
}
|
||||
|
||||
pub fn read_to_string(&self, path: impl AsRef<SystemPath>) -> Result<String> {
|
||||
fn read_to_string(fs: &MemoryFileSystem, path: &SystemPath) -> Result<String> {
|
||||
pub fn read_to_end(&self, path: impl AsRef<SystemPath>) -> Result<Vec<u8>> {
|
||||
fn read_to_end(fs: &MemoryFileSystem, path: &SystemPath) -> Result<Vec<u8>> {
|
||||
let by_path = fs.inner.by_path.read().unwrap();
|
||||
let normalized = fs.normalize_path(path);
|
||||
|
||||
@@ -127,13 +127,18 @@ impl MemoryFileSystem {
|
||||
}
|
||||
}
|
||||
|
||||
read_to_string(self, path.as_ref())
|
||||
read_to_end(self, path.as_ref())
|
||||
}
|
||||
|
||||
pub(crate) fn read_virtual_path_to_string(
|
||||
pub fn read_to_string(&self, path: impl AsRef<SystemPath>) -> Result<String> {
|
||||
self.read_to_end(path)
|
||||
.and_then(|bytes| String::from_utf8(bytes).map_err(io::Error::other))
|
||||
}
|
||||
|
||||
pub(crate) fn read_virtual_path_to_end(
|
||||
&self,
|
||||
path: impl AsRef<SystemVirtualPath>,
|
||||
) -> Result<String> {
|
||||
) -> Result<Vec<u8>> {
|
||||
let virtual_files = self.inner.virtual_files.read().unwrap();
|
||||
let file = virtual_files
|
||||
.get(&path.as_ref().to_path_buf())
|
||||
@@ -142,6 +147,14 @@ impl MemoryFileSystem {
|
||||
Ok(file.content.clone())
|
||||
}
|
||||
|
||||
pub(crate) fn read_virtual_path_to_string(
|
||||
&self,
|
||||
path: impl AsRef<SystemVirtualPath>,
|
||||
) -> Result<String> {
|
||||
self.read_virtual_path_to_end(path)
|
||||
.and_then(|bytes| String::from_utf8(bytes).map_err(io::Error::other))
|
||||
}
|
||||
|
||||
pub fn exists(&self, path: &SystemPath) -> bool {
|
||||
let by_path = self.inner.by_path.read().unwrap();
|
||||
let normalized = self.normalize_path(path);
|
||||
@@ -161,7 +174,7 @@ impl MemoryFileSystem {
|
||||
match by_path.entry(normalized) {
|
||||
btree_map::Entry::Vacant(entry) => {
|
||||
entry.insert(Entry::File(File {
|
||||
content: String::new(),
|
||||
content: Vec::new(),
|
||||
last_modified: file_time_now(),
|
||||
}));
|
||||
|
||||
@@ -177,13 +190,17 @@ impl MemoryFileSystem {
|
||||
/// Stores a new file in the file system.
|
||||
///
|
||||
/// The operation overrides the content for an existing file with the same normalized `path`.
|
||||
pub fn write_file(&self, path: impl AsRef<SystemPath>, content: impl ToString) -> Result<()> {
|
||||
pub fn write_file(
|
||||
&self,
|
||||
path: impl AsRef<SystemPath>,
|
||||
content: impl Into<Vec<u8>>,
|
||||
) -> Result<()> {
|
||||
let mut by_path = self.inner.by_path.write().unwrap();
|
||||
|
||||
let normalized = self.normalize_path(path.as_ref());
|
||||
|
||||
let file = get_or_create_file(&mut by_path, &normalized)?;
|
||||
file.content = content.to_string();
|
||||
file.content = content.into();
|
||||
file.last_modified = file_time_now();
|
||||
|
||||
Ok(())
|
||||
@@ -214,7 +231,7 @@ impl MemoryFileSystem {
|
||||
pub fn write_file_all(
|
||||
&self,
|
||||
path: impl AsRef<SystemPath>,
|
||||
content: impl ToString,
|
||||
content: impl Into<Vec<u8>>,
|
||||
) -> Result<()> {
|
||||
let path = path.as_ref();
|
||||
|
||||
@@ -228,19 +245,23 @@ impl MemoryFileSystem {
|
||||
/// Stores a new virtual file in the file system.
|
||||
///
|
||||
/// The operation overrides the content for an existing virtual file with the same `path`.
|
||||
pub fn write_virtual_file(&self, path: impl AsRef<SystemVirtualPath>, content: impl ToString) {
|
||||
pub fn write_virtual_file(
|
||||
&self,
|
||||
path: impl AsRef<SystemVirtualPath>,
|
||||
content: impl Into<Vec<u8>>,
|
||||
) {
|
||||
let path = path.as_ref();
|
||||
let mut virtual_files = self.inner.virtual_files.write().unwrap();
|
||||
|
||||
match virtual_files.entry(path.to_path_buf()) {
|
||||
std::collections::hash_map::Entry::Vacant(entry) => {
|
||||
entry.insert(File {
|
||||
content: content.to_string(),
|
||||
content: content.into(),
|
||||
last_modified: file_time_now(),
|
||||
});
|
||||
}
|
||||
std::collections::hash_map::Entry::Occupied(mut entry) => {
|
||||
entry.get_mut().content = content.to_string();
|
||||
entry.get_mut().content = content.into();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -468,7 +489,7 @@ impl Entry {
|
||||
|
||||
#[derive(Debug)]
|
||||
struct File {
|
||||
content: String,
|
||||
content: Vec<u8>,
|
||||
last_modified: FileTime,
|
||||
}
|
||||
|
||||
@@ -533,7 +554,7 @@ fn get_or_create_file<'a>(
|
||||
|
||||
let entry = paths.entry(normalized.to_path_buf()).or_insert_with(|| {
|
||||
Entry::File(File {
|
||||
content: String::new(),
|
||||
content: Vec::new(),
|
||||
last_modified: file_time_now(),
|
||||
})
|
||||
});
|
||||
|
||||
@@ -93,6 +93,10 @@ impl System for OsSystem {
|
||||
})
|
||||
}
|
||||
|
||||
fn read_to_end(&self, path: &SystemPath) -> Result<Vec<u8>> {
|
||||
std::fs::read(path.as_std_path())
|
||||
}
|
||||
|
||||
fn read_to_string(&self, path: &SystemPath) -> Result<String> {
|
||||
std::fs::read_to_string(path.as_std_path())
|
||||
}
|
||||
@@ -357,7 +361,7 @@ impl WritableSystem for OsSystem {
|
||||
std::fs::File::create_new(path).map(drop)
|
||||
}
|
||||
|
||||
fn write_file(&self, path: &SystemPath, content: &str) -> Result<()> {
|
||||
fn write_file(&self, path: &SystemPath, content: &[u8]) -> Result<()> {
|
||||
std::fs::write(path.as_std_path(), content)
|
||||
}
|
||||
|
||||
|
||||
@@ -762,7 +762,17 @@ impl SystemVirtualPath {
|
||||
}
|
||||
|
||||
/// An owned, virtual path on [`System`](`super::System`) (akin to [`String`]).
|
||||
#[derive(Eq, PartialEq, Clone, Hash, PartialOrd, Ord, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Eq,
|
||||
PartialEq,
|
||||
Clone,
|
||||
Hash,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub struct SystemVirtualPathBuf(String);
|
||||
|
||||
impl SystemVirtualPathBuf {
|
||||
|
||||
@@ -75,6 +75,10 @@ impl System for TestSystem {
|
||||
self.system().canonicalize_path(path)
|
||||
}
|
||||
|
||||
fn read_to_end(&self, path: &SystemPath) -> Result<Vec<u8>> {
|
||||
self.system().read_to_end(path)
|
||||
}
|
||||
|
||||
fn read_to_string(&self, path: &SystemPath) -> Result<String> {
|
||||
self.system().read_to_string(path)
|
||||
}
|
||||
@@ -165,7 +169,7 @@ impl WritableSystem for TestSystem {
|
||||
self.system().create_new_file(path)
|
||||
}
|
||||
|
||||
fn write_file(&self, path: &SystemPath, content: &str) -> Result<()> {
|
||||
fn write_file(&self, path: &SystemPath, content: &[u8]) -> Result<()> {
|
||||
self.system().write_file(path, content)
|
||||
}
|
||||
|
||||
@@ -185,7 +189,9 @@ pub trait DbWithWritableSystem: Db + Sized {
|
||||
/// Writes the content of the given file and notifies the Db about the change.
|
||||
fn write_file(&mut self, path: impl AsRef<SystemPath>, content: impl AsRef<str>) -> Result<()> {
|
||||
let path = path.as_ref();
|
||||
match self.writable_system().write_file(path, content.as_ref()) {
|
||||
let content = content.as_ref();
|
||||
|
||||
match self.writable_system().write_file(path, content.as_bytes()) {
|
||||
Ok(()) => {
|
||||
File::sync_path(self, path);
|
||||
Ok(())
|
||||
@@ -198,7 +204,8 @@ pub trait DbWithWritableSystem: Db + Sized {
|
||||
File::sync_path(self, ancestor);
|
||||
}
|
||||
|
||||
self.writable_system().write_file(path, content.as_ref())?;
|
||||
self.writable_system()
|
||||
.write_file(path, content.as_bytes())?;
|
||||
File::sync_path(self, path);
|
||||
|
||||
Ok(())
|
||||
@@ -243,8 +250,14 @@ pub trait DbWithTestSystem: Db + Sized {
|
||||
///
|
||||
/// ## Panics
|
||||
/// If the db isn't using the [`InMemorySystem`].
|
||||
fn write_virtual_file(&mut self, path: impl AsRef<SystemVirtualPath>, content: impl ToString) {
|
||||
fn write_virtual_file(
|
||||
&mut self,
|
||||
path: impl AsRef<SystemVirtualPath>,
|
||||
content: impl Into<Vec<u8>>,
|
||||
) {
|
||||
let path = path.as_ref();
|
||||
let content = content.into();
|
||||
|
||||
self.test_system()
|
||||
.memory_file_system()
|
||||
.write_virtual_file(path, content);
|
||||
@@ -322,6 +335,10 @@ impl System for InMemorySystem {
|
||||
self.memory_fs.canonicalize(path)
|
||||
}
|
||||
|
||||
fn read_to_end(&self, path: &SystemPath) -> Result<Vec<u8>> {
|
||||
self.memory_fs.read_to_end(path)
|
||||
}
|
||||
|
||||
fn read_to_string(&self, path: &SystemPath) -> Result<String> {
|
||||
self.memory_fs.read_to_string(path)
|
||||
}
|
||||
@@ -412,7 +429,7 @@ impl WritableSystem for InMemorySystem {
|
||||
self.memory_fs.create_new_file(path)
|
||||
}
|
||||
|
||||
fn write_file(&self, path: &SystemPath, content: &str) -> Result<()> {
|
||||
fn write_file(&self, path: &SystemPath, content: &[u8]) -> Result<()> {
|
||||
self.memory_fs.write_file(path, content)
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ impl ToOwned for VendoredPath {
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub struct VendoredPathBuf(Utf8PathBuf);
|
||||
|
||||
impl get_size2::GetSize for VendoredPathBuf {
|
||||
|
||||
@@ -19,6 +19,7 @@ where
|
||||
/// A unique index for a node within an AST.
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
|
||||
#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct NodeIndex(NonZeroU32);
|
||||
|
||||
impl NodeIndex {
|
||||
|
||||
@@ -23,8 +23,8 @@ use crossbeam::channel as crossbeam_channel;
|
||||
use rayon::ThreadPoolBuilder;
|
||||
use ruff_db::diagnostic::{Diagnostic, DisplayDiagnosticConfig, Severity};
|
||||
use ruff_db::files::File;
|
||||
use ruff_db::max_parallelism;
|
||||
use ruff_db::system::{OsSystem, SystemPath, SystemPathBuf};
|
||||
use ruff_db::{Db as _, max_parallelism};
|
||||
use salsa::Database;
|
||||
use ty_project::metadata::options::ProjectOptionsOverrides;
|
||||
use ty_project::watch::ProjectWatcher;
|
||||
@@ -171,6 +171,13 @@ fn run_check(args: CheckCommand) -> anyhow::Result<ExitStatus> {
|
||||
Err(_) => {}
|
||||
}
|
||||
|
||||
// Write the database to the persistent cache.
|
||||
if let Ok(path) = db.system().env_var(EnvVars::TY_PERSIST) {
|
||||
if let Err(err) = db.persist(SystemPath::new(&path)) {
|
||||
tracing::warn!("failed to write to persistent cache: {err:?}");
|
||||
}
|
||||
}
|
||||
|
||||
std::mem::forget(db);
|
||||
|
||||
if exit_zero {
|
||||
|
||||
@@ -21,10 +21,12 @@ ruff_python_ast = { workspace = true, features = ["serde"] }
|
||||
ruff_python_formatter = { workspace = true, optional = true }
|
||||
ruff_text_size = { workspace = true }
|
||||
ty_combine = { workspace = true }
|
||||
ty_python_semantic = { workspace = true, features = ["serde"] }
|
||||
ty_python_semantic = { workspace = true }
|
||||
ty_static = { workspace = true }
|
||||
ty_vendored = { workspace = true }
|
||||
|
||||
anyhow = { workspace = true }
|
||||
bincode = { workspace = true }
|
||||
camino = { workspace = true }
|
||||
colored = { workspace = true }
|
||||
crossbeam = { workspace = true }
|
||||
@@ -32,7 +34,7 @@ get-size2 = { workspace = true }
|
||||
globset = { workspace = true }
|
||||
notify = { workspace = true }
|
||||
pep440_rs = { workspace = true, features = ["version-ranges"] }
|
||||
ordermap = { workspace = true, features = ["serde"] }
|
||||
ordermap = { workspace = true }
|
||||
rayon = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
regex-automata = { workspace = true }
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
use std::fmt::Formatter;
|
||||
use std::panic::RefUnwindSafe;
|
||||
use std::sync::Arc;
|
||||
use std::{cmp, fmt};
|
||||
use std::{cmp, fmt, io};
|
||||
|
||||
pub use self::changes::ChangeResult;
|
||||
use crate::metadata::settings::file_settings;
|
||||
use crate::metadata::value::{ValueSource, ValueSourceGuard};
|
||||
use crate::{CollectReporter, DEFAULT_LINT_REGISTRY};
|
||||
use crate::{ProgressReporter, Project, ProjectMetadata};
|
||||
use ruff_db::Db as SourceDb;
|
||||
use ruff_db::diagnostic::Diagnostic;
|
||||
use ruff_db::files::{File, Files};
|
||||
use ruff_db::system::System;
|
||||
use ruff_db::diagnostic::{Diagnostic, LintRegistryGuard};
|
||||
use ruff_db::files::{File, FileRoot, Files};
|
||||
use ruff_db::system::{System, SystemPath};
|
||||
use ruff_db::vendored::VendoredFileSystem;
|
||||
use salsa::{Database, Event, Setter};
|
||||
use ty_python_semantic::lint::{LintRegistry, RuleSelection};
|
||||
use ty_python_semantic::{Db as SemanticDb, Program};
|
||||
use ty_static::EnvVars;
|
||||
|
||||
mod changes;
|
||||
|
||||
@@ -66,18 +68,27 @@ impl ProjectDatabase {
|
||||
system: Arc::new(system),
|
||||
};
|
||||
|
||||
// Load the database from the persistent cache.
|
||||
//
|
||||
// TODO: Use the `program_settings` to compute the key for the database's persistent
|
||||
// cache and load the cache if it exists.
|
||||
// we may want to have a dedicated method for this?
|
||||
// cache, to support multiple persistent caches for different configurations.
|
||||
if let Ok(path) = db.system().env_var(EnvVars::TY_PERSIST) {
|
||||
if let Err(err) = db.deserialize(SystemPath::new(&path), &project_metadata) {
|
||||
tracing::warn!("failed to read from persistent cache: {err:?}");
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the `Program` singleton
|
||||
// Initialize the `Program` singleton.
|
||||
let program_settings = project_metadata.to_program_settings(db.system(), db.vendored())?;
|
||||
Program::from_settings(&db, program_settings);
|
||||
Program::init_or_update(&mut db, program_settings);
|
||||
|
||||
db.project = Some(
|
||||
Project::from_metadata(&db, project_metadata)
|
||||
.map_err(|error| anyhow::anyhow!("{}", error.pretty(&db)))?,
|
||||
);
|
||||
// Initialize the `Project`, unless it was persisted.
|
||||
if db.project.is_none() {
|
||||
db.project = Some(
|
||||
Project::from_metadata(&db, project_metadata)
|
||||
.map_err(|error| anyhow::anyhow!("{}", error.pretty(&db)))?,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(db)
|
||||
}
|
||||
@@ -126,6 +137,77 @@ impl ProjectDatabase {
|
||||
)
|
||||
}
|
||||
|
||||
/// Deserialize the database from the persistent cache.
|
||||
fn deserialize(&mut self, path: &SystemPath, metadata: &ProjectMetadata) -> anyhow::Result<()> {
|
||||
// Read from the persistent cache.
|
||||
let contents = match self.system.read_to_end(path) {
|
||||
Ok(contents) => contents,
|
||||
Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(()),
|
||||
Err(err) => return Err(err.into()),
|
||||
};
|
||||
|
||||
let _lint_registry = LintRegistryGuard::new(|name| {
|
||||
ty_python_semantic::default_lint_registry()
|
||||
.get(name)
|
||||
.map(|lint| lint.name())
|
||||
.ok()
|
||||
});
|
||||
|
||||
// We have to a `ValueSource` to deserialize the `ProjectMetadata`, but we end up using
|
||||
// the new `metadata` value to resolve the settings, so this value doesn't really matter.
|
||||
let _value_source = ValueSourceGuard::new(ValueSource::Cli, false);
|
||||
|
||||
// Deserialize the database.
|
||||
let mut decoder = bincode::serde::BorrowedSerdeDecoder::from_slice(
|
||||
&contents,
|
||||
bincode::config::standard(),
|
||||
(),
|
||||
);
|
||||
|
||||
<dyn salsa::Database>::deserialize(self, decoder.as_deserializer())?;
|
||||
|
||||
// Sync any deserialized inputs.
|
||||
//
|
||||
// TODO: Consider parallelizing the work here similar to `ProjectFilesFilter::collect_vec`,
|
||||
// as the stat(2) calls can be expensive on large projects.
|
||||
for file in File::load_all(self) {
|
||||
file.sync(self);
|
||||
self.files.seed(file, self);
|
||||
}
|
||||
|
||||
for root in FileRoot::load_all(self) {
|
||||
self.files.seed_root(root, self);
|
||||
}
|
||||
|
||||
for project in Project::load_all(self) {
|
||||
if project.resolve_deserialized_settings(metadata, self)? {
|
||||
self.project = Some(project);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Persist the current state of the database to disk.
|
||||
pub fn persist(&mut self, path: &SystemPath) -> anyhow::Result<()> {
|
||||
if self.system.as_writable().is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let contents = bincode::serde::encode_to_vec(
|
||||
<dyn salsa::Database>::as_serialize(self),
|
||||
bincode::config::standard(),
|
||||
)?;
|
||||
|
||||
self.system
|
||||
.as_writable()
|
||||
.unwrap()
|
||||
.write_file(path, &contents)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns a [`SalsaMemoryDump`] that can be use to dump Salsa memory usage information
|
||||
/// to the CLI after a typechecker run.
|
||||
pub fn salsa_memory_dump(&self) -> SalsaMemoryDump {
|
||||
@@ -204,8 +286,17 @@ impl std::fmt::Debug for ProjectDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, get_size2::GetSize)]
|
||||
#[cfg_attr(test, derive(serde::Serialize))]
|
||||
#[derive(
|
||||
Default,
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub enum CheckMode {
|
||||
/// Checks the open files in the project.
|
||||
OpenFiles,
|
||||
|
||||
@@ -287,8 +287,7 @@ impl ProjectDatabase {
|
||||
);
|
||||
|
||||
project
|
||||
.set_settings_diagnostics(self)
|
||||
.to(vec![error.into_diagnostic()]);
|
||||
.set_settings_diagnostics(self, vec![error.into_diagnostic()]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ impl Default for IndexedFiles {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, get_size2::GetSize)]
|
||||
#[derive(Debug, get_size2::GetSize, serde::Serialize, serde::Deserialize)]
|
||||
enum State {
|
||||
/// The files of a package haven't been indexed yet.
|
||||
Lazy,
|
||||
@@ -150,7 +150,7 @@ pub struct Indexed<'db> {
|
||||
_lifetime: PhantomData<&'db ()>,
|
||||
}
|
||||
|
||||
#[derive(Debug, get_size2::GetSize)]
|
||||
#[derive(Debug, get_size2::GetSize, serde::Serialize, serde::Deserialize)]
|
||||
struct IndexedInner {
|
||||
files: FxHashSet<File>,
|
||||
diagnostics: Vec<IOErrorDiagnostic>,
|
||||
|
||||
@@ -21,11 +21,12 @@ use ruff_db::system::{SystemPath, SystemPathBuf};
|
||||
use rustc_hash::FxHashSet;
|
||||
use salsa::Durability;
|
||||
use salsa::Setter;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::backtrace::BacktraceStatus;
|
||||
use std::collections::hash_set;
|
||||
use std::iter::FusedIterator;
|
||||
use std::panic::{AssertUnwindSafe, UnwindSafe};
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, OnceLock};
|
||||
use thiserror::Error;
|
||||
use tracing::error;
|
||||
use ty_python_semantic::lint::{LintRegistry, LintRegistryBuilder, RuleSelection};
|
||||
@@ -59,7 +60,7 @@ pub fn default_lints_registry() -> LintRegistry {
|
||||
/// 2. Running `ruff check` with different target versions results in different programs (settings) but
|
||||
/// it remains the same project. That's why program is a narrowed view of the project only
|
||||
/// holding on to the most fundamental settings required for checking.
|
||||
#[salsa::input(heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::input(persist(serialize = Project::serialize, deserialize = Project::deserialize), heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(Debug)]
|
||||
pub struct Project {
|
||||
/// The files that are open in the project, [`None`] if there are no open files.
|
||||
@@ -85,8 +86,13 @@ pub struct Project {
|
||||
/// We box the metadata here because it's a fairly large type and
|
||||
/// reducing the size of `Project` helps reduce the size of the
|
||||
/// salsa allocated table for `Project`.
|
||||
#[returns(deref)]
|
||||
pub settings: Box<Settings>,
|
||||
///
|
||||
/// This field is uninitialized temporarily after deserialization,
|
||||
/// until `Project::resolve_settings` is called. We use a `OnceLock`
|
||||
/// to initialize it without bumping the Salsa revision, as we know
|
||||
/// the settings will remain the same if the metadata has not changed.
|
||||
#[returns(ref)]
|
||||
raw_settings: OnceLock<Box<Settings>>,
|
||||
|
||||
/// The paths that should be included when checking this project.
|
||||
///
|
||||
@@ -113,8 +119,14 @@ pub struct Project {
|
||||
included_paths_list: Vec<SystemPathBuf>,
|
||||
|
||||
/// Diagnostics that were generated when resolving the project settings.
|
||||
#[returns(deref)]
|
||||
settings_diagnostics: Vec<OptionDiagnostic>,
|
||||
///
|
||||
/// This field is uninitialized temporarily after deserialization,
|
||||
/// until `Project::resolve_settings` is called. We use a `OnceLock`
|
||||
/// to initialize it without bumping the Salsa revision, as we know
|
||||
/// the settings diagnostics will remain the same if the metadata has
|
||||
/// not changed.
|
||||
#[returns(ref)]
|
||||
raw_settings_diagnostics: OnceLock<Vec<OptionDiagnostic>>,
|
||||
|
||||
/// The mode in which the project should be checked.
|
||||
///
|
||||
@@ -124,6 +136,39 @@ pub struct Project {
|
||||
check_mode: CheckMode,
|
||||
}
|
||||
|
||||
type ProjectFields = (
|
||||
FxHashSet<File>,
|
||||
IndexedFiles,
|
||||
Box<ProjectMetadata>,
|
||||
OnceLock<Box<Settings>>,
|
||||
Vec<SystemPathBuf>,
|
||||
OnceLock<Vec<OptionDiagnostic>>,
|
||||
CheckMode,
|
||||
);
|
||||
|
||||
/// The serialized representation of a `Project`.
|
||||
///
|
||||
/// Notably, this type does not contain the `settings` or `settings_diagnostics` fields, as the
|
||||
/// resolved settings contain types that are not easily serializable.
|
||||
///
|
||||
/// It also doesn't include the `IndexedFiles`, as those always need to be reloaded anyways.
|
||||
#[derive(serde::Deserialize)]
|
||||
struct ProjectWire {
|
||||
open_fileset: FxHashSet<File>,
|
||||
metadata: Box<ProjectMetadata>,
|
||||
included_paths_list: Vec<SystemPathBuf>,
|
||||
check_mode: CheckMode,
|
||||
}
|
||||
|
||||
/// A reference to the serialized representation of a `Project`.
|
||||
#[derive(serde::Serialize)]
|
||||
struct ProjectWireRef<'a> {
|
||||
open_fileset: &'a FxHashSet<File>,
|
||||
metadata: &'a ProjectMetadata,
|
||||
included_paths_list: &'a Vec<SystemPathBuf>,
|
||||
check_mode: &'a CheckMode,
|
||||
}
|
||||
|
||||
/// A progress reporter.
|
||||
pub trait ProgressReporter: Send + Sync {
|
||||
/// Initialize the reporter with the number of files.
|
||||
@@ -183,11 +228,15 @@ impl Project {
|
||||
db.files()
|
||||
.try_add_root(db, metadata.root(), FileRootKind::Project);
|
||||
|
||||
let project = Project::builder(Box::new(metadata), Box::new(settings), diagnostics)
|
||||
.durability(Durability::MEDIUM)
|
||||
.open_fileset_durability(Durability::LOW)
|
||||
.file_set_durability(Durability::LOW)
|
||||
.new(db);
|
||||
let project = Project::builder(
|
||||
Box::new(metadata),
|
||||
Box::new(settings).into(),
|
||||
diagnostics.into(),
|
||||
)
|
||||
.durability(Durability::MEDIUM)
|
||||
.open_fileset_durability(Durability::LOW)
|
||||
.file_set_durability(Durability::LOW)
|
||||
.new(db);
|
||||
|
||||
Ok(project)
|
||||
}
|
||||
@@ -232,20 +281,21 @@ impl Project {
|
||||
tracing::debug!("Reloading project");
|
||||
assert_eq!(self.root(db), metadata.root());
|
||||
|
||||
if &metadata != self.metadata(db) {
|
||||
if metadata != *self.metadata(db) {
|
||||
match metadata.options().to_settings(db, metadata.root()) {
|
||||
Ok((settings, settings_diagnostics)) => {
|
||||
if self.settings(db) != &settings {
|
||||
self.set_settings(db).to(Box::new(settings));
|
||||
if *self.settings(db) != settings {
|
||||
self.set_raw_settings(db).to(Box::new(settings).into());
|
||||
}
|
||||
|
||||
if self.settings_diagnostics(db) != settings_diagnostics {
|
||||
self.set_settings_diagnostics(db).to(settings_diagnostics);
|
||||
self.set_raw_settings_diagnostics(db)
|
||||
.to(settings_diagnostics.into());
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
self.set_settings_diagnostics(db)
|
||||
.to(vec![error.into_diagnostic()]);
|
||||
self.set_raw_settings_diagnostics(db)
|
||||
.to(vec![error.into_diagnostic()].into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,8 +423,10 @@ impl Project {
|
||||
pub fn set_included_paths(self, db: &mut dyn Db, paths: Vec<SystemPathBuf>) {
|
||||
tracing::debug!("Setting included paths: {paths}", paths = paths.len());
|
||||
|
||||
self.set_included_paths_list(db).to(paths);
|
||||
self.reload_files(db);
|
||||
if self.included_paths_list(db) != paths {
|
||||
self.set_included_paths_list(db).to(paths);
|
||||
self.reload_files(db);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the paths that should be checked.
|
||||
@@ -526,6 +578,124 @@ impl Project {
|
||||
.map(OptionDiagnostic::to_diagnostic)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Returns the resolved project settings.
|
||||
pub fn settings<'db>(&self, db: &'db dyn Db) -> &'db Settings {
|
||||
match self.raw_settings(db).get() {
|
||||
Some(settings) => settings,
|
||||
None => panic!("`Project::resolve_settings` must be called after deserialization"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns any diagnostics that were generated when resolving the project settings.
|
||||
pub fn settings_diagnostics<'db>(&self, db: &'db dyn Db) -> &'db [OptionDiagnostic] {
|
||||
match self.raw_settings_diagnostics(db).get() {
|
||||
Some(diagnostics) => diagnostics.as_slice(),
|
||||
None => panic!("`Project::resolve_settings` must be called after deserialization"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set diagnostics that were generated when resolving the project settings.
|
||||
pub fn set_settings_diagnostics(self, db: &mut dyn Db, paths: Vec<OptionDiagnostic>) {
|
||||
self.set_raw_settings_diagnostics(db).to(paths.into());
|
||||
}
|
||||
|
||||
/// Resolve the project settings after deserialization.
|
||||
///
|
||||
/// Returns `Ok(true)` if the settings were resolved and the `Project` was updated,
|
||||
/// or returns `Ok(false)` if a new `Project` should be created.
|
||||
pub fn resolve_deserialized_settings(
|
||||
&self,
|
||||
metadata: &ProjectMetadata,
|
||||
db: &mut dyn Db,
|
||||
) -> Result<bool, ToSettingsError> {
|
||||
// Incompatible project metadata.
|
||||
if self.root(db) != metadata.root() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
// Reload the project if the metadata has changed.
|
||||
if self.metadata(db) != metadata {
|
||||
self.reload(db, metadata.clone());
|
||||
}
|
||||
|
||||
// Otherwise, resolve the settings and diagnostics, which are not persisted.
|
||||
//
|
||||
// Note that we use the new `ProjectMetadata` here instead of the one that was
|
||||
// persisted to ensure the `ValueSource` fields are updated even if the metadata
|
||||
// values compare equal (but may come from new sources).
|
||||
let (settings, diagnostics) = metadata.options().to_settings(db, metadata.root())?;
|
||||
|
||||
// Note that we use interior mutability here to avoid bumping the Salsa revision
|
||||
// for these fields because we know that the settings and settings diagnostics will
|
||||
// not change if the metadata compared equal.
|
||||
self.raw_settings(db)
|
||||
.set(Box::new(settings))
|
||||
.expect("`Project::resolve_settings` should only be called once");
|
||||
|
||||
self.raw_settings_diagnostics(db)
|
||||
.set(diagnostics)
|
||||
.expect("`Project::resolve_settings` should only be called once");
|
||||
|
||||
// Note that `IndexedFiles` are not deserialized, so this is already set to
|
||||
// `IndexedFiles::lazy`. However, we need to make sure any queries that depend
|
||||
// on the file set are invalidated.
|
||||
//
|
||||
// TODO: This currently causes the persisted `infer_scope_types` query to be
|
||||
// re-executed, because `semantic_index` has a dependency on the file set. See
|
||||
// if we can work around this with a intermediate query.
|
||||
self.set_file_set(db).to(IndexedFiles::lazy());
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
/// Loads all existing [`Project`]s in the database.
|
||||
pub fn load_all(db: &dyn Db) -> Vec<Project> {
|
||||
Project::ingredient(db)
|
||||
.entries(db.zalsa())
|
||||
.map(|entry| entry.as_struct())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn serialize<S>(fields: &ProjectFields, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let (
|
||||
open_fileset,
|
||||
_file_set,
|
||||
metadata,
|
||||
_settings,
|
||||
included_paths_list,
|
||||
_settings_diagnostics,
|
||||
check_mode,
|
||||
) = fields;
|
||||
|
||||
ProjectWireRef {
|
||||
open_fileset,
|
||||
metadata,
|
||||
included_paths_list,
|
||||
check_mode,
|
||||
}
|
||||
.serialize(serializer)
|
||||
}
|
||||
|
||||
fn deserialize<'de, D>(deserializer: D) -> Result<ProjectFields, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let wire = ProjectWire::deserialize(deserializer)?;
|
||||
|
||||
Ok((
|
||||
wire.open_fileset,
|
||||
IndexedFiles::lazy(),
|
||||
wire.metadata,
|
||||
OnceLock::new(),
|
||||
wire.included_paths_list,
|
||||
OnceLock::new(),
|
||||
wire.check_mode,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::tracked(returns(ref), heap_size=ruff_memory_usage::heap_size)]
|
||||
@@ -640,7 +810,7 @@ impl Iterator for ProjectFilesIter<'_> {
|
||||
|
||||
impl FusedIterator for ProjectFilesIter<'_> {}
|
||||
|
||||
#[derive(Debug, Clone, get_size2::GetSize)]
|
||||
#[derive(Debug, Clone, get_size2::GetSize, serde::Serialize, serde::Deserialize)]
|
||||
pub struct IOErrorDiagnostic {
|
||||
file: Option<File>,
|
||||
error: IOErrorKind,
|
||||
@@ -656,7 +826,7 @@ impl IOErrorDiagnostic {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, Clone, get_size2::GetSize)]
|
||||
#[derive(Error, Debug, Clone, get_size2::GetSize, serde::Serialize, serde::Deserialize)]
|
||||
enum IOErrorKind {
|
||||
#[error(transparent)]
|
||||
Walk(#[from] walk::WalkError),
|
||||
|
||||
@@ -19,8 +19,7 @@ pub mod pyproject;
|
||||
pub mod settings;
|
||||
pub mod value;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, get_size2::GetSize)]
|
||||
#[cfg_attr(test, derive(serde::Serialize))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, get_size2::GetSize, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ProjectMetadata {
|
||||
pub(super) name: Name,
|
||||
|
||||
@@ -35,7 +34,6 @@ pub struct ProjectMetadata {
|
||||
/// knowing which files to watch for changes.
|
||||
///
|
||||
/// The path ordering doesn't imply precedence.
|
||||
#[cfg_attr(test, serde(skip_serializing_if = "Vec::is_empty"))]
|
||||
pub(super) extra_configuration_paths: Vec<SystemPathBuf>,
|
||||
}
|
||||
|
||||
@@ -375,7 +373,14 @@ mod tests {
|
||||
ProjectMetadata(
|
||||
name: Name("app"),
|
||||
root: "/app",
|
||||
options: Options(),
|
||||
options: Options(
|
||||
environment: None,
|
||||
src: None,
|
||||
rules: None,
|
||||
terminal: None,
|
||||
overrides: None,
|
||||
),
|
||||
extra_configuration_paths: [],
|
||||
)
|
||||
"#);
|
||||
});
|
||||
@@ -413,7 +418,14 @@ mod tests {
|
||||
ProjectMetadata(
|
||||
name: Name("backend"),
|
||||
root: "/app",
|
||||
options: Options(),
|
||||
options: Options(
|
||||
environment: None,
|
||||
src: None,
|
||||
rules: None,
|
||||
terminal: None,
|
||||
overrides: None,
|
||||
),
|
||||
extra_configuration_paths: [],
|
||||
)
|
||||
"#);
|
||||
});
|
||||
@@ -506,10 +518,18 @@ unclosed table, expected `]`
|
||||
name: Name("nested-project"),
|
||||
root: "/app/packages/a",
|
||||
options: Options(
|
||||
environment: None,
|
||||
src: Some(SrcOptions(
|
||||
root: Some("src"),
|
||||
r#respect-ignore-files: None,
|
||||
include: None,
|
||||
exclude: None,
|
||||
)),
|
||||
rules: None,
|
||||
terminal: None,
|
||||
overrides: None,
|
||||
),
|
||||
extra_configuration_paths: [],
|
||||
)
|
||||
"#);
|
||||
});
|
||||
@@ -556,10 +576,18 @@ unclosed table, expected `]`
|
||||
name: Name("project-root"),
|
||||
root: "/app",
|
||||
options: Options(
|
||||
environment: None,
|
||||
src: Some(SrcOptions(
|
||||
root: Some("src"),
|
||||
r#respect-ignore-files: None,
|
||||
include: None,
|
||||
exclude: None,
|
||||
)),
|
||||
rules: None,
|
||||
terminal: None,
|
||||
overrides: None,
|
||||
),
|
||||
extra_configuration_paths: [],
|
||||
)
|
||||
"#);
|
||||
});
|
||||
@@ -599,7 +627,14 @@ unclosed table, expected `]`
|
||||
ProjectMetadata(
|
||||
name: Name("nested-project"),
|
||||
root: "/app/packages/a",
|
||||
options: Options(),
|
||||
options: Options(
|
||||
environment: None,
|
||||
src: None,
|
||||
rules: None,
|
||||
terminal: None,
|
||||
overrides: None,
|
||||
),
|
||||
extra_configuration_paths: [],
|
||||
)
|
||||
"#);
|
||||
});
|
||||
@@ -644,9 +679,19 @@ unclosed table, expected `]`
|
||||
root: "/app",
|
||||
options: Options(
|
||||
environment: Some(EnvironmentOptions(
|
||||
root: None,
|
||||
r#python-version: Some("3.10"),
|
||||
r#python-platform: None,
|
||||
r#extra-paths: None,
|
||||
typeshed: None,
|
||||
python: None,
|
||||
)),
|
||||
src: None,
|
||||
rules: None,
|
||||
terminal: None,
|
||||
overrides: None,
|
||||
),
|
||||
extra_configuration_paths: [],
|
||||
)
|
||||
"#);
|
||||
});
|
||||
@@ -696,12 +741,24 @@ unclosed table, expected `]`
|
||||
root: "/app",
|
||||
options: Options(
|
||||
environment: Some(EnvironmentOptions(
|
||||
root: None,
|
||||
r#python-version: Some("3.12"),
|
||||
r#python-platform: None,
|
||||
r#extra-paths: None,
|
||||
typeshed: None,
|
||||
python: None,
|
||||
)),
|
||||
src: Some(SrcOptions(
|
||||
root: Some("src"),
|
||||
r#respect-ignore-files: None,
|
||||
include: None,
|
||||
exclude: None,
|
||||
)),
|
||||
rules: None,
|
||||
terminal: None,
|
||||
overrides: None,
|
||||
),
|
||||
extra_configuration_paths: [],
|
||||
)
|
||||
"#);
|
||||
});
|
||||
|
||||
@@ -52,10 +52,8 @@ use ty_python_semantic::{
|
||||
pub struct Options {
|
||||
/// Configures the type checking environment.
|
||||
#[option_group]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub environment: Option<EnvironmentOptions>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[option_group]
|
||||
pub src: Option<SrcOptions>,
|
||||
|
||||
@@ -69,7 +67,6 @@ pub struct Options {
|
||||
/// * `warn`: Enable the rule and create a warning diagnostic.
|
||||
/// * `error`: Enable the rule and create an error diagnostic.
|
||||
/// ty will exit with a non-zero code if any error diagnostics are emitted.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[option(
|
||||
default = r#"{...}"#,
|
||||
value_type = r#"dict[RuleName, "ignore" | "warn" | "error"]"#,
|
||||
@@ -81,7 +78,6 @@ pub struct Options {
|
||||
)]
|
||||
pub rules: Option<Rules>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[option_group]
|
||||
pub terminal: Option<TerminalOptions>,
|
||||
|
||||
@@ -90,7 +86,6 @@ pub struct Options {
|
||||
/// Each override specifies include/exclude patterns and rule configurations
|
||||
/// that apply to matching files. Multiple overrides can match the same file,
|
||||
/// with later overrides taking precedence.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[option_group]
|
||||
pub overrides: Option<OverridesOptions>,
|
||||
}
|
||||
@@ -430,7 +425,6 @@ pub struct EnvironmentOptions {
|
||||
///
|
||||
/// Besides, if a `./tests` directory exists and is not a package (i.e. it does not contain an `__init__.py` file),
|
||||
/// it will also be included in the first party search path.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[option(
|
||||
default = r#"null"#,
|
||||
value_type = "list[str]",
|
||||
@@ -458,7 +452,6 @@ pub struct EnvironmentOptions {
|
||||
/// For some language features, ty can also understand conditionals based on comparisons
|
||||
/// with `sys.version_info`. These are commonly found in typeshed, for example,
|
||||
/// to reflect the differing contents of the standard library across Python versions.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[option(
|
||||
default = r#""3.13""#,
|
||||
value_type = r#""3.7" | "3.8" | "3.9" | "3.10" | "3.11" | "3.12" | "3.13" | <major>.<minor>"#,
|
||||
@@ -479,7 +472,6 @@ pub struct EnvironmentOptions {
|
||||
/// - `android` for Android
|
||||
/// - `ios` for iOS
|
||||
/// - `linux` for everything else
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[option(
|
||||
default = r#"<current-platform>"#,
|
||||
value_type = r#""win32" | "darwin" | "android" | "ios" | "linux" | "all" | str"#,
|
||||
@@ -493,7 +485,6 @@ pub struct EnvironmentOptions {
|
||||
/// List of user-provided paths that should take first priority in the module resolution.
|
||||
/// Examples in other type checkers are mypy's `MYPYPATH` environment variable,
|
||||
/// or pyright's `stubPath` configuration setting.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[option(
|
||||
default = r#"[]"#,
|
||||
value_type = "list[str]",
|
||||
@@ -506,7 +497,6 @@ pub struct EnvironmentOptions {
|
||||
/// Optional path to a "typeshed" directory on disk for us to use for standard-library types.
|
||||
/// If this is not provided, we will fallback to our vendored typeshed stubs for the stdlib,
|
||||
/// bundled as a zip file in the binary
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[option(
|
||||
default = r#"null"#,
|
||||
value_type = "str",
|
||||
@@ -522,7 +512,6 @@ pub struct EnvironmentOptions {
|
||||
/// third-party imports.
|
||||
///
|
||||
/// This option is commonly used to specify the path to a virtual environment.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[option(
|
||||
default = r#"null"#,
|
||||
value_type = "str",
|
||||
@@ -558,7 +547,6 @@ pub struct SrcOptions {
|
||||
///
|
||||
/// Besides, if a `./tests` directory exists and is not a package (i.e. it does not contain an `__init__.py` file),
|
||||
/// it will also be included in the first party search path.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[option(
|
||||
default = r#"null"#,
|
||||
value_type = "str",
|
||||
@@ -579,7 +567,6 @@ pub struct SrcOptions {
|
||||
respect-ignore-files = false
|
||||
"#
|
||||
)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub respect_ignore_files: Option<bool>,
|
||||
|
||||
/// A list of files and directories to check. The `include` option
|
||||
@@ -602,7 +589,6 @@ pub struct SrcOptions {
|
||||
/// matches `<project_root>/src` and not `<project_root>/test/src`).
|
||||
///
|
||||
/// `exclude` takes precedence over `include`.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[option(
|
||||
default = r#"null"#,
|
||||
value_type = r#"list[str]"#,
|
||||
@@ -672,7 +658,6 @@ pub struct SrcOptions {
|
||||
]
|
||||
"#
|
||||
)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub exclude: Option<RangedValue<Vec<RelativeGlobPattern>>>,
|
||||
}
|
||||
|
||||
@@ -1084,7 +1069,6 @@ pub struct TerminalOptions {
|
||||
/// The format to use for printing diagnostic messages.
|
||||
///
|
||||
/// Defaults to `full`.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[option(
|
||||
default = r#"full"#,
|
||||
value_type = "full | concise",
|
||||
@@ -1192,7 +1176,6 @@ pub struct OverrideOptions {
|
||||
/// are affected by this override.
|
||||
///
|
||||
/// If not specified, defaults to `["**"]` (matches all files).
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[option(
|
||||
default = r#"null"#,
|
||||
value_type = r#"list[str]"#,
|
||||
@@ -1212,7 +1195,6 @@ pub struct OverrideOptions {
|
||||
/// Exclude patterns take precedence over include patterns within the same override.
|
||||
///
|
||||
/// If not specified, defaults to `[]` (excludes no files).
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[option(
|
||||
default = r#"null"#,
|
||||
value_type = r#"list[str]"#,
|
||||
@@ -1233,7 +1215,6 @@ pub struct OverrideOptions {
|
||||
/// These rules will be merged with the global rules, with override rules
|
||||
/// taking precedence for matching files. You can set rules to different
|
||||
/// severity levels or disable them entirely.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[option(
|
||||
default = r#"{...}"#,
|
||||
value_type = r#"dict[RuleName, "ignore" | "warn" | "error"]"#,
|
||||
@@ -1400,7 +1381,17 @@ impl RangedValue<OverrideOptions> {
|
||||
}
|
||||
|
||||
/// The options for an override but without the include/exclude patterns.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Combine, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
Combine,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub(super) struct InnerOverrideOptions {
|
||||
/// Raw rule options as specified in the configuration.
|
||||
/// Used when multiple overrides match a file and need to be merged.
|
||||
|
||||
@@ -57,14 +57,14 @@ thread_local! {
|
||||
static VALUE_SOURCE: RefCell<Option<(ValueSource, bool)>> = const { RefCell::new(None) };
|
||||
}
|
||||
|
||||
/// Guard to safely change the [`VALUE_SOURCE`] for the current thread.
|
||||
/// Guard to safely change the [`ValueSource`] for the current thread.
|
||||
#[must_use]
|
||||
pub(super) struct ValueSourceGuard {
|
||||
pub struct ValueSourceGuard {
|
||||
prev_value: Option<(ValueSource, bool)>,
|
||||
}
|
||||
|
||||
impl ValueSourceGuard {
|
||||
pub(super) fn new(source: ValueSource, is_toml: bool) -> Self {
|
||||
pub fn new(source: ValueSource, is_toml: bool) -> Self {
|
||||
let prev = VALUE_SOURCE.replace(Some((source, is_toml)));
|
||||
Self { prev_value: prev }
|
||||
}
|
||||
|
||||
@@ -291,7 +291,7 @@ impl<'a> ProjectFilesWalker<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, Clone, get_size2::GetSize)]
|
||||
#[derive(Error, Debug, Clone, get_size2::GetSize, serde::Serialize, serde::Deserialize)]
|
||||
pub(crate) enum WalkError {
|
||||
#[error("`{path}`: {error}")]
|
||||
IOPathError { path: SystemPathBuf, error: String },
|
||||
|
||||
@@ -11,12 +11,12 @@ repository = { workspace = true }
|
||||
license = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
ruff_db = { workspace = true }
|
||||
ruff_db = { workspace = true, features = ["serde"] }
|
||||
ruff_annotate_snippets = { workspace = true }
|
||||
ruff_index = { workspace = true, features = ["salsa"] }
|
||||
ruff_macros = { workspace = true }
|
||||
ruff_memory_usage = { workspace = true }
|
||||
ruff_python_ast = { workspace = true, features = ["salsa"] }
|
||||
ruff_python_ast = { workspace = true, features = ["salsa", "serde"] }
|
||||
ruff_python_parser = { workspace = true }
|
||||
ruff_python_stdlib = { workspace = true }
|
||||
ruff_source_file = { workspace = true }
|
||||
@@ -42,7 +42,7 @@ tracing = { workspace = true }
|
||||
rustc-hash = { workspace = true }
|
||||
hashbrown = { workspace = true }
|
||||
schemars = { workspace = true, optional = true }
|
||||
serde = { workspace = true, optional = true }
|
||||
serde = { workspace = true }
|
||||
smallvec = { workspace = true }
|
||||
static_assertions = { workspace = true }
|
||||
test-case = { workspace = true }
|
||||
@@ -68,7 +68,6 @@ quickcheck = { version = "1.0.3", default-features = false }
|
||||
quickcheck_macros = { version = "1.0.0" }
|
||||
|
||||
[features]
|
||||
serde = ["ruff_db/serde", "dep:serde", "ruff_python_ast/serde"]
|
||||
testing = []
|
||||
|
||||
[lints]
|
||||
|
||||
@@ -37,7 +37,7 @@ pub struct AstNodeRef<T> {
|
||||
|
||||
/// Debug information.
|
||||
#[cfg(debug_assertions)]
|
||||
kind: ruff_python_ast::NodeKind,
|
||||
kind: Option<ruff_python_ast::NodeKind>,
|
||||
#[cfg(debug_assertions)]
|
||||
range: ruff_text_size::TextRange,
|
||||
// Note that because the module address is not stored in release builds, `AstNodeRef`
|
||||
@@ -68,7 +68,7 @@ where
|
||||
#[cfg(debug_assertions)]
|
||||
module_addr: module_ref.module().addr(),
|
||||
#[cfg(debug_assertions)]
|
||||
kind: AnyNodeRef::from(node).kind(),
|
||||
kind: Some(AnyNodeRef::from(node).kind()),
|
||||
#[cfg(debug_assertions)]
|
||||
range: node.range(),
|
||||
_node: PhantomData,
|
||||
@@ -81,7 +81,7 @@ where
|
||||
/// different file or Salsa revision than the module to which the node belongs.
|
||||
pub fn node<'ast>(&self, module_ref: &'ast ParsedModuleRef) -> &'ast T {
|
||||
#[cfg(debug_assertions)]
|
||||
assert_eq!(module_ref.module().addr(), self.module_addr);
|
||||
assert!(self.module_addr == 0 || module_ref.module().addr() == self.module_addr);
|
||||
|
||||
// The user guarantees that the module is from the same file and Salsa
|
||||
// revision, so the file contents cannot have changed.
|
||||
@@ -109,6 +109,33 @@ unsafe impl<T> salsa::Update for AstNodeRef<T> {
|
||||
|
||||
impl<T> get_size2::GetSize for AstNodeRef<T> {}
|
||||
|
||||
impl<T> serde::Serialize for AstNodeRef<T> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
self.index.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T> serde::Deserialize<'de> for AstNodeRef<T> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
Ok(AstNodeRef {
|
||||
#[cfg(debug_assertions)]
|
||||
module_addr: 0,
|
||||
#[cfg(debug_assertions)]
|
||||
kind: None,
|
||||
#[cfg(debug_assertions)]
|
||||
range: ruff_text_size::TextRange::default(),
|
||||
index: NodeIndex::deserialize(deserializer)?,
|
||||
_node: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::missing_fields_in_debug)]
|
||||
impl<T> Debug for AstNodeRef<T>
|
||||
where
|
||||
|
||||
@@ -32,12 +32,10 @@ pub struct LintMetadata {
|
||||
pub line: u32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Serialize, serde::Deserialize),
|
||||
serde(rename_all = "kebab-case")
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, serde::Serialize, serde::Deserialize,
|
||||
)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub enum Level {
|
||||
/// # Ignore
|
||||
|
||||
@@ -13,7 +13,18 @@ use crate::{db::Db, module_resolver::file_to_module};
|
||||
/// A module name, e.g. `foo.bar`.
|
||||
///
|
||||
/// Always normalized to the absolute form (never a relative module name, i.e., never `.foo`).
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Hash,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub struct ModuleName(compact_str::CompactString);
|
||||
|
||||
impl ModuleName {
|
||||
|
||||
@@ -13,7 +13,17 @@ use crate::module_name::ModuleName;
|
||||
use crate::module_resolver::path::SystemOrVendoredPathRef;
|
||||
|
||||
/// Representation of a Python module.
|
||||
#[derive(Clone, Copy, Eq, Hash, PartialEq, salsa::Supertype, salsa::Update)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Eq,
|
||||
Hash,
|
||||
PartialEq,
|
||||
salsa::Supertype,
|
||||
salsa::Update,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub enum Module<'db> {
|
||||
File(FileModule<'db>),
|
||||
Namespace(NamespacePackage<'db>),
|
||||
@@ -214,7 +224,7 @@ fn all_submodule_names_for_package(db: &dyn Db, file: File) -> Option<Vec<Name>>
|
||||
}
|
||||
|
||||
/// A module that resolves to a file (`lib.py` or `package/__init__.py`)
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
pub struct FileModule<'db> {
|
||||
#[returns(ref)]
|
||||
pub(super) name: ModuleName,
|
||||
@@ -229,13 +239,23 @@ pub struct FileModule<'db> {
|
||||
///
|
||||
/// Namespace packages are special because there are
|
||||
/// multiple possible paths and they have no corresponding code file.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
pub struct NamespacePackage<'db> {
|
||||
#[returns(ref)]
|
||||
pub(super) name: ModuleName,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Copy,
|
||||
Clone,
|
||||
Debug,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Hash,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub enum ModuleKind {
|
||||
/// A single-file module (e.g. `foo.py` or `foo.pyi`)
|
||||
Module,
|
||||
@@ -254,7 +274,18 @@ impl ModuleKind {
|
||||
}
|
||||
|
||||
/// Enumeration of various core stdlib modules in which important types are located
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum_macros::EnumString, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
strum_macros::EnumString,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
#[cfg_attr(test, derive(strum_macros::EnumIter))]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum KnownModule {
|
||||
|
||||
@@ -449,7 +449,9 @@ impl From<SitePackagesDiscoveryError> for SearchPathValidationError {
|
||||
|
||||
type SearchPathResult<T> = Result<T, SearchPathValidationError>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize, serde::Serialize, serde::Deserialize,
|
||||
)]
|
||||
enum SearchPathInner {
|
||||
Extra(SystemPathBuf),
|
||||
FirstParty(SystemPathBuf),
|
||||
@@ -487,7 +489,9 @@ enum SearchPathInner {
|
||||
/// or the "Editable" category. For the "First-party", "Site-packages"
|
||||
/// and "Standard-library" categories, however, there will always be exactly
|
||||
/// one search path from that category in any given list of search paths.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize, serde::Serialize, serde::Deserialize,
|
||||
)]
|
||||
pub struct SearchPath(Arc<SearchPathInner>);
|
||||
|
||||
impl SearchPath {
|
||||
|
||||
@@ -172,7 +172,7 @@ pub(crate) fn search_paths(db: &dyn Db, resolve_mode: ModuleResolveMode) -> Sear
|
||||
Program::get(db).search_paths(db).iter(db, resolve_mode)
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, get_size2::GetSize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, get_size2::GetSize, serde::Serialize, serde::Deserialize)]
|
||||
pub struct SearchPaths {
|
||||
/// Search paths that have been statically determined purely from reading
|
||||
/// ty's configuration settings. These shouldn't ever change unless the
|
||||
|
||||
@@ -74,7 +74,7 @@ pub(crate) enum TypeshedVersionsParseErrorKind {
|
||||
VersionParseError(#[from] PythonVersionDeserializationError),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, get_size2::GetSize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, get_size2::GetSize, serde::Serialize, serde::Deserialize)]
|
||||
pub(crate) struct TypeshedVersions(FxHashMap<ModuleName, PyVersionRange>);
|
||||
|
||||
impl TypeshedVersions {
|
||||
@@ -230,7 +230,9 @@ impl fmt::Display for TypeshedVersions {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Debug, Clone, Eq, PartialEq, Hash, get_size2::GetSize, serde::Serialize, serde::Deserialize,
|
||||
)]
|
||||
pub(crate) enum PyVersionRange {
|
||||
AvailableFrom(RangeFrom<PythonVersion>),
|
||||
AvailableWithin(RangeInclusive<PythonVersion>),
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
use ruff_python_ast::{HasNodeIndex, NodeIndex};
|
||||
|
||||
/// Compact key for a node for use in a hash map.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Copy,
|
||||
Clone,
|
||||
Debug,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Hash,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub(super) struct NodeKey(NodeIndex);
|
||||
|
||||
impl NodeKey {
|
||||
|
||||
@@ -13,7 +13,7 @@ use ruff_text_size::TextRange;
|
||||
use salsa::Durability;
|
||||
use salsa::Setter;
|
||||
|
||||
#[salsa::input(singleton, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::input(persist, singleton, heap_size=ruff_memory_usage::heap_size)]
|
||||
pub struct Program {
|
||||
#[returns(ref)]
|
||||
pub python_version_with_source: PythonVersionWithSource,
|
||||
@@ -93,7 +93,9 @@ pub struct ProgramSettings {
|
||||
pub search_paths: SearchPaths,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Default, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Clone, Debug, Eq, PartialEq, Default, get_size2::GetSize, serde::Serialize, serde::Deserialize,
|
||||
)]
|
||||
pub enum PythonVersionSource {
|
||||
/// Value loaded from a project's configuration file.
|
||||
ConfigFile(PythonVersionFileSource),
|
||||
@@ -123,7 +125,7 @@ pub enum PythonVersionSource {
|
||||
|
||||
/// Information regarding the file and [`TextRange`] of the configuration
|
||||
/// from which we inferred the Python version.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, get_size2::GetSize)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, get_size2::GetSize, serde::Serialize, serde::Deserialize)]
|
||||
pub struct PythonVersionFileSource {
|
||||
path: Arc<SystemPathBuf>,
|
||||
range: Option<TextRange>,
|
||||
@@ -145,7 +147,7 @@ impl PythonVersionFileSource {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug, Clone, get_size2::GetSize)]
|
||||
#[derive(Eq, PartialEq, Debug, Clone, get_size2::GetSize, serde::Serialize, serde::Deserialize)]
|
||||
pub struct PythonVersionWithSource {
|
||||
pub version: PythonVersion,
|
||||
pub source: PythonVersionSource,
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
/// The target platform to assume when resolving types.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, get_size2::GetSize)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Serialize, serde::Deserialize, ruff_macros::RustDoc),
|
||||
serde(rename_all = "kebab-case")
|
||||
)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, get_size2::GetSize, ruff_macros::RustDoc)]
|
||||
pub enum PythonPlatform {
|
||||
/// Do not make any assumptions about the target platform.
|
||||
All,
|
||||
@@ -16,25 +11,52 @@ pub enum PythonPlatform {
|
||||
/// We use a string (instead of individual enum variants), as the set of possible platforms
|
||||
/// may change over time. See <https://docs.python.org/3/library/sys.html#sys.platform> for
|
||||
/// some known platform identifiers.
|
||||
#[cfg_attr(feature = "serde", serde(untagged))]
|
||||
Identifier(String),
|
||||
}
|
||||
|
||||
impl serde::Serialize for PythonPlatform {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serde::Serialize::serialize(&self.as_ref(), serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::de::Deserialize<'de> for PythonPlatform {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let platform = String::deserialize(deserializer)?;
|
||||
match platform.as_str() {
|
||||
"all" => Ok(PythonPlatform::All),
|
||||
_ => Ok(PythonPlatform::Identifier(platform)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for PythonPlatform {
|
||||
fn from(platform: String) -> Self {
|
||||
match platform.as_str() {
|
||||
"all" => PythonPlatform::All,
|
||||
_ => PythonPlatform::Identifier(platform.to_string()),
|
||||
_ => PythonPlatform::Identifier(platform),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for PythonPlatform {
|
||||
fn as_ref(&self) -> &str {
|
||||
match self {
|
||||
PythonPlatform::All => "all",
|
||||
PythonPlatform::Identifier(platform) => platform,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PythonPlatform {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
PythonPlatform::All => f.write_str("all"),
|
||||
PythonPlatform::Identifier(name) => f.write_str(name),
|
||||
}
|
||||
f.write_str(self.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -115,7 +115,18 @@ pub(crate) mod node_key {
|
||||
|
||||
use crate::node_key::NodeKey;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, salsa::Update, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Copy,
|
||||
Clone,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Hash,
|
||||
Debug,
|
||||
salsa::Update,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub(crate) struct ExpressionNodeKey(NodeKey);
|
||||
|
||||
impl From<ast::ExprRef<'_>> for ExpressionNodeKey {
|
||||
|
||||
@@ -22,7 +22,7 @@ use crate::unpack::{Unpack, UnpackPosition};
|
||||
/// because a new scope gets inserted before the `Definition` or a new place is inserted
|
||||
/// before this `Definition`. However, the ID can be considered stable and it is okay to use
|
||||
/// `Definition` in cross-module` salsa queries or as a field on other salsa tracked structs.
|
||||
#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::tracked(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
pub struct Definition<'db> {
|
||||
/// The file in which the definition occurs.
|
||||
pub file: File,
|
||||
@@ -652,7 +652,7 @@ impl DefinitionCategory {
|
||||
/// [`DefinitionKind`] fields in salsa tracked structs should be tracked (attributed with `#[tracked]`)
|
||||
/// because the kind is a thin wrapper around [`AstNodeRef`]. See the [`AstNodeRef`] documentation
|
||||
/// for an in-depth explanation of why this is necessary.
|
||||
#[derive(Clone, Debug, get_size2::GetSize)]
|
||||
#[derive(Clone, Debug, get_size2::GetSize, serde::Serialize, serde::Deserialize)]
|
||||
pub enum DefinitionKind<'db> {
|
||||
Import(ImportDefinitionKind),
|
||||
ImportFrom(ImportFromDefinitionKind),
|
||||
@@ -852,7 +852,9 @@ impl DefinitionKind<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Hash, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Copy, Clone, Debug, PartialEq, Hash, get_size2::GetSize, serde::Serialize, serde::Deserialize,
|
||||
)]
|
||||
pub(crate) enum TargetKind<'db> {
|
||||
Sequence(UnpackPosition, Unpack<'db>),
|
||||
/// Name, attribute, or subscript.
|
||||
@@ -868,7 +870,7 @@ impl<'db> From<Option<(UnpackPosition, Unpack<'db>)>> for TargetKind<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, get_size2::GetSize)]
|
||||
#[derive(Clone, Debug, get_size2::GetSize, serde::Serialize, serde::Deserialize)]
|
||||
pub struct StarImportDefinitionKind {
|
||||
node: AstNodeRef<ast::StmtImportFrom>,
|
||||
symbol_id: ScopedSymbolId,
|
||||
@@ -898,7 +900,7 @@ impl StarImportDefinitionKind {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, get_size2::GetSize)]
|
||||
#[derive(Clone, Debug, get_size2::GetSize, serde::Serialize, serde::Deserialize)]
|
||||
pub struct MatchPatternDefinitionKind {
|
||||
pattern: AstNodeRef<ast::Pattern>,
|
||||
identifier: AstNodeRef<ast::Identifier>,
|
||||
@@ -920,7 +922,7 @@ impl MatchPatternDefinitionKind {
|
||||
/// But if the target is an attribute or subscript, its definition is not in the comprehension's scope;
|
||||
/// it is in the scope in which the root variable is bound.
|
||||
/// TODO: currently we don't model this correctly and simply assume that it is in a scope outside the comprehension.
|
||||
#[derive(Clone, Debug, get_size2::GetSize)]
|
||||
#[derive(Clone, Debug, get_size2::GetSize, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ComprehensionDefinitionKind<'db> {
|
||||
target_kind: TargetKind<'db>,
|
||||
iterable: AstNodeRef<ast::Expr>,
|
||||
@@ -951,7 +953,7 @@ impl<'db> ComprehensionDefinitionKind<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, get_size2::GetSize)]
|
||||
#[derive(Clone, Debug, get_size2::GetSize, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ImportDefinitionKind {
|
||||
node: AstNodeRef<ast::StmtImport>,
|
||||
alias_index: usize,
|
||||
@@ -972,7 +974,7 @@ impl ImportDefinitionKind {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, get_size2::GetSize)]
|
||||
#[derive(Clone, Debug, get_size2::GetSize, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ImportFromDefinitionKind {
|
||||
node: AstNodeRef<ast::StmtImportFrom>,
|
||||
alias_index: usize,
|
||||
@@ -993,7 +995,7 @@ impl ImportFromDefinitionKind {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, get_size2::GetSize)]
|
||||
#[derive(Clone, Debug, get_size2::GetSize, serde::Serialize, serde::Deserialize)]
|
||||
pub struct AssignmentDefinitionKind<'db> {
|
||||
target_kind: TargetKind<'db>,
|
||||
value: AstNodeRef<ast::Expr>,
|
||||
@@ -1014,7 +1016,7 @@ impl<'db> AssignmentDefinitionKind<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, get_size2::GetSize)]
|
||||
#[derive(Clone, Debug, get_size2::GetSize, serde::Serialize, serde::Deserialize)]
|
||||
pub struct AnnotatedAssignmentDefinitionKind {
|
||||
annotation: AstNodeRef<ast::Expr>,
|
||||
value: Option<AstNodeRef<ast::Expr>>,
|
||||
@@ -1035,7 +1037,7 @@ impl AnnotatedAssignmentDefinitionKind {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, get_size2::GetSize)]
|
||||
#[derive(Clone, Debug, get_size2::GetSize, serde::Serialize, serde::Deserialize)]
|
||||
pub struct WithItemDefinitionKind<'db> {
|
||||
target_kind: TargetKind<'db>,
|
||||
context_expr: AstNodeRef<ast::Expr>,
|
||||
@@ -1061,7 +1063,7 @@ impl<'db> WithItemDefinitionKind<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, get_size2::GetSize)]
|
||||
#[derive(Clone, Debug, get_size2::GetSize, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ForStmtDefinitionKind<'db> {
|
||||
target_kind: TargetKind<'db>,
|
||||
iterable: AstNodeRef<ast::Expr>,
|
||||
@@ -1087,7 +1089,7 @@ impl<'db> ForStmtDefinitionKind<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, get_size2::GetSize)]
|
||||
#[derive(Clone, Debug, get_size2::GetSize, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ExceptHandlerDefinitionKind {
|
||||
handler: AstNodeRef<ast::ExceptHandlerExceptHandler>,
|
||||
is_star: bool,
|
||||
|
||||
@@ -10,7 +10,17 @@ use salsa;
|
||||
/// a type expression. For example, in `self.x: <annotation> = <value>`, the
|
||||
/// `<annotation>` is inferred as a type expression, while `<value>` is inferred
|
||||
/// as a normal expression.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub(crate) enum ExpressionKind {
|
||||
Normal,
|
||||
TypeExpression,
|
||||
@@ -31,7 +41,7 @@ pub(crate) enum ExpressionKind {
|
||||
/// * a return type of a cross-module query
|
||||
/// * a field of a type that is a return type of a cross-module query
|
||||
/// * an argument of a cross-module query
|
||||
#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::tracked(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
pub(crate) struct Expression<'db> {
|
||||
/// The file in which the expression occurs.
|
||||
pub(crate) file: File,
|
||||
|
||||
@@ -347,7 +347,8 @@ impl Hash for MemberExprRef<'_> {
|
||||
|
||||
/// Uniquely identifies a member in a scope.
|
||||
#[newtype_index]
|
||||
#[derive(get_size2::GetSize, salsa::Update)]
|
||||
#[allow(clippy::unsafe_derive_deserialize)]
|
||||
#[derive(get_size2::GetSize, salsa::Update, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ScopedMemberId;
|
||||
|
||||
/// The members of a scope. Allows lookup by member path and [`ScopedMemberId`].
|
||||
|
||||
@@ -133,7 +133,18 @@ impl std::fmt::Display for PlaceExprRef<'_> {
|
||||
}
|
||||
|
||||
/// ID that uniquely identifies a place inside a [`Scope`](super::FileScopeId).
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, get_size2::GetSize, salsa::Update)]
|
||||
#[derive(
|
||||
Debug,
|
||||
Copy,
|
||||
Clone,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Hash,
|
||||
get_size2::GetSize,
|
||||
salsa::Update,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub enum ScopedPlaceId {
|
||||
Symbol(ScopedSymbolId),
|
||||
Member(ScopedMemberId),
|
||||
|
||||
@@ -14,7 +14,13 @@ use crate::{
|
||||
};
|
||||
|
||||
/// A cross-module identifier of a scope that can be used as a salsa query parameter.
|
||||
#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
///
|
||||
/// This type is an interned struct as opposed to a tracked struct as it is the input of
|
||||
/// the `infer_scope_types` query, which is the root query of the persistent cache.
|
||||
///
|
||||
/// Additionally, this type disables garbage collection, as garbage collectable interned values are volatile,
|
||||
/// and would cause `infer_scope_types` to have a dependency on every single `ScopeId` in the file.
|
||||
#[salsa::interned(persist, debug, revisions=usize::MAX, heap_size=ruff_memory_usage::heap_size)]
|
||||
pub struct ScopeId<'db> {
|
||||
pub file: File,
|
||||
|
||||
@@ -70,7 +76,8 @@ impl<'db> ScopeId<'db> {
|
||||
|
||||
/// ID that uniquely identifies a scope inside of a module.
|
||||
#[newtype_index]
|
||||
#[derive(salsa::Update, get_size2::GetSize)]
|
||||
#[allow(clippy::unsafe_derive_deserialize)]
|
||||
#[derive(salsa::Update, get_size2::GetSize, serde::Serialize, serde::Deserialize)]
|
||||
pub struct FileScopeId;
|
||||
|
||||
impl FileScopeId {
|
||||
|
||||
@@ -8,7 +8,8 @@ use std::ops::{Deref, DerefMut};
|
||||
|
||||
/// Uniquely identifies a symbol in a given scope.
|
||||
#[newtype_index]
|
||||
#[derive(get_size2::GetSize)]
|
||||
#[allow(clippy::unsafe_derive_deserialize)]
|
||||
#[derive(get_size2::GetSize, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ScopedSymbolId;
|
||||
|
||||
/// A symbol in a given scope.
|
||||
|
||||
@@ -466,7 +466,17 @@ impl Suppression {
|
||||
/// The wrapped `TextRange` is the suppression's range.
|
||||
/// This is unique enough because it is its exact
|
||||
/// location in the source.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Copy,
|
||||
Clone,
|
||||
Debug,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Hash,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub(crate) struct FileSuppressionId(TextRange);
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, get_size2::GetSize)]
|
||||
|
||||
@@ -363,8 +363,41 @@ impl std::fmt::Display for TodoType {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
impl serde::Serialize for TodoType {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serde::Serialize::serialize(&(), serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
impl<'de> serde::Deserialize<'de> for TodoType {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
<()>::deserialize(deserializer)?;
|
||||
|
||||
// TODO: Use an enum for `TodoType` instead of a static string to enable deserialization.
|
||||
Ok(TodoType("<deserialized>"))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Copy,
|
||||
Clone,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub struct TodoType;
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
@@ -400,6 +433,7 @@ macro_rules! todo_type {
|
||||
i += 1;
|
||||
}
|
||||
};
|
||||
|
||||
$crate::types::Type::Dynamic($crate::types::DynamicType::Todo($crate::types::TodoType(
|
||||
$message,
|
||||
)))
|
||||
@@ -432,7 +466,7 @@ pub(crate) use todo_type;
|
||||
/// # Ordering
|
||||
/// Ordering is based on the property instance's salsa-assigned id and not on its values.
|
||||
/// The id may change between runs, or when the property instance was garbage collected and recreated.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct PropertyInstanceType<'db> {
|
||||
getter: Option<Type<'db>>,
|
||||
@@ -502,7 +536,7 @@ bitflags! {
|
||||
/// that were passed in. For the precise meaning of the fields, see [1].
|
||||
///
|
||||
/// [1]: https://docs.python.org/3/library/dataclasses.html
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub struct DataclassParams: u16 {
|
||||
const INIT = 0b0000_0000_0001;
|
||||
const REPR = 0b0000_0000_0010;
|
||||
@@ -561,7 +595,19 @@ impl From<DataclassTransformerParams> for DataclassParams {
|
||||
|
||||
/// Representation of a type: a set of possible values at runtime.
|
||||
///
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)]
|
||||
#[allow(clippy::unsafe_derive_deserialize)]
|
||||
#[derive(
|
||||
Copy,
|
||||
Clone,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
salsa::Update,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub enum Type<'db> {
|
||||
/// The dynamic type: a statically unknown set of values
|
||||
Dynamic(DynamicType),
|
||||
@@ -6577,7 +6623,9 @@ impl<'db> VarianceInferable<'db> for Type<'db> {
|
||||
/// This is represented as an enum (with some variants using `Cow`), and not an `FnMut` trait,
|
||||
/// since we sometimes have to apply type mappings lazily (e.g., to the signature of a function
|
||||
/// literal).
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize, serde::Serialize, serde::Deserialize,
|
||||
)]
|
||||
pub enum TypeMapping<'a, 'db> {
|
||||
/// Applies a specialization to the type
|
||||
Specialization(Specialization<'db>),
|
||||
@@ -6676,7 +6724,18 @@ impl<'db> TypeMapping<'_, 'db> {
|
||||
/// Ordering within variants is based on the wrapped data's salsa-assigned id and not on its values.
|
||||
/// The id may change between runs, or when e.g. a `TypeVarInstance` was garbage-collected and recreated.
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Eq, Hash, PartialEq, salsa::Update, Ord, PartialOrd, get_size2::GetSize,
|
||||
Copy,
|
||||
Clone,
|
||||
Debug,
|
||||
Eq,
|
||||
Hash,
|
||||
PartialEq,
|
||||
salsa::Update,
|
||||
Ord,
|
||||
PartialOrd,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub enum KnownInstanceType<'db> {
|
||||
/// The type of `Protocol[T]`, `Protocol[U, S]`, etc -- usually only found in a class's bases list.
|
||||
@@ -6816,7 +6875,17 @@ impl<'db> KnownInstanceType<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Copy,
|
||||
Clone,
|
||||
Debug,
|
||||
Eq,
|
||||
Hash,
|
||||
PartialEq,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub enum DynamicType {
|
||||
/// An explicitly annotated `typing.Any`
|
||||
Any,
|
||||
@@ -6884,7 +6953,7 @@ impl std::fmt::Display for DynamicType {
|
||||
|
||||
bitflags! {
|
||||
/// Type qualifiers that appear in an annotation expression.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, salsa::Update, Hash)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, salsa::Update, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub(crate) struct TypeQualifiers: u8 {
|
||||
/// `typing.ClassVar`
|
||||
const CLASS_VAR = 1 << 0;
|
||||
@@ -7125,7 +7194,7 @@ impl<'db> InvalidTypeExpression<'db> {
|
||||
}
|
||||
|
||||
/// Data regarding a `warnings.deprecated` or `typing_extensions.deprecated` decorator.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct DeprecatedInstance<'db> {
|
||||
/// The message for the deprecation
|
||||
@@ -7137,7 +7206,7 @@ impl get_size2::GetSize for DeprecatedInstance<'_> {}
|
||||
|
||||
/// Contains information about instances of `dataclasses.Field`, typically created using
|
||||
/// `dataclasses.field()`.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct FieldInstance<'db> {
|
||||
/// The type of the default value for this field. This is derived from the `default` or
|
||||
@@ -7167,7 +7236,17 @@ impl<'db> FieldInstance<'db> {
|
||||
|
||||
/// Whether this typevar was created via the legacy `TypeVar` constructor, using PEP 695 syntax,
|
||||
/// or an implicit typevar like `Self` was used.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
Eq,
|
||||
Hash,
|
||||
PartialEq,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub enum TypeVarKind {
|
||||
/// `T = TypeVar("T")`
|
||||
Legacy,
|
||||
@@ -7213,7 +7292,7 @@ pub enum TypeVarKind {
|
||||
/// # Ordering
|
||||
/// Ordering is based on the type var instance's salsa-assigned id and not on its values.
|
||||
/// The id may change between runs, or when the type var instance was garbage collected and recreated.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct TypeVarInstance<'db> {
|
||||
/// The name of this TypeVar (e.g. `T`)
|
||||
@@ -7413,7 +7492,18 @@ impl<'db> TypeVarInstance<'db> {
|
||||
}
|
||||
|
||||
/// Where a type variable is bound and usable.
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
Hash,
|
||||
PartialEq,
|
||||
Eq,
|
||||
salsa::Update,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub enum BindingContext<'db> {
|
||||
/// The definition of the generic class, function, or type alias that binds this typevar.
|
||||
Definition(Definition<'db>),
|
||||
@@ -7433,7 +7523,7 @@ impl<'db> BindingContext<'db> {
|
||||
|
||||
/// A type variable that has been bound to a generic context, and which can be specialized to a
|
||||
/// concrete type.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct BoundTypeVarInstance<'db> {
|
||||
pub typevar: TypeVarInstance<'db>,
|
||||
@@ -7534,7 +7624,18 @@ impl<'db> BoundTypeVarInstance<'db> {
|
||||
}
|
||||
|
||||
/// Whether a typevar default is eagerly specified or lazily evaluated.
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
Hash,
|
||||
PartialEq,
|
||||
Eq,
|
||||
salsa::Update,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub enum TypeVarDefaultEvaluation<'db> {
|
||||
/// The default type is lazily evaluated.
|
||||
Lazy,
|
||||
@@ -7549,7 +7650,18 @@ impl<'db> From<Type<'db>> for TypeVarDefaultEvaluation<'db> {
|
||||
}
|
||||
|
||||
/// Whether a typevar bound/constraints is eagerly specified or lazily evaluated.
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
Hash,
|
||||
PartialEq,
|
||||
Eq,
|
||||
salsa::Update,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub enum TypeVarBoundOrConstraintsEvaluation<'db> {
|
||||
/// There is a lazily-evaluated upper bound.
|
||||
LazyUpperBound,
|
||||
@@ -7565,7 +7677,18 @@ impl<'db> From<TypeVarBoundOrConstraints<'db>> for TypeVarBoundOrConstraintsEval
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
Hash,
|
||||
PartialEq,
|
||||
Eq,
|
||||
salsa::Update,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub enum TypeVarBoundOrConstraints<'db> {
|
||||
UpperBound(Type<'db>),
|
||||
Constraints(UnionType<'db>),
|
||||
@@ -8676,7 +8799,7 @@ impl From<bool> for Truthiness {
|
||||
/// # Ordering
|
||||
/// Ordering is based on the bounded method's salsa-assigned id and not on its values.
|
||||
/// The id may change between runs, or when the bounded method was garbage collected and recreated.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct BoundMethodType<'db> {
|
||||
/// The function that is being bound. Corresponds to the `__func__` attribute on a
|
||||
@@ -8790,7 +8913,7 @@ impl<'db> BoundMethodType<'db> {
|
||||
/// # Ordering
|
||||
/// Ordering is based on the callable type's salsa-assigned id and not on its values.
|
||||
/// The id may change between runs, or when the callable type was garbage collected and recreated.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct CallableType<'db> {
|
||||
#[returns(ref)]
|
||||
@@ -8934,7 +9057,18 @@ impl<'db> CallableType<'db> {
|
||||
|
||||
/// Represents a specific instance of `types.MethodWrapperType`
|
||||
#[derive(
|
||||
Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, salsa::Update, get_size2::GetSize,
|
||||
Debug,
|
||||
Copy,
|
||||
Clone,
|
||||
Hash,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
salsa::Update,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub enum MethodWrapperKind<'db> {
|
||||
/// Method wrapper for `some_function.__get__`
|
||||
@@ -9076,7 +9210,18 @@ impl<'db> MethodWrapperKind<'db> {
|
||||
|
||||
/// Represents a specific instance of `types.WrapperDescriptorType`
|
||||
#[derive(
|
||||
Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, salsa::Update, get_size2::GetSize,
|
||||
Debug,
|
||||
Copy,
|
||||
Clone,
|
||||
Hash,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
salsa::Update,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub enum WrapperDescriptorKind {
|
||||
/// `FunctionType.__get__`
|
||||
@@ -9090,7 +9235,7 @@ pub enum WrapperDescriptorKind {
|
||||
/// # Ordering
|
||||
/// Ordering is based on the module literal's salsa-assigned id and not on its values.
|
||||
/// The id may change between runs, or when the module literal was garbage collected and recreated.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct ModuleLiteralType<'db> {
|
||||
/// The imported module.
|
||||
@@ -9200,7 +9345,7 @@ impl<'db> ModuleLiteralType<'db> {
|
||||
/// # Ordering
|
||||
/// Ordering is based on the type alias's salsa-assigned id and not on its values.
|
||||
/// The id may change between runs, or when the alias was garbage collected and recreated.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct PEP695TypeAliasType<'db> {
|
||||
#[returns(ref)]
|
||||
@@ -9262,7 +9407,7 @@ fn value_type_cycle_initial<'db>(_db: &'db dyn Db, _self: PEP695TypeAliasType<'d
|
||||
/// # Ordering
|
||||
/// Ordering is based on the type alias's salsa-assigned id and not on its values.
|
||||
/// The id may change between runs, or when the alias was garbage collected and recreated.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct ManualPEP695TypeAliasType<'db> {
|
||||
#[returns(ref)]
|
||||
@@ -9294,7 +9439,18 @@ impl<'db> ManualPEP695TypeAliasType<'db> {
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, salsa::Update, get_size2::GetSize,
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Hash,
|
||||
salsa::Update,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub enum TypeAliasType<'db> {
|
||||
/// A type alias defined using the PEP 695 `type` statement.
|
||||
@@ -9359,7 +9515,7 @@ pub(super) struct MetaclassCandidate<'db> {
|
||||
explicit_metaclass_of: ClassLiteral<'db>,
|
||||
}
|
||||
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
pub struct UnionType<'db> {
|
||||
/// The union type includes values in any of these types.
|
||||
#[returns(deref)]
|
||||
@@ -9581,7 +9737,7 @@ impl<'db> UnionType<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::interned(debug, heap_size=IntersectionType::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=IntersectionType::heap_size)]
|
||||
pub struct IntersectionType<'db> {
|
||||
/// The intersection type includes only values in all of these types.
|
||||
#[returns(ref)]
|
||||
@@ -9790,7 +9946,7 @@ impl<'db> IntersectionType<'db> {
|
||||
/// # Ordering
|
||||
/// Ordering is based on the string literal's salsa-assigned id and not on its value.
|
||||
/// The id may change between runs, or when the string literal was garbage collected and recreated.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct StringLiteralType<'db> {
|
||||
#[returns(deref)]
|
||||
@@ -9818,7 +9974,7 @@ impl<'db> StringLiteralType<'db> {
|
||||
/// # Ordering
|
||||
/// Ordering is based on the byte literal's salsa-assigned id and not on its value.
|
||||
/// The id may change between runs, or when the byte literal was garbage collected and recreated.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct BytesLiteralType<'db> {
|
||||
#[returns(deref)]
|
||||
@@ -9843,7 +9999,7 @@ impl<'db> BytesLiteralType<'db> {
|
||||
/// NO = 0
|
||||
/// YES = 1
|
||||
/// ```
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct EnumLiteralType<'db> {
|
||||
/// A reference to the enum class this literal belongs to
|
||||
@@ -9864,7 +10020,18 @@ impl<'db> EnumLiteralType<'db> {
|
||||
|
||||
/// Type that represents the set of all inhabitants (`dict` instances) that conform to
|
||||
/// a given `TypedDict` schema.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, salsa::Update, Hash, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Debug,
|
||||
Copy,
|
||||
Clone,
|
||||
PartialEq,
|
||||
Eq,
|
||||
salsa::Update,
|
||||
Hash,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub struct TypedDictType<'db> {
|
||||
/// A reference to the class (inheriting from `typing.TypedDict`) that specifies the
|
||||
/// schema of this `TypedDict`.
|
||||
@@ -9945,7 +10112,17 @@ impl BoundSuperError<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Debug,
|
||||
Copy,
|
||||
Clone,
|
||||
Hash,
|
||||
PartialEq,
|
||||
Eq,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub enum SuperOwnerKind<'db> {
|
||||
Dynamic(DynamicType),
|
||||
Class(ClassType<'db>),
|
||||
@@ -10034,7 +10211,7 @@ impl<'db> From<SuperOwnerKind<'db>> for Type<'db> {
|
||||
}
|
||||
|
||||
/// Represent a bound super object like `super(PivotClass, owner)`
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
pub struct BoundSuperType<'db> {
|
||||
pub pivot_class: ClassBase<'db>,
|
||||
pub owner: SuperOwnerKind<'db>,
|
||||
@@ -10243,7 +10420,7 @@ impl<'db> BoundSuperType<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
pub struct TypeIsType<'db> {
|
||||
return_type: Type<'db>,
|
||||
/// The ID of the scope to which the place belongs
|
||||
|
||||
@@ -244,7 +244,7 @@ impl CodeGeneratorKind {
|
||||
/// # Ordering
|
||||
/// Ordering is based on the generic aliases's salsa-assigned id and not on its values.
|
||||
/// The id may change between runs, or when the alias was garbage collected and recreated.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct GenericAlias<'db> {
|
||||
pub(crate) origin: ClassLiteral<'db>,
|
||||
@@ -365,6 +365,7 @@ impl<'db> VarianceInferable<'db> for GenericAlias<'db> {
|
||||
|
||||
/// Represents a class type, which might be a non-generic class, or a specialization of a generic
|
||||
/// class.
|
||||
#[allow(clippy::unsafe_derive_deserialize)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
@@ -377,6 +378,8 @@ impl<'db> VarianceInferable<'db> for GenericAlias<'db> {
|
||||
salsa::Supertype,
|
||||
salsa::Update,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub enum ClassType<'db> {
|
||||
NonGeneric(ClassLiteral<'db>),
|
||||
@@ -1280,7 +1283,7 @@ impl<'db> Field<'db> {
|
||||
/// # Ordering
|
||||
/// Ordering is based on the class's id assigned by salsa and not on the class literal's values.
|
||||
/// The id may change between runs, or when the class literal was garbage collected and recreated.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct ClassLiteral<'db> {
|
||||
/// Name of the class at definition
|
||||
@@ -3357,7 +3360,17 @@ pub(super) enum SolidBaseKind {
|
||||
/// Feel free to expand this enum if you ever find yourself using the same class in multiple
|
||||
/// places.
|
||||
/// Note: good candidates are any classes in `[crate::module_resolver::module::KnownModule]`
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
#[cfg_attr(test, derive(strum_macros::EnumIter))]
|
||||
pub enum KnownClass {
|
||||
// To figure out where an stdlib symbol is defined, you can go into `crates/ty_vendored`
|
||||
|
||||
@@ -14,7 +14,18 @@ use crate::types::{
|
||||
/// Note that a non-specialized generic class _cannot_ be a class base. When we see a
|
||||
/// non-specialized generic class in any type expression (including the list of base classes), we
|
||||
/// automatically construct the default specialization for that class.
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, salsa::Update, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Debug,
|
||||
Copy,
|
||||
Clone,
|
||||
Hash,
|
||||
PartialEq,
|
||||
Eq,
|
||||
salsa::Update,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub enum ClassBase<'db> {
|
||||
Dynamic(DynamicType),
|
||||
Class(ClassType<'db>),
|
||||
|
||||
@@ -1759,7 +1759,7 @@ declare_lint! {
|
||||
}
|
||||
|
||||
/// A collection of type check diagnostics.
|
||||
#[derive(Default, Eq, PartialEq, get_size2::GetSize)]
|
||||
#[derive(Default, Eq, PartialEq, get_size2::GetSize, serde::Serialize, serde::Deserialize)]
|
||||
pub struct TypeCheckDiagnostics {
|
||||
diagnostics: Vec<Diagnostic>,
|
||||
used_suppressions: FxHashSet<FileSuppressionId>,
|
||||
|
||||
@@ -102,7 +102,7 @@ pub(crate) struct FunctionSpans {
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Hash)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub struct FunctionDecorators: u8 {
|
||||
/// `@classmethod`
|
||||
const CLASSMETHOD = 1 << 0;
|
||||
@@ -160,7 +160,7 @@ bitflags! {
|
||||
/// arguments that were passed in. For the precise meaning of the fields, see [1].
|
||||
///
|
||||
/// [1]: https://docs.python.org/3/library/typing.html#typing.dataclass_transform
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update, serde::Serialize, serde::Deserialize)]
|
||||
pub struct DataclassTransformerParams: u8 {
|
||||
const EQ_DEFAULT = 1 << 0;
|
||||
const ORDER_DEFAULT = 1 << 1;
|
||||
@@ -188,7 +188,7 @@ impl Default for DataclassTransformerParams {
|
||||
/// Ordering is based on the function's id assigned by salsa and not on the function literal's
|
||||
/// values. The id may change between runs, or when the function literal was garbage collected and
|
||||
/// recreated.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct OverloadLiteral<'db> {
|
||||
/// Name of the function at definition.
|
||||
@@ -410,7 +410,7 @@ impl<'db> OverloadLiteral<'db> {
|
||||
/// Ordering is based on the function's id assigned by salsa and not on the function literal's
|
||||
/// values. The id may change between runs, or when the function literal was garbage collected and
|
||||
/// recreated.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct FunctionLiteral<'db> {
|
||||
pub(crate) last_definition: OverloadLiteral<'db>,
|
||||
@@ -616,7 +616,7 @@ impl<'db> FunctionLiteral<'db> {
|
||||
|
||||
/// Represents a function type, which might be a non-generic function, or a specialization of a
|
||||
/// generic function.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct FunctionType<'db> {
|
||||
pub(crate) literal: FunctionLiteral<'db>,
|
||||
@@ -1082,6 +1082,8 @@ fn last_definition_signature_cycle_initial<'db>(
|
||||
strum_macros::EnumString,
|
||||
strum_macros::IntoStaticStr,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
#[cfg_attr(test, derive(strum_macros::EnumIter))]
|
||||
|
||||
@@ -91,7 +91,7 @@ pub(crate) fn bind_typevar<'db>(
|
||||
/// # Ordering
|
||||
/// Ordering is based on the context's salsa-assigned id and not on its values.
|
||||
/// The id may change between runs, or when the context was garbage collected and recreated.
|
||||
#[salsa::interned(debug, heap_size=GenericContext::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=GenericContext::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct GenericContext<'db> {
|
||||
#[returns(ref)]
|
||||
@@ -402,7 +402,7 @@ impl std::fmt::Display for LegacyGenericBase {
|
||||
///
|
||||
/// TODO: Handle nested specializations better, with actual parent links to the specialization of
|
||||
/// the lexically containing context.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
pub struct Specialization<'db> {
|
||||
pub(crate) generic_context: GenericContext<'db>,
|
||||
#[returns(deref)]
|
||||
@@ -688,7 +688,9 @@ impl<'db> Specialization<'db> {
|
||||
///
|
||||
/// You will usually use [`Specialization`] instead of this type. This type is used when we need to
|
||||
/// substitute types for type variables before we have fully constructed a [`Specialization`].
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize, serde::Serialize, serde::Deserialize,
|
||||
)]
|
||||
pub struct PartialSpecialization<'a, 'db> {
|
||||
generic_context: GenericContext<'db>,
|
||||
types: Cow<'a, [Type<'db>]>,
|
||||
|
||||
@@ -135,7 +135,7 @@ use crate::{Db, FxOrderSet, Program};
|
||||
/// Infer all types for a [`ScopeId`], including all definitions and expressions in that scope.
|
||||
/// Use when checking a scope, or needing to provide a type for an arbitrary expression in the
|
||||
/// scope.
|
||||
#[salsa::tracked(returns(ref), cycle_fn=scope_cycle_recover, cycle_initial=scope_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::tracked(persist, returns(ref), cycle_fn=scope_cycle_recover, cycle_initial=scope_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
|
||||
pub(crate) fn infer_scope_types<'db>(db: &'db dyn Db, scope: ScopeId<'db>) -> ScopeInference<'db> {
|
||||
let file = scope.file(db);
|
||||
let _span = tracing::trace_span!("infer_scope_types", scope=?scope.as_id(), ?file).entered();
|
||||
@@ -421,7 +421,9 @@ struct TypeAndRange<'db> {
|
||||
}
|
||||
|
||||
/// The inferred types for a scope region.
|
||||
#[derive(Debug, Eq, PartialEq, salsa::Update, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Debug, Eq, PartialEq, salsa::Update, get_size2::GetSize, serde::Serialize, serde::Deserialize,
|
||||
)]
|
||||
pub(crate) struct ScopeInference<'db> {
|
||||
/// The types of every expression in this region.
|
||||
expressions: FxHashMap<ExpressionNodeKey, Type<'db>>,
|
||||
@@ -430,7 +432,16 @@ pub(crate) struct ScopeInference<'db> {
|
||||
extra: Option<Box<ScopeInferenceExtra>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, get_size2::GetSize, salsa::Update, Default)]
|
||||
#[derive(
|
||||
Debug,
|
||||
Eq,
|
||||
PartialEq,
|
||||
get_size2::GetSize,
|
||||
salsa::Update,
|
||||
Default,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
struct ScopeInferenceExtra {
|
||||
/// The fallback type for missing expressions/bindings/declarations.
|
||||
///
|
||||
|
||||
@@ -117,7 +117,18 @@ impl<'db> Type<'db> {
|
||||
}
|
||||
|
||||
/// A type representing the set of runtime objects which are instances of a certain nominal class.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, salsa::Update, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Copy,
|
||||
Clone,
|
||||
Debug,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Hash,
|
||||
salsa::Update,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub struct NominalInstanceType<'db>(
|
||||
// Keep this field private, so that the only way of constructing `NominalInstanceType` instances
|
||||
// is through the `Type::instance` constructor function.
|
||||
@@ -399,7 +410,18 @@ impl<'db> From<NominalInstanceType<'db>> for Type<'db> {
|
||||
/// [`NominalInstanceType`] is split into two variants internally as a pure
|
||||
/// optimization to avoid having to materialize the [`ClassType`] for tuple
|
||||
/// instances where it would be unnecessary (this is somewhat expensive!).
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, salsa::Update, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Debug,
|
||||
Copy,
|
||||
Clone,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Hash,
|
||||
salsa::Update,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
enum NominalInstanceInner<'db> {
|
||||
/// A tuple type, e.g. `tuple[int, str]`.
|
||||
///
|
||||
@@ -429,7 +451,18 @@ impl<'db> VarianceInferable<'db> for NominalInstanceType<'db> {
|
||||
/// A `ProtocolInstanceType` represents the set of all possible runtime objects
|
||||
/// that conform to the interface described by a certain protocol.
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Eq, PartialEq, Hash, salsa::Update, PartialOrd, Ord, get_size2::GetSize,
|
||||
Copy,
|
||||
Clone,
|
||||
Debug,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Hash,
|
||||
salsa::Update,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub struct ProtocolInstanceType<'db> {
|
||||
pub(super) inner: Protocol<'db>,
|
||||
@@ -633,7 +666,18 @@ impl<'db> VarianceInferable<'db> for ProtocolInstanceType<'db> {
|
||||
/// An enumeration of the two kinds of protocol types: those that originate from a class
|
||||
/// definition in source code, and those that are synthesized from a set of members.
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Eq, PartialEq, Hash, salsa::Update, PartialOrd, Ord, get_size2::GetSize,
|
||||
Copy,
|
||||
Clone,
|
||||
Debug,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Hash,
|
||||
salsa::Update,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub(super) enum Protocol<'db> {
|
||||
FromClass(ClassType<'db>),
|
||||
@@ -685,7 +729,18 @@ mod synthesized_protocol {
|
||||
/// The constructor method of this type maintains the invariant that a synthesized protocol type
|
||||
/// is always constructed from a *normalized* protocol interface.
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Eq, PartialEq, Hash, salsa::Update, PartialOrd, Ord, get_size2::GetSize,
|
||||
Copy,
|
||||
Clone,
|
||||
Debug,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Hash,
|
||||
salsa::Update,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub(in crate::types) struct SynthesizedProtocolType<'db>(ProtocolInterface<'db>);
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ impl<'db> Deref for ProtocolClassLiteral<'db> {
|
||||
/// # Ordering
|
||||
/// Ordering is based on the protocol interface member's salsa-assigned id and not on its members.
|
||||
/// The id may change between runs, or when the protocol instance members was garbage collected and recreated.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub(super) struct ProtocolInterface<'db> {
|
||||
#[returns(ref)]
|
||||
@@ -318,7 +318,17 @@ impl<'db> VarianceInferable<'db> for ProtocolInterface<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash, salsa::Update, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Clone,
|
||||
Hash,
|
||||
salsa::Update,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub(super) struct ProtocolMemberData<'db> {
|
||||
kind: ProtocolMemberKind<'db>,
|
||||
qualifiers: TypeQualifiers,
|
||||
@@ -403,7 +413,18 @@ impl<'db> ProtocolMemberData<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, salsa::Update, Hash, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Debug,
|
||||
Copy,
|
||||
Clone,
|
||||
PartialEq,
|
||||
Eq,
|
||||
salsa::Update,
|
||||
Hash,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
enum ProtocolMemberKind<'db> {
|
||||
Method(CallableType<'db>),
|
||||
Property(PropertyInstanceType<'db>),
|
||||
|
||||
@@ -28,7 +28,17 @@ use ruff_python_ast::{self as ast, name::Name};
|
||||
|
||||
/// The signature of a single callable. If the callable is overloaded, there is a separate
|
||||
/// [`Signature`] for each overload.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
salsa::Update,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub struct CallableSignature<'db> {
|
||||
/// The signatures of each overload of this callable. Will be empty if the type is not
|
||||
/// callable.
|
||||
@@ -247,7 +257,17 @@ impl<'db> VarianceInferable<'db> for &CallableSignature<'db> {
|
||||
}
|
||||
|
||||
/// The signature of one of the overloads of a callable.
|
||||
#[derive(Clone, Debug, salsa::Update, get_size2::GetSize, PartialEq, Eq, Hash)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
salsa::Update,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub struct Signature<'db> {
|
||||
/// The generic context for this overload, if it is generic.
|
||||
pub(crate) generic_context: Option<GenericContext<'db>>,
|
||||
@@ -1015,7 +1035,17 @@ impl<'db> VarianceInferable<'db> for &Signature<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
salsa::Update,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub(crate) struct Parameters<'db> {
|
||||
// TODO: use SmallVec here once invariance bug is fixed
|
||||
value: Vec<Parameter<'db>>,
|
||||
@@ -1314,7 +1344,17 @@ impl<'db> std::ops::Index<usize> for Parameters<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
salsa::Update,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub(crate) struct Parameter<'db> {
|
||||
/// Annotated type of the parameter.
|
||||
annotated_type: Option<Type<'db>>,
|
||||
@@ -1576,7 +1616,17 @@ impl<'db> Parameter<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
salsa::Update,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub(crate) enum ParameterKind<'db> {
|
||||
/// Positional-only parameter, e.g. `def f(x, /): ...`
|
||||
PositionalOnly {
|
||||
@@ -1642,7 +1692,17 @@ impl<'db> ParameterKind<'db> {
|
||||
}
|
||||
|
||||
/// Whether a parameter is used as a value or a type form.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
Eq,
|
||||
Hash,
|
||||
PartialEq,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub(crate) enum ParameterForm {
|
||||
Value,
|
||||
Type,
|
||||
|
||||
@@ -25,6 +25,8 @@ use std::str::FromStr;
|
||||
Ord,
|
||||
strum_macros::EnumString,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub enum SpecialFormType {
|
||||
/// The symbol `typing.Annotated` (which can also be found as `typing_extensions.Annotated`)
|
||||
|
||||
@@ -14,7 +14,18 @@ use crate::{Db, FxOrderSet};
|
||||
use super::{TypeVarBoundOrConstraints, TypeVarKind, TypeVarVariance};
|
||||
|
||||
/// A type that represents `type[C]`, i.e. the class object `C` and class objects that are subclasses of `C`.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
salsa::Update,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub struct SubclassOfType<'db> {
|
||||
// Keep this field private, so that the only way of constructing the struct is through the `from` method.
|
||||
subclass_of: SubclassOfInner<'db>,
|
||||
@@ -249,7 +260,18 @@ impl<'db> VarianceInferable<'db> for SubclassOfType<'db> {
|
||||
/// Note that this enum is similar to the [`super::ClassBase`] enum,
|
||||
/// but does not include the `ClassBase::Protocol` and `ClassBase::Generic` variants
|
||||
/// (`type[Protocol]` and `type[Generic]` are not valid types).
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
salsa::Update,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub(crate) enum SubclassOfInner<'db> {
|
||||
Class(ClassType<'db>),
|
||||
Dynamic(DynamicType),
|
||||
|
||||
@@ -126,7 +126,7 @@ impl TupleLength {
|
||||
/// # Ordering
|
||||
/// Ordering is based on the tuple's salsa-assigned id and not on its elements.
|
||||
/// The id may change between runs, or when the tuple was garbage collected and recreated.
|
||||
#[salsa::interned(debug, constructor=new_internal, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::interned(persist, debug, constructor=new_internal, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct TupleType<'db> {
|
||||
#[returns(ref)]
|
||||
@@ -316,7 +316,9 @@ pub(crate) type TupleSpec<'db> = Tuple<Type<'db>>;
|
||||
///
|
||||
/// Our tuple representation can hold instances of any Rust type. For tuples containing Python
|
||||
/// types, use [`TupleSpec`], which defines some additional type-specific methods.
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize, serde::Serialize, serde::Deserialize,
|
||||
)]
|
||||
pub struct FixedLengthTuple<T>(Box<[T]>);
|
||||
|
||||
impl<T> FixedLengthTuple<T> {
|
||||
@@ -523,7 +525,9 @@ impl<'db> PySlice<'db> for FixedLengthTuple<Type<'db>> {
|
||||
///
|
||||
/// Our tuple representation can hold instances of any Rust type. For tuples containing Python
|
||||
/// types, use [`TupleSpec`], which defines some additional type-specific methods.
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize, serde::Serialize, serde::Deserialize,
|
||||
)]
|
||||
pub struct VariableLengthTuple<T> {
|
||||
pub(crate) prefix: Box<[T]>,
|
||||
pub(crate) variable: T,
|
||||
@@ -964,7 +968,9 @@ impl<'db> PyIndex<'db> for &VariableLengthTuple<Type<'db>> {
|
||||
///
|
||||
/// Our tuple representation can hold instances of any Rust type. For tuples containing Python
|
||||
/// types, use [`TupleSpec`], which defines some additional type-specific methods.
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize, serde::Serialize, serde::Deserialize,
|
||||
)]
|
||||
pub enum Tuple<T> {
|
||||
Fixed(FixedLengthTuple<T>),
|
||||
Variable(VariableLengthTuple<T>),
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
use crate::{Db, types::BoundTypeVarInstance};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
Hash,
|
||||
PartialEq,
|
||||
Eq,
|
||||
salsa::Update,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub enum TypeVarVariance {
|
||||
Invariant,
|
||||
Covariant,
|
||||
|
||||
@@ -26,7 +26,7 @@ use crate::semantic_index::scope::{FileScopeId, ScopeId};
|
||||
/// * a return type of a cross-module query
|
||||
/// * a field of a type that is a return type of a cross-module query
|
||||
/// * an argument of a cross-module query
|
||||
#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[salsa::tracked(persist, debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
pub(crate) struct Unpack<'db> {
|
||||
pub(crate) file: File,
|
||||
|
||||
@@ -70,7 +70,16 @@ impl<'db> Unpack<'db> {
|
||||
}
|
||||
|
||||
/// The expression that is being unpacked.
|
||||
#[derive(Clone, Copy, Debug, Hash, salsa::Update, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
Hash,
|
||||
salsa::Update,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub(crate) struct UnpackValue<'db> {
|
||||
/// The kind of unpack expression
|
||||
kind: UnpackKind,
|
||||
@@ -102,7 +111,16 @@ impl<'db> UnpackValue<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Hash, salsa::Update, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
Hash,
|
||||
salsa::Update,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub(crate) enum EvaluationMode {
|
||||
Sync,
|
||||
Async,
|
||||
@@ -122,7 +140,16 @@ impl EvaluationMode {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Hash, salsa::Update, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
Hash,
|
||||
salsa::Update,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub(crate) enum UnpackKind {
|
||||
/// An iterable expression like the one in a `for` loop or a comprehension.
|
||||
Iterable { mode: EvaluationMode },
|
||||
@@ -133,7 +160,17 @@ pub(crate) enum UnpackKind {
|
||||
}
|
||||
|
||||
/// The position of the target element in an unpacking.
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, salsa::Update, get_size2::GetSize)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
Hash,
|
||||
PartialEq,
|
||||
salsa::Update,
|
||||
get_size2::GetSize,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
)]
|
||||
pub(crate) enum UnpackPosition {
|
||||
/// The target element is in the first position of the unpacking.
|
||||
First,
|
||||
|
||||
@@ -199,6 +199,15 @@ impl System for LSPSystem {
|
||||
self.native_system.path_exists_case_sensitive(path, prefix)
|
||||
}
|
||||
|
||||
fn read_to_end(&self, path: &SystemPath) -> Result<Vec<u8>> {
|
||||
let document = self.system_path_to_document_ref(path);
|
||||
|
||||
match document {
|
||||
Some(DocumentQuery::Text { document, .. }) => Ok(document.contents().into()),
|
||||
_ => self.native_system.read_to_end(path),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_to_string(&self, path: &SystemPath) -> Result<String> {
|
||||
let document = self.system_path_to_document_ref(path);
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ impl EnvVars {
|
||||
pub const TY_LOG: &'static str = "TY_LOG";
|
||||
|
||||
/// If set to `"1"` or `"true"`, ty will enable flamegraph profiling.
|
||||
///
|
||||
/// This creates a `tracing.folded` file that can be used to generate flame graphs
|
||||
/// for performance analysis.
|
||||
pub const TY_LOG_PROFILE: &'static str = "TY_LOG_PROFILE";
|
||||
@@ -39,6 +40,14 @@ impl EnvVars {
|
||||
/// when necessary, e.g. to watch for file system changes or a dedicated UI thread.
|
||||
pub const TY_MAX_PARALLELISM: &'static str = "TY_MAX_PARALLELISM";
|
||||
|
||||
/// Used to enable persistent caching at the specified path.
|
||||
///
|
||||
/// This improves performance and memory usage by caching to disk across ty runs.
|
||||
/// Note that this feature is currently experimental and may result in decreased
|
||||
/// performance.
|
||||
#[attr_hidden]
|
||||
pub const TY_PERSIST: &'static str = "TY_PERSIST";
|
||||
|
||||
/// Used to detect an activated virtual environment.
|
||||
pub const VIRTUAL_ENV: &'static str = "VIRTUAL_ENV";
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ ruff_python_trivia = { workspace = true }
|
||||
ruff_source_file = { workspace = true }
|
||||
ruff_text_size = { workspace = true }
|
||||
ruff_python_ast = { workspace = true }
|
||||
ty_python_semantic = { workspace = true, features = ["serde", "testing"] }
|
||||
ty_python_semantic = { workspace = true, features = ["testing"] }
|
||||
ty_static = { workspace = true }
|
||||
ty_vendored = { workspace = true }
|
||||
|
||||
|
||||
@@ -186,6 +186,10 @@ impl System for MdtestSystem {
|
||||
}
|
||||
}
|
||||
|
||||
fn read_to_end(&self, path: &SystemPath) -> ruff_db::system::Result<Vec<u8>> {
|
||||
self.as_system().read_to_end(&self.normalize_path(path))
|
||||
}
|
||||
|
||||
fn read_to_string(&self, path: &SystemPath) -> ruff_db::system::Result<String> {
|
||||
self.as_system().read_to_string(&self.normalize_path(path))
|
||||
}
|
||||
@@ -279,7 +283,7 @@ impl WritableSystem for MdtestSystem {
|
||||
self.as_system().create_new_file(&self.normalize_path(path))
|
||||
}
|
||||
|
||||
fn write_file(&self, path: &SystemPath, content: &str) -> ruff_db::system::Result<()> {
|
||||
fn write_file(&self, path: &SystemPath, content: &[u8]) -> ruff_db::system::Result<()> {
|
||||
self.as_system()
|
||||
.write_file(&self.normalize_path(path), content)
|
||||
}
|
||||
|
||||
@@ -1154,6 +1154,10 @@ impl System for WasmSystem {
|
||||
self.fs.canonicalize(path)
|
||||
}
|
||||
|
||||
fn read_to_end(&self, path: &SystemPath) -> ruff_db::system::Result<Vec<u8>> {
|
||||
self.fs.read_to_end(path)
|
||||
}
|
||||
|
||||
fn read_to_string(&self, path: &SystemPath) -> ruff_db::system::Result<String> {
|
||||
self.fs.read_to_string(path)
|
||||
}
|
||||
|
||||
@@ -30,11 +30,12 @@ ty_python_semantic = { path = "../crates/ty_python_semantic" }
|
||||
ty_vendored = { path = "../crates/ty_vendored" }
|
||||
|
||||
libfuzzer-sys = { git = "https://github.com/rust-fuzz/libfuzzer", default-features = false }
|
||||
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "a3ffa22cb26756473d56f867aedec3fd907c4dd9", default-features = false, features = [
|
||||
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "a0e7a06", default-features = false, features = [
|
||||
"compact_str",
|
||||
"macros",
|
||||
"salsa_unstable",
|
||||
"inventory",
|
||||
"persistence",
|
||||
] }
|
||||
similar = { version = "2.5.0" }
|
||||
tracing = { version = "0.1.40" }
|
||||
|
||||
Reference in New Issue
Block a user