mirror of
https://github.com/astral-sh/ruff.git
synced 2026-10-28 10:45:37 +08:00
[ty] Move Python version source out of Program (#27434)
## Summary `PythonVersionWithSource` contains from where ty derived the `PythonVersion`. If the Python version comes from the project's `pyproject.toml`, then the source includes the configuration file name and the text range of the `requires-python` or `python-version` key-value pair. Any change to that `pyproject.toml` would result in a new and different `PythonVersionWithSource`, which, in turn, results in a distinct interned `Program`. This results in invalidating every single type inference query (more accurately, we now get a new instance of each type inference query, but we loose all cached results). Storing `PythonVersionWithSource` also has the downside that it prevents scripts that otherwise have identical `Program` settings to share the same `Program`, only because their `python-version` comes from different script metadata blocks. This PR moves `PythonVersionWithSource` out of `Program`. `Program` should only contain information that changes type inference. `PythonVersionWithSource` does not. `Db` now has a new `python_version_with_source` method that, given a file, returns the source for it (A script can return a different source than a project file). ## Test Plan Testing: Semantic, project, core, server, Markdown, and corpus tests pass; repository hooks pass.
This commit is contained in:
1 parent
aa5b04ad92
commit
e87ddc00ea
14 files changed
+62
-34
No files matched your search
@@ -17,7 +17,7 @@ use salsa::{Database, Event, Setter};
|
||||
use ty_python_core::ProgramFile;
|
||||
use ty_python_core::program::{FallibleStrategy, MisconfigurationStrategy, UseDefaultStrategy};
|
||||
use ty_python_semantic::lint::{LintRegistry, RuleSelection};
|
||||
use ty_python_semantic::{AnalysisSettings, Db as SemanticDb};
|
||||
use ty_python_semantic::{AnalysisSettings, Db as SemanticDb, PythonVersionWithSource};
|
||||
|
||||
mod changes;
|
||||
|
||||
@@ -542,6 +542,10 @@ impl SemanticDb for ProjectDatabase {
|
||||
self.project().program(self).program_file(self, file)
|
||||
}
|
||||
|
||||
fn python_version_with_source(&self, _file: File) -> &PythonVersionWithSource {
|
||||
&self.project().program_settings(self).python_version
|
||||
}
|
||||
|
||||
fn rule_selection(&self, file: File) -> &RuleSelection {
|
||||
let settings = file_settings(self, file);
|
||||
settings.rules(self)
|
||||
@@ -784,6 +788,10 @@ pub(crate) mod testing {
|
||||
self.project().program(self).program_file(self, file)
|
||||
}
|
||||
|
||||
fn python_version_with_source(&self, _file: File) -> &PythonVersionWithSource {
|
||||
&self.project().program_settings(self).python_version
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_file(&self, file: File) -> Vec<Diagnostic> {
|
||||
crate::check_file(self, file)
|
||||
|
||||
@@ -14,11 +14,6 @@ pub use ty_module_resolver::{FallibleStrategy, MisconfigurationStrategy, UseDefa
|
||||
|
||||
#[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,
|
||||
|
||||
@@ -39,7 +34,7 @@ impl<'db> Program<'db> {
|
||||
|
||||
let resolver_environment =
|
||||
ResolverEnvironment::new(db, python_version.version, &search_paths);
|
||||
Program::new(db, python_version, python_platform, resolver_environment)
|
||||
Program::new(db, python_platform, resolver_environment)
|
||||
}
|
||||
|
||||
pub fn python_version(self, db: &'db dyn Db) -> PythonVersion {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::AnalysisSettings;
|
||||
use crate::lint::{LintRegistry, RuleSelection};
|
||||
use crate::{AnalysisSettings, PythonVersionWithSource};
|
||||
use ruff_db::diagnostic::Diagnostic;
|
||||
use ruff_db::files::File;
|
||||
use ty_python_core::{Db as PythonCoreDb, ProgramFile};
|
||||
@@ -12,6 +12,9 @@ pub trait Db: PythonCoreDb {
|
||||
/// Returns the program file for `file`.
|
||||
fn program_file(&self, file: File) -> ProgramFile<'_>;
|
||||
|
||||
/// Returns the Python version and its configuration source for `file`.
|
||||
fn python_version_with_source(&self, file: File) -> &PythonVersionWithSource;
|
||||
|
||||
/// Resolves the rule selection for a given file.
|
||||
fn rule_selection(&self, file: File) -> &RuleSelection;
|
||||
|
||||
@@ -177,6 +180,10 @@ pub(crate) mod tests {
|
||||
self.program().program_file(self, file)
|
||||
}
|
||||
|
||||
fn python_version_with_source(&self, _file: File) -> &PythonVersionWithSource {
|
||||
&self.program_settings.python_version
|
||||
}
|
||||
|
||||
fn rule_selection(&self, _file: File) -> &RuleSelection {
|
||||
&self.rule_selection
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
Db, ProgramEnvironment, PythonVersionSource, PythonVersionWithSource,
|
||||
lint::lint_documentation_url, types::TypeCheckDiagnostics,
|
||||
Db, PythonVersionSource, PythonVersionWithSource, lint::lint_documentation_url,
|
||||
types::TypeCheckDiagnostics,
|
||||
};
|
||||
use levenshtein::{HideUnderscoredSuggestions, find_best_suggestion};
|
||||
use ruff_db::{
|
||||
@@ -46,12 +46,11 @@ 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,
|
||||
file: File,
|
||||
diagnostic: &mut Diagnostic,
|
||||
action: &str,
|
||||
) {
|
||||
let PythonVersionWithSource { version, source } =
|
||||
env.program(db).python_version_with_source(db);
|
||||
let PythonVersionWithSource { version, source } = db.python_version_with_source(file);
|
||||
|
||||
match source {
|
||||
crate::PythonVersionSource::Cli => {
|
||||
|
||||
@@ -206,7 +206,7 @@ pub fn check_file(db: &dyn Db, file: ProgramFile<'_>) -> Result<Box<[Diagnostic]
|
||||
let mut error = Diagnostic::invalid_syntax(source_file, error, error);
|
||||
add_inferred_python_version_hint_to_diagnostic(
|
||||
db,
|
||||
&ProgramEnvironment::from_file(file),
|
||||
source_file,
|
||||
&mut error,
|
||||
"parsing syntax",
|
||||
);
|
||||
|
||||
@@ -5,8 +5,7 @@ use super::{
|
||||
CallArguments, CallDunderError, ClassBase, ClassLiteral, GenericAlias, KnownClass,
|
||||
StaticClassLiteral, add_inferred_python_version_hint_to_diagnostic,
|
||||
};
|
||||
use crate::diagnostic::did_you_mean;
|
||||
use crate::diagnostic::format_enumeration;
|
||||
use crate::diagnostic::{did_you_mean, format_enumeration};
|
||||
use crate::lint::{Level, LintRegistryBuilder, LintStatus};
|
||||
use crate::place::{DefinedPlace, Place, place_from_bindings};
|
||||
use crate::suppression::FileSuppressionId;
|
||||
@@ -37,6 +36,7 @@ use itertools::Itertools;
|
||||
use ruff_db::source::source_text;
|
||||
use ruff_db::{
|
||||
diagnostic::{Annotation, Diagnostic, Span, SubDiagnostic, SubDiagnosticSeverity},
|
||||
files::File,
|
||||
parsed::parsed_module,
|
||||
};
|
||||
use ruff_diagnostics::{Edit, Fix, IsolationLevel};
|
||||
@@ -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,
|
||||
file: File,
|
||||
env: &ProgramEnvironment<'_>,
|
||||
diagnostic: &mut Diagnostic,
|
||||
full_submodule_name: &ModuleName,
|
||||
@@ -4722,7 +4723,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, env, diagnostic, "resolving modules");
|
||||
add_inferred_python_version_hint_to_diagnostic(db, file, diagnostic, "resolving modules");
|
||||
|
||||
true
|
||||
}
|
||||
@@ -4737,7 +4738,7 @@ pub(super) fn hint_if_stdlib_submodule_exists_on_other_versions(
|
||||
/// misconfigured their Python version.
|
||||
pub(super) fn hint_if_stdlib_attribute_exists_on_other_versions(
|
||||
db: &dyn Db,
|
||||
env: &ProgramEnvironment<'_>,
|
||||
source_file: ProgramFile<'_>,
|
||||
mut diagnostic: LintDiagnosticGuard,
|
||||
value_type: Type,
|
||||
attr: &str,
|
||||
@@ -4750,7 +4751,7 @@ pub(super) fn hint_if_stdlib_attribute_exists_on_other_versions(
|
||||
return;
|
||||
};
|
||||
let module = module_ty.module(db);
|
||||
let Some(file) = module.file(db) else {
|
||||
let Some(module_file) = module.file(db) else {
|
||||
return;
|
||||
};
|
||||
let Some(search_path) = module.search_path(db) else {
|
||||
@@ -4763,7 +4764,7 @@ pub(super) fn hint_if_stdlib_attribute_exists_on_other_versions(
|
||||
// We populate place_table entries for stdlib items across all known versions and platforms,
|
||||
// so if this lookup succeeds then we know that this lookup *could* succeed with possible
|
||||
// configuration changes.
|
||||
let program_file = ProgramFile::new(db, file, env.program(db));
|
||||
let program_file = ProgramFile::new(db, module_file, source_file.program(db));
|
||||
let symbol_table = place_table(db, global_scope(db, program_file));
|
||||
let Some(symbol) = symbol_table.symbol_by_name(attr) else {
|
||||
return;
|
||||
@@ -4779,7 +4780,12 @@ 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, env, &mut diagnostic, action);
|
||||
add_inferred_python_version_hint_to_diagnostic(
|
||||
db,
|
||||
source_file.file(db),
|
||||
&mut diagnostic,
|
||||
action,
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn report_invalid_concatenate_last_arg<'db>(
|
||||
|
||||
@@ -10163,7 +10163,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
));
|
||||
add_inferred_python_version_hint_to_diagnostic(
|
||||
db,
|
||||
self.program_environment(),
|
||||
self.file(),
|
||||
&mut diagnostic,
|
||||
"resolving types",
|
||||
);
|
||||
@@ -10509,7 +10509,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
} else {
|
||||
hint_if_stdlib_attribute_exists_on_other_versions(
|
||||
db,
|
||||
env,
|
||||
self.program_file(),
|
||||
diagnostic,
|
||||
value_type,
|
||||
attr_name,
|
||||
|
||||
@@ -86,7 +86,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
));
|
||||
add_inferred_python_version_hint_to_diagnostic(
|
||||
db,
|
||||
self.program_environment(),
|
||||
self.file(),
|
||||
&mut diagnostic,
|
||||
"resolving modules",
|
||||
);
|
||||
@@ -515,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.file(),
|
||||
self.program_environment(),
|
||||
&mut diagnostic,
|
||||
&full_submodule_name,
|
||||
@@ -525,7 +526,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
if !submodule_hint_added {
|
||||
hint_if_stdlib_attribute_exists_on_other_versions(
|
||||
db,
|
||||
self.program_environment(),
|
||||
self.program_file(),
|
||||
diagnostic,
|
||||
module_ty,
|
||||
name,
|
||||
@@ -638,6 +639,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
|
||||
hint_if_stdlib_submodule_exists_on_other_versions(
|
||||
self.db(),
|
||||
self.file(),
|
||||
self.program_environment(),
|
||||
&mut diagnostic,
|
||||
&full_submodule_name,
|
||||
|
||||
@@ -356,7 +356,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
));
|
||||
add_inferred_python_version_hint_to_diagnostic(
|
||||
db,
|
||||
self.program_environment(),
|
||||
self.file(),
|
||||
&mut diagnostic,
|
||||
"inferring types",
|
||||
);
|
||||
|
||||
@@ -202,7 +202,7 @@ fn program_file_changes_with_python_version() -> anyhow::Result<()> {
|
||||
let equivalent_program = Program::from_settings(
|
||||
&db,
|
||||
ProgramSettings {
|
||||
python_version: program.python_version_with_source(&db).clone(),
|
||||
python_version: db.program_settings().python_version.clone(),
|
||||
python_platform: program.python_platform(&db).clone(),
|
||||
search_paths: program.search_paths(&db).clone(),
|
||||
},
|
||||
|
||||
@@ -8,7 +8,9 @@ use ruff_db::vendored::VendoredFileSystem;
|
||||
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_python_semantic::{
|
||||
AnalysisSettings, Db as _, PythonVersionWithSource, check_file_unwrap, default_lint_registry,
|
||||
};
|
||||
|
||||
use ruff_db::diagnostic::Diagnostic;
|
||||
use test_case::test_case;
|
||||
@@ -220,6 +222,10 @@ impl ty_python_semantic::Db for CorpusDb {
|
||||
self.program().program_file(self, file)
|
||||
}
|
||||
|
||||
fn python_version_with_source(&self, _file: File) -> &PythonVersionWithSource {
|
||||
&self.program_settings.python_version
|
||||
}
|
||||
|
||||
fn rule_selection(&self, _file: File) -> &RuleSelection {
|
||||
&self.rule_selection
|
||||
}
|
||||
|
||||
@@ -68,11 +68,7 @@ fn debug_information(session: &Session) -> crate::Result<String> {
|
||||
writeln!(buffer, "Project at {}", db.project().root(db))?;
|
||||
let program = db.project().program(db);
|
||||
writeln!(buffer, "Program:")?;
|
||||
writeln!(
|
||||
buffer,
|
||||
" python-version: {}",
|
||||
program.python_version_with_source(db).version
|
||||
)?;
|
||||
writeln!(buffer, " python-version: {}", program.python_version(db))?;
|
||||
writeln!(buffer, " python-platform: {}", program.python_platform(db))?;
|
||||
let mut writer = IndentingWriter {
|
||||
inner: &mut buffer,
|
||||
|
||||
@@ -19,7 +19,8 @@ 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,
|
||||
AnalysisSettings, Db as SemanticDb, PythonVersionWithSource, check_file_unwrap,
|
||||
default_lint_registry,
|
||||
};
|
||||
|
||||
#[salsa::db]
|
||||
@@ -146,6 +147,10 @@ impl SemanticDb for Db {
|
||||
self.program().program_file(self, file)
|
||||
}
|
||||
|
||||
fn python_version_with_source(&self, _file: File) -> &PythonVersionWithSource {
|
||||
&self.settings().program(self).python_version
|
||||
}
|
||||
|
||||
fn rule_selection(&self, file: File) -> &RuleSelection {
|
||||
file_settings(self, file).rules(self)
|
||||
}
|
||||
|
||||
@@ -127,6 +127,10 @@ impl SemanticDb for TestDb {
|
||||
self.program().program_file(self, file)
|
||||
}
|
||||
|
||||
fn python_version_with_source(&self, _file: File) -> &PythonVersionWithSource {
|
||||
&self.program_settings.python_version
|
||||
}
|
||||
|
||||
fn rule_selection(&self, _file: File) -> &RuleSelection {
|
||||
&self.rule_selection
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user