mirror of
https://github.com/astral-sh/ruff.git
synced 2026-10-28 10:45:37 +08:00
[ty] Support different type inference settings in a single Salsa DB (#27152)
## Summary This is the last PR that adds support for type checking multiple programs with different Python Version, search paths, and platform using a single Salsa DB. This PR changes `Program` from a singleton input to an interned struct. `ProgramFile` changes from `(File, ResolverEnvironment)` to `(File, Program)`. Changing `Program` from an input to an interned means that it now has a `'db` lifetime. This requires changing how we construct and store `Program`. That's why some of the db initialization code changes. I doubt that it will be noticeable. But this should speed up mdtests that contain tests with different Python versions (e.g. Py312, Py311, Py312, Py311), because we now reuse parsed modules, and even type inference results because the inference is now keyed by Program (instead of updating an existing Program) ## Memory regression I had codex look into the CodSpeed reported memory regression. The reason is that `Program` now being an interned and being initialized later changes the assigned `File` ids, which in turn changes in which order we iterate over the project files. And this change the max peak memory usage. It can also change the usage reported on the memory usage report (we enter fixpoint cycles from different heads, etc). The changes in the memory report are mainly from the new `ProgramFile::python_version` (needed to mitigate a larger regression, but we can reconsider), and that `Project` now stores `ProgramSettings`.
This commit is contained in:
1 parent
95734a549e
commit
aa5b04ad92
48 files changed
+617
-531
No files matched your search
Generated
-1
@@ -3163,7 +3163,6 @@ dependencies = [
|
||||
"tracing",
|
||||
"ty_module_resolver",
|
||||
"ty_project",
|
||||
"ty_python_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -23,7 +23,6 @@ ruff_python_formatter = { workspace = true, optional = true }
|
||||
ruff_python_parser = { workspace = true, optional = true }
|
||||
ruff_python_trivia = { workspace = true, optional = true }
|
||||
ty_module_resolver = { workspace = true, optional = true }
|
||||
ty_python_core = { workspace = true, optional = true }
|
||||
ty_project = { workspace = true, optional = true }
|
||||
|
||||
anyhow = { workspace = true }
|
||||
@@ -61,7 +60,7 @@ ruff_instrumented = [
|
||||
# Enables the ty instrumented benchmarks
|
||||
ty_instrumented = ["criterion", "ty_project", "ruff_python_trivia"]
|
||||
# Enables the module-resolution benchmark
|
||||
module_resolution = ["divan", "ty_module_resolver", "ty_project", "ty_python_core"]
|
||||
module_resolution = ["divan", "ty_module_resolver", "ty_project"]
|
||||
codspeed = ["codspeed-criterion-compat"]
|
||||
# Enables the ty_walltime benchmarks
|
||||
ty_walltime = ["ruff_db/os", "ty_project", "divan"]
|
||||
@@ -114,7 +113,6 @@ ignored = [
|
||||
"ruff_python_parser",
|
||||
"ruff_python_trivia",
|
||||
"ty_module_resolver",
|
||||
"ty_python_core",
|
||||
"mimalloc",
|
||||
"tikv-jemallocator"
|
||||
]
|
||||
@@ -12,8 +12,7 @@ use ty_module_resolver::{ImportingFile, ModuleName, resolve_module};
|
||||
use ty_project::metadata::options::{EnvironmentOptions, Options};
|
||||
use ty_project::metadata::python_version::SupportedPythonVersion;
|
||||
use ty_project::metadata::value::RelativePathBuf;
|
||||
use ty_project::{ProjectDatabase, ProjectMetadata};
|
||||
use ty_python_core::program::Program;
|
||||
use ty_project::{Db as _, ProjectDatabase, ProjectMetadata};
|
||||
|
||||
const SEEDED_TARGETS: &[&str] = &["target_0", "target_1", "target_2", "target_3", "target_4"];
|
||||
// Exercise stub-overlay discovery followed by normal fallback.
|
||||
@@ -73,8 +72,10 @@ fn setup_case(n: usize) -> Case {
|
||||
});
|
||||
|
||||
let db = ProjectDatabase::fallible(metadata, system).unwrap();
|
||||
// Intern the resolver environment before timing so its initial allocation is not benchmarked.
|
||||
let _ = Program::get(&db).resolver_environment(&db);
|
||||
|
||||
// Keep lazy program and resolver initialization out of the measured module-resolution queries.
|
||||
let _ = db.project().program(&db).resolver_environment(&db);
|
||||
|
||||
let importing_file = system_path_to_file(&db, &importing_path).unwrap();
|
||||
|
||||
let resolves = SEEDED_TARGETS
|
||||
@@ -96,7 +97,11 @@ fn ty_module_resolver<const PATHS: usize>(bencher: Bencher) {
|
||||
bencher
|
||||
.with_inputs(|| setup_case(PATHS))
|
||||
.bench_local_refs(|case| {
|
||||
let environment = Program::get(&case.db).resolver_environment(&case.db);
|
||||
let environment = case
|
||||
.db
|
||||
.project()
|
||||
.program(&case.db)
|
||||
.resolver_environment(&case.db);
|
||||
for name in &case.resolves {
|
||||
black_box(resolve_module(
|
||||
&case.db,
|
||||
|
||||
@@ -19,7 +19,6 @@ use ty_project::metadata::value::{RelativeGlobPattern, RelativePathBuf};
|
||||
use ty_project::watch::{ChangeEvent, ProjectWatcher, directory_watcher};
|
||||
use ty_project::{ChangeResult, Db, ProjectDatabase, ProjectMetadata};
|
||||
use ty_python_core::platform::PythonPlatform;
|
||||
use ty_python_core::program::Program;
|
||||
use ty_static::EnvVars;
|
||||
|
||||
struct TestCase {
|
||||
@@ -39,7 +38,7 @@ fn resolve_module_confident<'db>(
|
||||
) -> Option<Module<'db>> {
|
||||
ty_module_resolver::resolve_module_confident(
|
||||
db,
|
||||
Program::get(db).resolver_environment(db),
|
||||
db.project().program(db).resolver_environment(db),
|
||||
module_name,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -892,8 +892,6 @@ mod tests {
|
||||
let mut db =
|
||||
ty_project::TestDb::new(ProjectMetadata::new("test", SystemPathBuf::from("/")));
|
||||
|
||||
db.init_program().unwrap();
|
||||
|
||||
let mut cleansed = dedent(source).to_string();
|
||||
|
||||
let start = cleansed
|
||||
|
||||
@@ -819,8 +819,6 @@ mod tests {
|
||||
let mut db =
|
||||
ty_project::TestDb::new(ProjectMetadata::new("test", SystemPathBuf::from("/")));
|
||||
|
||||
db.init_program().unwrap();
|
||||
|
||||
let source = dedent(source);
|
||||
|
||||
let start = source.find(START);
|
||||
|
||||
@@ -409,7 +409,7 @@ mod tests {
|
||||
use ruff_db::files::{File, FileRootKind, system_path_to_file};
|
||||
use ruff_db::parsed::{ParsedModuleRef, parsed_module};
|
||||
use ruff_db::source::{SourceText, source_text};
|
||||
use ruff_db::system::{DbWithTestSystem, DbWithWritableSystem, SystemPath, SystemPathBuf};
|
||||
use ruff_db::system::{DbWithWritableSystem, SystemPath, SystemPathBuf};
|
||||
use ruff_python_ast::PythonVersion;
|
||||
use ruff_python_codegen::Stylist;
|
||||
use ruff_python_trivia::textwrap::dedent;
|
||||
@@ -418,7 +418,7 @@ mod tests {
|
||||
use ty_project::{Db as _, ProjectMetadata, SemanticDb as _};
|
||||
use ty_python_core::ProgramFile;
|
||||
use ty_python_core::platform::PythonPlatform;
|
||||
use ty_python_core::program::{FallibleStrategy, Program, ProgramSettings};
|
||||
use ty_python_core::program::{FallibleStrategy, ProgramSettings};
|
||||
use ty_python_semantic::PythonVersionWithSource;
|
||||
|
||||
/// A way to create a simple single-file (named `main.py`) cursor test.
|
||||
@@ -525,10 +525,9 @@ mod tests {
|
||||
let mut db =
|
||||
ty_project::TestDb::new(ProjectMetadata::new("test", SystemPathBuf::from("/")));
|
||||
|
||||
db.init_program_with_python_version(
|
||||
self.python_version.unwrap_or_else(PythonVersion::latest_ty),
|
||||
)
|
||||
.unwrap();
|
||||
if let Some(python_version) = self.python_version {
|
||||
db.set_python_version(python_version);
|
||||
}
|
||||
|
||||
let mut cursor: Option<Cursor> = None;
|
||||
for &Source {
|
||||
@@ -656,7 +655,7 @@ mod tests {
|
||||
let mut db =
|
||||
ty_project::TestDb::new(ProjectMetadata::new("test", project_root.clone()));
|
||||
|
||||
// Write site-packages files first (before init)
|
||||
// Write site-packages files first.
|
||||
for Source {
|
||||
path,
|
||||
contents,
|
||||
@@ -668,11 +667,6 @@ mod tests {
|
||||
.expect("write to memory file system to be successful");
|
||||
}
|
||||
|
||||
// Create /src directory for first-party code
|
||||
db.memory_file_system()
|
||||
.create_directory_all(&project_root)
|
||||
.expect("create /src directory");
|
||||
|
||||
// Configure search paths with site-packages
|
||||
let search_paths = SearchPathSettings {
|
||||
src_roots: vec![project_root.clone()],
|
||||
@@ -682,8 +676,8 @@ mod tests {
|
||||
.to_search_paths(db.system(), db.vendored(), &FallibleStrategy)
|
||||
.expect("valid search paths");
|
||||
|
||||
Program::from_settings(
|
||||
&db,
|
||||
db.project().update_program(
|
||||
&mut db,
|
||||
ProgramSettings {
|
||||
python_version: PythonVersionWithSource::default(),
|
||||
python_platform: PythonPlatform::default(),
|
||||
|
||||
@@ -4708,8 +4708,6 @@ from pathlib import Missing as Alias
|
||||
let mut db =
|
||||
ty_project::TestDb::new(ProjectMetadata::new("test", SystemPathBuf::from("/")));
|
||||
|
||||
db.init_program().unwrap();
|
||||
|
||||
let path = SystemPath::new("src/main.py");
|
||||
db.write_file(path, ruff_python_trivia::textwrap::dedent(source))
|
||||
.expect("Write to memory file system to always succeed");
|
||||
|
||||
@@ -3229,8 +3229,7 @@ class C: ...
|
||||
let metadata = ProjectMetadata::new("test", SystemPathBuf::from("/"));
|
||||
let mut db = TestDb::new(metadata);
|
||||
|
||||
db.init_program_with_python_version(self.python_version.unwrap_or_default())
|
||||
.unwrap();
|
||||
db.set_python_version(self.python_version.unwrap_or_default());
|
||||
|
||||
for Source { path, contents } in &self.sources {
|
||||
db.write_file(path, contents)
|
||||
|
||||
@@ -15,7 +15,6 @@ pub fn workspace_symbols(db: &dyn Db, query: &str) -> Vec<WorkspaceSymbolInfo> {
|
||||
let _span = workspace_symbols_span.enter();
|
||||
|
||||
let project = db.project();
|
||||
|
||||
let query = QueryPattern::fuzzy(query);
|
||||
let files = project.files(db);
|
||||
let files: Vec<_> = files.iter().copied().collect();
|
||||
|
||||
@@ -836,9 +836,8 @@ impl SearchPaths {
|
||||
|
||||
/// Returns a new `SearchPaths` with no search paths configured.
|
||||
///
|
||||
/// This is primarily useful for testing.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn empty(vendored: &VendoredFileSystem) -> Self {
|
||||
/// The vendored standard library remains available.
|
||||
pub fn empty(vendored: &VendoredFileSystem) -> Self {
|
||||
Self {
|
||||
static_paths: vec![],
|
||||
stdlib_path: Some(SearchPath::vendored_stdlib()),
|
||||
|
||||
+63
-74
@@ -13,12 +13,9 @@ use ruff_db::diagnostic::Diagnostic;
|
||||
use ruff_db::files::{File, Files};
|
||||
use ruff_db::system::System;
|
||||
use ruff_db::vendored::VendoredFileSystem;
|
||||
use ruff_python_ast::PythonVersion;
|
||||
use salsa::{Database, Event, Setter};
|
||||
use ty_python_core::ProgramFile;
|
||||
use ty_python_core::program::{
|
||||
FallibleStrategy, MisconfigurationStrategy, Program, UseDefaultStrategy,
|
||||
};
|
||||
use ty_python_core::program::{FallibleStrategy, MisconfigurationStrategy, UseDefaultStrategy};
|
||||
use ty_python_semantic::lint::{LintRegistry, RuleSelection};
|
||||
use ty_python_semantic::{AnalysisSettings, Db as SemanticDb};
|
||||
|
||||
@@ -26,9 +23,6 @@ mod changes;
|
||||
|
||||
#[salsa::db]
|
||||
pub trait Db: SemanticDb {
|
||||
/// Returns the Python version for files in the primary environment.
|
||||
fn python_version(&self) -> PythonVersion;
|
||||
|
||||
fn project(&self) -> Project;
|
||||
|
||||
fn dyn_clone(&self) -> Box<dyn Db>;
|
||||
@@ -87,16 +81,12 @@ impl ProjectDatabase {
|
||||
|
||||
/// Permanently freezes the most heavily read inputs that are immutable during a one-shot check.
|
||||
///
|
||||
/// This is intentionally not exhaustive. It includes every [`Program`] input, the most heavily
|
||||
/// This is intentionally not exhaustive. It includes the program, the most heavily
|
||||
/// read immutable [`Project`] inputs, and every field on files created after this call. Existing
|
||||
/// files retain their durability. This must not be used by incremental consumers or checks that
|
||||
/// apply fixes.
|
||||
pub fn freeze(&mut self) {
|
||||
let program = Program::try_get(self).expect("the program should be initialized");
|
||||
let project = self.project();
|
||||
|
||||
program.freeze(self);
|
||||
project.freeze(self);
|
||||
self.project().freeze(self);
|
||||
self.files.freeze();
|
||||
}
|
||||
|
||||
@@ -142,28 +132,30 @@ impl ProjectDatabase {
|
||||
|
||||
let merged_options = project_metadata.to_merged_options();
|
||||
|
||||
// Initialize the `Program` singleton
|
||||
let (program_settings, program_settings_diagnostics) = strategy
|
||||
.to_anyhow(merged_options.to_program_settings(db.system(), db.vendored(), strategy))?;
|
||||
|
||||
// This must be called before `from_settings`, or the `SearchPath` root
|
||||
// This must be called before `from_metadata`, or the `SearchPath` root
|
||||
// will take precedence over the `Project` root, resulting in
|
||||
// all project files having HIGH durability.
|
||||
project_metadata.try_add_project_root(&db);
|
||||
|
||||
Program::from_settings(&db, program_settings);
|
||||
|
||||
let (settings, settings_diagnostics) = strategy
|
||||
let (settings, mut settings_diagnostics) = strategy
|
||||
.map_err(merged_options.to_settings(&db, strategy), |error| {
|
||||
anyhow::anyhow!("{}", error.pretty(&db))
|
||||
})?;
|
||||
settings_diagnostics.extend(
|
||||
program_settings_diagnostics
|
||||
.into_iter()
|
||||
.map(|diagnostic| diagnostic.into_diagnostic(&db)),
|
||||
);
|
||||
|
||||
db.project = Some(Project::from_metadata(
|
||||
&db,
|
||||
project_metadata,
|
||||
settings,
|
||||
program_settings,
|
||||
settings_diagnostics,
|
||||
program_settings_diagnostics,
|
||||
));
|
||||
|
||||
Ok(db)
|
||||
@@ -547,7 +539,7 @@ impl SemanticDb for ProjectDatabase {
|
||||
}
|
||||
|
||||
fn program_file(&self, file: File) -> ProgramFile<'_> {
|
||||
Program::get(self).program_file(self, file)
|
||||
self.project().program(self).program_file(self, file)
|
||||
}
|
||||
|
||||
fn rule_selection(&self, file: File) -> &RuleSelection {
|
||||
@@ -610,10 +602,6 @@ impl salsa::Database for ProjectDatabase {}
|
||||
|
||||
#[salsa::db]
|
||||
impl Db for ProjectDatabase {
|
||||
fn python_version(&self) -> PythonVersion {
|
||||
Program::get(self).python_version(self)
|
||||
}
|
||||
|
||||
fn project(&self) -> Project {
|
||||
self.project.unwrap()
|
||||
}
|
||||
@@ -625,15 +613,17 @@ impl Db for ProjectDatabase {
|
||||
|
||||
#[cfg(feature = "format")]
|
||||
mod format {
|
||||
use crate::{Db as _, ProjectDatabase};
|
||||
use crate::ProjectDatabase;
|
||||
use ruff_db::files::File;
|
||||
use ruff_python_formatter::{Db as FormatDb, PyFormatOptions};
|
||||
use ty_python_semantic::Db as _;
|
||||
|
||||
#[salsa::db]
|
||||
impl FormatDb for ProjectDatabase {
|
||||
fn format_options(&self, file: File) -> PyFormatOptions {
|
||||
let source_ty = file.source_type(self);
|
||||
PyFormatOptions::from_source_type(source_ty).with_target_version(self.python_version())
|
||||
PyFormatOptions::from_source_type(source_ty)
|
||||
.with_target_version(self.program_file(file).python_version(self))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -648,13 +638,16 @@ pub(crate) mod testing {
|
||||
use ruff_db::files::{File, FileRootKind, Files};
|
||||
use ruff_db::system::{DbWithTestSystem, System, TestSystem};
|
||||
use ruff_db::vendored::VendoredFileSystem;
|
||||
#[cfg(feature = "testing")]
|
||||
use ruff_python_ast::PythonVersion;
|
||||
use ty_module_resolver::SearchPathSettings;
|
||||
use ty_python_core::ProgramFile;
|
||||
use ty_python_core::platform::PythonPlatform;
|
||||
use ty_python_core::program::{FallibleStrategy, Program, ProgramSettings};
|
||||
use ty_python_core::program::{FallibleStrategy, ProgramSettings};
|
||||
#[cfg(feature = "testing")]
|
||||
use ty_python_semantic::ProgramEnvironment;
|
||||
use ty_python_semantic::lint::{LintRegistry, RuleSelection};
|
||||
use ty_python_semantic::{AnalysisSettings, ProgramEnvironment, PythonVersionWithSource};
|
||||
use ty_python_semantic::{AnalysisSettings, PythonVersionWithSource};
|
||||
|
||||
use crate::db::Db;
|
||||
use crate::{Project, ProjectMetadata};
|
||||
@@ -694,51 +687,52 @@ pub(crate) mod testing {
|
||||
.to_merged_options()
|
||||
.to_settings(&db, &FallibleStrategy)
|
||||
.unwrap();
|
||||
let project =
|
||||
Project::from_metadata(&db, project, settings, settings_diagnostics, Vec::new());
|
||||
let root = project.root().to_path_buf();
|
||||
db.system
|
||||
.memory_file_system()
|
||||
.create_directory_all(&root)
|
||||
.expect("create project root");
|
||||
let search_paths = SearchPathSettings::new(vec![root.clone()])
|
||||
.to_search_paths(db.system(), db.vendored(), &FallibleStrategy)
|
||||
.expect("Valid search path settings");
|
||||
|
||||
db.files().try_add_root(&db, &root, FileRootKind::Project);
|
||||
|
||||
let program_settings = ProgramSettings {
|
||||
python_version: PythonVersionWithSource::default(),
|
||||
python_platform: PythonPlatform::default(),
|
||||
search_paths,
|
||||
};
|
||||
let project = Project::from_metadata(
|
||||
&db,
|
||||
project,
|
||||
settings,
|
||||
program_settings,
|
||||
settings_diagnostics,
|
||||
);
|
||||
db.project = Some(project);
|
||||
db
|
||||
}
|
||||
|
||||
pub fn init_program(&mut self) -> anyhow::Result<()> {
|
||||
self.init_program_with_python_version(PythonVersion::latest_ty())
|
||||
}
|
||||
|
||||
pub fn init_program_with_python_version(
|
||||
&mut self,
|
||||
python_version: PythonVersion,
|
||||
) -> anyhow::Result<()> {
|
||||
let root = self.project().root(self);
|
||||
|
||||
let search_paths = SearchPathSettings::new(vec![root.to_path_buf()])
|
||||
.to_search_paths(self.system(), self.vendored(), &FallibleStrategy)
|
||||
.expect("Valid search path settings");
|
||||
|
||||
self.files().try_add_root(self, root, FileRootKind::Project);
|
||||
|
||||
Program::from_settings(
|
||||
self,
|
||||
ProgramSettings {
|
||||
python_version: PythonVersionWithSource {
|
||||
source: ty_python_semantic::PythonVersionSource::Default,
|
||||
version: python_version,
|
||||
},
|
||||
python_platform: PythonPlatform::default(),
|
||||
search_paths,
|
||||
#[cfg(feature = "testing")]
|
||||
pub fn set_python_version(&mut self, python_version: PythonVersion) {
|
||||
let program = self.project().program(self);
|
||||
let settings = ProgramSettings {
|
||||
python_version: PythonVersionWithSource {
|
||||
source: ty_python_semantic::PythonVersionSource::Default,
|
||||
version: python_version,
|
||||
},
|
||||
);
|
||||
|
||||
Ok(())
|
||||
python_platform: program.python_platform(self).clone(),
|
||||
search_paths: program.search_paths(self).clone(),
|
||||
};
|
||||
self.project().update_program(self, settings);
|
||||
}
|
||||
}
|
||||
|
||||
impl TestDb {
|
||||
pub fn python_version(&self) -> PythonVersion {
|
||||
Program::get(self).python_version(self)
|
||||
}
|
||||
|
||||
#[cfg(feature = "testing")]
|
||||
pub fn program_environment(&self) -> ProgramEnvironment<'_> {
|
||||
ProgramEnvironment::from_program(Program::get(self).resolver_environment(self))
|
||||
ProgramEnvironment::from_program(self.project().program(self))
|
||||
}
|
||||
|
||||
/// Takes the salsa events.
|
||||
@@ -786,15 +780,15 @@ pub(crate) mod testing {
|
||||
|
||||
#[salsa::db]
|
||||
impl ty_python_semantic::Db for TestDb {
|
||||
fn program_file(&self, file: File) -> ProgramFile<'_> {
|
||||
self.project().program(self).program_file(self, file)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_file(&self, file: File) -> Vec<Diagnostic> {
|
||||
crate::check_file(self, file)
|
||||
}
|
||||
|
||||
fn program_file(&self, file: File) -> ProgramFile<'_> {
|
||||
Program::get(self).program_file(self, file)
|
||||
}
|
||||
|
||||
fn rule_selection(&self, _file: ruff_db::files::File) -> &RuleSelection {
|
||||
self.project().rules(self)
|
||||
}
|
||||
@@ -822,10 +816,6 @@ pub(crate) mod testing {
|
||||
|
||||
#[salsa::db]
|
||||
impl Db for TestDb {
|
||||
fn python_version(&self) -> PythonVersion {
|
||||
Program::get(self).python_version(self)
|
||||
}
|
||||
|
||||
fn project(&self) -> Project {
|
||||
self.project.unwrap()
|
||||
}
|
||||
@@ -845,9 +835,8 @@ mod tests {
|
||||
use ruff_db::files::FileRootKind;
|
||||
use ruff_db::system::{SystemPathBuf, TestSystem};
|
||||
use ty_module_resolver::list_modules;
|
||||
use ty_python_core::program::Program;
|
||||
|
||||
use crate::{ProjectDatabase, ProjectMetadata};
|
||||
use crate::{Db as _, ProjectDatabase, ProjectMetadata};
|
||||
|
||||
#[test]
|
||||
fn frozen_inputs_support_a_one_shot_check() -> anyhow::Result<()> {
|
||||
@@ -891,7 +880,7 @@ mod tests {
|
||||
let metadata = ProjectMetadata::discover(&project, &system)?;
|
||||
let db = ProjectDatabase::fallible(metadata, system)?;
|
||||
|
||||
let modules = list_modules(&db, Program::get(&db).resolver_environment(&db));
|
||||
let modules = list_modules(&db, db.project().program(&db).resolver_environment(&db));
|
||||
assert!(
|
||||
modules
|
||||
.iter()
|
||||
|
||||
@@ -8,7 +8,7 @@ use ruff_db::Db as _;
|
||||
use ruff_db::files::{File, Files, system_path_to_file};
|
||||
use ruff_db::system::{SystemPath, SystemPathBuf};
|
||||
use rustc_hash::FxHashSet;
|
||||
use ty_python_core::program::{FallibleStrategy, Program};
|
||||
use ty_python_core::program::FallibleStrategy;
|
||||
|
||||
/// Represents the result of applying changes to the project database.
|
||||
pub struct ChangeResult {
|
||||
@@ -34,7 +34,7 @@ impl ProjectDatabase {
|
||||
let project = self.project();
|
||||
let project_root = project.root(self).to_path_buf();
|
||||
let configuration_paths = ConfigurationPaths::from_metadata(project.metadata(self));
|
||||
let program = Program::get(self);
|
||||
let program = self.project().program(self);
|
||||
let custom_stdlib_versions_path = program
|
||||
.custom_stdlib_search_path(self)
|
||||
.map(|path| path.join("VERSIONS"));
|
||||
@@ -253,8 +253,7 @@ impl ProjectDatabase {
|
||||
&FallibleStrategy,
|
||||
) {
|
||||
Ok((program_settings, diagnostics)) => {
|
||||
let program = Program::get(self);
|
||||
program.update_from_settings(self, program_settings);
|
||||
project.update_program(self, program_settings);
|
||||
diagnostics
|
||||
}
|
||||
Err(error) => {
|
||||
@@ -265,7 +264,7 @@ impl ProjectDatabase {
|
||||
}
|
||||
};
|
||||
|
||||
let (settings, settings_diagnostics) = match merged_options
|
||||
let (settings, mut settings_diagnostics) = match merged_options
|
||||
.to_settings(self, &FallibleStrategy)
|
||||
{
|
||||
Ok((settings, diagnostics)) => (Some(settings), diagnostics),
|
||||
@@ -276,15 +275,14 @@ impl ProjectDatabase {
|
||||
(None, vec![error.into_diagnostic()])
|
||||
}
|
||||
};
|
||||
settings_diagnostics.extend(
|
||||
program_settings_diagnostics
|
||||
.into_iter()
|
||||
.map(|diagnostic| diagnostic.into_diagnostic(self)),
|
||||
);
|
||||
|
||||
tracing::debug!("Reloading project after structural change");
|
||||
match project.reload(
|
||||
self,
|
||||
metadata,
|
||||
settings,
|
||||
settings_diagnostics,
|
||||
program_settings_diagnostics,
|
||||
) {
|
||||
match project.reload(self, metadata, settings, settings_diagnostics) {
|
||||
ProjectReloadResult::Unchanged => {}
|
||||
ProjectReloadResult::Changed { files_changed } => {
|
||||
result.project_changed = true;
|
||||
@@ -325,17 +323,18 @@ impl ProjectDatabase {
|
||||
&FallibleStrategy,
|
||||
) {
|
||||
Ok((program_settings, program_settings_diagnostics)) => {
|
||||
let settings_diagnostics =
|
||||
let mut settings_diagnostics =
|
||||
match merged_options.to_settings(self, &FallibleStrategy) {
|
||||
Ok((_, diagnostics)) => diagnostics,
|
||||
Err(error) => vec![error.into_diagnostic()],
|
||||
};
|
||||
program.update_from_settings(self, program_settings);
|
||||
project.update_settings_diagnostics(
|
||||
self,
|
||||
settings_diagnostics,
|
||||
program_settings_diagnostics,
|
||||
project.update_program(self, program_settings);
|
||||
settings_diagnostics.extend(
|
||||
program_settings_diagnostics
|
||||
.into_iter()
|
||||
.map(|diagnostic| diagnostic.into_diagnostic(self)),
|
||||
);
|
||||
project.update_settings_diagnostics(self, settings_diagnostics);
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::error!("Failed to resolve program settings: {error}");
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
reason = "Prefer System trait methods over std methods in ty crates"
|
||||
)]
|
||||
use crate::glob::{GlobFilterCheckMode, IncludeResult};
|
||||
use crate::metadata::options::{OptionDiagnostic, ProgramSettingsDiagnostic};
|
||||
use crate::metadata::options::OptionDiagnostic;
|
||||
use crate::parallel::ParallelIteratorExt;
|
||||
use crate::walk::{ProjectFilesFilter, ProjectFilesWalker};
|
||||
#[cfg(feature = "testing")]
|
||||
@@ -28,6 +28,7 @@ use std::iter::FusedIterator;
|
||||
use std::panic::{AssertUnwindSafe, UnwindSafe};
|
||||
use std::sync::Arc;
|
||||
use ty_python_core::ProgramFile;
|
||||
use ty_python_core::program::{Program, ProgramSettings};
|
||||
pub use ty_python_semantic::Db as SemanticDb;
|
||||
use ty_python_semantic::lint::RuleSelection;
|
||||
|
||||
@@ -44,7 +45,7 @@ pub mod watch;
|
||||
/// ## How is a project different from a program?
|
||||
/// There are two (related) motivations:
|
||||
///
|
||||
/// 1. Program is defined in `ruff_db` and it can't reference the settings types for the linter and formatter
|
||||
/// 1. Program is defined in `ty_python_core` and it can't reference the settings types for the linter and formatter
|
||||
/// without introducing a cyclic dependency. The project is defined in a higher level crate
|
||||
/// where it can reference these setting types.
|
||||
/// 2. Running `ruff check` with different target versions results in different programs (settings) but
|
||||
@@ -79,6 +80,10 @@ pub struct Project {
|
||||
#[returns(deref)]
|
||||
pub settings: Box<Settings>,
|
||||
|
||||
/// The settings used to construct the Python program for this project.
|
||||
#[returns(ref)]
|
||||
pub program_settings: ProgramSettings,
|
||||
|
||||
/// The paths that should be included when checking this project.
|
||||
///
|
||||
/// The default (when this list is empty) is to include all files in the project root
|
||||
@@ -175,34 +180,33 @@ impl ProgressReporter for CollectReporter {
|
||||
#[salsa::tracked]
|
||||
impl Project {
|
||||
/// Create a project from resolved metadata and settings.
|
||||
///
|
||||
/// Program-settings diagnostics are accepted separately so callers do not need to know how to
|
||||
/// convert and merge them into the stored project settings diagnostics.
|
||||
fn from_metadata(
|
||||
db: &dyn Db,
|
||||
metadata: ProjectMetadata,
|
||||
settings: Settings,
|
||||
program_settings: ProgramSettings,
|
||||
settings_diagnostics: Vec<OptionDiagnostic>,
|
||||
program_settings_diagnostics: Vec<ProgramSettingsDiagnostic>,
|
||||
) -> Self {
|
||||
let diagnostics = Self::settings_diagnostics_with_program_diagnostics(
|
||||
db,
|
||||
settings_diagnostics,
|
||||
program_settings_diagnostics,
|
||||
);
|
||||
program_settings.search_paths.try_register_static_roots(db);
|
||||
|
||||
Project::builder(Box::new(metadata), Box::new(settings), diagnostics)
|
||||
.durability(Durability::MEDIUM)
|
||||
.open_fileset_durability(Durability::LOW)
|
||||
.file_set_durability(Durability::LOW)
|
||||
.new(db)
|
||||
Project::builder(
|
||||
Box::new(metadata),
|
||||
Box::new(settings),
|
||||
program_settings,
|
||||
settings_diagnostics,
|
||||
)
|
||||
.durability(Durability::MEDIUM)
|
||||
.open_fileset_durability(Durability::LOW)
|
||||
.file_set_durability(Durability::LOW)
|
||||
.new(db)
|
||||
}
|
||||
|
||||
/// Permanently freezes the most heavily read immutable project inputs.
|
||||
/// Permanently freezes the most heavily read immutable project and program inputs.
|
||||
///
|
||||
/// This is intentionally not exhaustive.
|
||||
fn freeze(self, db: &mut dyn Db) {
|
||||
let durability = Durability::NEVER_CHANGE;
|
||||
let program_settings = self.program_settings(db).clone();
|
||||
let metadata = Box::new(self.metadata(db).clone());
|
||||
let settings = Box::new(self.settings(db).clone());
|
||||
let included_paths = self.included_paths_list(db).to_vec();
|
||||
@@ -216,6 +220,9 @@ impl Project {
|
||||
self.set_settings(db)
|
||||
.with_durability(durability)
|
||||
.to(settings);
|
||||
self.set_program_settings(db)
|
||||
.with_durability(durability)
|
||||
.to(program_settings);
|
||||
self.set_included_paths_list(db)
|
||||
.with_durability(durability)
|
||||
.to(included_paths);
|
||||
@@ -232,6 +239,18 @@ impl Project {
|
||||
IndexedFiles::freeze(db, self);
|
||||
}
|
||||
|
||||
#[salsa::tracked(returns(copy), heap_size=ruff_memory_usage::heap_size)]
|
||||
pub fn program(self, db: &dyn Db) -> Program<'_> {
|
||||
Program::from_settings(db, self.program_settings(db).clone())
|
||||
}
|
||||
|
||||
pub fn update_program(self, db: &mut dyn Db, settings: ProgramSettings) {
|
||||
if self.program_settings(db) != &settings {
|
||||
settings.search_paths.try_register_static_roots(db);
|
||||
self.set_program_settings(db).to(settings);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn root(self, db: &dyn Db) -> &SystemPath {
|
||||
self.metadata(db).root()
|
||||
}
|
||||
@@ -270,25 +289,15 @@ impl Project {
|
||||
}
|
||||
|
||||
/// Reload the project after its metadata or settings have changed.
|
||||
///
|
||||
/// Program-settings diagnostics are converted and merged here to keep reload behavior
|
||||
/// consistent with initial project creation.
|
||||
pub fn reload(
|
||||
self,
|
||||
db: &mut dyn Db,
|
||||
metadata: ProjectMetadata,
|
||||
settings: Option<Settings>,
|
||||
settings_diagnostics: Vec<OptionDiagnostic>,
|
||||
program_settings_diagnostics: Vec<ProgramSettingsDiagnostic>,
|
||||
) -> ProjectReloadResult {
|
||||
tracing::debug!("Reloading project");
|
||||
let metadata_changed = &metadata != self.metadata(db);
|
||||
let settings_diagnostics = Self::settings_diagnostics_with_program_diagnostics(
|
||||
db,
|
||||
settings_diagnostics,
|
||||
program_settings_diagnostics,
|
||||
);
|
||||
|
||||
let root_changed = metadata.root() != self.root(db);
|
||||
let (settings_changed, files_changed) = if let Some(settings) = settings
|
||||
&& self.settings(db) != &settings
|
||||
@@ -331,32 +340,12 @@ impl Project {
|
||||
self,
|
||||
db: &mut dyn Db,
|
||||
settings_diagnostics: Vec<OptionDiagnostic>,
|
||||
program_settings_diagnostics: Vec<ProgramSettingsDiagnostic>,
|
||||
) {
|
||||
let settings_diagnostics = Self::settings_diagnostics_with_program_diagnostics(
|
||||
db,
|
||||
settings_diagnostics,
|
||||
program_settings_diagnostics,
|
||||
);
|
||||
|
||||
if self.settings_diagnostics(db) != settings_diagnostics {
|
||||
self.set_settings_diagnostics(db).to(settings_diagnostics);
|
||||
}
|
||||
}
|
||||
|
||||
fn settings_diagnostics_with_program_diagnostics(
|
||||
db: &dyn Db,
|
||||
mut settings_diagnostics: Vec<OptionDiagnostic>,
|
||||
program_settings_diagnostics: Vec<ProgramSettingsDiagnostic>,
|
||||
) -> Vec<OptionDiagnostic> {
|
||||
settings_diagnostics.extend(
|
||||
program_settings_diagnostics
|
||||
.into_iter()
|
||||
.map(|diagnostic| diagnostic.into_diagnostic(db)),
|
||||
);
|
||||
settings_diagnostics
|
||||
}
|
||||
|
||||
/// Checks the project and its dependencies according to the project's check mode.
|
||||
fn check(self, db: &ProjectDatabase, reporter: &mut dyn ProgressReporter) {
|
||||
let project_span = tracing::debug_span!("Project::check");
|
||||
@@ -906,7 +895,6 @@ mod tests {
|
||||
fn check_file_skips_type_checking_when_file_cant_be_read() -> ruff_db::system::Result<()> {
|
||||
let project = ProjectMetadata::new("test", SystemPathBuf::from("/"));
|
||||
let mut db = TestDb::new(project);
|
||||
db.init_program().unwrap();
|
||||
let path = SystemPath::new("test.py");
|
||||
|
||||
db.write_file(path, "x = 10")?;
|
||||
|
||||
@@ -613,7 +613,7 @@ pub enum ProgramSettingsDiagnostic {
|
||||
|
||||
impl ProgramSettingsDiagnostic {
|
||||
/// Convert this program-settings diagnostic into a diagnostic that can be stored on a project.
|
||||
pub(crate) fn into_diagnostic(self, db: &dyn Db) -> OptionDiagnostic {
|
||||
pub fn into_diagnostic(self, db: &dyn Db) -> OptionDiagnostic {
|
||||
match self {
|
||||
Self::UnsupportedInferredPythonVersion(python_version) => {
|
||||
unsupported_inferred_python_version_diagnostic(db, &python_version)
|
||||
|
||||
@@ -6,7 +6,6 @@ use tracing::info;
|
||||
use ruff_cache::{CacheKey, CacheKeyHasher};
|
||||
use ruff_db::system::{SystemPath, SystemPathBuf};
|
||||
use ty_module_resolver::system_module_search_paths;
|
||||
use ty_python_core::program::Program;
|
||||
|
||||
use crate::db::{Db, ProjectDatabase};
|
||||
use crate::watch::Watcher;
|
||||
@@ -41,7 +40,7 @@ impl ProjectWatcher {
|
||||
}
|
||||
|
||||
pub fn update(&mut self, db: &ProjectDatabase) {
|
||||
let environment = Program::get(db).resolver_environment(db);
|
||||
let environment = db.project().program(db).resolver_environment(db);
|
||||
let search_paths: Vec<_> = system_module_search_paths(db, environment).collect();
|
||||
let project_path = db.project().root(db);
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ anyhow = { workspace = true }
|
||||
[features]
|
||||
serde = ["dep:serde", "dep:ruff_macros"]
|
||||
schemars = ["dep:schemars", "dep:serde_json"]
|
||||
testing = []
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -254,6 +254,7 @@ pub(super) struct SemanticIndexBuilder<'db, 'ast> {
|
||||
|
||||
// Used for checking semantic syntax errors
|
||||
resolver_environment: ResolverEnvironment<'db>,
|
||||
python_version: PythonVersion,
|
||||
source_text: OnceCell<SourceText>,
|
||||
semantic_checker: SemanticSyntaxChecker,
|
||||
in_try: bool,
|
||||
@@ -345,6 +346,7 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
|
||||
enclosing_snapshots: FxHashMap::default(),
|
||||
|
||||
resolver_environment: file.resolver_environment(db),
|
||||
python_version: file.python_version(db),
|
||||
source_text: OnceCell::new(),
|
||||
semantic_checker: SemanticSyntaxChecker::default(),
|
||||
in_try: false,
|
||||
@@ -5015,7 +5017,7 @@ impl SemanticSyntaxContext for SemanticIndexBuilder<'_, '_> {
|
||||
}
|
||||
|
||||
fn python_version(&self) -> PythonVersion {
|
||||
self.resolver_environment.python_version(self.db)
|
||||
self.python_version
|
||||
}
|
||||
|
||||
fn source(&self) -> &str {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
use ruff_db::files::File;
|
||||
use ty_module_resolver::Db as ModuleResolverDb;
|
||||
|
||||
#[cfg(any(test, feature = "testing"))]
|
||||
use crate::program::{Program, ProgramSettings};
|
||||
|
||||
/// Database giving access to semantic information about a Python program.
|
||||
#[salsa::db]
|
||||
pub trait Db: ModuleResolverDb {
|
||||
@@ -8,6 +11,25 @@ pub trait Db: ModuleResolverDb {
|
||||
fn should_check_file(&self, file: File) -> bool;
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "testing"))]
|
||||
#[salsa::db]
|
||||
pub trait TestProgramDb: Db {
|
||||
fn program_settings(&self) -> &ProgramSettings;
|
||||
|
||||
// Salsa-cached because interning a program requires hashing all search paths.
|
||||
fn program(&self) -> Program<'_>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
#[salsa::tracked(returns(copy), heap_size=ruff_memory_usage::heap_size)]
|
||||
fn program_inner(db: &dyn TestProgramDb) -> Program<'_> {
|
||||
Program::from_settings(db, db.program_settings().clone())
|
||||
}
|
||||
|
||||
program_inner(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use std::sync::{Arc, Mutex};
|
||||
@@ -25,9 +47,9 @@ pub(crate) mod tests {
|
||||
use ty_site_packages::{PythonVersionSource, PythonVersionWithSource};
|
||||
|
||||
use crate::platform::PythonPlatform;
|
||||
use crate::program::{Program, ProgramSettings};
|
||||
use crate::program::ProgramSettings;
|
||||
|
||||
use super::Db;
|
||||
use super::{Db, TestProgramDb};
|
||||
|
||||
type Events = Arc<Mutex<Vec<salsa::Event>>>;
|
||||
|
||||
@@ -38,11 +60,14 @@ pub(crate) mod tests {
|
||||
files: Files,
|
||||
system: TestSystem,
|
||||
vendored: VendoredFileSystem,
|
||||
program_settings: ProgramSettings,
|
||||
}
|
||||
|
||||
impl TestDb {
|
||||
fn new() -> Self {
|
||||
let events = Events::default();
|
||||
let vendored = ty_vendored::file_system().clone();
|
||||
let program_settings = ProgramSettings::empty(&vendored);
|
||||
Self {
|
||||
storage: salsa::Storage::new(Some(Box::new({
|
||||
move |event| {
|
||||
@@ -52,8 +77,9 @@ pub(crate) mod tests {
|
||||
}
|
||||
}))),
|
||||
system: TestSystem::default(),
|
||||
vendored: ty_vendored::file_system().clone(),
|
||||
vendored,
|
||||
files: Files::default(),
|
||||
program_settings,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,6 +119,13 @@ pub(crate) mod tests {
|
||||
#[salsa::db]
|
||||
impl ModuleResolverDb for TestDb {}
|
||||
|
||||
#[salsa::db]
|
||||
impl TestProgramDb for TestDb {
|
||||
fn program_settings(&self) -> &ProgramSettings {
|
||||
&self.program_settings
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::db]
|
||||
impl salsa::Database for TestDb {}
|
||||
|
||||
@@ -132,19 +165,18 @@ pub(crate) mod tests {
|
||||
db.write_files(self.files)
|
||||
.context("Failed to write test files")?;
|
||||
|
||||
Program::from_settings(
|
||||
&db,
|
||||
ProgramSettings {
|
||||
python_version: PythonVersionWithSource {
|
||||
version: self.python_version,
|
||||
source: PythonVersionSource::default(),
|
||||
},
|
||||
python_platform: self.python_platform,
|
||||
search_paths: SearchPathSettings::new(vec![src_root])
|
||||
.to_search_paths(db.system(), db.vendored(), &FallibleStrategy)
|
||||
.context("Invalid search path settings")?,
|
||||
let program_settings = ProgramSettings {
|
||||
python_version: PythonVersionWithSource {
|
||||
version: self.python_version,
|
||||
source: PythonVersionSource::default(),
|
||||
},
|
||||
);
|
||||
python_platform: self.python_platform,
|
||||
search_paths: SearchPathSettings::new(vec![src_root])
|
||||
.to_search_paths(db.system(), db.vendored(), &FallibleStrategy)
|
||||
.context("Invalid search path settings")?,
|
||||
};
|
||||
program_settings.search_paths.try_register_static_roots(&db);
|
||||
db.program_settings = program_settings;
|
||||
|
||||
Ok(db)
|
||||
}
|
||||
|
||||
@@ -15,11 +15,7 @@ use ruff_text_size::TextRange;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use salsa::plumbing::AsId;
|
||||
use smallvec::SmallVec;
|
||||
use ty_module_resolver::{ModuleName, ResolverEnvironment};
|
||||
|
||||
// FIXME: Replace this temporary alias once semantic query keys can use the environment-bearing
|
||||
// `Program` Salsa ingredient directly.
|
||||
pub type Program<'db> = ResolverEnvironment<'db>;
|
||||
use ty_module_resolver::ModuleName;
|
||||
|
||||
use crate::frozen::{FrozenMap, FrozenSet};
|
||||
use crate::place::ScopedPlaceId;
|
||||
@@ -64,8 +60,11 @@ pub mod symbol;
|
||||
pub mod unpack;
|
||||
mod use_def;
|
||||
pub use db::Db;
|
||||
#[cfg(any(test, feature = "testing"))]
|
||||
pub use db::TestProgramDb;
|
||||
pub mod program;
|
||||
pub mod program_file;
|
||||
pub use program::Program;
|
||||
pub use program_file::ProgramFile;
|
||||
|
||||
/// Returns the semantic index for `file`.
|
||||
@@ -1126,7 +1125,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn program_file(db: &TestDb, file: File) -> ProgramFile<'_> {
|
||||
Program::get(db).program_file(db, file)
|
||||
db.program().program_file(db, file)
|
||||
}
|
||||
|
||||
fn names(table: &PlaceTable) -> Vec<String> {
|
||||
@@ -1861,13 +1860,14 @@ class C[T]:
|
||||
scopes: impl Iterator<Item = (FileScopeId, &'db Scope)>,
|
||||
db: &'db dyn Db,
|
||||
file: File,
|
||||
program: Program<'db>,
|
||||
module: &'a ParsedModuleRef,
|
||||
) -> Vec<&'a str> {
|
||||
scopes
|
||||
.into_iter()
|
||||
.map(|(scope_id, _)| {
|
||||
scope_id
|
||||
.to_scope_id(db, Program::get(db).program_file(db, file))
|
||||
.to_scope_id(db, program.program_file(db, file))
|
||||
.name(db, module)
|
||||
})
|
||||
.collect()
|
||||
@@ -1891,17 +1891,20 @@ def x():
|
||||
|
||||
let descendants = index.descendent_scopes(FileScopeId::global());
|
||||
assert_eq!(
|
||||
scope_names(descendants, &db, file, &module),
|
||||
scope_names(descendants, &db, file, db.program(), &module),
|
||||
vec!["Test", "foo", "bar", "baz", "x"]
|
||||
);
|
||||
|
||||
let children = index.child_scopes(FileScopeId::global());
|
||||
assert_eq!(scope_names(children, &db, file, &module), vec!["Test", "x"]);
|
||||
assert_eq!(
|
||||
scope_names(children, &db, file, db.program(), &module),
|
||||
vec!["Test", "x"]
|
||||
);
|
||||
|
||||
let test_class = index.child_scopes(FileScopeId::global()).next().unwrap().0;
|
||||
let test_child_scopes = index.child_scopes(test_class);
|
||||
assert_eq!(
|
||||
scope_names(test_child_scopes, &db, file, &module),
|
||||
scope_names(test_child_scopes, &db, file, db.program(), &module),
|
||||
vec!["foo", "baz"]
|
||||
);
|
||||
|
||||
@@ -1913,7 +1916,7 @@ def x():
|
||||
let ancestors = index.ancestor_scopes(bar_scope);
|
||||
|
||||
assert_eq!(
|
||||
scope_names(ancestors, &db, file, &module),
|
||||
scope_names(ancestors, &db, file, db.program(), &module),
|
||||
vec!["bar", "foo", "Test", "<module>"]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::fmt::{Display, Formatter};
|
||||
use ty_combine::Combine;
|
||||
|
||||
/// The target platform to assume when resolving types.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, get_size2::GetSize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Serialize, serde::Deserialize, ruff_macros::RustDoc),
|
||||
|
||||
@@ -2,9 +2,8 @@ use crate::{Db, platform::PythonPlatform};
|
||||
|
||||
use ruff_db::files::File;
|
||||
use ruff_db::system::SystemPath;
|
||||
use ruff_db::vendored::VendoredFileSystem;
|
||||
use ruff_python_ast::PythonVersion;
|
||||
use salsa::Durability;
|
||||
use salsa::Setter;
|
||||
use ty_module_resolver::{ResolverEnvironment, SearchPaths};
|
||||
use ty_site_packages::PythonVersionWithSource;
|
||||
|
||||
@@ -13,110 +12,66 @@ use crate::ProgramFile;
|
||||
// Re-export the misconfiguration strategy types from ty_module_resolver.
|
||||
pub use ty_module_resolver::{FallibleStrategy, MisconfigurationStrategy, UseDefaultStrategy};
|
||||
|
||||
#[salsa::input(singleton, heap_size=ruff_memory_usage::heap_size)]
|
||||
pub struct Program {
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
pub struct Program<'db> {
|
||||
// FIXME: Move the source out of `Program`. Different source locations prevent otherwise
|
||||
// equivalent programs from being reused across scripts.
|
||||
#[returns(ref)]
|
||||
pub python_version_with_source: PythonVersionWithSource,
|
||||
|
||||
#[returns(ref)]
|
||||
pub python_platform: PythonPlatform,
|
||||
|
||||
#[returns(ref)]
|
||||
pub search_paths: SearchPaths,
|
||||
#[returns(copy)]
|
||||
pub resolver_environment: ResolverEnvironment<'db>,
|
||||
}
|
||||
|
||||
#[salsa::tracked]
|
||||
impl Program {
|
||||
pub fn init_or_update(db: &mut dyn Db, settings: ProgramSettings) -> Self {
|
||||
match Self::try_get(db) {
|
||||
Some(program) => {
|
||||
program.update_from_settings(db, settings);
|
||||
program
|
||||
}
|
||||
None => Self::from_settings(db, settings),
|
||||
}
|
||||
}
|
||||
impl get_size2::GetSize for Program<'_> {}
|
||||
|
||||
pub fn from_settings(db: &dyn Db, settings: ProgramSettings) -> Self {
|
||||
impl<'db> Program<'db> {
|
||||
/// Creates a program from settings whose search roots have already been registered.
|
||||
pub fn from_settings(db: &'db dyn Db, settings: ProgramSettings) -> Self {
|
||||
let ProgramSettings {
|
||||
python_version,
|
||||
python_platform,
|
||||
search_paths,
|
||||
} = settings;
|
||||
|
||||
search_paths.try_register_static_roots(db);
|
||||
|
||||
Program::builder(python_version, python_platform, search_paths)
|
||||
.durability(Durability::HIGH)
|
||||
.new(db)
|
||||
let resolver_environment =
|
||||
ResolverEnvironment::new(db, python_version.version, &search_paths);
|
||||
Program::new(db, python_version, python_platform, resolver_environment)
|
||||
}
|
||||
|
||||
pub fn python_version(self, db: &dyn Db) -> PythonVersion {
|
||||
self.python_version_with_source(db).version
|
||||
pub fn python_version(self, db: &'db dyn Db) -> PythonVersion {
|
||||
self.resolver_environment(db).python_version(db)
|
||||
}
|
||||
|
||||
/// Returns the module-resolution environment for this program.
|
||||
pub fn resolver_environment(self, db: &dyn Db) -> ResolverEnvironment<'_> {
|
||||
ResolverEnvironment::new(db, self.python_version(db), self.search_paths(db))
|
||||
pub fn search_paths(self, db: &'db dyn Db) -> &'db SearchPaths {
|
||||
self.resolver_environment(db).search_paths(db)
|
||||
}
|
||||
|
||||
pub fn program_file(self, db: &dyn Db, file: File) -> ProgramFile<'_> {
|
||||
ProgramFile::new(db, file, self.resolver_environment(db))
|
||||
pub fn program_file(self, db: &'db dyn Db, file: File) -> ProgramFile<'db> {
|
||||
ProgramFile::new(db, file, self)
|
||||
}
|
||||
|
||||
pub fn update_from_settings(self, db: &mut dyn Db, settings: ProgramSettings) {
|
||||
let ProgramSettings {
|
||||
python_version,
|
||||
python_platform,
|
||||
search_paths,
|
||||
} = settings;
|
||||
|
||||
if self.search_paths(db) != &search_paths {
|
||||
tracing::debug!("Updating search paths");
|
||||
search_paths.try_register_static_roots(db);
|
||||
self.set_search_paths(db).to(search_paths);
|
||||
}
|
||||
|
||||
if &python_platform != self.python_platform(db) {
|
||||
tracing::debug!("Updating python platform: `{python_platform:?}`");
|
||||
self.set_python_platform(db).to(python_platform);
|
||||
}
|
||||
|
||||
if &python_version != self.python_version_with_source(db) {
|
||||
tracing::debug!(
|
||||
"Updating python version: Python {version}",
|
||||
version = python_version.version
|
||||
);
|
||||
self.set_python_version_with_source(db).to(python_version);
|
||||
}
|
||||
}
|
||||
|
||||
/// Permanently freezes all program inputs.
|
||||
pub fn freeze(self, db: &mut dyn Db) {
|
||||
let durability = Durability::NEVER_CHANGE;
|
||||
let python_version = self.python_version_with_source(db).clone();
|
||||
let python_platform = self.python_platform(db).clone();
|
||||
let search_paths = self.search_paths(db).clone();
|
||||
|
||||
self.set_python_version_with_source(db)
|
||||
.with_durability(durability)
|
||||
.to(python_version);
|
||||
self.set_python_platform(db)
|
||||
.with_durability(durability)
|
||||
.to(python_platform);
|
||||
self.set_search_paths(db)
|
||||
.with_durability(durability)
|
||||
.to(search_paths);
|
||||
}
|
||||
|
||||
pub fn custom_stdlib_search_path(self, db: &dyn Db) -> Option<&SystemPath> {
|
||||
pub fn custom_stdlib_search_path(self, db: &'db dyn Db) -> Option<&'db SystemPath> {
|
||||
self.search_paths(db).custom_stdlib()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, get_size2::GetSize)]
|
||||
pub struct ProgramSettings {
|
||||
pub python_version: PythonVersionWithSource,
|
||||
pub python_platform: PythonPlatform,
|
||||
pub search_paths: SearchPaths,
|
||||
}
|
||||
|
||||
impl ProgramSettings {
|
||||
pub fn empty(vendored: &VendoredFileSystem) -> Self {
|
||||
Self {
|
||||
python_version: PythonVersionWithSource::default(),
|
||||
python_platform: PythonPlatform::default(),
|
||||
search_paths: SearchPaths::empty(vendored),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ use ruff_db::files::File;
|
||||
use ruff_python_ast::PythonVersion;
|
||||
use ty_module_resolver::{ResolverEnvironment, ResolverFile};
|
||||
|
||||
use crate::{Db, Program};
|
||||
use crate::{Db, program::Program};
|
||||
|
||||
/// A file interpreted within a particular Python program.
|
||||
///
|
||||
@@ -54,24 +54,21 @@ use crate::{Db, Program};
|
||||
heap_size = ruff_memory_usage::heap_size
|
||||
)]
|
||||
pub struct ProgramFile<'db> {
|
||||
/// The cached parser key for `file` and the environment's Python version.
|
||||
/// Cache the parser key even though its Python version is redundant with `program`:
|
||||
/// program files are created infrequently, but their parser keys are looked up extensively.
|
||||
#[returns(copy)]
|
||||
pub python_file: PythonFile<'db>,
|
||||
|
||||
#[returns(copy)]
|
||||
pub resolver_environment: ResolverEnvironment<'db>,
|
||||
pub program: Program<'db>,
|
||||
}
|
||||
|
||||
impl get_size2::GetSize for ProgramFile<'_> {}
|
||||
|
||||
impl<'db> ProgramFile<'db> {
|
||||
pub fn new(
|
||||
db: &'db dyn Db,
|
||||
file: File,
|
||||
resolver_environment: ResolverEnvironment<'db>,
|
||||
) -> Self {
|
||||
let python_file = PythonFile::new(db, file, resolver_environment.python_version(db));
|
||||
Self::new_internal(db, python_file, resolver_environment)
|
||||
pub fn new(db: &'db dyn Db, file: File, program: Program<'db>) -> Self {
|
||||
let python_file = PythonFile::new(db, file, program.python_version(db));
|
||||
Self::new_internal(db, python_file, program)
|
||||
}
|
||||
|
||||
/// Returns the physical file represented by this program file.
|
||||
@@ -79,18 +76,18 @@ impl<'db> ProgramFile<'db> {
|
||||
self.python_file(db).file(db)
|
||||
}
|
||||
|
||||
/// Returns the module-resolution environment for this program file.
|
||||
pub fn resolver_environment(self, db: &'db dyn Db) -> ResolverEnvironment<'db> {
|
||||
self.program(db).resolver_environment(db)
|
||||
}
|
||||
|
||||
/// Returns the resolver key for this file.
|
||||
pub fn resolver_file(self, db: &'db dyn Db) -> ResolverFile<'db> {
|
||||
ResolverFile::new(db, self.file(db), self.resolver_environment(db))
|
||||
}
|
||||
|
||||
/// Returns the program associated with this file.
|
||||
pub fn program(self, db: &'db dyn Db) -> Program<'db> {
|
||||
self.resolver_environment(db)
|
||||
}
|
||||
|
||||
/// Returns the Python version associated with this file's resolver environment.
|
||||
/// Returns the Python version associated with this file's program.
|
||||
pub fn python_version(self, db: &'db dyn Db) -> PythonVersion {
|
||||
self.resolver_environment(db).python_version(db)
|
||||
self.program(db).python_version(db)
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,7 @@ serde = [
|
||||
"ruff_python_ast/serde",
|
||||
"ty_python_core/serde",
|
||||
]
|
||||
testing = []
|
||||
testing = ["ty_python_core/testing"]
|
||||
|
||||
[[test]]
|
||||
name = "mdtest"
|
||||
|
||||
@@ -48,7 +48,8 @@ pub(crate) mod tests {
|
||||
use ruff_db::vendored::VendoredFileSystem;
|
||||
use ruff_python_ast::PythonVersion;
|
||||
use ty_module_resolver::{Db as ModuleResolverDb, SearchPathSettings};
|
||||
use ty_python_core::program::{FallibleStrategy, Program, ProgramSettings};
|
||||
use ty_python_core::TestProgramDb;
|
||||
use ty_python_core::program::{FallibleStrategy, ProgramSettings};
|
||||
use ty_site_packages::{PythonVersionSource, PythonVersionWithSource};
|
||||
|
||||
type Events = Arc<Mutex<Vec<salsa::Event>>>;
|
||||
@@ -64,11 +65,14 @@ pub(crate) mod tests {
|
||||
rule_selection: Arc<RuleSelection>,
|
||||
analysis_settings: Arc<AnalysisSettings>,
|
||||
open_files: rustc_hash::FxHashSet<File>,
|
||||
program_settings: ProgramSettings,
|
||||
}
|
||||
|
||||
impl TestDb {
|
||||
fn new() -> Self {
|
||||
let events = Events::default();
|
||||
let vendored = ty_vendored::file_system().clone();
|
||||
let program_settings = ProgramSettings::empty(&vendored);
|
||||
Self {
|
||||
storage: salsa::Storage::new(Some(Box::new({
|
||||
let events = events.clone();
|
||||
@@ -79,21 +83,22 @@ pub(crate) mod tests {
|
||||
}
|
||||
}))),
|
||||
system: TestSystem::default(),
|
||||
vendored: ty_vendored::file_system().clone(),
|
||||
vendored,
|
||||
events,
|
||||
files: Files::default(),
|
||||
rule_selection: Arc::new(RuleSelection::from_registry(default_lint_registry())),
|
||||
analysis_settings: AnalysisSettings::default().into(),
|
||||
open_files: rustc_hash::FxHashSet::default(),
|
||||
program_settings,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn python_version(&self) -> PythonVersion {
|
||||
Program::get(self).python_version(self)
|
||||
self.program().python_version(self)
|
||||
}
|
||||
|
||||
pub(crate) fn program_environment(&self) -> ProgramEnvironment<'_> {
|
||||
ProgramEnvironment::from_program(Program::get(self).resolver_environment(self))
|
||||
ProgramEnvironment::from_program(self.program())
|
||||
}
|
||||
|
||||
/// Marks `file` as open in the editor.
|
||||
@@ -151,6 +156,13 @@ pub(crate) mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::db]
|
||||
impl TestProgramDb for TestDb {
|
||||
fn program_settings(&self) -> &ProgramSettings {
|
||||
&self.program_settings
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::db]
|
||||
impl Db for TestDb {
|
||||
fn check_file(&self, file: File) -> Vec<Diagnostic> {
|
||||
@@ -162,7 +174,7 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
fn program_file(&self, file: File) -> ProgramFile<'_> {
|
||||
Program::get(self).program_file(self, file)
|
||||
self.program().program_file(self, file)
|
||||
}
|
||||
|
||||
fn rule_selection(&self, _file: File) -> &RuleSelection {
|
||||
@@ -242,19 +254,18 @@ pub(crate) mod tests {
|
||||
db.write_files(self.files)
|
||||
.context("Failed to write test files")?;
|
||||
|
||||
Program::from_settings(
|
||||
&db,
|
||||
ProgramSettings {
|
||||
python_version: PythonVersionWithSource {
|
||||
version: self.python_version,
|
||||
source: PythonVersionSource::default(),
|
||||
},
|
||||
python_platform: self.python_platform,
|
||||
search_paths: SearchPathSettings::new(vec![src_root])
|
||||
.to_search_paths(db.system(), db.vendored(), &FallibleStrategy)
|
||||
.context("Invalid search path settings")?,
|
||||
let program_settings = ProgramSettings {
|
||||
python_version: PythonVersionWithSource {
|
||||
version: self.python_version,
|
||||
source: PythonVersionSource::default(),
|
||||
},
|
||||
);
|
||||
python_platform: self.python_platform,
|
||||
search_paths: SearchPathSettings::new(vec![src_root])
|
||||
.to_search_paths(db.system(), db.vendored(), &FallibleStrategy)
|
||||
.context("Invalid search path settings")?,
|
||||
};
|
||||
program_settings.search_paths.try_register_static_roots(&db);
|
||||
db.program_settings = program_settings;
|
||||
|
||||
Ok(db)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
Db, PythonVersionSource, PythonVersionWithSource, lint::lint_documentation_url,
|
||||
types::TypeCheckDiagnostics,
|
||||
Db, ProgramEnvironment, PythonVersionSource, PythonVersionWithSource,
|
||||
lint::lint_documentation_url, types::TypeCheckDiagnostics,
|
||||
};
|
||||
use levenshtein::{HideUnderscoredSuggestions, find_best_suggestion};
|
||||
use ruff_db::{
|
||||
@@ -46,11 +46,12 @@ pub fn inferred_python_version_source_annotation(
|
||||
/// configuration files, or defaults.
|
||||
pub(crate) fn add_inferred_python_version_hint_to_diagnostic(
|
||||
db: &dyn Db,
|
||||
env: &ProgramEnvironment,
|
||||
diagnostic: &mut Diagnostic,
|
||||
action: &str,
|
||||
) {
|
||||
let program = ty_python_core::program::Program::get(db);
|
||||
let PythonVersionWithSource { version, source } = program.python_version_with_source(db);
|
||||
let PythonVersionWithSource { version, source } =
|
||||
env.program(db).python_version_with_source(db);
|
||||
|
||||
match source {
|
||||
crate::PythonVersionSource::Cli => {
|
||||
|
||||
@@ -204,7 +204,12 @@ pub fn check_file(db: &dyn Db, file: ProgramFile<'_>) -> Result<Box<[Diagnostic]
|
||||
|
||||
diagnostics.extend(parsed_ref.unsupported_syntax_errors().iter().map(|error| {
|
||||
let mut error = Diagnostic::invalid_syntax(source_file, error, error);
|
||||
add_inferred_python_version_hint_to_diagnostic(db, &mut error, "parsing syntax");
|
||||
add_inferred_python_version_hint_to_diagnostic(
|
||||
db,
|
||||
&ProgramEnvironment::from_file(file),
|
||||
&mut error,
|
||||
"parsing syntax",
|
||||
);
|
||||
error
|
||||
}));
|
||||
|
||||
|
||||
@@ -664,6 +664,7 @@ fn builtins_symbol_impl<'db>(
|
||||
visibility: BuiltinVisibility,
|
||||
) -> Option<(ScopeId<'db>, PlaceAndQualifiers<'db>)> {
|
||||
let program = env.program(db);
|
||||
let resolver_environment = program.resolver_environment(db);
|
||||
let resolver = |module: Module<'db>| {
|
||||
let file = ProgramFile::new(db, module.file(db)?, program);
|
||||
let scope = global_scope(db, file);
|
||||
@@ -695,12 +696,13 @@ fn builtins_symbol_impl<'db>(
|
||||
// If this symbol is not present in project-level builtins, search in the default ones.
|
||||
resolve_module_confident(
|
||||
db,
|
||||
program,
|
||||
resolver_environment,
|
||||
&ModuleName::new_static("__builtins__").unwrap(),
|
||||
)
|
||||
.and_then(&resolver)
|
||||
.or_else(|| {
|
||||
resolve_module_confident(db, program, &KnownModule::Builtins.name()).and_then(resolver)
|
||||
resolve_module_confident(db, resolver_environment, &KnownModule::Builtins.name())
|
||||
.and_then(resolver)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1424,7 +1426,7 @@ fn symbol_impl<'db>(
|
||||
"version_info" => {
|
||||
return Place::bound(Type::sys_version_info()).into();
|
||||
}
|
||||
"platform" => match ty_python_core::program::Program::get(db).python_platform(db) {
|
||||
"platform" => match scope.program(db).python_platform(db) {
|
||||
crate::PythonPlatform::Identifier(platform) => {
|
||||
return Place::bound(Type::string_literal(db, platform.as_str())).into();
|
||||
}
|
||||
@@ -1437,7 +1439,7 @@ fn symbol_impl<'db>(
|
||||
}
|
||||
|
||||
if name == "name" && is_known_module(KnownModule::Os) {
|
||||
match ty_python_core::program::Program::get(db).python_platform(db) {
|
||||
match scope.program(db).python_platform(db) {
|
||||
crate::PythonPlatform::Identifier(platform) => {
|
||||
// In CPython, `os.name` is `"nt"` on Windows and `"posix"` otherwise.
|
||||
let os_name = if platform == "win32" { "nt" } else { "posix" };
|
||||
|
||||
@@ -2058,21 +2058,19 @@ impl<'db> KnownClassLookupError<'db> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::db::tests::setup_db;
|
||||
use crate::db::tests::{TestDbBuilder, setup_db};
|
||||
use crate::{PythonVersionSource, PythonVersionWithSource};
|
||||
use salsa::Setter;
|
||||
use strum::IntoEnumIterator;
|
||||
use ty_module_resolver::resolve_module_confident;
|
||||
use ty_python_core::TestProgramDb as _;
|
||||
use ty_python_core::program::{Program, ProgramSettings};
|
||||
|
||||
#[test]
|
||||
fn known_class_roundtrip_from_str() {
|
||||
let mut db = setup_db();
|
||||
ty_python_core::program::Program::get(&db)
|
||||
.set_python_version_with_source(&mut db)
|
||||
.to(PythonVersionWithSource {
|
||||
version: PythonVersion::latest_preview(),
|
||||
source: PythonVersionSource::default(),
|
||||
});
|
||||
let db = TestDbBuilder::new()
|
||||
.with_python_version(PythonVersion::latest_preview())
|
||||
.build()
|
||||
.expect("valid TestDb setup");
|
||||
let python_version = db.python_version();
|
||||
let resolver_environment = db.program_environment().resolver_environment(&db);
|
||||
for class in KnownClass::iter() {
|
||||
@@ -2101,14 +2099,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn known_class_doesnt_fallback_to_unknown_unexpectedly_on_latest_version() {
|
||||
let mut db = setup_db();
|
||||
|
||||
ty_python_core::program::Program::get(&db)
|
||||
.set_python_version_with_source(&mut db)
|
||||
.to(PythonVersionWithSource {
|
||||
version: PythonVersion::latest_ty(),
|
||||
source: PythonVersionSource::default(),
|
||||
});
|
||||
let db = TestDbBuilder::new()
|
||||
.with_python_version(PythonVersion::latest_ty())
|
||||
.build()
|
||||
.expect("valid TestDb setup");
|
||||
|
||||
let python_version = db.python_version();
|
||||
let env = db.program_environment();
|
||||
@@ -2134,7 +2128,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn known_class_doesnt_fallback_to_unknown_unexpectedly_on_low_python_version() {
|
||||
let mut db = setup_db();
|
||||
let db = setup_db();
|
||||
|
||||
// First, collect the `KnownClass` variants
|
||||
// and sort them according to the version they were added in.
|
||||
@@ -2164,22 +2158,27 @@ mod tests {
|
||||
|
||||
classes.sort_unstable_by_key(|(_, version)| *version);
|
||||
|
||||
let program = ty_python_core::program::Program::get(&db);
|
||||
let mut program = db.program();
|
||||
let mut current_version = program.python_version(&db);
|
||||
let python_platform = program.python_platform(&db).clone();
|
||||
let search_paths = program.search_paths(&db).clone();
|
||||
|
||||
for (class, version_added) in classes {
|
||||
if version_added != current_version {
|
||||
program
|
||||
.set_python_version_with_source(&mut db)
|
||||
.to(PythonVersionWithSource {
|
||||
let settings = ProgramSettings {
|
||||
python_version: PythonVersionWithSource {
|
||||
version: version_added,
|
||||
source: PythonVersionSource::default(),
|
||||
});
|
||||
},
|
||||
python_platform: python_platform.clone(),
|
||||
search_paths: search_paths.clone(),
|
||||
};
|
||||
program = Program::from_settings(&db, settings);
|
||||
current_version = version_added;
|
||||
}
|
||||
|
||||
// Check the class can be looked up successfully
|
||||
let env = db.program_environment();
|
||||
let env = ProgramEnvironment::from_program(program);
|
||||
class.try_to_class_literal(&db, &env).unwrap();
|
||||
|
||||
// We can't call `KnownClass::Tuple.to_instance()`;
|
||||
|
||||
@@ -74,7 +74,7 @@ impl<'db> ProgramEnvironment<'db> {
|
||||
#[inline]
|
||||
pub fn program(&self, db: &'db dyn Db) -> Program<'db> {
|
||||
let program = match self.environment.get() {
|
||||
ProgramSource::Program(id) => return ResolverEnvironment::from_id(id),
|
||||
ProgramSource::Program(id) => return Program::from_id(id),
|
||||
ProgramSource::File(file) => {
|
||||
cold_path();
|
||||
// The source handle and database share `'db`; re-wrapping the stored ingredient
|
||||
@@ -109,7 +109,7 @@ impl<'db> ProgramEnvironment<'db> {
|
||||
/// Returns the resolver environment used by this operation.
|
||||
#[inline]
|
||||
pub fn resolver_environment(&self, db: &'db dyn Db) -> ResolverEnvironment<'db> {
|
||||
self.program(db)
|
||||
self.program(db).resolver_environment(db)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4689,6 +4689,7 @@ pub(super) fn report_invalid_total_ordering_call(
|
||||
/// The function returns `true` if a hint was added, `false` otherwise.
|
||||
pub(super) fn hint_if_stdlib_submodule_exists_on_other_versions(
|
||||
db: &dyn Db,
|
||||
env: &ProgramEnvironment<'_>,
|
||||
diagnostic: &mut Diagnostic,
|
||||
full_submodule_name: &ModuleName,
|
||||
parent_module: Module,
|
||||
@@ -4701,7 +4702,7 @@ pub(super) fn hint_if_stdlib_submodule_exists_on_other_versions(
|
||||
return false;
|
||||
}
|
||||
|
||||
let program = ty_python_core::program::Program::get(db);
|
||||
let program = env.program(db);
|
||||
let typeshed_versions = program.search_paths(db).typeshed_versions();
|
||||
|
||||
let Some(version_range) = typeshed_versions.exact(full_submodule_name) else {
|
||||
@@ -4721,7 +4722,7 @@ pub(super) fn hint_if_stdlib_submodule_exists_on_other_versions(
|
||||
version_range = version_range.diagnostic_display(),
|
||||
));
|
||||
|
||||
add_inferred_python_version_hint_to_diagnostic(db, diagnostic, "resolving modules");
|
||||
add_inferred_python_version_hint_to_diagnostic(db, env, diagnostic, "resolving modules");
|
||||
|
||||
true
|
||||
}
|
||||
@@ -4778,7 +4779,7 @@ pub(super) fn hint_if_stdlib_attribute_exists_on_other_versions(
|
||||
// TODO: determine what version they need to be on
|
||||
// TODO: also mention the platform we're assuming
|
||||
// TODO: determine what platform they need to be on
|
||||
add_inferred_python_version_hint_to_diagnostic(db, &mut diagnostic, action);
|
||||
add_inferred_python_version_hint_to_diagnostic(db, env, &mut diagnostic, action);
|
||||
}
|
||||
|
||||
pub(super) fn report_invalid_concatenate_last_arg<'db>(
|
||||
|
||||
@@ -10162,7 +10162,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
"`{id}` was added as a builtin in Python 3.{version_added_to_builtins}"
|
||||
));
|
||||
add_inferred_python_version_hint_to_diagnostic(
|
||||
self.db(),
|
||||
db,
|
||||
self.program_environment(),
|
||||
&mut diagnostic,
|
||||
"resolving types",
|
||||
);
|
||||
|
||||
@@ -86,6 +86,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
));
|
||||
add_inferred_python_version_hint_to_diagnostic(
|
||||
db,
|
||||
self.program_environment(),
|
||||
&mut diagnostic,
|
||||
"resolving modules",
|
||||
);
|
||||
@@ -514,6 +515,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
if let Some(full_submodule_name) = full_submodule_name {
|
||||
submodule_hint_added = hint_if_stdlib_submodule_exists_on_other_versions(
|
||||
db,
|
||||
self.program_environment(),
|
||||
&mut diagnostic,
|
||||
&full_submodule_name,
|
||||
module,
|
||||
@@ -635,7 +637,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
));
|
||||
|
||||
hint_if_stdlib_submodule_exists_on_other_versions(
|
||||
db,
|
||||
self.db(),
|
||||
self.program_environment(),
|
||||
&mut diagnostic,
|
||||
&full_submodule_name,
|
||||
module,
|
||||
|
||||
@@ -355,7 +355,8 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
self.type_expression_context()
|
||||
));
|
||||
add_inferred_python_version_hint_to_diagnostic(
|
||||
self.db(),
|
||||
db,
|
||||
self.program_environment(),
|
||||
&mut diagnostic,
|
||||
"inferring types",
|
||||
);
|
||||
|
||||
@@ -8,11 +8,14 @@ use ruff_db::files::{File, system_path_to_file};
|
||||
use ruff_db::system::DbWithWritableSystem as _;
|
||||
use ruff_db::testing::{assert_function_query_was_not_run, assert_function_query_was_run};
|
||||
use ruff_python_ast::PythonVersion;
|
||||
use ty_module_resolver::ResolverEnvironment;
|
||||
use salsa::plumbing::AsId;
|
||||
use ty_python_core::definition::Definition;
|
||||
use ty_python_core::program::Program as ProjectProgram;
|
||||
use ty_python_core::program::{Program, ProgramSettings};
|
||||
use ty_python_core::scope::FileScopeId;
|
||||
use ty_python_core::{ProgramFile, global_scope, place_table, semantic_index, use_def_map};
|
||||
use ty_python_core::{
|
||||
ProgramFile, TestProgramDb as _, global_scope, place_table, semantic_index, use_def_map,
|
||||
};
|
||||
use ty_site_packages::{PythonVersionSource, PythonVersionWithSource};
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -111,16 +114,38 @@ fn same_file_at_different_python_versions() -> anyhow::Result<()> {
|
||||
db.write_dedented("src/py312_dependency.py", "value: int = 312")?;
|
||||
|
||||
let file = system_path_to_file(&db, "src/main.py").expect("file to exist");
|
||||
let search_paths = ProjectProgram::get(&db).search_paths(&db);
|
||||
let default_program = db.program();
|
||||
let search_paths = default_program.search_paths(&db).clone();
|
||||
let python_platform = default_program.python_platform(&db).clone();
|
||||
let py311 = ProgramFile::new(
|
||||
&db,
|
||||
file,
|
||||
ResolverEnvironment::new(&db, PythonVersion::PY311, search_paths),
|
||||
Program::from_settings(
|
||||
&db,
|
||||
ProgramSettings {
|
||||
python_version: PythonVersionWithSource {
|
||||
version: PythonVersion::PY311,
|
||||
source: PythonVersionSource::Default,
|
||||
},
|
||||
python_platform: python_platform.clone(),
|
||||
search_paths: search_paths.clone(),
|
||||
},
|
||||
),
|
||||
);
|
||||
let py312 = ProgramFile::new(
|
||||
&db,
|
||||
file,
|
||||
ResolverEnvironment::new(&db, PythonVersion::PY312, search_paths),
|
||||
Program::from_settings(
|
||||
&db,
|
||||
ProgramSettings {
|
||||
python_version: PythonVersionWithSource {
|
||||
version: PythonVersion::PY312,
|
||||
source: PythonVersionSource::Default,
|
||||
},
|
||||
python_platform,
|
||||
search_paths,
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
let check = |file, expected_type, expect_invalid_syntax, expect_unresolved_import| {
|
||||
@@ -161,6 +186,52 @@ fn same_file_at_different_python_versions() -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn program_file_changes_with_python_version() -> anyhow::Result<()> {
|
||||
let db = TestDbBuilder::new()
|
||||
.with_python_version(PythonVersion::PY311)
|
||||
.with_file("src/main.py", "type Alias = int")
|
||||
.build()?;
|
||||
let file = system_path_to_file(&db, "src/main.py").expect("file to exist");
|
||||
let program = db.program();
|
||||
let (program_file_id, py311) = {
|
||||
let program_file = program.program_file(&db, file);
|
||||
(program_file.as_id(), program_file.python_file(&db).as_id())
|
||||
};
|
||||
|
||||
let equivalent_program = Program::from_settings(
|
||||
&db,
|
||||
ProgramSettings {
|
||||
python_version: program.python_version_with_source(&db).clone(),
|
||||
python_platform: program.python_platform(&db).clone(),
|
||||
search_paths: program.search_paths(&db).clone(),
|
||||
},
|
||||
);
|
||||
assert_eq!(program, equivalent_program);
|
||||
assert_eq!(
|
||||
program_file_id,
|
||||
equivalent_program.program_file(&db, file).as_id()
|
||||
);
|
||||
|
||||
let py312_program = Program::from_settings(
|
||||
&db,
|
||||
ProgramSettings {
|
||||
python_version: PythonVersionWithSource {
|
||||
version: PythonVersion::PY312,
|
||||
source: PythonVersionSource::Default,
|
||||
},
|
||||
python_platform: program.python_platform(&db).clone(),
|
||||
search_paths: program.search_paths(&db).clone(),
|
||||
},
|
||||
);
|
||||
|
||||
let program_file = py312_program.program_file(&db, file);
|
||||
assert_ne!(program_file_id, program_file.as_id());
|
||||
assert_eq!(program_file.python_version(&db), PythonVersion::PY312);
|
||||
assert_ne!(py311, program_file.python_file(&db).as_id());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expected_types_are_collected_only_for_open_files() -> anyhow::Result<()> {
|
||||
let has_expected_type = |open_file: bool| -> anyhow::Result<bool> {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
use super::*;
|
||||
use crate::Db;
|
||||
use crate::ProgramEnvironment;
|
||||
use crate::db::tests::{TestDbBuilder, setup_db};
|
||||
use crate::place::{typing_extensions_symbol, typing_symbol};
|
||||
use crate::types::type_alias::PEP695TypeAliasType;
|
||||
use crate::{Db, ProgramEnvironment};
|
||||
use ruff_db::system::DbWithWritableSystem as _;
|
||||
use ruff_python_ast as ast;
|
||||
use ruff_python_ast::PythonVersion;
|
||||
use test_case::test_case;
|
||||
use ty_python_core::ProgramFile;
|
||||
use ty_python_core::program::Program;
|
||||
use ty_python_core::{ProgramFile, TestProgramDb as _};
|
||||
|
||||
/// Explicitly test for Python version <3.13 and >=3.13, to ensure that
|
||||
/// the fallback to `typing_extensions` is working correctly.
|
||||
@@ -73,23 +73,20 @@ fn oscillating_generic_alias_cycle_recover<'db>(
|
||||
cycle: &salsa::Cycle,
|
||||
previous: &Type<'db>,
|
||||
current: Type<'db>,
|
||||
program: Program<'db>,
|
||||
) -> Type<'db> {
|
||||
let env = ProgramEnvironment::from_program(
|
||||
ty_python_core::program::Program::get(db).resolver_environment(db),
|
||||
);
|
||||
let env = ProgramEnvironment::from_program(program);
|
||||
current.cycle_normalized(db, &env, *previous, cycle)
|
||||
}
|
||||
|
||||
#[salsa::tracked(
|
||||
returns(copy),
|
||||
cycle_initial=|_, id| Type::divergent(id),
|
||||
cycle_initial=|_, id, _| Type::divergent(id),
|
||||
cycle_fn=oscillating_generic_alias_cycle_recover,
|
||||
)]
|
||||
fn oscillating_generic_alias(db: &dyn Db) -> Type<'_> {
|
||||
let env = ProgramEnvironment::from_program(
|
||||
ty_python_core::program::Program::get(db).resolver_environment(db),
|
||||
);
|
||||
let previous = oscillating_generic_alias(db);
|
||||
fn oscillating_generic_alias<'db>(db: &'db dyn Db, program: Program<'db>) -> Type<'db> {
|
||||
let env = ProgramEnvironment::from_program(program);
|
||||
let previous = oscillating_generic_alias(db, program);
|
||||
let argument = if let Type::GenericAlias(alias) = previous
|
||||
&& alias.specialization(db).types(db) == [Type::unknown()]
|
||||
{
|
||||
@@ -104,7 +101,7 @@ fn oscillating_generic_alias(db: &dyn Db) -> Type<'_> {
|
||||
#[test]
|
||||
fn generic_alias_cycle_recovery_normalizes_same_origin_unknown_oscillation() {
|
||||
let db = setup_db();
|
||||
let Type::GenericAlias(alias) = oscillating_generic_alias(&db) else {
|
||||
let Type::GenericAlias(alias) = oscillating_generic_alias(&db, db.program()) else {
|
||||
panic!("cycle recovery should preserve the generic alias");
|
||||
};
|
||||
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use ruff_db::Db;
|
||||
use ruff_db::files::{File, Files, system_path_to_file};
|
||||
use ruff_db::system::{DbWithTestSystem, System, SystemPath, SystemPathBuf, TestSystem};
|
||||
use ruff_db::vendored::VendoredFileSystem;
|
||||
use ruff_python_ast::PythonVersion;
|
||||
|
||||
use ty_module_resolver::SearchPathSettings;
|
||||
use ty_python_core::platform::PythonPlatform;
|
||||
use ty_python_core::program::{FallibleStrategy, Program, ProgramSettings};
|
||||
use ty_python_core::program::ProgramSettings;
|
||||
use ty_python_semantic::lint::{LintRegistry, RuleSelection};
|
||||
use ty_python_semantic::pull_types::pull_types;
|
||||
use ty_python_semantic::{AnalysisSettings, Db as _, check_file_unwrap, default_lint_registry};
|
||||
use ty_site_packages::{PythonVersionSource, PythonVersionWithSource};
|
||||
|
||||
use ruff_db::diagnostic::Diagnostic;
|
||||
use test_case::test_case;
|
||||
use ty_python_core::{Db as _, ProgramFile};
|
||||
use ty_python_core::{Db as _, ProgramFile, TestProgramDb};
|
||||
|
||||
fn get_cargo_workspace_root() -> anyhow::Result<&'static SystemPath> {
|
||||
SystemPath::new(env!("CARGO_MANIFEST_DIR"))
|
||||
@@ -149,35 +144,23 @@ pub struct CorpusDb {
|
||||
system: TestSystem,
|
||||
vendored: VendoredFileSystem,
|
||||
analysis_settings: Arc<AnalysisSettings>,
|
||||
program_settings: ProgramSettings,
|
||||
}
|
||||
|
||||
impl CorpusDb {
|
||||
#[expect(clippy::new_without_default)]
|
||||
pub fn new() -> Self {
|
||||
let db = Self {
|
||||
let vendored = ty_vendored::file_system().clone();
|
||||
let program_settings = ProgramSettings::empty(&vendored);
|
||||
Self {
|
||||
storage: salsa::Storage::new(None),
|
||||
system: TestSystem::default(),
|
||||
vendored: ty_vendored::file_system().clone(),
|
||||
vendored,
|
||||
rule_selection: RuleSelection::from_registry(default_lint_registry()),
|
||||
files: Files::default(),
|
||||
analysis_settings: Arc::new(AnalysisSettings::default()),
|
||||
};
|
||||
|
||||
Program::from_settings(
|
||||
&db,
|
||||
ProgramSettings {
|
||||
python_version: PythonVersionWithSource {
|
||||
version: PythonVersion::latest_ty(),
|
||||
source: PythonVersionSource::default(),
|
||||
},
|
||||
python_platform: PythonPlatform::default(),
|
||||
search_paths: SearchPathSettings::new(vec![])
|
||||
.to_search_paths(db.system(), db.vendored(), &FallibleStrategy)
|
||||
.unwrap(),
|
||||
},
|
||||
);
|
||||
|
||||
db
|
||||
program_settings,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,6 +199,13 @@ impl ty_python_core::Db for CorpusDb {
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::db]
|
||||
impl TestProgramDb for CorpusDb {
|
||||
fn program_settings(&self) -> &ProgramSettings {
|
||||
&self.program_settings
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::db]
|
||||
impl ty_python_semantic::Db for CorpusDb {
|
||||
fn check_file(&self, file: File) -> Vec<Diagnostic> {
|
||||
@@ -227,7 +217,7 @@ impl ty_python_semantic::Db for CorpusDb {
|
||||
}
|
||||
|
||||
fn program_file(&self, file: File) -> ProgramFile<'_> {
|
||||
Program::get(self).program_file(self, file)
|
||||
self.program().program_file(self, file)
|
||||
}
|
||||
|
||||
fn rule_selection(&self, _file: File) -> &RuleSelection {
|
||||
|
||||
@@ -12,7 +12,6 @@ use std::fmt::{self, Write};
|
||||
use std::str::FromStr;
|
||||
use ty_module_resolver::ModuleResolveMode;
|
||||
use ty_project::Db as _;
|
||||
use ty_python_core::program::Program;
|
||||
|
||||
pub(crate) struct ExecuteCommand;
|
||||
|
||||
@@ -67,7 +66,7 @@ fn debug_information(session: &Session) -> crate::Result<String> {
|
||||
|
||||
for db in session.project_dbs() {
|
||||
writeln!(buffer, "Project at {}", db.project().root(db))?;
|
||||
let program = Program::get(db);
|
||||
let program = db.project().program(db);
|
||||
writeln!(buffer, "Program:")?;
|
||||
writeln!(
|
||||
buffer,
|
||||
|
||||
@@ -27,7 +27,7 @@ use ty_project::watch::{ChangeEvent, CreatedKind};
|
||||
use ty_project::{ChangeResult, Db as _, ProjectDatabase, ProjectMetadata};
|
||||
|
||||
use index::DocumentError;
|
||||
use ty_python_core::program::{Program, UseDefaultStrategy};
|
||||
use ty_python_core::program::UseDefaultStrategy;
|
||||
|
||||
pub(crate) use self::options::InitializationOptions;
|
||||
pub use self::options::{ClientOptions, DiagnosticMode, GlobalOptions, WorkspaceOptions};
|
||||
@@ -1089,7 +1089,7 @@ impl Session {
|
||||
.flat_map(|db| {
|
||||
ty_module_resolver::system_module_search_paths(
|
||||
db,
|
||||
Program::get(db).resolver_environment(db),
|
||||
db.project().program(db).resolver_environment(db),
|
||||
)
|
||||
.map(move |path| (db, path))
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use anyhow::Result;
|
||||
use anyhow::{Context, Result};
|
||||
use lsp_types::{ExecuteCommandParams, ExecuteCommandRequest, WorkDoneProgressParams};
|
||||
use ruff_db::system::SystemPath;
|
||||
|
||||
@@ -49,6 +49,21 @@ python-platform = \"linux\"
|
||||
.as_str()
|
||||
.expect("debug command to return a string response");
|
||||
|
||||
let (before_structs, salsa_structs) = response
|
||||
.split_once("=======SALSA STRUCTS=======\n")
|
||||
.context("debug response missing Salsa structs section")?;
|
||||
let (salsa_structs, after_structs) = salsa_structs
|
||||
.split_once("=======SALSA QUERIES=======\n")
|
||||
.context("debug response missing Salsa queries section")?;
|
||||
|
||||
// The production report orders structs by memory usage, which varies between platforms.
|
||||
let mut salsa_structs = salsa_structs.lines().collect::<Vec<_>>();
|
||||
salsa_structs.sort_unstable();
|
||||
let response = format!(
|
||||
"{before_structs}=======SALSA STRUCTS=======\n{}\n=======SALSA QUERIES=======\n{after_structs}",
|
||||
salsa_structs.join("\n")
|
||||
);
|
||||
|
||||
let mut settings = insta::Settings::clone_current();
|
||||
settings.add_filter(r"\b[0-9]+.[0-9]+MB\b", "[X.XXMB]");
|
||||
settings.add_filter(r"Workspace .+\)", "Workspace XXX");
|
||||
|
||||
@@ -180,14 +180,16 @@ Settings: Settings {
|
||||
|
||||
Memory report:
|
||||
=======SALSA STRUCTS=======
|
||||
`Program` metadata=[X.XXMB] fields=[X.XXMB] count=1
|
||||
`ResolverEnvironment` metadata=[X.XXMB] fields=[X.XXMB] count=1
|
||||
`Project` metadata=[X.XXMB] fields=[X.XXMB] count=1
|
||||
`FileRoot` metadata=[X.XXMB] fields=[X.XXMB] count=1
|
||||
`ModuleResolveModeIngredient` metadata=[X.XXMB] fields=[X.XXMB] count=1
|
||||
`Program` metadata=[X.XXMB] fields=[X.XXMB] count=1
|
||||
`Project` metadata=[X.XXMB] fields=[X.XXMB] count=1
|
||||
`ResolverEnvironment` metadata=[X.XXMB] fields=[X.XXMB] count=1
|
||||
=======SALSA QUERIES=======
|
||||
`dynamic_resolution_paths -> alloc::vec::Vec<ty_module_resolver::path::SearchPath>`
|
||||
metadata=[X.XXMB] fields=[X.XXMB] count=1
|
||||
`Project::program_ -> ty_python_core::program::Program<'_>`
|
||||
metadata=[X.XXMB] fields=[X.XXMB] count=1
|
||||
=======SALSA SUMMARY=======
|
||||
TOTAL MEMORY USAGE: [X.XXMB]
|
||||
struct metadata = [X.XXMB]
|
||||
|
||||
@@ -10,7 +10,7 @@ use ruff_python_ast::PythonVersion;
|
||||
use ruff_text_size::TextRange;
|
||||
|
||||
/// The source of the Python version.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Default, get_size2::GetSize)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash, Default, get_size2::GetSize)]
|
||||
pub enum PythonVersionSource {
|
||||
/// Value loaded from a project's configuration file.
|
||||
ConfigFile(PythonVersionFileSource),
|
||||
@@ -49,7 +49,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, Hash, Clone, get_size2::GetSize)]
|
||||
pub struct PythonVersionFileSource {
|
||||
path: Arc<SystemPathBuf>,
|
||||
range: Option<TextRange>,
|
||||
@@ -72,7 +72,7 @@ impl PythonVersionFileSource {
|
||||
}
|
||||
|
||||
/// A Python version with its source.
|
||||
#[derive(Eq, PartialEq, Debug, Clone, get_size2::GetSize)]
|
||||
#[derive(Eq, PartialEq, Hash, Debug, Clone, get_size2::GetSize)]
|
||||
pub struct PythonVersionWithSource {
|
||||
pub version: PythonVersion,
|
||||
pub source: PythonVersionSource,
|
||||
|
||||
@@ -15,8 +15,8 @@ use std::borrow::Cow;
|
||||
use std::sync::Arc;
|
||||
use tempfile::TempDir;
|
||||
use ty_module_resolver::ModuleGlobSetBuilder;
|
||||
use ty_python_core::program::Program;
|
||||
use ty_python_core::{Db as _, ProgramFile};
|
||||
use ty_python_core::program::ProgramSettings;
|
||||
use ty_python_core::{Db as _, ProgramFile, TestProgramDb};
|
||||
use ty_python_semantic::lint::{LintRegistry, RuleSelection};
|
||||
use ty_python_semantic::{
|
||||
AnalysisSettings, Db as SemanticDb, check_file_unwrap, default_lint_registry,
|
||||
@@ -34,6 +34,8 @@ pub(crate) struct Db {
|
||||
|
||||
impl Db {
|
||||
pub(crate) fn setup() -> Self {
|
||||
let vendored = ty_vendored::file_system().clone();
|
||||
let program_settings = ProgramSettings::empty(&vendored);
|
||||
let mut db = Self {
|
||||
system: MdtestSystem::in_memory(),
|
||||
storage: salsa::Storage::new(Some(Box::new({
|
||||
@@ -41,12 +43,12 @@ impl Db {
|
||||
tracing::trace!("event: {:?}", event);
|
||||
}
|
||||
}))),
|
||||
vendored: ty_vendored::file_system().clone(),
|
||||
vendored,
|
||||
files: Files::default(),
|
||||
settings: None,
|
||||
};
|
||||
|
||||
db.settings = Some(Settings::new(&db));
|
||||
db.settings = Some(Settings::new(&db, program_settings));
|
||||
db
|
||||
}
|
||||
|
||||
@@ -54,6 +56,14 @@ impl Db {
|
||||
self.settings.unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn update_program(&mut self, settings: ProgramSettings) {
|
||||
let db_settings = self.settings();
|
||||
if db_settings.program(self) != &settings {
|
||||
settings.search_paths.try_register_static_roots(self);
|
||||
db_settings.set_program(self).to(settings);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_verbosity(&mut self, verbose: bool) {
|
||||
self.settings().set_verbose(self).to(verbose);
|
||||
}
|
||||
@@ -133,7 +143,7 @@ impl SemanticDb for Db {
|
||||
}
|
||||
|
||||
fn program_file(&self, file: File) -> ProgramFile<'_> {
|
||||
Program::get(self).program_file(self, file)
|
||||
self.program().program_file(self, file)
|
||||
}
|
||||
|
||||
fn rule_selection(&self, file: File) -> &RuleSelection {
|
||||
@@ -161,6 +171,13 @@ impl SemanticDb for Db {
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::db]
|
||||
impl TestProgramDb for Db {
|
||||
fn program_settings(&self) -> &ProgramSettings {
|
||||
self.settings().program(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::db]
|
||||
impl salsa::Database for Db {}
|
||||
|
||||
@@ -214,6 +231,8 @@ impl FileSettings {
|
||||
|
||||
#[salsa::input(debug)]
|
||||
struct Settings {
|
||||
#[returns(ref)]
|
||||
program: ProgramSettings,
|
||||
#[default]
|
||||
#[returns(ref)]
|
||||
analysis: AnalysisSettings,
|
||||
|
||||
@@ -20,8 +20,9 @@ use std::fmt::Write;
|
||||
use ty_module_resolver::{
|
||||
Module, SearchPath, SearchPathSettings, list_modules, resolve_module_confident,
|
||||
};
|
||||
use ty_python_core::TestProgramDb as _;
|
||||
use ty_python_core::platform::PythonPlatform;
|
||||
use ty_python_core::program::{FallibleStrategy, Program, ProgramSettings};
|
||||
use ty_python_core::program::{FallibleStrategy, ProgramSettings};
|
||||
use ty_python_semantic::pull_types::pull_types;
|
||||
use ty_python_semantic::types::UNDEFINED_REVEAL;
|
||||
use ty_python_semantic::{
|
||||
@@ -304,7 +305,7 @@ fn run_test(
|
||||
.expect("Failed to resolve search path settings"),
|
||||
};
|
||||
|
||||
Program::init_or_update(db, settings);
|
||||
db.update_program(settings);
|
||||
db.update_analysis_options(configuration.analysis.as_ref());
|
||||
db.update_mdtest_rule_selection(configuration.rules.as_ref(), options.default_error_rule);
|
||||
db.set_verbosity(test.configuration().verbose());
|
||||
@@ -493,7 +494,7 @@ struct ModuleInconsistency<'db> {
|
||||
/// `list_module`.
|
||||
fn run_module_resolution_consistency_test(db: &db::Db) -> Result<(), Vec<ModuleInconsistency<'_>>> {
|
||||
let mut errs = vec![];
|
||||
let environment = Program::get(db).resolver_environment(db);
|
||||
let environment = db.program().resolver_environment(db);
|
||||
for from_list in list_modules(db, environment).iter().copied() {
|
||||
// TODO: For now list_modules does not partake in desperate module resolution so
|
||||
// only compare against confident module resolution.
|
||||
|
||||
+15
-13
@@ -2,7 +2,6 @@ use std::any::Any;
|
||||
|
||||
use js_sys::{Error, JsString};
|
||||
use ruff_db::Db as _;
|
||||
use ruff_db::PythonFile;
|
||||
use ruff_db::diagnostic::{self, DisplayDiagnosticConfig};
|
||||
use ruff_db::files::{File, FilePath, FileRange, system_path_to_file, vendored_path_to_file};
|
||||
use ruff_db::source::{SourceText, line_index, source_text};
|
||||
@@ -28,7 +27,7 @@ use ty_project::metadata::options::Options;
|
||||
use ty_project::watch::{ChangeEvent, ChangedKind, CreatedKind, DeletedKind};
|
||||
use ty_project::{CheckMode, ProjectMetadata};
|
||||
use ty_project::{Db, ProjectDatabase, SemanticDb as _};
|
||||
use ty_python_core::program::{FallibleStrategy, Program};
|
||||
use ty_python_core::program::FallibleStrategy;
|
||||
use ty_python_semantic::ProgramEnvironment;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
@@ -171,20 +170,23 @@ impl Workspace {
|
||||
let (program_settings, program_settings_diagnostics) = merged_options
|
||||
.to_program_settings(&self.system, self.db.vendored(), &FallibleStrategy)
|
||||
.map_err(into_error)?;
|
||||
Program::get(&self.db).update_from_settings(&mut self.db, program_settings);
|
||||
self.db
|
||||
.project()
|
||||
.update_program(&mut self.db, program_settings);
|
||||
|
||||
let (settings, settings_diagnostics) = merged_options
|
||||
let (settings, mut settings_diagnostics) = merged_options
|
||||
.to_settings(&self.db, &FallibleStrategy)
|
||||
.map_err(into_error)?;
|
||||
|
||||
self.db.project().reload(
|
||||
&mut self.db,
|
||||
project,
|
||||
Some(settings),
|
||||
settings_diagnostics,
|
||||
program_settings_diagnostics,
|
||||
settings_diagnostics.extend(
|
||||
program_settings_diagnostics
|
||||
.into_iter()
|
||||
.map(|diagnostic| diagnostic.into_diagnostic(&self.db)),
|
||||
);
|
||||
|
||||
self.db
|
||||
.project()
|
||||
.reload(&mut self.db, project, Some(settings), settings_diagnostics);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -294,7 +296,7 @@ impl Workspace {
|
||||
pub fn parsed(&self, file_id: &FileHandle) -> Result<String, Error> {
|
||||
let parsed = ruff_db::parsed::parsed_module(
|
||||
&self.db,
|
||||
PythonFile::new(&self.db, file_id.file, self.db.python_version()),
|
||||
self.db.program_file(file_id.file).python_file(&self.db),
|
||||
)
|
||||
.load(&self.db);
|
||||
|
||||
@@ -309,7 +311,7 @@ impl Workspace {
|
||||
pub fn tokens(&self, file_id: &FileHandle) -> Result<String, Error> {
|
||||
let parsed = ruff_db::parsed::parsed_module(
|
||||
&self.db,
|
||||
PythonFile::new(&self.db, file_id.file, self.db.python_version()),
|
||||
self.db.program_file(file_id.file).python_file(&self.db),
|
||||
)
|
||||
.load(&self.db);
|
||||
|
||||
|
||||
Generated
+71
-62
@@ -115,7 +115,7 @@ dependencies = [
|
||||
"manyhow",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -131,7 +131,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote-use",
|
||||
"syn",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -287,7 +287,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -422,7 +422,7 @@ dependencies = [
|
||||
"defmt-parser",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -442,7 +442,7 @@ checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -453,7 +453,7 @@ checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -558,7 +558,7 @@ checksum = "c736d226c32e496b8377813b52269e11ad3a48d8373b68862d0364f04fd1229d"
|
||||
dependencies = [
|
||||
"attribute-derive",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -816,7 +816,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -873,7 +873,7 @@ checksum = "d0879bd39df99c4c5e2c6615ccc026391a423dde10532c573e6086eb94a802cc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -940,7 +940,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0903173ea316c34a44d0497161e04d9210af44f5f5e89bf2f55d9a254c9a0e8d"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -982,7 +982,7 @@ dependencies = [
|
||||
"manyhow-macros",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1093,21 +1093,18 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "path-absolutize"
|
||||
version = "3.1.1"
|
||||
version = "4.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4af381fe79fa195b4909485d99f73a80792331df0625188e707854f0b3383f5"
|
||||
checksum = "f808742975794703469f67a28dd14b1d1009a1743c18b0353b4b951dbb0068ad"
|
||||
dependencies = [
|
||||
"path-dedot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "path-dedot"
|
||||
version = "3.1.1"
|
||||
version = "4.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07ba0ad7e047712414213ff67533e6dd477af0a4e1d14fb52343e53d30ea9397"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
checksum = "03351d0f1c066c114015408dc6a3e101f080fc03ef9d8d799aa58ec760cac5a6"
|
||||
|
||||
[[package]]
|
||||
name = "path-slash"
|
||||
@@ -1371,7 +1368,7 @@ dependencies = [
|
||||
"proc-macro-utils",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1513,7 +1510,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_annotate_snippets"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"memchr",
|
||||
@@ -1522,7 +1519,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_cache"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"char_str",
|
||||
"filetime",
|
||||
@@ -1535,7 +1532,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_db"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"arc-swap",
|
||||
@@ -1573,7 +1570,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_diagnostics"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"get-size2",
|
||||
"is-macro",
|
||||
@@ -1583,7 +1580,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_formatter"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"drop_bomb",
|
||||
"ruff_cache",
|
||||
@@ -1598,7 +1595,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_index"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"get-size2",
|
||||
"ruff_macros",
|
||||
@@ -1607,7 +1604,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_linter"
|
||||
version = "0.15.22"
|
||||
version = "0.16.1"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"anyhow",
|
||||
@@ -1665,7 +1662,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"itertools 0.15.0",
|
||||
@@ -1673,19 +1670,19 @@ dependencies = [
|
||||
"quote",
|
||||
"regex",
|
||||
"ruff_python_trivia",
|
||||
"syn",
|
||||
"syn 3.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_memory_usage"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"get-size2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_notebook"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"rand 0.10.2",
|
||||
@@ -1700,7 +1697,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_ast"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"arrayvec",
|
||||
@@ -1724,7 +1721,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_codegen"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"ruff_python_ast",
|
||||
"ruff_python_literal",
|
||||
@@ -1735,7 +1732,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_formatter"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -1763,7 +1760,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_importer"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"ruff_diagnostics",
|
||||
@@ -1776,7 +1773,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_index"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"ruff_python_ast",
|
||||
"ruff_python_trivia",
|
||||
@@ -1786,7 +1783,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_literal"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"icu_properties",
|
||||
@@ -1796,7 +1793,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_parser"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"bstr",
|
||||
@@ -1816,7 +1813,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_semantic"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"is-macro",
|
||||
@@ -1833,7 +1830,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_stdlib"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"unicode-ident",
|
||||
@@ -1841,7 +1838,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_trivia"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"itertools 0.15.0",
|
||||
"ruff_source_file",
|
||||
@@ -1852,7 +1849,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_ranged_value"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"ruff_db",
|
||||
"ruff_text_size",
|
||||
@@ -1862,7 +1859,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_source_file"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"get-size2",
|
||||
"memchr",
|
||||
@@ -1872,7 +1869,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_text_size"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"get-size2",
|
||||
"serde",
|
||||
@@ -1947,7 +1944,7 @@ checksum = "ec5c48c5a4a53a6e2be9762f56566f1325d4394c011c00b3ea86ba2d13411e71"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1998,7 +1995,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2101,7 +2098,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2121,6 +2118,17 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "3.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.2"
|
||||
@@ -2129,7 +2137,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2173,7 +2181,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2184,7 +2192,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2294,7 +2302,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2308,7 +2316,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ty_combine"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"ordermap",
|
||||
"ruff_db",
|
||||
@@ -2318,12 +2326,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ty_module_resolver"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"camino",
|
||||
"compact_str",
|
||||
"get-size2",
|
||||
"ordermap",
|
||||
"regex",
|
||||
"regex-syntax",
|
||||
"ruff_db",
|
||||
@@ -2340,7 +2349,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ty_python_core"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"bitvec",
|
||||
@@ -2368,7 +2377,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ty_python_semantic"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"char_str",
|
||||
@@ -2408,7 +2417,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ty_site_packages"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"camino",
|
||||
"colored",
|
||||
@@ -2428,14 +2437,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ty_static"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"ruff_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ty_vendored"
|
||||
version = "0.0.5"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"path-slash",
|
||||
"ruff_db",
|
||||
@@ -2618,7 +2627,7 @@ dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.119",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -2711,7 +2720,7 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.119",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -2732,7 +2741,7 @@ checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2752,7 +2761,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.119",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -2786,7 +2795,7 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ ruff_python_formatter = { path = "../crates/ruff_python_formatter" }
|
||||
ruff_text_size = { path = "../crates/ruff_text_size" }
|
||||
|
||||
ty_module_resolver = { path = "../crates/ty_module_resolver" }
|
||||
ty_python_semantic = { path = "../crates/ty_python_semantic" }
|
||||
ty_python_semantic = { path = "../crates/ty_python_semantic", features = ["testing"] }
|
||||
ty_vendored = { path = "../crates/ty_vendored" }
|
||||
ty_python_core = { path = "../crates/ty_python_core" }
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@ use ruff_db::vendored::VendoredFileSystem;
|
||||
use ruff_python_parser::{Mode, ParseOptions, parse_unchecked};
|
||||
use ty_module_resolver::{Db as ModuleResolverDb, SearchPathSettings};
|
||||
use ty_python_core::platform::PythonPlatform;
|
||||
use ty_python_core::program::{FallibleStrategy, Program, ProgramSettings};
|
||||
use ty_python_core::{Db as _, ProgramFile};
|
||||
use ty_python_core::program::{FallibleStrategy, ProgramSettings};
|
||||
use ty_python_core::{Db as _, ProgramFile, TestProgramDb};
|
||||
use ty_python_semantic::lint::LintRegistry;
|
||||
use ty_python_semantic::types::check_types;
|
||||
use ty_python_semantic::{
|
||||
@@ -38,22 +38,43 @@ struct TestDb {
|
||||
vendored: VendoredFileSystem,
|
||||
rule_selection: Arc<RuleSelection>,
|
||||
analysis_settings: Arc<AnalysisSettings>,
|
||||
program_settings: ProgramSettings,
|
||||
}
|
||||
|
||||
impl TestDb {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
let vendored = ty_vendored::file_system().clone();
|
||||
let program_settings = ProgramSettings::empty(&vendored);
|
||||
let mut db = Self {
|
||||
storage: salsa::Storage::new(Some(Box::new({
|
||||
move |event| {
|
||||
tracing::trace!("event: {:?}", event);
|
||||
}
|
||||
}))),
|
||||
system: TestSystem::default(),
|
||||
vendored: ty_vendored::file_system().clone(),
|
||||
vendored,
|
||||
files: Files::default(),
|
||||
rule_selection: RuleSelection::from_registry(default_lint_registry()).into(),
|
||||
analysis_settings: AnalysisSettings::default().into(),
|
||||
}
|
||||
program_settings,
|
||||
};
|
||||
|
||||
let src_root = SystemPathBuf::from("/src");
|
||||
db.memory_file_system()
|
||||
.create_directory_all(&src_root)
|
||||
.unwrap();
|
||||
|
||||
let program_settings = ProgramSettings {
|
||||
python_version: PythonVersionWithSource::default(),
|
||||
python_platform: PythonPlatform::default(),
|
||||
search_paths: SearchPathSettings::new(vec![src_root])
|
||||
.to_search_paths(db.system(), db.vendored(), &FallibleStrategy)
|
||||
.expect("Valid search path settings"),
|
||||
};
|
||||
program_settings.search_paths.try_register_static_roots(&db);
|
||||
db.program_settings = program_settings;
|
||||
|
||||
db
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +124,7 @@ impl SemanticDb for TestDb {
|
||||
}
|
||||
|
||||
fn program_file(&self, file: File) -> ProgramFile<'_> {
|
||||
Program::get(self).program_file(self, file)
|
||||
self.program().program_file(self, file)
|
||||
}
|
||||
|
||||
fn rule_selection(&self, _file: File) -> &RuleSelection {
|
||||
@@ -132,30 +153,15 @@ impl SemanticDb for TestDb {
|
||||
}
|
||||
|
||||
#[salsa::db]
|
||||
impl salsa::Database for TestDb {}
|
||||
|
||||
fn setup_db() -> TestDb {
|
||||
let db = TestDb::new();
|
||||
|
||||
let src_root = SystemPathBuf::from("/src");
|
||||
db.memory_file_system()
|
||||
.create_directory_all(&src_root)
|
||||
.unwrap();
|
||||
|
||||
Program::from_settings(
|
||||
&db,
|
||||
ProgramSettings {
|
||||
python_version: PythonVersionWithSource::default(),
|
||||
python_platform: PythonPlatform::default(),
|
||||
search_paths: SearchPathSettings::new(vec![src_root])
|
||||
.to_search_paths(db.system(), db.vendored(), &FallibleStrategy)
|
||||
.expect("Valid search path settings"),
|
||||
},
|
||||
);
|
||||
|
||||
db
|
||||
impl TestProgramDb for TestDb {
|
||||
fn program_settings(&self) -> &ProgramSettings {
|
||||
&self.program_settings
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::db]
|
||||
impl salsa::Database for TestDb {}
|
||||
|
||||
static TEST_DB: OnceLock<Mutex<TestDb>> = OnceLock::new();
|
||||
|
||||
fn do_fuzz(case: &[u8]) -> Corpus {
|
||||
@@ -169,7 +175,7 @@ fn do_fuzz(case: &[u8]) -> Corpus {
|
||||
}
|
||||
|
||||
let mut db = TEST_DB
|
||||
.get_or_init(|| Mutex::new(setup_db()))
|
||||
.get_or_init(|| Mutex::new(TestDb::new()))
|
||||
.lock()
|
||||
.unwrap();
|
||||
|
||||
|
||||
Reference in new issue
Block a user