Compare commits

...

15 Commits

Author SHA1 Message Date
Micha Reiser
0ad2e09430 [ty] Split out ty_python_types crate 2025-12-25 17:54:44 +01:00
Simon Lamon
dd3a985109 [ty] Spell out "method resolution order" in unsupported-base subdiagnostic (#22194) 2025-12-25 10:26:44 +00:00
Micha Reiser
12f5ea51e3 [ty] Invert dependencies of ty_combine and ty_python_semantic (#22191) 2025-12-25 10:06:06 +01:00
Alex Waygood
f9afcc400c [ty] Improve diagnostic when a user tries to access a function attribute on a Callable type (#22182)
## Summary

Other type checkers allow you to access all `FunctionType` attributes on
any object with a `Callable` type. ty does not, because this is
demonstrably unsound, but this is often a source of confusion for users.
And there were lots of diagnostics in the ecosystem report for
https://github.com/astral-sh/ruff/pull/22145 that were complaining that
"Object of type `(...) -> Unknown` has no attribute `__name__`", for
example.

The discrepancy between what ty does here and what other type checkers
do is discussed a bit in https://github.com/astral-sh/ty/issues/1495.
You can see that there have been lots of issues closed as duplicates of
that issue; we should probably also add an FAQ entry for it.

Anyway, this PR adds a subdiagnostic to help users out when they hit
this diagnostic. Unfortunately something I did meant that rustfmt
increased the indentation of the whole of this huge closure, so this PR
is best reviewed with the "No whitespace" option selected for viewing
the diff.

## Test Plan

Snapshot added
2025-12-24 15:47:11 -05:00
Alex Waygood
768c5a2285 [ty] Use Type::string_literal() more (#22184) 2025-12-24 20:06:57 +00:00
Alex Waygood
139149f87b [ty] Improve diagnostic when callable is used in a type expression instead of collections.abc.Callable or typing.Callable (#22180) 2025-12-24 19:18:51 +00:00
Charlie Marsh
2de4464e92 [ty] Fix implementation of Top[Callable[..., object]] (#22145)
## Summary

Add a proper representation for the `Callable` top type, and use it to
get `callable()` narrowing right.

Closes https://github.com/astral-sh/ty/issues/1426.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-12-24 12:49:09 -05:00
Micha Reiser
ded4d4bbe9 [ty] Fix classification of module in import x as y (#22175) 2025-12-24 18:25:29 +01:00
Micha Reiser
eef403f6cf Revert "[ty] Fix completion in decorator without class or function definition" (#22176) 2025-12-24 17:27:21 +01:00
Micha Reiser
adef89eb7c [ty] Fix completion in decorator without class or function definition 2025-12-24 16:57:36 +01:00
Micha Reiser
e3498121b4 [ty] Fix module resolution on network drives (#22173) 2025-12-24 16:36:18 +01:00
Brent Westbrook
5e7fc9a4e1 Render the entire diagnostic message in all output formats (#22164)
Summary
--

This PR fixes https://github.com/astral-sh/ty/issues/2186 by replacing
uses of
`Diagnostic::body` with [`Diagnostic::concise_message`][d]. The initial
report
was only about ty's GitHub and GitLab output formats, but I think it
makes sense
to prefer `concise_message` in the other output formats too. Ruff
currently only
sets the primary message on its diagnostics, which is why this has no
effect on
Ruff, and ty currently only supports the GitHub and GitLab formats that
used
`body`, but removing `body` should help to avoid this problem as Ruff
starts to
use more diagnostic features or ty gets new output formats.

Test Plan
--

Updated existing GitLab and GitHub CLI tests to have `reveal_type`
diagnostics

[d]:
https://github.com/astral-sh/ruff/blob/395bf106ab/crates/ruff_db/src/diagnostic/mod.rs#L185
[t]:
https://github.com/astral-sh/ruff/blob/395bf106ab/crates/ruff/tests/cli/lint.rs#L3509
2025-12-24 10:28:24 -05:00
Alex Waygood
3c5956e93d [ty] Include the specialization of a generic TypedDict as part of its display (#22174)
## Summary

This is the easy bit of https://github.com/astral-sh/ty/issues/2190

## Test Plan

mdtests updated
2025-12-24 14:39:46 +00:00
Charlie Marsh
81f34fbc8e [ty] Store un-widened type in Place (#22093)
## Summary

See: https://github.com/astral-sh/ruff/pull/22025#discussion_r2632724156
2025-12-23 23:19:57 -05:00
Charlie Marsh
184f487c84 [ty] Add a dedicated diagnostic for TypedDict deletions (#22123)
## Summary

Provides a message like:

```
  error[invalid-argument-type]: Cannot delete required key "name" from TypedDict `Movie`
    --> test.py:15:7
     |
  15 | del m["name"]
     |       ^^^^^^
     |
  info: Field defined here
   --> test.py:4:5
    |
  4 |     name: str
    |     --------- `name` declared as required here; consider making it `NotRequired`
    |
  info: Only keys marked as `NotRequired` (or in a TypedDict with `total=False`) can be deleted
```
2025-12-24 03:49:42 +00:00
1004 changed files with 3889 additions and 2485 deletions

View File

@@ -0,0 +1,67 @@
{
"permissions": {
"allow": [
"Bash(cargo build:*)",
"Read(//home/micha/astral/discord.py/discord/**)",
"Bash(source:*)",
"Bash(./target/profiling/ty check:*)",
"Bash(tee:*)",
"Read(//home/micha/astral/discord.py/.venv/**)",
"Read(//home/micha/astral/discord.py/**)",
"Bash(perf record:*)",
"Bash(perf report:*)",
"Bash(time:*)",
"Bash(../ruff/target/profiling/ty check discord/audit_logs.py -vv)",
"Bash(sed:*)",
"Read(//home/micha/git/TypeScript/**)",
"Bash(cargo test:*)",
"Bash(MDTEST_TEST_FILTER='union_types.md - Union types - Unions of tuples' cargo test -p ty_python_semantic --test mdtest -- mdtest__union_types)",
"Bash(timeout 10 cargo test --package ty_python_semantic --lib types::tests::divergent_type)",
"Bash(timeout 30 cargo test:*)",
"Bash(git stash:*)",
"Bash(timeout 60 time:*)",
"Bash(for i in 1 2 3 4 5)",
"Bash(do echo \"Run $i:\")",
"Bash(done)",
"Bash(for i in 1 2 3)",
"Bash(cargo fuzz run:*)",
"Bash(timeout 60 cargo fuzz run -s none ty_check_invalid_syntax -- -timeout=1)",
"Bash(for:*)",
"Bash(do echo \"=== Checking $crash ===\")",
"Bash(uvx ty@latest check \"$crash\")",
"Bash(do echo \"=== $crash ===\")",
"Bash(while read crash)",
"Bash(cargo fuzz cmin:*)",
"Bash(cargo +nightly fuzz cmin:*)",
"Bash(timeout 120 cargo fuzz run -s none ty_check_invalid_syntax -- -timeout=1)",
"Bash(awk:*)",
"Bash(while read file)",
"Bash(cat:*)",
"Bash(uvx ty@latest:*)",
"Bash(do cp \"$crash\" /tmp/isolated_crash.py)",
"Bash(echo \"=== $crash ===\")",
"Bash(do echo \"=== test$i.py ===\")",
"Bash(do echo \"=== Testing $crash ===\")",
"Bash(tree:*)",
"Bash(cut:*)",
"Bash(grep:*)",
"Bash(ls:*)",
"Bash(xargs basename:*)",
"Bash(wc:*)",
"Bash(find:*)",
"Bash({} ;)",
"Bash(git checkout:*)",
"Bash(do)",
"Bash(if ! grep -q \"use crate::types\" \"$f\")",
"Bash(! grep -q \"crate::types::\" \"$f\")",
"Bash(then)",
"Bash(else)",
"Bash(fi)",
"Bash(1)",
"Bash(__NEW_LINE__ echo \"Done\")",
"Bash(rm:*)"
],
"deny": [],
"ask": []
}
}

View File

@@ -5,7 +5,7 @@ exclude: |
.github/workflows/release.yml|
crates/ty_vendored/vendor/.*|
crates/ty_project/resources/.*|
crates/ty_python_semantic/resources/corpus/.*|
crates/ty_python_types/resources/corpus/.*|
crates/ty/docs/(configuration|rules|cli|environment).md|
crates/ruff_benchmark/resources/.*|
crates/ruff_linter/resources/.*|

65
Cargo.lock generated
View File

@@ -3083,6 +3083,7 @@ dependencies = [
"ty",
"ty_project",
"ty_python_semantic",
"ty_python_types",
"ty_static",
"url",
]
@@ -3131,6 +3132,7 @@ dependencies = [
"serde",
"ty_module_resolver",
"ty_python_semantic",
"ty_python_types",
"zip",
]
@@ -4391,7 +4393,6 @@ dependencies = [
"ordermap",
"ruff_db",
"ruff_python_ast",
"ty_python_semantic",
]
[[package]]
@@ -4443,6 +4444,7 @@ dependencies = [
"ty_module_resolver",
"ty_project",
"ty_python_semantic",
"ty_python_types",
"ty_vendored",
]
@@ -4505,6 +4507,7 @@ dependencies = [
"ty_combine",
"ty_module_resolver",
"ty_python_semantic",
"ty_python_types",
"ty_static",
"ty_vendored",
]
@@ -4518,19 +4521,12 @@ dependencies = [
"bitvec",
"camino",
"colored 3.0.0",
"compact_str",
"datatest-stable",
"drop_bomb",
"get-size2",
"glob",
"hashbrown 0.16.1",
"indexmap",
"indoc",
"insta",
"itertools 0.14.0",
"memchr",
"ordermap",
"pretty_assertions",
"quickcheck",
"quickcheck_macros",
"ruff_annotate_snippets",
@@ -4540,9 +4536,7 @@ dependencies = [
"ruff_macros",
"ruff_memory_usage",
"ruff_python_ast",
"ruff_python_literal",
"ruff_python_parser",
"ruff_python_stdlib",
"ruff_python_trivia",
"ruff_source_file",
"ruff_text_size",
@@ -4556,11 +4550,59 @@ dependencies = [
"strsim",
"strum",
"strum_macros",
"test-case",
"thiserror 2.0.17",
"tracing",
"ty_combine",
"ty_module_resolver",
"ty_static",
"ty_vendored",
]
[[package]]
name = "ty_python_types"
version = "0.0.0"
dependencies = [
"anyhow",
"bitflags 2.10.0",
"camino",
"compact_str",
"datatest-stable",
"drop_bomb",
"get-size2",
"glob",
"indexmap",
"indoc",
"insta",
"itertools 0.14.0",
"memchr",
"ordermap",
"pretty_assertions",
"quickcheck",
"quickcheck_macros",
"ruff_db",
"ruff_diagnostics",
"ruff_macros",
"ruff_memory_usage",
"ruff_python_ast",
"ruff_python_literal",
"ruff_python_parser",
"ruff_python_stdlib",
"ruff_source_file",
"ruff_text_size",
"rustc-hash",
"salsa",
"schemars",
"serde",
"serde_json",
"smallvec",
"static_assertions",
"strum",
"strum_macros",
"test-case",
"tracing",
"ty_module_resolver",
"ty_python_semantic",
"ty_static",
"ty_test",
"ty_vendored",
]
@@ -4641,6 +4683,7 @@ dependencies = [
"tracing",
"ty_module_resolver",
"ty_python_semantic",
"ty_python_types",
"ty_static",
"ty_vendored",
]

View File

@@ -48,6 +48,7 @@ ty_ide = { path = "crates/ty_ide" }
ty_module_resolver = { path = "crates/ty_module_resolver" }
ty_project = { path = "crates/ty_project", default-features = false }
ty_python_semantic = { path = "crates/ty_python_semantic" }
ty_python_types = { path = "crates/ty_python_types" }
ty_server = { path = "crates/ty_server" }
ty_static = { path = "crates/ty_static" }
ty_test = { path = "crates/ty_test" }

View File

@@ -1,4 +1,4 @@
use std::{fmt::Formatter, path::Path, sync::Arc};
use std::{borrow::Cow, fmt::Formatter, path::Path, sync::Arc};
use ruff_diagnostics::{Applicability, Fix};
use ruff_source_file::{LineColumn, SourceCode, SourceFile};
@@ -411,11 +411,6 @@ impl Diagnostic {
self.id().is_invalid_syntax()
}
/// Returns the message body to display to the user.
pub fn body(&self) -> &str {
self.primary_message()
}
/// Returns the message of the first sub-diagnostic with a `Help` severity.
///
/// Note that this is used as the fix title/suggestion for some of Ruff's output formats, but in
@@ -1492,6 +1487,15 @@ pub enum ConciseMessage<'a> {
Custom(&'a str),
}
impl<'a> ConciseMessage<'a> {
pub fn to_str(&self) -> Cow<'a, str> {
match self {
ConciseMessage::MainDiagnostic(s) | ConciseMessage::Custom(s) => Cow::Borrowed(s),
ConciseMessage::Both { .. } => Cow::Owned(self.to_string()),
}
}
}
impl std::fmt::Display for ConciseMessage<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
@@ -1508,6 +1512,16 @@ impl std::fmt::Display for ConciseMessage<'_> {
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for ConciseMessage<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.collect_str(self)
}
}
/// A diagnostic message string.
///
/// This is, for all intents and purposes, equivalent to a `Box<str>`.

View File

@@ -52,7 +52,7 @@ impl AzureRenderer<'_> {
f,
"code={code};]{body}",
code = diag.secondary_code_or_id(),
body = diag.body(),
body = diag.concise_message(),
)?;
}

View File

@@ -87,7 +87,7 @@ impl<'a> GithubRenderer<'a> {
write!(f, "{id}:", id = diagnostic.id())?;
}
writeln!(f, " {}", diagnostic.body())?;
writeln!(f, " {}", diagnostic.concise_message())?;
}
Ok(())

View File

@@ -98,7 +98,7 @@ impl Serialize for SerializedMessages<'_> {
}
fingerprints.insert(message_fingerprint);
let description = diagnostic.body();
let description = diagnostic.concise_message();
let check_name = diagnostic.secondary_code_or_id();
let severity = match diagnostic.severity() {
Severity::Info => "info",

View File

@@ -6,7 +6,7 @@ use ruff_notebook::NotebookIndex;
use ruff_source_file::{LineColumn, OneIndexed};
use ruff_text_size::Ranged;
use crate::diagnostic::{Diagnostic, DiagnosticSource, DisplayDiagnosticConfig};
use crate::diagnostic::{ConciseMessage, Diagnostic, DiagnosticSource, DisplayDiagnosticConfig};
use super::FileResolver;
@@ -101,7 +101,7 @@ pub(super) fn diagnostic_to_json<'a>(
JsonDiagnostic {
code: diagnostic.secondary_code_or_id(),
url: diagnostic.documentation_url(),
message: diagnostic.body(),
message: diagnostic.concise_message(),
fix,
cell: notebook_cell_index,
location: start_location.map(JsonLocation::from),
@@ -113,7 +113,7 @@ pub(super) fn diagnostic_to_json<'a>(
JsonDiagnostic {
code: diagnostic.secondary_code_or_id(),
url: diagnostic.documentation_url(),
message: diagnostic.body(),
message: diagnostic.concise_message(),
fix,
cell: notebook_cell_index,
location: Some(start_location.unwrap_or_default().into()),
@@ -226,7 +226,7 @@ pub(crate) struct JsonDiagnostic<'a> {
filename: Option<&'a str>,
fix: Option<JsonFix<'a>>,
location: Option<JsonLocation>,
message: &'a str,
message: ConciseMessage<'a>,
noqa_row: Option<OneIndexed>,
url: Option<&'a str>,
}

View File

@@ -56,17 +56,17 @@ impl<'a> JunitRenderer<'a> {
start_location: location,
} = diagnostic;
let mut status = TestCaseStatus::non_success(NonSuccessKind::Failure);
status.set_message(diagnostic.body());
status.set_message(diagnostic.concise_message().to_str());
if let Some(location) = location {
status.set_description(format!(
"line {row}, col {col}, {body}",
row = location.line,
col = location.column,
body = diagnostic.body()
body = diagnostic.concise_message()
));
} else {
status.set_description(diagnostic.body());
status.set_description(diagnostic.concise_message().to_str());
}
let code = diagnostic

View File

@@ -55,7 +55,7 @@ impl PylintRenderer<'_> {
f,
"{path}:{row}: [{code}] {body}",
path = filename,
body = diagnostic.body()
body = diagnostic.concise_message()
)?;
}

View File

@@ -5,7 +5,7 @@ use ruff_diagnostics::{Edit, Fix};
use ruff_source_file::{LineColumn, SourceCode};
use ruff_text_size::Ranged;
use crate::diagnostic::Diagnostic;
use crate::diagnostic::{ConciseMessage, Diagnostic};
use super::FileResolver;
@@ -76,7 +76,7 @@ fn diagnostic_to_rdjson<'a>(
let edits = diagnostic.fix().map(Fix::edits).unwrap_or_default();
RdjsonDiagnostic {
message: diagnostic.body(),
message: diagnostic.concise_message(),
location,
code: RdjsonCode {
value: diagnostic
@@ -155,7 +155,7 @@ struct RdjsonDiagnostic<'a> {
code: RdjsonCode<'a>,
#[serde(skip_serializing_if = "Option::is_none")]
location: Option<RdjsonLocation<'a>>,
message: &'a str,
message: ConciseMessage<'a>,
#[serde(skip_serializing_if = "Vec::is_empty")]
suggestions: Vec<RdjsonSuggestion<'a>>,
}

View File

@@ -275,16 +275,16 @@ impl OsSystem {
/// instead of at least one system call for each component between `path` and `prefix`.
///
/// However, using `canonicalize` to resolve the path's casing doesn't work in two cases:
/// * if `path` is a symlink because `canonicalize` then returns the symlink's target and not the symlink's source path.
/// * on Windows: If `path` is a mapped network drive because `canonicalize` then returns the UNC path
/// (e.g. `Z:\` is mapped to `\\server\share` and `canonicalize` then returns `\\?\UNC\server\share`).
/// * if `path` is a symlink, `canonicalize` returns the symlink's target and not the symlink's source path.
/// * on Windows: If `path` is a mapped network drive, `canonicalize` returns the UNC path
/// (e.g. `Z:\` is mapped to `\\server\share` and `canonicalize` returns `\\?\UNC\server\share`).
///
/// Symlinks and mapped network drives should be rare enough that this fast path is worth trying first,
/// even if it comes at a cost for those rare use cases.
fn path_exists_case_sensitive_fast(&self, path: &SystemPath) -> Option<bool> {
// This is a more forgiving version of `dunce::simplified` that removes all `\\?\` prefixes on Windows.
// We use this more forgiving version because we don't intend on using either path for anything other than comparison
// and the prefix is only relevant when passing the path to other programs and its longer than 200 something
// and the prefix is only relevant when passing the path to other programs and it's longer than 200 something
// characters.
fn simplify_ignore_verbatim(path: &SystemPath) -> &SystemPath {
if cfg!(windows) {
@@ -298,9 +298,7 @@ impl OsSystem {
}
}
let simplified = simplify_ignore_verbatim(path);
let Ok(canonicalized) = simplified.as_std_path().canonicalize() else {
let Ok(canonicalized) = path.as_std_path().canonicalize() else {
// The path doesn't exist or can't be accessed. The path doesn't exist.
return Some(false);
};
@@ -309,12 +307,13 @@ impl OsSystem {
// The original path is valid UTF8 but the canonicalized path isn't. This definitely suggests
// that a symlink is involved. Fall back to the slow path.
tracing::debug!(
"Falling back to the slow case-sensitive path existence check because the canonicalized path of `{simplified}` is not valid UTF-8"
"Falling back to the slow case-sensitive path existence check because the canonicalized path of `{path}` is not valid UTF-8"
);
return None;
};
let simplified_canonicalized = simplify_ignore_verbatim(&canonicalized);
let simplified = simplify_ignore_verbatim(path);
// Test if the paths differ by anything other than casing. If so, that suggests that
// `path` pointed to a symlink (or some other none reversible path normalization happened).

View File

@@ -14,6 +14,7 @@ license = { workspace = true }
ty = { workspace = true }
ty_project = { workspace = true, features = ["schemars"] }
ty_python_semantic = { workspace = true }
ty_python_types = { workspace = true }
ty_static = { workspace = true }
ruff = { workspace = true }
ruff_formatter = { workspace = true }

View File

@@ -52,7 +52,7 @@ pub(crate) fn main(args: &Args) -> Result<()> {
}
fn generate_markdown() -> String {
let registry = ty_python_semantic::default_lint_registry();
let registry = ty_python_types::default_lint_registry();
let mut output = String::new();

View File

@@ -18,6 +18,7 @@ ruff_python_ast = { workspace = true }
ruff_python_parser = { workspace = true }
ty_module_resolver = { workspace = true }
ty_python_semantic = { workspace = true }
ty_python_types = { workspace = true }
anyhow = { workspace = true }
clap = { workspace = true, optional = true }

View File

@@ -11,8 +11,9 @@ use ty_module_resolver::{SearchPathSettings, SearchPaths};
use ty_python_semantic::lint::{LintRegistry, RuleSelection};
use ty_python_semantic::{
AnalysisSettings, Db, Program, ProgramSettings, PythonEnvironment, PythonPlatform,
PythonVersionSource, PythonVersionWithSource, SysPrefixPathOrigin, default_lint_registry,
PythonVersionSource, PythonVersionWithSource, SysPrefixPathOrigin,
};
use ty_python_types::default_lint_registry;
static EMPTY_VENDORED: std::sync::LazyLock<VendoredFileSystem> = std::sync::LazyLock::new(|| {
let mut builder = VendoredFileSystemBuilder::new(CompressionMethod::Stored);

View File

@@ -197,7 +197,7 @@ impl Display for RuleCodeAndBody<'_> {
f,
"{fix}{body}",
fix = format_args!("[{}] ", "*".cyan()),
body = self.message.body(),
body = self.message.concise_message(),
);
}
}
@@ -208,14 +208,14 @@ impl Display for RuleCodeAndBody<'_> {
f,
"{code} {body}",
code = code.red().bold(),
body = self.message.body(),
body = self.message.concise_message(),
)
} else {
write!(
f,
"{code}: {body}",
code = self.message.id().as_str().red().bold(),
body = self.message.body(),
body = self.message.concise_message(),
)
}
}

View File

@@ -334,7 +334,7 @@ impl<'a> SarifResult<'a> {
rule_id: RuleCode::from(diagnostic),
level: "error".to_string(),
message: SarifMessage {
text: diagnostic.body().to_string(),
text: diagnostic.concise_message().to_string(),
},
fixes: Self::fix(diagnostic, &uri).into_iter().collect(),
locations: vec![SarifLocation {

View File

@@ -243,7 +243,7 @@ fn to_lsp_diagnostic(
) -> (usize, lsp_types::Diagnostic) {
let diagnostic_range = diagnostic.range().unwrap_or_default();
let name = diagnostic.name();
let body = diagnostic.body().to_string();
let body = diagnostic.concise_message().to_string();
let fix = diagnostic.fix();
let suggestion = diagnostic.first_help_text();
let code = diagnostic.secondary_code();

View File

@@ -241,7 +241,7 @@ impl Workspace {
let range = msg.range().unwrap_or_default();
ExpandedMessage {
code: msg.secondary_code_or_id().to_string(),
message: msg.body().to_string(),
message: msg.concise_message().to_string(),
start_location: source_code
.source_location(range.start(), self.position_encoding)
.into(),

199
crates/ty/docs/rules.md generated
View File

@@ -8,7 +8,7 @@
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.20">0.0.1-alpha.20</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20ambiguous-protocol-member" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L512" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L538" target="_blank">View source</a>
</small>
@@ -49,7 +49,7 @@ class SubProto(BaseProto, Protocol):
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20byte-string-type-annotation" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fstring_annotation.rs#L36" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fstring_annotation.rs#L36" target="_blank">View source</a>
</small>
@@ -80,7 +80,7 @@ def test(): -> "int":
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20call-non-callable" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L136" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L137" target="_blank">View source</a>
</small>
@@ -98,13 +98,44 @@ Calling a non-callable object will raise a `TypeError` at runtime.
4() # TypeError: 'int' object is not callable
```
## `call-top-callable`
<small>
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.7">0.0.7</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20call-top-callable" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L155" target="_blank">View source</a>
</small>
**What it does**
Checks for calls to objects typed as `Top[Callable[..., T]]` (the infinite union of all
callable types with return type `T`).
**Why is this bad?**
When an object is narrowed to `Top[Callable[..., object]]` (e.g., via `callable(x)` or
`isinstance(x, Callable)`), we know the object is callable, but we don't know its
precise signature. This type represents the set of all possible callable types
(including, e.g., functions that take no arguments and functions that require arguments),
so no specific set of arguments can be guaranteed to be valid.
**Examples**
```python
def f(x: object):
if callable(x):
x() # error: We know `x` is callable, but not what arguments it accepts
```
## `conflicting-argument-forms`
<small>
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-argument-forms" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L180" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L206" target="_blank">View source</a>
</small>
@@ -136,7 +167,7 @@ f(int) # error
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-declarations" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L206" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L232" target="_blank">View source</a>
</small>
@@ -167,7 +198,7 @@ a = 1
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-metaclass" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L231" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L257" target="_blank">View source</a>
</small>
@@ -199,7 +230,7 @@ class C(A, B): ...
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20cyclic-class-definition" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L257" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L283" target="_blank">View source</a>
</small>
@@ -231,7 +262,7 @@ class B(A): ...
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Preview (since <a href="https://github.com/astral-sh/ty/releases/tag/1.0.0">1.0.0</a>) ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20cyclic-type-alias-definition" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L283" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L309" target="_blank">View source</a>
</small>
@@ -259,7 +290,7 @@ type B = A
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.16">0.0.1-alpha.16</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20deprecated" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L327" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L353" target="_blank">View source</a>
</small>
@@ -286,7 +317,7 @@ old_func() # emits [deprecated] diagnostic
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'ignore'."><code>ignore</code></a> ·
Preview (since <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a>) ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20division-by-zero" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L305" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L331" target="_blank">View source</a>
</small>
@@ -315,7 +346,7 @@ false positives it can produce.
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20duplicate-base" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L348" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L374" target="_blank">View source</a>
</small>
@@ -342,7 +373,7 @@ class B(A, A): ...
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.12">0.0.1-alpha.12</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20duplicate-kw-only" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L369" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L395" target="_blank">View source</a>
</small>
@@ -380,7 +411,7 @@ class A: # Crash at runtime
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20escape-character-in-forward-annotation" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fstring_annotation.rs#L154" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fstring_annotation.rs#L154" target="_blank">View source</a>
</small>
@@ -405,7 +436,7 @@ def foo() -> "intt\b": ...
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20fstring-type-annotation" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fstring_annotation.rs#L11" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fstring_annotation.rs#L11" target="_blank">View source</a>
</small>
@@ -436,7 +467,7 @@ def test(): -> "int":
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20ignore-comment-unknown-rule" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Fsuppression.rs#L50" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Fsuppression.rs#L49" target="_blank">View source</a>
</small>
@@ -467,7 +498,7 @@ a = 20 / 0 # ty: ignore[division-by-zero]
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20implicit-concatenated-string-type-annotation" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fstring_annotation.rs#L86" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fstring_annotation.rs#L86" target="_blank">View source</a>
</small>
@@ -498,7 +529,7 @@ def test(): -> "Literal[5]":
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20inconsistent-mro" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L595" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L621" target="_blank">View source</a>
</small>
@@ -528,7 +559,7 @@ class C(A, B): ...
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20index-out-of-bounds" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L619" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L645" target="_blank">View source</a>
</small>
@@ -554,7 +585,7 @@ t[3] # IndexError: tuple index out of range
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.12">0.0.1-alpha.12</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20instance-layout-conflict" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L401" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L427" target="_blank">View source</a>
</small>
@@ -643,7 +674,7 @@ an atypical memory layout.
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-argument-type" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L673" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L699" target="_blank">View source</a>
</small>
@@ -670,7 +701,7 @@ func("foo") # error: [invalid-argument-type]
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-assignment" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L713" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L739" target="_blank">View source</a>
</small>
@@ -698,7 +729,7 @@ a: int = ''
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-attribute-access" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2016" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2042" target="_blank">View source</a>
</small>
@@ -732,7 +763,7 @@ C.instance_var = 3 # error: Cannot assign to instance variable
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.19">0.0.1-alpha.19</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-await" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L735" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L761" target="_blank">View source</a>
</small>
@@ -768,7 +799,7 @@ asyncio.run(main())
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-base" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L765" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L791" target="_blank">View source</a>
</small>
@@ -792,7 +823,7 @@ class A(42): ... # error: [invalid-base]
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-context-manager" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L816" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L842" target="_blank">View source</a>
</small>
@@ -819,7 +850,7 @@ with 1:
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-declaration" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L837" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L863" target="_blank">View source</a>
</small>
@@ -848,7 +879,7 @@ a: str
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-exception-caught" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L860" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L886" target="_blank">View source</a>
</small>
@@ -892,7 +923,7 @@ except ZeroDivisionError:
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.28">0.0.1-alpha.28</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-explicit-override" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1686" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1712" target="_blank">View source</a>
</small>
@@ -934,7 +965,7 @@ class D(A):
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.35">0.0.1-alpha.35</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-frozen-dataclass-subclass" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2242" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2268" target="_blank">View source</a>
</small>
@@ -978,7 +1009,7 @@ class NonFrozenChild(FrozenBase): # Error raised here
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-generic-class" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L896" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L922" target="_blank">View source</a>
</small>
@@ -1016,7 +1047,7 @@ class D(Generic[U, T]): ...
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-ignore-comment" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Fsuppression.rs#L75" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Fsuppression.rs#L74" target="_blank">View source</a>
</small>
@@ -1046,7 +1077,7 @@ a = 20 / 0 # type: ignore
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.17">0.0.1-alpha.17</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-key" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L640" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L666" target="_blank">View source</a>
</small>
@@ -1085,7 +1116,7 @@ carol = Person(name="Carol", age=25) # typo!
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-legacy-type-variable" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L927" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L953" target="_blank">View source</a>
</small>
@@ -1120,7 +1151,7 @@ def f(t: TypeVar("U")): ...
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-metaclass" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1024" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1050" target="_blank">View source</a>
</small>
@@ -1154,7 +1185,7 @@ class B(metaclass=f): ...
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.20">0.0.1-alpha.20</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-method-override" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2144" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2170" target="_blank">View source</a>
</small>
@@ -1261,7 +1292,7 @@ Correct use of `@override` is enforced by ty's `invalid-explicit-override` rule.
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.19">0.0.1-alpha.19</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-named-tuple" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L547" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L573" target="_blank">View source</a>
</small>
@@ -1315,7 +1346,7 @@ AttributeError: Cannot overwrite NamedTuple attribute _asdict
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Preview (since <a href="https://github.com/astral-sh/ty/releases/tag/1.0.0">1.0.0</a>) ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-newtype" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1000" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1026" target="_blank">View source</a>
</small>
@@ -1345,7 +1376,7 @@ Baz = NewType("Baz", int | str) # error: invalid base for `typing.NewType`
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-overload" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1051" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1077" target="_blank">View source</a>
</small>
@@ -1395,7 +1426,7 @@ def foo(x: int) -> int: ...
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-parameter-default" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1150" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1176" target="_blank">View source</a>
</small>
@@ -1421,7 +1452,7 @@ def f(a: int = ''): ...
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-paramspec" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L955" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L981" target="_blank">View source</a>
</small>
@@ -1452,7 +1483,7 @@ P2 = ParamSpec("S2") # error: ParamSpec name must match the variable it's assig
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-protocol" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L483" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L509" target="_blank">View source</a>
</small>
@@ -1486,7 +1517,7 @@ TypeError: Protocols can only inherit from other protocols, got <class 'int'>
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-raise" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1170" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1196" target="_blank">View source</a>
</small>
@@ -1535,7 +1566,7 @@ def g():
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-return-type" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L694" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L720" target="_blank">View source</a>
</small>
@@ -1560,7 +1591,7 @@ def func() -> int:
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-super-argument" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1213" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1239" target="_blank">View source</a>
</small>
@@ -1606,7 +1637,7 @@ super(B, A) # error: `A` does not satisfy `issubclass(A, B)`
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-syntax-in-forward-annotation" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fstring_annotation.rs#L111" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fstring_annotation.rs#L111" target="_blank">View source</a>
</small>
@@ -1656,7 +1687,7 @@ class C: ...
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.6">0.0.1-alpha.6</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-alias-type" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L979" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1005" target="_blank">View source</a>
</small>
@@ -1683,7 +1714,7 @@ NewAlias = TypeAliasType(get_name(), int) # error: TypeAliasType name mus
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.29">0.0.1-alpha.29</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-arguments" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1445" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1471" target="_blank">View source</a>
</small>
@@ -1730,7 +1761,7 @@ Bar[int] # error: too few arguments
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-checking-constant" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1252" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1278" target="_blank">View source</a>
</small>
@@ -1760,7 +1791,7 @@ TYPE_CHECKING = ''
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-form" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1276" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1302" target="_blank">View source</a>
</small>
@@ -1790,7 +1821,7 @@ b: Annotated[int] # `Annotated` expects at least two arguments
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.11">0.0.1-alpha.11</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-guard-call" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1328" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1354" target="_blank">View source</a>
</small>
@@ -1824,7 +1855,7 @@ f(10) # Error
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.11">0.0.1-alpha.11</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-guard-definition" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1300" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1326" target="_blank">View source</a>
</small>
@@ -1858,7 +1889,7 @@ class C:
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-variable-constraints" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1356" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1382" target="_blank">View source</a>
</small>
@@ -1893,7 +1924,7 @@ T = TypeVar('T', bound=str) # valid bound TypeVar
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20missing-argument" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1385" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1411" target="_blank">View source</a>
</small>
@@ -1918,7 +1949,7 @@ func() # TypeError: func() missing 1 required positional argument: 'x'
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.20">0.0.1-alpha.20</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20missing-typed-dict-key" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2117" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2143" target="_blank">View source</a>
</small>
@@ -1951,7 +1982,7 @@ alice["age"] # KeyError
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20no-matching-overload" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1404" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1430" target="_blank">View source</a>
</small>
@@ -1980,7 +2011,7 @@ func("string") # error: [no-matching-overload]
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20non-subscriptable" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1427" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1453" target="_blank">View source</a>
</small>
@@ -2004,7 +2035,7 @@ Subscripting an object that does not support it will raise a `TypeError` at runt
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20not-iterable" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1486" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1512" target="_blank">View source</a>
</small>
@@ -2030,7 +2061,7 @@ for i in 34: # TypeError: 'int' object is not iterable
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.29">0.0.1-alpha.29</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20override-of-final-method" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1659" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1685" target="_blank">View source</a>
</small>
@@ -2063,7 +2094,7 @@ class B(A):
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20parameter-already-assigned" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1537" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1563" target="_blank">View source</a>
</small>
@@ -2090,7 +2121,7 @@ f(1, x=2) # Error raised here
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.22">0.0.1-alpha.22</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20positional-only-parameter-as-kwarg" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1870" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1896" target="_blank">View source</a>
</small>
@@ -2117,7 +2148,7 @@ f(x=1) # Error raised here
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.22">0.0.1-alpha.22</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-missing-attribute" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1558" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1584" target="_blank">View source</a>
</small>
@@ -2145,7 +2176,7 @@ A.c # AttributeError: type object 'A' has no attribute 'c'
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.22">0.0.1-alpha.22</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-missing-implicit-call" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L154" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L180" target="_blank">View source</a>
</small>
@@ -2177,7 +2208,7 @@ A()[0] # TypeError: 'A' object is not subscriptable
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'ignore'."><code>ignore</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.22">0.0.1-alpha.22</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-missing-import" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1580" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1606" target="_blank">View source</a>
</small>
@@ -2214,7 +2245,7 @@ from module import a # ImportError: cannot import name 'a' from 'module'
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'ignore'."><code>ignore</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unresolved-reference" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1610" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1636" target="_blank">View source</a>
</small>
@@ -2247,7 +2278,7 @@ print(x) # NameError: name 'x' is not defined
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20raw-string-type-annotation" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fstring_annotation.rs#L61" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fstring_annotation.rs#L61" target="_blank">View source</a>
</small>
@@ -2278,7 +2309,7 @@ def test(): -> "int":
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20redundant-cast" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2044" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2070" target="_blank">View source</a>
</small>
@@ -2305,7 +2336,7 @@ cast(int, f()) # Redundant
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20static-assert-error" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1992" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2018" target="_blank">View source</a>
</small>
@@ -2335,7 +2366,7 @@ static_assert(int(2.0 * 3.0) == 6) # error: does not have a statically known tr
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20subclass-of-final-class" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1636" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1662" target="_blank">View source</a>
</small>
@@ -2364,7 +2395,7 @@ class B(A): ... # Error raised here
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Preview (since <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.30">0.0.1-alpha.30</a>) ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20super-call-in-named-tuple-method" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1804" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1830" target="_blank">View source</a>
</small>
@@ -2398,7 +2429,7 @@ class F(NamedTuple):
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20too-many-positional-arguments" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1744" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1770" target="_blank">View source</a>
</small>
@@ -2425,7 +2456,7 @@ f("foo") # Error raised here
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20type-assertion-failure" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1722" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1748" target="_blank">View source</a>
</small>
@@ -2453,7 +2484,7 @@ def _(x: int):
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unavailable-implicit-super-arguments" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1765" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1791" target="_blank">View source</a>
</small>
@@ -2499,7 +2530,7 @@ class A:
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20undefined-reveal" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1831" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1857" target="_blank">View source</a>
</small>
@@ -2523,7 +2554,7 @@ reveal_type(1) # NameError: name 'reveal_type' is not defined
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unknown-argument" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1849" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1875" target="_blank">View source</a>
</small>
@@ -2550,7 +2581,7 @@ f(x=1, y=2) # Error raised here
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-attribute" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1891" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1917" target="_blank">View source</a>
</small>
@@ -2578,7 +2609,7 @@ A().foo # AttributeError: 'A' object has no attribute 'foo'
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.15">0.0.1-alpha.15</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-global" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2065" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L2091" target="_blank">View source</a>
</small>
@@ -2636,7 +2667,7 @@ def g():
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-import" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1913" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1939" target="_blank">View source</a>
</small>
@@ -2661,7 +2692,7 @@ import foo # ModuleNotFoundError: No module named 'foo'
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-reference" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1932" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1958" target="_blank">View source</a>
</small>
@@ -2686,7 +2717,7 @@ print(x) # NameError: name 'x' is not defined
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.7">0.0.1-alpha.7</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-base" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L783" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L809" target="_blank">View source</a>
</small>
@@ -2725,7 +2756,7 @@ class D(C): ... # error: [unsupported-base]
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-bool-conversion" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1506" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1532" target="_blank">View source</a>
</small>
@@ -2762,7 +2793,7 @@ b1 < b2 < b1 # exception raised here
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-operator" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1951" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1977" target="_blank">View source</a>
</small>
@@ -2790,7 +2821,7 @@ A() + A() # TypeError: unsupported operand type(s) for +: 'A' and 'A'
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'ignore'."><code>ignore</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unused-ignore-comment" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Fsuppression.rs#L25" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Fsuppression.rs#L24" target="_blank">View source</a>
</small>
@@ -2821,7 +2852,7 @@ a = 20 / 2
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.22">0.0.1-alpha.22</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20useless-overload-body" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1094" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1120" target="_blank">View source</a>
</small>
@@ -2884,7 +2915,7 @@ def foo(x: int | str) -> int | str:
Default level: <a href="../../rules#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20zero-stepsize-in-slice" target="_blank">Related issues</a> ·
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1973" target="_blank">View source</a>
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_types%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1999" target="_blank">View source</a>
</small>

View File

@@ -659,6 +659,8 @@ fn gitlab_diagnostics() -> anyhow::Result<()> {
r#"
print(x) # [unresolved-reference]
print(4[1]) # [non-subscriptable]
from typing_extensions import reveal_type
reveal_type('str'.lower()) # [revealed-type]
"#,
)?;
@@ -709,6 +711,25 @@ fn gitlab_diagnostics() -> anyhow::Result<()> {
}
}
}
},
{
"check_name": "revealed-type",
"description": "revealed-type: Revealed type: `LiteralString`",
"severity": "info",
"fingerprint": "[FINGERPRINT]",
"location": {
"path": "test.py",
"positions": {
"begin": {
"line": 5,
"column": 13
},
"end": {
"line": 5,
"column": 26
}
}
}
}
]
----- stderr -----
@@ -724,6 +745,8 @@ fn github_diagnostics() -> anyhow::Result<()> {
r#"
print(x) # [unresolved-reference]
print(4[1]) # [non-subscriptable]
from typing_extensions import reveal_type
reveal_type('str'.lower()) # [revealed-type]
"#,
)?;
@@ -733,6 +756,7 @@ fn github_diagnostics() -> anyhow::Result<()> {
----- stdout -----
::warning title=ty (unresolved-reference),file=<temp_dir>/test.py,line=2,col=7,endLine=2,endColumn=8::test.py:2:7: unresolved-reference: Name `x` used when not defined
::error title=ty (non-subscriptable),file=<temp_dir>/test.py,line=3,col=7,endLine=3,endColumn=11::test.py:3:7: non-subscriptable: Cannot subscript object of type `Literal[4]` with no `__getitem__` method
::notice title=ty (revealed-type),file=<temp_dir>/test.py,line=5,col=13,endLine=5,endColumn=26::test.py:5:13: revealed-type: Revealed type: `LiteralString`
----- stderr -----
");

View File

@@ -12,7 +12,6 @@ license.workspace = true
[dependencies]
ruff_db = { workspace = true }
ruff_python_ast = { workspace = true }
ty_python_semantic = { workspace = true }
ordermap = { workspace = true }

View File

@@ -8,7 +8,6 @@ use std::{collections::HashMap, hash::BuildHasher};
use ordermap::OrderMap;
use ruff_db::system::SystemPathBuf;
use ruff_python_ast::PythonVersion;
use ty_python_semantic::PythonPlatform;
/// Combine two values, preferring the values in `self`.
///
@@ -145,7 +144,6 @@ macro_rules! impl_noop_combine {
}
impl_noop_combine!(SystemPathBuf);
impl_noop_combine!(PythonPlatform);
impl_noop_combine!(PythonVersion);
// std types

View File

@@ -24,6 +24,7 @@ ruff_source_file = { workspace = true }
ruff_text_size = { workspace = true }
ty_module_resolver = { workspace = true }
ty_python_semantic = { workspace = true }
ty_python_types = { workspace = true }
ty_project = { workspace = true, features = ["testing"] }
ty_vendored = { workspace = true }

View File

@@ -7,7 +7,7 @@ use ruff_text_size::TextRange;
use ty_project::Db;
use ty_python_semantic::create_suppression_fix;
use ty_python_semantic::lint::LintId;
use ty_python_semantic::types::{UNDEFINED_REVEAL, UNRESOLVED_REFERENCE};
use ty_python_types::types::{UNDEFINED_REVEAL, UNRESOLVED_REFERENCE};
/// A `QuickFix` Code Action
#[derive(Debug, Clone)]
@@ -87,10 +87,8 @@ mod tests {
use ruff_python_trivia::textwrap::dedent;
use ruff_text_size::{TextRange, TextSize};
use ty_project::ProjectMetadata;
use ty_python_semantic::{
lint::LintMetadata,
types::{UNDEFINED_REVEAL, UNRESOLVED_REFERENCE},
};
use ty_python_semantic::lint::LintMetadata;
use ty_python_types::types::{UNDEFINED_REVEAL, UNRESOLVED_REFERENCE};
#[test]
fn add_ignore() {

View File

@@ -12,11 +12,8 @@ use ruff_python_codegen::Stylist;
use ruff_text_size::{Ranged, TextRange, TextSize};
use rustc_hash::FxHashSet;
use ty_module_resolver::{KnownModule, ModuleName};
use ty_python_semantic::types::UnionType;
use ty_python_semantic::{
Completion as SemanticCompletion, NameKind, SemanticModel,
types::{CycleDetector, KnownClass, Type},
};
use ty_python_types::types::{CycleDetector, KnownClass, Type, UnionType};
use ty_python_types::{Completion as SemanticCompletion, NameKind, SemanticModel};
use crate::docstring::Docstring;
use crate::goto::Definitions;

View File

@@ -3,7 +3,7 @@ use crate::references::{ReferencesMode, references};
use crate::{Db, ReferenceTarget};
use ruff_db::files::File;
use ruff_text_size::TextSize;
use ty_python_semantic::SemanticModel;
use ty_python_types::SemanticModel;
/// Find all document highlights for a symbol at the given position.
/// Document highlights are limited to the current file only.

View File

@@ -3,7 +3,7 @@ use crate::references::{ReferencesMode, references};
use crate::{Db, ReferenceTarget};
use ruff_db::files::File;
use ruff_text_size::TextSize;
use ty_python_semantic::SemanticModel;
use ty_python_types::SemanticModel;
/// Find all references to a symbol at the given position.
/// Search for references across all files in the project.

View File

@@ -12,12 +12,12 @@ use ruff_python_ast::token::{TokenKind, Tokens};
use ruff_python_ast::{self as ast, AnyNodeRef};
use ruff_text_size::{Ranged, TextRange, TextSize};
use ty_python_semantic::ResolvedDefinition;
use ty_python_semantic::types::Type;
use ty_python_semantic::types::ide_support::{
use ty_python_types::ResolvedDefinition;
use ty_python_types::types::Type;
use ty_python_types::types::ide_support::{
call_signature_details, call_type_simplified_by_overloads, definitions_for_keyword_argument,
};
use ty_python_semantic::{
use ty_python_types::{
HasDefinition, HasType, ImportAliasResolution, SemanticModel, definitions_for_imported_symbol,
definitions_for_name,
};
@@ -228,15 +228,15 @@ impl<'db> Definitions<'db> {
pub(crate) fn from_ty(db: &'db dyn crate::Db, ty: Type<'db>) -> Option<Self> {
let ty_def = ty.definition(db)?;
let resolved = match ty_def {
ty_python_semantic::types::TypeDefinition::Module(module) => {
ty_python_types::types::TypeDefinition::Module(module) => {
ResolvedDefinition::Module(module.file(db)?)
}
ty_python_semantic::types::TypeDefinition::Class(definition)
| ty_python_semantic::types::TypeDefinition::Function(definition)
| ty_python_semantic::types::TypeDefinition::TypeVar(definition)
| ty_python_semantic::types::TypeDefinition::TypeAlias(definition)
| ty_python_semantic::types::TypeDefinition::SpecialForm(definition)
| ty_python_semantic::types::TypeDefinition::NewType(definition) => {
ty_python_types::types::TypeDefinition::Class(definition)
| ty_python_types::types::TypeDefinition::Function(definition)
| ty_python_types::types::TypeDefinition::TypeVar(definition)
| ty_python_types::types::TypeDefinition::TypeAlias(definition)
| ty_python_types::types::TypeDefinition::SpecialForm(definition)
| ty_python_types::types::TypeDefinition::NewType(definition) => {
ResolvedDefinition::Definition(definition)
}
};
@@ -338,11 +338,11 @@ impl GotoTarget<'_> {
}
}
GotoTarget::BinOp { expression, .. } => {
let (_, ty) = ty_python_semantic::definitions_for_bin_op(model, expression)?;
let (_, ty) = ty_python_types::definitions_for_bin_op(model, expression)?;
Some(ty)
}
GotoTarget::UnaryOp { expression, .. } => {
let (_, ty) = ty_python_semantic::definitions_for_unary_op(model, expression)?;
let (_, ty) = ty_python_types::definitions_for_unary_op(model, expression)?;
Some(ty)
}
// TODO: Support identifier targets
@@ -526,15 +526,14 @@ impl GotoTarget<'_> {
}
GotoTarget::BinOp { expression, .. } => {
let (definitions, _) =
ty_python_semantic::definitions_for_bin_op(model, expression)?;
let (definitions, _) = ty_python_types::definitions_for_bin_op(model, expression)?;
Some(definitions)
}
GotoTarget::UnaryOp { expression, .. } => {
let (definitions, _) =
ty_python_semantic::definitions_for_unary_op(model, expression)?;
ty_python_types::definitions_for_unary_op(model, expression)?;
Some(definitions)
}
@@ -939,12 +938,12 @@ impl Ranged for GotoTarget<'_> {
/// Converts a collection of `ResolvedDefinition` items into `NavigationTarget` items.
fn convert_resolved_definitions_to_targets<'db>(
db: &'db dyn ty_python_semantic::Db,
definitions: Vec<ty_python_semantic::ResolvedDefinition<'db>>,
definitions: Vec<ty_python_types::ResolvedDefinition<'db>>,
) -> Vec<crate::NavigationTarget> {
definitions
.into_iter()
.map(|resolved_definition| match resolved_definition {
ty_python_semantic::ResolvedDefinition::Definition(definition) => {
ty_python_types::ResolvedDefinition::Definition(definition) => {
// Get the parsed module for range calculation
let definition_file = definition.file(db);
let module = ruff_db::parsed::parsed_module(db, definition_file).load(db);
@@ -959,11 +958,11 @@ fn convert_resolved_definitions_to_targets<'db>(
full_range: full_range.range(),
}
}
ty_python_semantic::ResolvedDefinition::Module(file) => {
ty_python_types::ResolvedDefinition::Module(file) => {
// For modules, navigate to the start of the file
crate::NavigationTarget::new(file, TextRange::default())
}
ty_python_semantic::ResolvedDefinition::FileWithRange(file_range) => {
ty_python_types::ResolvedDefinition::FileWithRange(file_range) => {
// For file ranges, navigate to the specific range within the file
crate::NavigationTarget::from(file_range)
}
@@ -984,9 +983,9 @@ fn definitions_for_expression<'db>(
expression.into(),
alias_resolution,
)),
ast::ExprRef::Attribute(attribute) => Some(ty_python_semantic::definitions_for_attribute(
model, attribute,
)),
ast::ExprRef::Attribute(attribute) => {
Some(ty_python_types::definitions_for_attribute(model, attribute))
}
_ => None,
}
}
@@ -1007,7 +1006,7 @@ fn definitions_for_callable<'db>(
fn definitions_to_navigation_targets<'db>(
db: &dyn ty_python_semantic::Db,
stub_mapper: Option<&StubMapper<'db>>,
mut definitions: Vec<ty_python_semantic::ResolvedDefinition<'db>>,
mut definitions: Vec<ty_python_types::ResolvedDefinition<'db>>,
) -> Option<crate::NavigationTargets> {
if let Some(mapper) = stub_mapper {
definitions = mapper.map_definitions(definitions);

View File

@@ -3,7 +3,7 @@ use crate::{Db, NavigationTargets, RangedValue};
use ruff_db::files::{File, FileRange};
use ruff_db::parsed::parsed_module;
use ruff_text_size::{Ranged, TextSize};
use ty_python_semantic::{ImportAliasResolution, SemanticModel};
use ty_python_types::{ImportAliasResolution, SemanticModel};
/// Navigate to the declaration of a symbol.
///

View File

@@ -3,7 +3,7 @@ use crate::{Db, NavigationTargets, RangedValue};
use ruff_db::files::{File, FileRange};
use ruff_db::parsed::parsed_module;
use ruff_text_size::{Ranged, TextSize};
use ty_python_semantic::{ImportAliasResolution, SemanticModel};
use ty_python_types::{ImportAliasResolution, SemanticModel};
/// Navigate to the definition of a symbol.
///

View File

@@ -3,7 +3,7 @@ use crate::{Db, HasNavigationTargets, NavigationTargets, RangedValue};
use ruff_db::files::{File, FileRange};
use ruff_db::parsed::parsed_module;
use ruff_text_size::{Ranged, TextSize};
use ty_python_semantic::SemanticModel;
use ty_python_types::SemanticModel;
pub fn goto_type_definition(
db: &dyn Db,

View File

@@ -6,8 +6,8 @@ use ruff_db::parsed::parsed_module;
use ruff_text_size::{Ranged, TextSize};
use std::fmt;
use std::fmt::Formatter;
use ty_python_semantic::types::{KnownInstanceType, Type, TypeVarVariance};
use ty_python_semantic::{DisplaySettings, SemanticModel};
use ty_python_types::types::{KnownInstanceType, Type, TypeVarVariance};
use ty_python_types::{DisplaySettings, SemanticModel};
pub fn hover(db: &dyn Db, file: File, offset: TextSize) -> Option<RangedValue<Hover<'_>>> {
let parsed = parsed_module(db, file).load(db);
@@ -23,7 +23,7 @@ pub fn hover(db: &dyn Db, file: File, offset: TextSize) -> Option<RangedValue<Ho
let docs = goto_target
.get_definition_targets(
&model,
ty_python_semantic::ImportAliasResolution::ResolveAliases,
ty_python_types::ImportAliasResolution::ResolveAliases,
)
.and_then(|definitions| definitions.docstring(db))
.map(HoverContent::Docstring);

View File

@@ -32,8 +32,8 @@ use ruff_text_size::{Ranged, TextRange, TextSize};
use ty_module_resolver::ModuleName;
use ty_project::Db;
use ty_python_semantic::semantic_index::definition::DefinitionKind;
use ty_python_semantic::types::Type;
use ty_python_semantic::{MemberDefinition, SemanticModel};
use ty_python_types::types::Type;
use ty_python_types::{MemberDefinition, SemanticModel};
pub(crate) struct Importer<'a> {
/// The ty Salsa database.
@@ -883,9 +883,8 @@ mod tests {
use ruff_text_size::TextSize;
use ty_module_resolver::SearchPathSettings;
use ty_project::ProjectMetadata;
use ty_python_semantic::{
Program, ProgramSettings, PythonPlatform, PythonVersionWithSource, SemanticModel,
};
use ty_python_semantic::{Program, ProgramSettings, PythonPlatform, PythonVersionWithSource};
use ty_python_types::SemanticModel;
use super::*;

View File

@@ -6,9 +6,9 @@ use ruff_db::parsed::parsed_module;
use ruff_python_ast::visitor::source_order::{self, SourceOrderVisitor, TraversalSignal};
use ruff_python_ast::{AnyNodeRef, ArgOrKeyword, Expr, ExprUnaryOp, Stmt, UnaryOp};
use ruff_text_size::{Ranged, TextRange, TextSize};
use ty_python_semantic::types::ide_support::inlay_hint_call_argument_details;
use ty_python_semantic::types::{Type, TypeDetail};
use ty_python_semantic::{HasType, SemanticModel};
use ty_python_types::types::ide_support::inlay_hint_call_argument_details;
use ty_python_types::types::{Type, TypeDetail};
use ty_python_types::{HasType, SemanticModel};
#[derive(Debug, Clone)]
pub struct InlayHint {

View File

@@ -57,7 +57,7 @@ use ruff_text_size::{Ranged, TextRange};
use rustc_hash::FxHashSet;
use std::ops::{Deref, DerefMut};
use ty_project::Db;
use ty_python_semantic::types::{Type, TypeDefinition};
use ty_python_types::types::{Type, TypeDefinition};
/// Information associated with a text range.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]

View File

@@ -20,7 +20,7 @@ use ruff_python_ast::{
visitor::source_order::{SourceOrderVisitor, TraversalSignal},
};
use ruff_text_size::{Ranged, TextRange};
use ty_python_semantic::{ImportAliasResolution, SemanticModel};
use ty_python_types::{ImportAliasResolution, SemanticModel};
/// Mode for references search behavior
#[derive(Debug, Clone, Copy, PartialEq, Eq)]

View File

@@ -3,7 +3,7 @@ use crate::references::{ReferencesMode, references};
use crate::{Db, ReferenceTarget};
use ruff_db::files::File;
use ruff_text_size::{Ranged, TextSize};
use ty_python_semantic::SemanticModel;
use ty_python_types::SemanticModel;
/// Returns the range of the symbol if it can be renamed, None if not.
pub fn can_rename(db: &dyn Db, file: File, offset: TextSize) -> Option<ruff_text_size::TextRange> {

View File

@@ -17,9 +17,6 @@
//! TODO: Need to properly handle Annotated expressions. All type arguments other
//! than the first should be treated as value expressions, not as type expressions.
//!
//! TODO: An identifier that resolves to a parameter when used within a function
//! should be classified as a parameter, selfParameter, or clsParameter token.
//!
//! TODO: Properties (or perhaps more generally, descriptor objects?) should be
//! classified as property tokens rather than just variables.
//!
@@ -47,10 +44,10 @@ use ruff_python_ast::{
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
use std::ops::Deref;
use ty_python_semantic::semantic_index::definition::Definition;
use ty_python_semantic::types::TypeVarKind;
use ty_python_semantic::{
HasType, SemanticModel, semantic_index::definition::DefinitionKind, types::Type,
types::ide_support::definition_for_name,
use ty_python_semantic::semantic_index::definition::DefinitionKind;
use ty_python_types::types::TypeVarKind;
use ty_python_types::{
HasType, SemanticModel, types::Type, types::ide_support::definition_for_name,
};
/// Semantic token types supported by the language server.
@@ -230,6 +227,11 @@ impl<'db> SemanticTokenVisitor<'db> {
modifiers: SemanticTokenModifier,
) {
let range = ranged.range();
if range.is_empty() {
return;
}
// Only emit tokens that intersect with the range filter, if one is specified
if let Some(range_filter) = self.range_filter {
// Only include ranges that have a non-empty overlap. Adjacent ranges
@@ -270,7 +272,7 @@ impl<'db> SemanticTokenVisitor<'db> {
let definition = definition_for_name(
self.model,
name,
ty_python_semantic::ImportAliasResolution::ResolveAliases,
ty_python_types::ImportAliasResolution::ResolveAliases,
);
if let Some(definition) = definition {
@@ -707,15 +709,15 @@ impl SourceOrderVisitor<'_> for SemanticTokenVisitor<'_> {
}
ast::Stmt::Import(import) => {
for alias in &import.names {
// Create separate tokens for each part of a dotted module name
self.add_dotted_name_tokens(&alias.name, SemanticTokenType::Namespace);
if let Some(asname) = &alias.asname {
self.add_token(
asname.range(),
SemanticTokenType::Namespace,
SemanticTokenModifier::empty(),
);
} else {
// Create separate tokens for each part of a dotted module name
self.add_dotted_name_tokens(&alias.name, SemanticTokenType::Namespace);
}
}
}
@@ -1131,7 +1133,7 @@ mod tests {
use ty_project::ProjectMetadata;
#[test]
fn test_semantic_tokens_basic() {
fn semantic_tokens_basic() {
let test = SemanticTokenTest::new("def foo(): pass");
let tokens = test.highlight_file();
@@ -1142,7 +1144,7 @@ mod tests {
}
#[test]
fn test_semantic_tokens_class() {
fn semantic_tokens_class() {
let test = SemanticTokenTest::new("class MyClass: pass");
let tokens = test.highlight_file();
@@ -1153,7 +1155,7 @@ mod tests {
}
#[test]
fn test_semantic_tokens_class_args() {
fn semantic_tokens_class_args() {
// This used to cause a panic because of an incorrect
// insertion-order when visiting arguments inside
// class definitions.
@@ -1169,7 +1171,7 @@ mod tests {
}
#[test]
fn test_semantic_tokens_variables() {
fn semantic_tokens_variables() {
let test = SemanticTokenTest::new(
"
x = 42
@@ -1188,7 +1190,7 @@ y = 'hello'
}
#[test]
fn test_semantic_tokens_walrus() {
fn semantic_tokens_walrus() {
let test = SemanticTokenTest::new(
"
if x := 42:
@@ -1207,7 +1209,7 @@ if x := 42:
}
#[test]
fn test_semantic_tokens_self_parameter() {
fn semantic_tokens_self_parameter() {
let test = SemanticTokenTest::new(
"
class MyClass:
@@ -1238,7 +1240,7 @@ class MyClass:
}
#[test]
fn test_semantic_tokens_cls_parameter() {
fn semantic_tokens_cls_parameter() {
let test = SemanticTokenTest::new(
"
class MyClass:
@@ -1261,7 +1263,7 @@ class MyClass:
}
#[test]
fn test_semantic_tokens_staticmethod_parameter() {
fn semantic_tokens_staticmethod_parameter() {
let test = SemanticTokenTest::new(
"
class MyClass:
@@ -1282,7 +1284,7 @@ class MyClass:
}
#[test]
fn test_semantic_tokens_custom_self_cls_names() {
fn semantic_tokens_custom_self_cls_names() {
let test = SemanticTokenTest::new(
"
class MyClass:
@@ -1317,7 +1319,7 @@ class MyClass:
}
#[test]
fn test_semantic_tokens_modifiers() {
fn semantic_tokens_modifiers() {
let test = SemanticTokenTest::new(
"
class MyClass:
@@ -1338,7 +1340,7 @@ class MyClass:
}
#[test]
fn test_semantic_classification_vs_heuristic() {
fn semantic_classification_vs_heuristic() {
let test = SemanticTokenTest::new(
"
import sys
@@ -1372,7 +1374,7 @@ z = sys.version
}
#[test]
fn test_builtin_constants() {
fn builtin_constants() {
let test = SemanticTokenTest::new(
"
x = True
@@ -1394,7 +1396,7 @@ z = None
}
#[test]
fn test_builtin_constants_in_expressions() {
fn builtin_constants_in_expressions() {
let test = SemanticTokenTest::new(
"
def check(value):
@@ -1422,7 +1424,7 @@ result = check(None)
}
#[test]
fn test_builtin_types() {
fn builtin_types() {
let test = SemanticTokenTest::new(
r#"
type U = str | int
@@ -1467,7 +1469,7 @@ result = check(None)
}
#[test]
fn test_semantic_tokens_range() {
fn semantic_tokens_range() {
let test = SemanticTokenTest::new(
"
def function1():
@@ -1532,7 +1534,7 @@ def function2():
/// When a token starts right at where the requested range ends,
/// don't include it in the semantic tokens.
#[test]
fn test_semantic_tokens_range_excludes_boundary_tokens() {
fn semantic_tokens_range_excludes_boundary_tokens() {
let test = SemanticTokenTest::new(
"
x = 1
@@ -1555,7 +1557,7 @@ z = 3
}
#[test]
fn test_dotted_module_names() {
fn dotted_module_names() {
let test = SemanticTokenTest::new(
"
import os.path
@@ -1582,7 +1584,7 @@ from collections.abc import Mapping
}
#[test]
fn test_module_type_classification() {
fn module_type_classification() {
let test = SemanticTokenTest::new(
"
import os
@@ -1610,7 +1612,7 @@ y = sys
}
#[test]
fn test_import_classification() {
fn import_classification() {
let test = SemanticTokenTest::new(
"
from os import path
@@ -1641,7 +1643,7 @@ from mymodule import CONSTANT, my_function, MyClass
}
#[test]
fn test_str_annotation() {
fn str_annotation() {
let test = SemanticTokenTest::new(
r#"
x: int = 1
@@ -1685,7 +1687,7 @@ w5: "float
}
#[test]
fn test_attribute_classification() {
fn attribute_classification() {
let test = SemanticTokenTest::new(
"
import os
@@ -1759,7 +1761,7 @@ u = List.__name__ # __name__ should be variable
}
#[test]
fn test_attribute_fallback_classification() {
fn attribute_fallback_classification() {
let test = SemanticTokenTest::new(
"
class MyClass:
@@ -1790,7 +1792,7 @@ y = obj.unknown_attr # Should fall back to variable
}
#[test]
fn test_constant_name_detection() {
fn constant_name_detection() {
let test = SemanticTokenTest::new(
"
class MyClass:
@@ -1837,7 +1839,7 @@ w = obj.A # Should not have readonly modifier (length == 1)
}
#[test]
fn test_type_annotations() {
fn type_annotations() {
let test = SemanticTokenTest::new(
r#"
from typing import List, Optional
@@ -2325,7 +2327,7 @@ class MyClass:
}
#[test]
fn test_debug_int_classification() {
fn debug_int_classification() {
let test = SemanticTokenTest::new(
"
x: int = 42
@@ -2342,7 +2344,7 @@ x: int = 42
}
#[test]
fn test_debug_user_defined_type_classification() {
fn debug_user_defined_type_classification() {
let test = SemanticTokenTest::new(
"
class MyClass:
@@ -2363,7 +2365,7 @@ x: MyClass = MyClass()
}
#[test]
fn test_type_annotation_vs_variable_classification() {
fn type_annotation_vs_variable_classification() {
let test = SemanticTokenTest::new(
"
from typing import List, Optional
@@ -2413,7 +2415,7 @@ def test_function(param: int, other: MyClass) -> Optional[List[str]]:
}
#[test]
fn test_protocol_types_in_annotations() {
fn protocol_types_in_annotations() {
let test = SemanticTokenTest::new(
"
from typing import Protocol
@@ -2444,7 +2446,7 @@ def test_function(param: MyProtocol) -> None:
}
#[test]
fn test_protocol_type_annotation_vs_value_context() {
fn protocol_type_annotation_vs_value_context() {
let test = SemanticTokenTest::new(
"
from typing import Protocol
@@ -2530,7 +2532,7 @@ def test_function(param: my_type_alias): ...
}
#[test]
fn test_type_parameters_pep695() {
fn type_parameters_pep695() {
let test = SemanticTokenTest::new(
"
# Test Python 3.12 PEP 695 type parameter syntax
@@ -2654,7 +2656,7 @@ class BoundedContainer[T: int, U = str]:
}
#[test]
fn test_type_parameters_usage_in_function_body() {
fn type_parameters_usage_in_function_body() {
let test = SemanticTokenTest::new(
"
def generic_function[T](value: T) -> T:
@@ -2683,7 +2685,7 @@ def generic_function[T](value: T) -> T:
}
#[test]
fn test_decorator_classification() {
fn decorator_classification() {
let test = SemanticTokenTest::new(
r#"
@staticmethod
@@ -2713,7 +2715,7 @@ class MyClass:
}
#[test]
fn test_constant_variations() {
fn constant_variations() {
let test = SemanticTokenTest::new(
r#"
A = 1
@@ -2756,7 +2758,7 @@ A_1 = 1
}
#[test]
fn test_implicitly_concatenated_strings() {
fn implicitly_concatenated_strings() {
let test = SemanticTokenTest::new(
r#"x = "hello" "world"
y = ("multi"
@@ -2783,7 +2785,7 @@ z = 'single' "mixed" 'quotes'"#,
}
#[test]
fn test_bytes_literals() {
fn bytes_literals() {
let test = SemanticTokenTest::new(
r#"x = b"hello" b"world"
y = (b"multi"
@@ -2810,7 +2812,7 @@ z = b'single' b"mixed" b'quotes'"#,
}
#[test]
fn test_mixed_string_and_bytes_literals() {
fn mixed_string_and_bytes_literals() {
let test = SemanticTokenTest::new(
r#"# Test mixed string and bytes literals
string_concat = "hello" "world"
@@ -2846,7 +2848,7 @@ regular_bytes = b"just bytes""#,
}
#[test]
fn test_fstring_with_mixed_literals() {
fn fstring_with_mixed_literals() {
let test = SemanticTokenTest::new(
r#"
# Test f-strings with various literal types
@@ -2898,7 +2900,7 @@ complex_fstring = f"User: {name.upper()}, Count: {len(data)}, Hex: {value:x}"
}
#[test]
fn test_nonlocal_and_global_statements() {
fn nonlocal_and_global_statements() {
let test = SemanticTokenTest::new(
r#"
x = "global_value"
@@ -2960,7 +2962,7 @@ def outer():
}
#[test]
fn test_nonlocal_global_edge_cases() {
fn nonlocal_global_edge_cases() {
let test = SemanticTokenTest::new(
r#"
# Single variable statements
@@ -3000,7 +3002,7 @@ def test():
}
#[test]
fn test_pattern_matching() {
fn pattern_matching() {
let test = SemanticTokenTest::new(
r#"
def process_data(data):
@@ -3056,7 +3058,7 @@ def process_data(data):
}
#[test]
fn test_exception_handlers() {
fn exception_handlers() {
let test = SemanticTokenTest::new(
r#"
try:
@@ -3095,7 +3097,7 @@ finally:
}
#[test]
fn test_self_attribute_expression() {
fn self_attribute_expression() {
let test = SemanticTokenTest::new(
r#"
from typing import Self
@@ -3135,7 +3137,7 @@ class C:
}
#[test]
fn test_augmented_assignment() {
fn augmented_assignment() {
let test = SemanticTokenTest::new(
r#"
x = 0
@@ -3154,7 +3156,7 @@ x += 1
}
#[test]
fn test_type_alias() {
fn type_alias() {
let test = SemanticTokenTest::new("type MyList[T] = list[T]");
let tokens = test.highlight_file();
@@ -3168,7 +3170,7 @@ x += 1
}
#[test]
fn test_for_stmt() {
fn for_stmt() {
let test = SemanticTokenTest::new(
r#"
for item in []:
@@ -3190,7 +3192,7 @@ else:
}
#[test]
fn test_with_stmt() {
fn with_stmt() {
let test = SemanticTokenTest::new(
r#"
with open("file.txt") as f:
@@ -3210,7 +3212,7 @@ with open("file.txt") as f:
}
#[test]
fn test_comprehensions() {
fn comprehensions() {
let test = SemanticTokenTest::new(
r#"
list_comp = [x for x in range(10) if x % 2 == 0]
@@ -3255,7 +3257,7 @@ generator = (x for x in range(10))
/// Regression test for <https://github.com/astral-sh/ty/issues/1406>
#[test]
fn test_invalid_kwargs() {
fn invalid_kwargs() {
let test = SemanticTokenTest::new(
r#"
def foo(self, **key, value=10):
@@ -3274,6 +3276,24 @@ def foo(self, **key, value=10):
"#);
}
#[test]
fn import_as() {
let test = SemanticTokenTest::new(
r#"
import pathlib as path
from pathlib import Path
"#,
);
let tokens = test.highlight_file();
assert_snapshot!(test.to_snapshot(&tokens), @r#"
"pathlib" @ 8..15: Namespace
"path" @ 19..23: Namespace
"pathlib" @ 29..36: Namespace
"Path" @ 44..48: Class
"#);
}
pub(super) struct SemanticTokenTest {
pub(super) db: ty_project::TestDb,
file: File,

View File

@@ -15,13 +15,13 @@ use ruff_python_ast::find_node::covering_node;
use ruff_python_ast::token::TokenKind;
use ruff_python_ast::{self as ast, AnyNodeRef};
use ruff_text_size::{Ranged, TextRange, TextSize};
use ty_python_semantic::ResolvedDefinition;
use ty_python_semantic::SemanticModel;
use ty_python_semantic::semantic_index::definition::Definition;
use ty_python_semantic::types::ide_support::{
use ty_python_types::ResolvedDefinition;
use ty_python_types::SemanticModel;
use ty_python_types::types::ide_support::{
CallSignatureDetails, call_signature_details, find_active_signature_from_details,
};
use ty_python_semantic::types::{ParameterKind, Type};
use ty_python_types::types::{ParameterKind, Type};
// TODO: We may want to add special-case handling for calls to constructors
// so the class docstring is used in place of (or inaddition to) any docstring

View File

@@ -1,6 +1,6 @@
use itertools::Either;
use ruff_db::system::SystemPathBuf;
use ty_python_semantic::{ResolvedDefinition, map_stub_definition};
use ty_python_types::{ResolvedDefinition, map_stub_definition};
use crate::cached_vendored_root;

View File

@@ -23,6 +23,7 @@ ruff_text_size = { workspace = true }
ty_combine = { workspace = true }
ty_module_resolver = { workspace = true }
ty_python_semantic = { workspace = true, features = ["serde"] }
ty_python_types = { workspace = true }
ty_static = { workspace = true }
ty_vendored = { workspace = true }

View File

@@ -467,7 +467,7 @@ impl SemanticDb for ProjectDatabase {
}
fn lint_registry(&self) -> &LintRegistry {
ty_python_semantic::default_lint_registry()
ty_python_types::default_lint_registry()
}
fn analysis_settings(&self) -> &AnalysisSettings {
@@ -658,7 +658,7 @@ pub(crate) mod tests {
}
fn lint_registry(&self) -> &LintRegistry {
ty_python_semantic::default_lint_registry()
ty_python_types::default_lint_registry()
}
fn analysis_settings(&self) -> &AnalysisSettings {

View File

@@ -29,7 +29,7 @@ use std::sync::Arc;
use thiserror::Error;
use ty_python_semantic::add_inferred_python_version_hint_to_diagnostic;
use ty_python_semantic::lint::RuleSelection;
use ty_python_semantic::types::check_types;
use ty_python_types::types::check_types;
mod db;
mod files;
@@ -769,7 +769,7 @@ mod tests {
use ruff_db::system::{DbWithTestSystem, DbWithWritableSystem as _, SystemPath, SystemPathBuf};
use ruff_db::testing::assert_function_query_was_not_run;
use ruff_python_ast::name::Name;
use ty_python_semantic::types::check_types;
use ty_python_types::types::check_types;
#[test]
fn check_file_skips_type_checking_when_file_cant_be_read() -> ruff_db::system::Result<()> {

View File

@@ -1666,7 +1666,7 @@ mod schema {
fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
use serde_json::{Map, Value};
let registry = ty_python_semantic::default_lint_registry();
let registry = ty_python_types::default_lint_registry();
let level_schema = generator.subschema_for::<super::Level>();
let properties: Map<String, Value> = registry

View File

@@ -19,12 +19,11 @@ ruff_macros = { workspace = true }
ruff_memory_usage = { workspace = true }
ruff_python_ast = { workspace = true, features = ["salsa"] }
ruff_python_parser = { workspace = true }
ruff_python_stdlib = { workspace = true }
ruff_source_file = { workspace = true }
ruff_text_size = { workspace = true }
ruff_python_literal = { workspace = true }
ruff_python_trivia = { workspace = true }
ty_module_resolver = { workspace = true }
ty_combine = { workspace = true }
ty_static = { workspace = true }
anyhow = { workspace = true }
@@ -32,8 +31,6 @@ bitflags = { workspace = true }
bitvec = { workspace = true }
camino = { workspace = true }
colored = { workspace = true }
compact_str = { workspace = true }
drop_bomb = { workspace = true }
get-size2 = { workspace = true, features = ["indexmap", "ordermap"] }
indexmap = { workspace = true }
itertools = { workspace = true }
@@ -47,9 +44,8 @@ schemars = { workspace = true, optional = true }
serde = { workspace = true, optional = true }
serde_json = { workspace = true, optional = true }
smallvec = { workspace = true }
ty_vendored = { workspace = true, optional = true }
static_assertions = { workspace = true }
test-case = { workspace = true }
memchr = { workspace = true }
strum = { workspace = true }
strum_macros = { workspace = true }
strsim = "0.11.1"
@@ -57,27 +53,19 @@ strsim = "0.11.1"
[dev-dependencies]
ruff_db = { workspace = true, features = ["testing", "os"] }
ruff_python_parser = { workspace = true }
ty_static = { workspace = true }
ty_test = { workspace = true }
ty_vendored = { workspace = true }
anyhow = { workspace = true }
datatest-stable = { workspace = true }
glob = { workspace = true }
indoc = { workspace = true }
insta = { workspace = true }
pretty_assertions = { workspace = true }
quickcheck = { version = "1.0.3", default-features = false }
quickcheck_macros = { version = "1.0.0" }
[features]
schemars = ["dep:schemars", "dep:serde_json"]
serde = ["ruff_db/serde", "dep:serde", "ruff_python_ast/serde"]
testing = []
[[test]]
name = "mdtest"
harness = false
testing = ["dep:ty_vendored", "ruff_db/testing"]
[lints]
workspace = true
[package.metadata.cargo-shear]
# Used via macro expansion.
ignored = ["quickcheck"]

View File

@@ -20,19 +20,19 @@ pub trait Db: ModuleResolverDb {
fn verbose(&self) -> bool;
}
#[cfg(test)]
pub(crate) mod tests {
#[cfg(any(test, feature = "testing"))]
pub mod tests {
use std::sync::{Arc, Mutex};
use crate::program::Program;
use crate::{
AnalysisSettings, ProgramSettings, PythonPlatform, PythonVersionSource,
PythonVersionWithSource, default_lint_registry,
PythonVersionWithSource,
};
use ty_module_resolver::SearchPathSettings;
use super::Db;
use crate::lint::{LintRegistry, RuleSelection};
use crate::lint::{LintRegistry, LintRegistryBuilder, RuleSelection};
use anyhow::Context;
use ruff_db::Db as SourceDb;
use ruff_db::files::{File, Files};
@@ -46,21 +46,44 @@ pub(crate) mod tests {
type Events = Arc<Mutex<Vec<salsa::Event>>>;
/// A default lint registry containing only the suppression lints.
/// Full lint registry should be obtained from `ty_python_types`.
fn test_lint_registry() -> &'static LintRegistry {
static REGISTRY: std::sync::LazyLock<LintRegistry> = std::sync::LazyLock::new(|| {
let mut builder = LintRegistryBuilder::default();
crate::register_suppression_lints(&mut builder);
builder.build()
});
&REGISTRY
}
#[salsa::db]
#[derive(Clone)]
pub(crate) struct TestDb {
pub struct TestDb {
storage: salsa::Storage<Self>,
files: Files,
system: TestSystem,
vendored: VendoredFileSystem,
events: Events,
rule_selection: Arc<RuleSelection>,
lint_registry: Arc<LintRegistry>,
analysis_settings: Arc<AnalysisSettings>,
}
impl Default for TestDb {
fn default() -> Self {
Self::new()
}
}
impl TestDb {
pub(crate) fn new() -> Self {
pub fn new() -> Self {
Self::with_vendored(ty_vendored::file_system().clone())
}
pub fn with_vendored(vendored: VendoredFileSystem) -> Self {
let events = Events::default();
let lint_registry = test_lint_registry();
Self {
storage: salsa::Storage::new(Some(Box::new({
let events = events.clone();
@@ -71,16 +94,17 @@ 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())),
rule_selection: Arc::new(RuleSelection::from_registry(lint_registry)),
lint_registry: Arc::new(lint_registry.clone()),
analysis_settings: AnalysisSettings::default().into(),
}
}
/// Takes the salsa events.
pub(crate) fn take_salsa_events(&mut self) -> Vec<salsa::Event> {
pub fn take_salsa_events(&mut self) -> Vec<salsa::Event> {
let mut events = self.events.lock().unwrap();
std::mem::take(&mut *events)
@@ -90,7 +114,7 @@ pub(crate) mod tests {
///
/// ## Panics
/// If there are any pending salsa snapshots.
pub(crate) fn clear_salsa_events(&mut self) {
pub fn clear_salsa_events(&mut self) {
self.take_salsa_events();
}
}
@@ -135,7 +159,7 @@ pub(crate) mod tests {
}
fn lint_registry(&self) -> &LintRegistry {
default_lint_registry()
&self.lint_registry
}
fn analysis_settings(&self) -> &AnalysisSettings {
@@ -157,7 +181,7 @@ pub(crate) mod tests {
#[salsa::db]
impl salsa::Database for TestDb {}
pub(crate) struct TestDbBuilder<'a> {
pub struct TestDbBuilder<'a> {
/// Target Python version
python_version: PythonVersion,
/// Target Python platform
@@ -166,8 +190,14 @@ pub(crate) mod tests {
files: Vec<(&'a str, &'a str)>,
}
impl Default for TestDbBuilder<'_> {
fn default() -> Self {
Self::new()
}
}
impl<'a> TestDbBuilder<'a> {
pub(crate) fn new() -> Self {
pub fn new() -> Self {
Self {
python_version: PythonVersion::default(),
python_platform: PythonPlatform::default(),
@@ -175,12 +205,14 @@ pub(crate) mod tests {
}
}
pub(crate) fn with_python_version(mut self, version: PythonVersion) -> Self {
#[must_use]
pub fn with_python_version(mut self, version: PythonVersion) -> Self {
self.python_version = version;
self
}
pub(crate) fn with_file(
#[must_use]
pub fn with_file(
mut self,
path: &'a (impl AsRef<SystemPath> + ?Sized),
content: &'a str,
@@ -189,7 +221,7 @@ pub(crate) mod tests {
self
}
pub(crate) fn build(self) -> anyhow::Result<TestDb> {
pub fn build(self) -> anyhow::Result<TestDb> {
let mut db = TestDb::new();
let src_root = SystemPathBuf::from("/src");
@@ -216,7 +248,7 @@ pub(crate) mod tests {
}
}
pub(crate) fn setup_db() -> TestDb {
pub fn setup_db() -> TestDb {
TestDbBuilder::new().build().expect("valid TestDb setup")
}
}

View File

@@ -1,5 +1,6 @@
use crate::{
Db, Program, PythonVersionWithSource, lint::lint_documentation_url, types::TypeCheckDiagnostics,
Db, Program, PythonVersionWithSource, lint::lint_documentation_url,
suppression::TypeCheckDiagnostics,
};
use ruff_db::{
diagnostic::{Annotation, Diagnostic, DiagnosticId, SubDiagnostic, SubDiagnosticSeverity},
@@ -9,7 +10,7 @@ use std::cell::RefCell;
use std::fmt::Write;
/// Suggest a name from `existing_names` that is similar to `wrong_name`.
pub(crate) fn did_you_mean<S: AsRef<str>, T: AsRef<str>>(
pub fn did_you_mean<S: AsRef<str>, T: AsRef<str>>(
existing_names: impl Iterator<Item = S>,
wrong_name: T,
) -> Option<String> {
@@ -127,7 +128,7 @@ pub fn add_inferred_python_version_hint_to_diagnostic(
/// Format a list of elements as a human-readable enumeration.
///
/// Encloses every element in backticks (`1`, `2` and `3`).
pub(crate) fn format_enumeration<I, IT, D>(elements: I) -> String
pub fn format_enumeration<I, IT, D>(elements: I) -> String
where
I: IntoIterator<IntoIter = IT>,
IT: ExactSizeIterator<Item = D> + DoubleEndedIterator,
@@ -160,7 +161,7 @@ where
/// associated file is equivalent to the file being type checked. As a result,
/// if either is violated, then the `Drop` impl on `DiagnosticGuard` will
/// panic.
pub(super) struct DiagnosticGuard<'sink> {
pub struct DiagnosticGuard<'sink> {
/// The file of the primary span (to which file does this diagnostic belong).
file: File,
@@ -187,7 +188,7 @@ pub(super) struct DiagnosticGuard<'sink> {
}
impl<'sink> DiagnosticGuard<'sink> {
pub(crate) fn new(
pub fn new(
file: File,
sink: &'sink std::cell::RefCell<TypeCheckDiagnostics>,
diag: Diagnostic,

View File

@@ -0,0 +1,15 @@
use ruff_python_ast::{self as ast, HasNodeIndex};
/// Implemented by types for which the semantic index tracks their scope.
pub trait HasTrackedScope: HasNodeIndex {}
impl HasTrackedScope for ast::Expr {}
impl HasTrackedScope for ast::ExprRef<'_> {}
impl HasTrackedScope for &ast::ExprRef<'_> {}
// We never explicitly register the scope of an `Identifier`.
// However, `ExpressionsScopeMap` stores the text ranges of each scope.
// That allows us to look up the identifier's scope for as long as it's
// inside an expression (because the ranges overlap).
impl HasTrackedScope for ast::Identifier {}

View File

@@ -4,7 +4,7 @@
)]
use std::hash::BuildHasherDefault;
use crate::lint::{LintRegistry, LintRegistryBuilder};
use crate::lint::LintRegistryBuilder;
use crate::suppression::{
IGNORE_COMMENT_UNKNOWN_RULE, INVALID_IGNORE_COMMENT, UNUSED_IGNORE_COMMENT,
};
@@ -15,60 +15,38 @@ pub use program::{
};
pub use python_platform::PythonPlatform;
use rustc_hash::FxHasher;
pub use semantic_model::{
Completion, HasDefinition, HasType, MemberDefinition, NameKind, SemanticModel,
};
pub use site_packages::{PythonEnvironment, SitePackagesPaths, SysPrefixPathOrigin};
pub use suppression::create_suppression_fix;
pub use suppression::{FileSuppressionId, TypeCheckDiagnostics, create_suppression_fix};
pub use ty_module_resolver::MisconfigurationMode;
pub use types::DisplaySettings;
pub use types::ide_support::{
ImportAliasResolution, ResolvedDefinition, definitions_for_attribute, definitions_for_bin_op,
definitions_for_imported_symbol, definitions_for_name, definitions_for_unary_op,
map_stub_definition,
};
pub mod ast_node_ref;
mod db;
mod dunder_all;
pub mod db;
pub(crate) mod has_tracked_scope;
pub mod lint;
pub(crate) mod list;
mod node_key;
pub(crate) mod place;
pub mod list;
pub mod node_key;
pub mod place_qualifiers;
mod program;
mod python_platform;
mod rank;
pub mod rank;
pub mod semantic_index;
mod semantic_model;
pub(crate) mod site_packages;
mod subscript;
mod suppression;
pub mod types;
mod unpack;
pub mod subscript;
pub mod suppression;
mod truthiness;
pub mod unpack;
mod diagnostic;
#[cfg(feature = "testing")]
pub mod pull_types;
pub use truthiness::Truthiness;
type FxOrderMap<K, V> = ordermap::map::OrderMap<K, V, BuildHasherDefault<FxHasher>>;
type FxOrderSet<V> = ordermap::set::OrderSet<V, BuildHasherDefault<FxHasher>>;
type FxIndexMap<K, V> = indexmap::IndexMap<K, V, BuildHasherDefault<FxHasher>>;
type FxIndexSet<V> = indexmap::IndexSet<V, BuildHasherDefault<FxHasher>>;
pub mod diagnostic;
/// Returns the default registry with all known semantic lints.
pub fn default_lint_registry() -> &'static LintRegistry {
static REGISTRY: std::sync::LazyLock<LintRegistry> = std::sync::LazyLock::new(|| {
let mut registry = LintRegistryBuilder::default();
register_lints(&mut registry);
registry.build()
});
pub type FxOrderMap<K, V> = ordermap::map::OrderMap<K, V, BuildHasherDefault<FxHasher>>;
pub type FxOrderSet<V> = ordermap::set::OrderSet<V, BuildHasherDefault<FxHasher>>;
pub type FxIndexMap<K, V> = indexmap::IndexMap<K, V, BuildHasherDefault<FxHasher>>;
pub type FxIndexSet<V> = indexmap::IndexSet<V, BuildHasherDefault<FxHasher>>;
&REGISTRY
}
/// Register all known semantic lints.
pub fn register_lints(registry: &mut LintRegistryBuilder) {
types::register_lints(registry);
/// Register suppression-related lints (used by `ty_python_types` to build the full registry).
pub fn register_suppression_lints(registry: &mut LintRegistryBuilder) {
registry.register_lint(&UNUSED_IGNORE_COMMENT);
registry.register_lint(&IGNORE_COMMENT_UNKNOWN_RULE);
registry.register_lint(&INVALID_IGNORE_COMMENT);

View File

@@ -70,7 +70,7 @@ use ruff_index::{IndexVec, newtype_index};
/// A handle to an association list. Use [`ListStorage`] to access its elements, and
/// [`ListBuilder`] to construct other lists based on this one.
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, get_size2::GetSize)]
pub(crate) struct List<K, V = ()> {
pub struct List<K, V = ()> {
last: Option<ListCellId>,
_phantom: PhantomData<(K, V)>,
}

View File

@@ -4,17 +4,17 @@ use crate::ast_node_ref::AstNodeRef;
/// Compact key for a node for use in a hash map.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, get_size2::GetSize)]
pub(super) struct NodeKey(NodeIndex);
pub struct NodeKey(NodeIndex);
impl NodeKey {
pub(super) fn from_node<N>(node: N) -> Self
pub fn from_node<N>(node: N) -> Self
where
N: HasNodeIndex,
{
NodeKey(node.node_index().load())
}
pub(super) fn from_node_ref<T>(node_ref: &AstNodeRef<T>) -> Self {
pub fn from_node_ref<T>(node_ref: &AstNodeRef<T>) -> Self {
NodeKey(node_ref.index())
}
}

View File

@@ -0,0 +1,70 @@
//! Place qualifier enums that are used by both semantic and type analysis.
//!
//! These simple enums don't depend on type information and are used by
//! `use_def.rs` and other semantic index components.
/// Specifies how the boundness of a place should be determined.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, salsa::Update)]
pub enum BoundnessAnalysis {
/// The place is always considered bound.
AssumeBound,
/// The boundness of the place is determined based on the visibility of the implicit
/// `unbound` binding. In the example below, when analyzing the visibility of the
/// `x = <unbound>` binding from the position of the end of the scope, it would be
/// `Truthiness::Ambiguous`, because it could either be visible or not, depending on the
/// `flag()` return value. This would result in a `Definedness::PossiblyUndefined` for `x`.
///
/// ```py
/// x = <unbound>
///
/// if flag():
/// x = 1
/// ```
BasedOnUnboundVisibility,
}
/// Specifies whether a place is always defined or might be undefined.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, get_size2::GetSize)]
pub enum Definedness {
/// The place is always defined at this point.
AlwaysDefined,
/// The place might or might not be defined at this point.
PossiblyUndefined,
}
/// Specifies how a type was determined.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Default, get_size2::GetSize)]
pub enum TypeOrigin {
/// The type was explicitly declared.
Declared,
/// The type was inferred from bindings.
#[default]
Inferred,
}
/// Specifies whether to widen the type with `Unknown`.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Default, get_size2::GetSize)]
pub enum Widening {
/// Don't widen the type.
#[default]
None,
/// Widen the type with `Unknown` to handle undefined bindings.
WithUnknown,
}
/// Specifies whether a re-export requires explicit `__all__` inclusion.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum RequiresExplicitReExport {
Yes,
No,
}
/// Specifies which definitions to consider when looking up a place.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Default)]
pub enum ConsideredDefinitions {
/// Consider all reachable definitions at a specific use.
AllReachable,
/// Consider definitions visible at the end of the scope.
#[default]
EndOfScope,
}

View File

@@ -1,4 +1,5 @@
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)]
@@ -54,6 +55,10 @@ impl Default for PythonPlatform {
}
}
impl Combine for PythonPlatform {
fn combine_with(&mut self, _other: Self) {}
}
#[cfg(feature = "schemars")]
mod schema {
use crate::PythonPlatform;

View File

@@ -24,7 +24,7 @@ use get_size2::GetSize;
/// This trick adds O(1.5) bits of overhead per large vector element on 64-bit platforms, and O(2)
/// bits of overhead on 32-bit platforms.
#[derive(Clone, Debug, Eq, PartialEq, GetSize)]
pub(crate) struct RankBitBox {
pub struct RankBitBox {
#[get_size(size_fn = bit_box_size)]
bits: BitBox<Chunk, Msb0>,
chunk_ranks: Box<[u32]>,
@@ -58,13 +58,13 @@ impl RankBitBox {
}
#[inline]
pub(crate) fn get_bit(&self, index: usize) -> Option<bool> {
pub fn get_bit(&self, index: usize) -> Option<bool> {
self.bits.get(index).map(|bit| *bit)
}
/// Returns the number of bits _before_ (and not including) the given index that are set.
#[inline]
pub(crate) fn rank(&self, index: usize) -> u32 {
pub fn rank(&self, index: usize) -> u32 {
let chunk_index = index / CHUNK_SIZE;
let index_within_chunk = index % CHUNK_SIZE;
let chunk_rank = self.chunk_ranks[chunk_index];

View File

@@ -12,7 +12,7 @@ use salsa::plumbing::AsId;
use ty_module_resolver::ModuleName;
use crate::Db;
use crate::node_key::NodeKey;
use crate::has_tracked_scope::HasTrackedScope;
use crate::semantic_index::ast_ids::AstIds;
use crate::semantic_index::ast_ids::node_key::ExpressionNodeKey;
use crate::semantic_index::builder::SemanticIndexBuilder;
@@ -26,23 +26,22 @@ use crate::semantic_index::scope::{
};
use crate::semantic_index::symbol::ScopedSymbolId;
use crate::semantic_index::use_def::{EnclosingSnapshotKey, ScopedEnclosingSnapshotId, UseDefMap};
use crate::semantic_model::HasTrackedScope;
pub mod ast_ids;
mod builder;
pub mod definition;
pub mod expression;
pub(crate) mod member;
pub(crate) mod narrowing_constraints;
pub mod member;
pub mod narrowing_constraints;
pub mod place;
pub(crate) mod predicate;
pub mod predicate;
mod re_exports;
mod reachability_constraints;
pub(crate) mod scope;
pub(crate) mod symbol;
mod use_def;
pub mod reachability_constraints;
pub mod scope;
pub mod symbol;
pub mod use_def;
pub(crate) use self::use_def::{
pub use self::use_def::{
ApplicableConstraints, BindingWithConstraints, BindingWithConstraintsIterator,
DeclarationWithConstraint, DeclarationsIterator,
};
@@ -51,7 +50,7 @@ pub(crate) use self::use_def::{
///
/// Prefer using [`symbol_table`] when working with symbols from a single scope.
#[salsa::tracked(returns(ref), no_eq, heap_size=ruff_memory_usage::heap_size)]
pub(crate) fn semantic_index(db: &dyn Db, file: File) -> SemanticIndex<'_> {
pub fn semantic_index(db: &dyn Db, file: File) -> SemanticIndex<'_> {
let _span = tracing::trace_span!("semantic_index", ?file).entered();
let module = parsed_module(db, file).load(db);
@@ -65,7 +64,7 @@ pub(crate) fn semantic_index(db: &dyn Db, file: File) -> SemanticIndex<'_> {
/// Salsa can avoid invalidating dependent queries if this scope's place table
/// is unchanged.
#[salsa::tracked(returns(deref), heap_size=ruff_memory_usage::heap_size)]
pub(crate) fn place_table<'db>(db: &'db dyn Db, scope: ScopeId<'db>) -> Arc<PlaceTable> {
pub fn place_table<'db>(db: &'db dyn Db, scope: ScopeId<'db>) -> Arc<PlaceTable> {
let file = scope.file(db);
let _span = tracing::trace_span!("place_table", scope=?scope.as_id(), ?file).entered();
let index = semantic_index(db, file);
@@ -78,7 +77,7 @@ pub(crate) fn place_table<'db>(db: &'db dyn Db, scope: ScopeId<'db>) -> Arc<Plac
/// See [`ModuleLiteralType::available_submodule_attributes`] for discussion
/// of why this analysis is intentionally limited.
#[salsa::tracked(returns(deref), heap_size=ruff_memory_usage::heap_size)]
pub(crate) fn imported_modules<'db>(db: &'db dyn Db, file: File) -> Arc<FxHashSet<ModuleName>> {
pub fn imported_modules<'db>(db: &'db dyn Db, file: File) -> Arc<FxHashSet<ModuleName>> {
semantic_index(db, file).imported_modules.clone()
}
@@ -88,7 +87,7 @@ pub(crate) fn imported_modules<'db>(db: &'db dyn Db, file: File) -> Arc<FxHashSe
/// Salsa can avoid invalidating dependent queries if this scope's use-def map
/// is unchanged.
#[salsa::tracked(returns(deref), heap_size=ruff_memory_usage::heap_size)]
pub(crate) fn use_def_map<'db>(db: &'db dyn Db, scope: ScopeId<'db>) -> Arc<UseDefMap<'db>> {
pub fn use_def_map<'db>(db: &'db dyn Db, scope: ScopeId<'db>) -> Arc<UseDefMap<'db>> {
let file = scope.file(db);
let _span = tracing::trace_span!("use_def_map", scope=?scope.as_id(), ?file).entered();
let index = semantic_index(db, file);
@@ -100,7 +99,7 @@ pub(crate) fn use_def_map<'db>(db: &'db dyn Db, scope: ScopeId<'db>) -> Arc<UseD
///
/// Only call this when doing type inference on the same file as `class_body_scope`, otherwise it
/// introduces a direct dependency on that file's AST.
pub(crate) fn attribute_assignments<'db, 's>(
pub fn attribute_assignments<'db, 's>(
db: &'db dyn Db,
class_body_scope: ScopeId<'db>,
name: &'s str,
@@ -121,7 +120,7 @@ pub(crate) fn attribute_assignments<'db, 's>(
///
/// Only call this when doing type inference on the same file as `class_body_scope`, otherwise it
/// introduces a direct dependency on that file's AST.
pub(crate) fn attribute_declarations<'db, 's>(
pub fn attribute_declarations<'db, 's>(
db: &'db dyn Db,
class_body_scope: ScopeId<'db>,
name: &'s str,
@@ -144,7 +143,7 @@ pub(crate) fn attribute_declarations<'db, 's>(
///
/// Only call this when doing type inference on the same file as `class_body_scope`, otherwise it
/// introduces a direct dependency on that file's AST.
pub(crate) fn attribute_scopes<'db>(
pub fn attribute_scopes<'db>(
db: &'db dyn Db,
class_body_scope: ScopeId<'db>,
) -> impl Iterator<Item = FileScopeId> + 'db {
@@ -198,13 +197,13 @@ pub(crate) fn attribute_scopes<'db>(
/// Returns the module global scope of `file`.
#[salsa::tracked(heap_size=ruff_memory_usage::heap_size)]
pub(crate) fn global_scope(db: &dyn Db, file: File) -> ScopeId<'_> {
pub fn global_scope(db: &dyn Db, file: File) -> ScopeId<'_> {
let _span = tracing::trace_span!("global_scope", ?file).entered();
FileScopeId::global().to_scope_id(db, file)
}
pub(crate) enum EnclosingSnapshotResult<'map, 'db> {
pub enum EnclosingSnapshotResult<'map, 'db> {
FoundConstraint(ScopedNarrowingConstraint),
FoundBindings(BindingWithConstraintsIterator<'map, 'db>),
NotFound,
@@ -213,7 +212,7 @@ pub(crate) enum EnclosingSnapshotResult<'map, 'db> {
/// The place tables and use-def maps for all scopes in a file.
#[derive(Debug, Update, get_size2::GetSize)]
pub(crate) struct SemanticIndex<'db> {
pub struct SemanticIndex<'db> {
/// List of all place tables in this file, indexed by scope.
place_tables: IndexVec<FileScopeId, Arc<PlaceTable>>,
@@ -266,7 +265,7 @@ impl<'db> SemanticIndex<'db> {
/// Use the Salsa cached [`place_table()`] query if you only need the
/// place table for a single scope.
#[track_caller]
pub(super) fn place_table(&self, scope_id: FileScopeId) -> &PlaceTable {
pub fn place_table(&self, scope_id: FileScopeId) -> &PlaceTable {
&self.place_tables[scope_id]
}
@@ -275,7 +274,7 @@ impl<'db> SemanticIndex<'db> {
/// Use the Salsa cached [`use_def_map()`] query if you only need the
/// use-def map for a single scope.
#[track_caller]
pub(super) fn use_def_map(&self, scope_id: FileScopeId) -> &UseDefMap<'db> {
pub fn use_def_map(&self, scope_id: FileScopeId) -> &UseDefMap<'db> {
&self.use_def_maps[scope_id]
}
@@ -286,7 +285,7 @@ impl<'db> SemanticIndex<'db> {
/// Returns the ID of the `expression`'s enclosing scope.
#[track_caller]
pub(crate) fn expression_scope_id<E>(&self, expression: &E) -> FileScopeId
pub fn expression_scope_id<E>(&self, expression: &E) -> FileScopeId
where
E: HasTrackedScope,
{
@@ -295,7 +294,7 @@ impl<'db> SemanticIndex<'db> {
}
/// Returns the ID of the `expression`'s enclosing scope.
pub(crate) fn try_expression_scope_id<E>(&self, expression: &E) -> Option<FileScopeId>
pub fn try_expression_scope_id<E>(&self, expression: &E) -> Option<FileScopeId>
where
E: HasTrackedScope,
{
@@ -305,52 +304,44 @@ impl<'db> SemanticIndex<'db> {
/// Returns the [`Scope`] of the `expression`'s enclosing scope.
#[allow(unused)]
#[track_caller]
pub(crate) fn expression_scope(&self, expression: &impl HasTrackedScope) -> &Scope {
pub fn expression_scope(&self, expression: &impl HasTrackedScope) -> &Scope {
&self.scopes[self.expression_scope_id(expression)]
}
/// Returns the [`Scope`] with the given id.
#[track_caller]
pub(crate) fn scope(&self, id: FileScopeId) -> &Scope {
pub fn scope(&self, id: FileScopeId) -> &Scope {
&self.scopes[id]
}
pub(crate) fn scope_ids(&self) -> impl Iterator<Item = ScopeId<'db>> + '_ {
pub fn scope_ids(&self) -> impl Iterator<Item = ScopeId<'db>> + '_ {
self.scope_ids_by_scope.iter().copied()
}
pub(crate) fn symbol_is_global_in_scope(
&self,
symbol: ScopedSymbolId,
scope: FileScopeId,
) -> bool {
pub fn symbol_is_global_in_scope(&self, symbol: ScopedSymbolId, scope: FileScopeId) -> bool {
self.place_table(scope).symbol(symbol).is_global()
}
pub(crate) fn symbol_is_nonlocal_in_scope(
&self,
symbol: ScopedSymbolId,
scope: FileScopeId,
) -> bool {
pub fn symbol_is_nonlocal_in_scope(&self, symbol: ScopedSymbolId, scope: FileScopeId) -> bool {
self.place_table(scope).symbol(symbol).is_nonlocal()
}
/// Returns the id of the parent scope.
pub(crate) fn parent_scope_id(&self, scope_id: FileScopeId) -> Option<FileScopeId> {
pub fn parent_scope_id(&self, scope_id: FileScopeId) -> Option<FileScopeId> {
let scope = self.scope(scope_id);
scope.parent()
}
/// Returns the parent scope of `scope_id`.
#[expect(unused)]
#[track_caller]
#[expect(unused)]
pub(crate) fn parent_scope(&self, scope_id: FileScopeId) -> Option<&Scope> {
Some(&self.scopes[self.parent_scope_id(scope_id)?])
}
/// Return the [`Definition`] of the class enclosing this method, given the
/// method's body scope, or `None` if it is not a method.
pub(crate) fn class_definition_of_method(
pub fn class_definition_of_method(
&self,
function_body_scope: FileScopeId,
) -> Option<Definition<'db>> {
@@ -381,55 +372,20 @@ impl<'db> SemanticIndex<'db> {
.map(|node_ref| self.expect_single_definition(node_ref))
}
fn is_scope_reachable(&self, db: &'db dyn Db, scope_id: FileScopeId) -> bool {
self.parent_scope_id(scope_id)
.is_none_or(|parent_scope_id| {
if !self.is_scope_reachable(db, parent_scope_id) {
return false;
}
let parent_use_def = self.use_def_map(parent_scope_id);
let reachability = self.scope(scope_id).reachability();
parent_use_def.is_reachable(db, reachability)
})
}
/// Returns true if a given AST node is reachable from the start of the scope. For example,
/// in the following code, expression `2` is reachable, but expressions `1` and `3` are not:
/// ```py
/// def f():
/// x = 1
/// if False:
/// x # 1
/// x # 2
/// return
/// x # 3
/// ```
pub(crate) fn is_node_reachable(
&self,
db: &'db dyn crate::Db,
scope_id: FileScopeId,
node_key: NodeKey,
) -> bool {
self.is_scope_reachable(db, scope_id)
&& self.use_def_map(scope_id).is_node_reachable(db, node_key)
}
/// Returns an iterator over the descendent scopes of `scope`.
#[allow(unused)]
pub(crate) fn descendent_scopes(&self, scope: FileScopeId) -> DescendantsIter<'_> {
pub fn descendent_scopes(&self, scope: FileScopeId) -> DescendantsIter<'_> {
DescendantsIter::new(&self.scopes, scope)
}
/// Returns an iterator over the direct child scopes of `scope`.
#[allow(unused)]
pub(crate) fn child_scopes(&self, scope: FileScopeId) -> ChildrenIter<'_> {
pub fn child_scopes(&self, scope: FileScopeId) -> ChildrenIter<'_> {
ChildrenIter::new(&self.scopes, scope)
}
/// Returns an iterator over all ancestors of `scope`, starting with `scope` itself.
pub(crate) fn ancestor_scopes(&self, scope: FileScopeId) -> AncestorsIter<'_> {
pub fn ancestor_scopes(&self, scope: FileScopeId) -> AncestorsIter<'_> {
AncestorsIter::new(&self.scopes, scope)
}
@@ -447,7 +403,7 @@ impl<'db> SemanticIndex<'db> {
/// print(x) # Refers to global x=1, not class x=2
/// ```
/// The `method` function can see the global scope but not the class scope.
pub(crate) fn visible_ancestor_scopes(&self, scope: FileScopeId) -> VisibleAncestorsIter<'_> {
pub fn visible_ancestor_scopes(&self, scope: FileScopeId) -> VisibleAncestorsIter<'_> {
VisibleAncestorsIter::new(&self.scopes, scope)
}
@@ -456,10 +412,7 @@ impl<'db> SemanticIndex<'db> {
/// There will only ever be >1 `Definition` associated with a `definition_key`
/// if the definition is created by a wildcard (`*`) import.
#[track_caller]
pub(crate) fn definitions(
&self,
definition_key: impl Into<DefinitionNodeKey>,
) -> &Definitions<'db> {
pub fn definitions(&self, definition_key: impl Into<DefinitionNodeKey>) -> &Definitions<'db> {
&self.definitions_by_node[&definition_key.into()]
}
@@ -470,7 +423,7 @@ impl<'db> SemanticIndex<'db> {
/// If the number of definitions associated with the key is not exactly 1 and
/// the `debug_assertions` feature is enabled, this method will panic.
#[track_caller]
pub(crate) fn expect_single_definition(
pub fn expect_single_definition(
&self,
definition_key: impl Into<DefinitionNodeKey> + std::fmt::Debug + Copy,
) -> Definition<'db> {
@@ -489,14 +442,11 @@ impl<'db> SemanticIndex<'db> {
/// standalone-inferable expressions, which we call `add_standalone_expression` for in
/// [`SemanticIndexBuilder`].
#[track_caller]
pub(crate) fn expression(
&self,
expression_key: impl Into<ExpressionNodeKey>,
) -> Expression<'db> {
pub fn expression(&self, expression_key: impl Into<ExpressionNodeKey>) -> Expression<'db> {
self.expressions_by_node[&expression_key.into()]
}
pub(crate) fn try_expression(
pub fn try_expression(
&self,
expression_key: impl Into<ExpressionNodeKey>,
) -> Option<Expression<'db>> {
@@ -505,10 +455,7 @@ impl<'db> SemanticIndex<'db> {
.copied()
}
pub(crate) fn is_standalone_expression(
&self,
expression_key: impl Into<ExpressionNodeKey>,
) -> bool {
pub fn is_standalone_expression(&self, expression_key: impl Into<ExpressionNodeKey>) -> bool {
self.expressions_by_node
.contains_key(&expression_key.into())
}
@@ -517,18 +464,18 @@ impl<'db> SemanticIndex<'db> {
/// This is different from [`definition::Definition::scope`] which
/// returns the scope in which that definition is defined in.
#[track_caller]
pub(crate) fn node_scope(&self, node: NodeWithScopeRef) -> FileScopeId {
pub fn node_scope(&self, node: NodeWithScopeRef) -> FileScopeId {
self.scopes_by_node[&node.node_key()]
}
/// Returns the id of the scope that `node` creates, if it exists.
pub(crate) fn try_node_scope(&self, node: NodeWithScopeRef) -> Option<FileScopeId> {
pub fn try_node_scope(&self, node: NodeWithScopeRef) -> Option<FileScopeId> {
self.scopes_by_node.get(&node.node_key()).copied()
}
/// Checks if there is an import of `__future__.annotations` in the global scope, which affects
/// the logic for type inference.
pub(super) fn has_future_annotations(&self) -> bool {
pub fn has_future_annotations(&self) -> bool {
self.has_future_annotations
}
@@ -538,7 +485,7 @@ impl<'db> SemanticIndex<'db> {
/// * an iterator of bindings for a particular nested scope reference if the bindings exist.
/// * a narrowing constraint if there are no bindings, but there is a narrowing constraint for an enclosing scope place.
/// * `NotFound` if the narrowing constraint / bindings do not exist in the nested scope.
pub(crate) fn enclosing_snapshot(
pub fn enclosing_snapshot(
&self,
enclosing_scope: FileScopeId,
expr: PlaceExprRef,
@@ -582,12 +529,12 @@ impl<'db> SemanticIndex<'db> {
self.use_def_maps[enclosing_scope].enclosing_snapshot(*id, key.nested_laziness)
}
pub(crate) fn semantic_syntax_errors(&self) -> &[SemanticSyntaxError] {
pub fn semantic_syntax_errors(&self) -> &[SemanticSyntaxError] {
&self.semantic_syntax_errors
}
}
pub(crate) struct AncestorsIter<'a> {
pub struct AncestorsIter<'a> {
scopes: &'a IndexSlice<FileScopeId, Scope>,
next_id: Option<FileScopeId>,
}
@@ -615,7 +562,7 @@ impl<'a> Iterator for AncestorsIter<'a> {
impl FusedIterator for AncestorsIter<'_> {}
pub(crate) struct VisibleAncestorsIter<'a> {
pub struct VisibleAncestorsIter<'a> {
inner: AncestorsIter<'a>,
starting_scope_kind: ScopeKind,
yielded_count: usize,
@@ -662,7 +609,7 @@ impl<'a> Iterator for VisibleAncestorsIter<'a> {
impl FusedIterator for VisibleAncestorsIter<'_> {}
pub(crate) struct DescendantsIter<'a> {
pub struct DescendantsIter<'a> {
next_id: FileScopeId,
descendants: std::slice::Iter<'a, Scope>,
}
@@ -699,7 +646,7 @@ impl FusedIterator for DescendantsIter<'_> {}
impl ExactSizeIterator for DescendantsIter<'_> {}
pub(crate) struct ChildrenIter<'a> {
pub struct ChildrenIter<'a> {
parent: FileScopeId,
descendants: DescendantsIter<'a>,
}

View File

@@ -110,13 +110,13 @@ impl AstIdsBuilder {
}
/// Node key that can only be constructed for expressions.
pub(crate) mod node_key {
pub mod node_key {
use ruff_python_ast as ast;
use crate::node_key::NodeKey;
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, salsa::Update, get_size2::GetSize)]
pub(crate) struct ExpressionNodeKey(NodeKey);
pub struct ExpressionNodeKey(NodeKey);
impl From<ast::ExprRef<'_>> for ExpressionNodeKey {
fn from(value: ast::ExprRef<'_>) -> Self {

View File

@@ -18,6 +18,7 @@ use ruff_text_size::TextRange;
use ty_module_resolver::{ModuleName, resolve_module};
use crate::ast_node_ref::AstNodeRef;
use crate::has_tracked_scope::HasTrackedScope;
use crate::node_key::NodeKey;
use crate::semantic_index::ast_ids::AstIdsBuilder;
use crate::semantic_index::ast_ids::node_key::ExpressionNodeKey;
@@ -47,7 +48,6 @@ use crate::semantic_index::use_def::{
EnclosingSnapshotKey, FlowSnapshot, ScopedEnclosingSnapshotId, UseDefMapBuilder,
};
use crate::semantic_index::{ExpressionsScopeMap, SemanticIndex, VisibleAncestorsIter};
use crate::semantic_model::HasTrackedScope;
use crate::unpack::{EvaluationMode, Unpack, UnpackKind, UnpackPosition, UnpackValue};
use crate::{Db, Program};

View File

@@ -35,10 +35,10 @@ pub struct Definition<'db> {
pub file: File,
/// The scope in which the definition occurs.
pub(crate) file_scope: FileScopeId,
pub file_scope: FileScopeId,
/// The place ID of the definition.
pub(crate) place: ScopedPlaceId,
pub place: ScopedPlaceId,
/// WARNING: Only access this field when doing type inference for the same
/// file as where `Definition` is defined to avoid cross-file query dependencies.
@@ -48,14 +48,14 @@ pub struct Definition<'db> {
pub kind: DefinitionKind<'db>,
/// This is a dedicated field to avoid accessing `kind` to compute this value.
pub(crate) is_reexported: bool,
pub is_reexported: bool,
}
// The Salsa heap is tracked separately.
impl get_size2::GetSize for Definition<'_> {}
impl<'db> Definition<'db> {
pub(crate) fn scope(self, db: &'db dyn Db) -> ScopeId<'db> {
pub fn scope(self, db: &'db dyn Db) -> ScopeId<'db> {
self.file_scope(db).to_scope_id(db, self.file(db))
}
@@ -137,7 +137,7 @@ impl<'db> Definition<'db> {
}
/// Get the module-level docstring for the given file.
pub(crate) fn module_docstring(db: &dyn Db, file: File) -> Option<String> {
pub fn module_docstring(db: &dyn Db, file: File) -> Option<String> {
let module = parsed_module(db, file).load(db);
docstring_from_body(module.suite())
.map(|docstring_expr| docstring_expr.value.to_str().to_owned())
@@ -204,13 +204,13 @@ pub struct Definitions<'db> {
}
impl<'db> Definitions<'db> {
pub(crate) fn single(definition: Definition<'db>) -> Self {
pub fn single(definition: Definition<'db>) -> Self {
Self {
definitions: smallvec::smallvec_inline![definition],
}
}
pub(crate) fn push(&mut self, definition: Definition<'db>) {
pub fn push(&mut self, definition: Definition<'db>) {
self.definitions.push(definition);
}
}
@@ -233,7 +233,7 @@ impl<'a, 'db> IntoIterator for &'a Definitions<'db> {
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, salsa::Update, get_size2::GetSize)]
pub(crate) enum DefinitionState<'db> {
pub enum DefinitionState<'db> {
Defined(Definition<'db>),
/// Represents the implicit "unbound"/"undeclared" definition of every place.
Undefined,
@@ -243,17 +243,17 @@ pub(crate) enum DefinitionState<'db> {
}
impl<'db> DefinitionState<'db> {
pub(crate) fn is_defined_and(self, f: impl Fn(Definition<'db>) -> bool) -> bool {
pub fn is_defined_and(self, f: impl Fn(Definition<'db>) -> bool) -> bool {
matches!(self, DefinitionState::Defined(def) if f(def))
}
pub(crate) fn is_undefined_or(self, f: impl Fn(Definition<'db>) -> bool) -> bool {
pub fn is_undefined_or(self, f: impl Fn(Definition<'db>) -> bool) -> bool {
matches!(self, DefinitionState::Undefined)
|| matches!(self, DefinitionState::Defined(def) if f(def))
}
#[allow(unused)]
pub(crate) fn definition(self) -> Option<Definition<'db>> {
pub fn definition(self) -> Option<Definition<'db>> {
match self {
DefinitionState::Defined(def) => Some(def),
DefinitionState::Deleted | DefinitionState::Undefined => None,
@@ -262,7 +262,7 @@ impl<'db> DefinitionState<'db> {
}
#[derive(Copy, Clone, Debug)]
pub(crate) enum DefinitionNodeRef<'ast, 'db> {
pub enum DefinitionNodeRef<'ast, 'db> {
Import(ImportDefinitionNodeRef<'ast>),
ImportFrom(ImportFromDefinitionNodeRef<'ast>),
ImportFromSubmodule(ImportFromSubmoduleDefinitionNodeRef<'ast>),
@@ -402,85 +402,85 @@ impl<'ast> From<StarImportDefinitionNodeRef<'ast>> for DefinitionNodeRef<'ast, '
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct ImportDefinitionNodeRef<'ast> {
pub(crate) node: &'ast ast::StmtImport,
pub(crate) alias_index: usize,
pub(crate) is_reexported: bool,
pub struct ImportDefinitionNodeRef<'ast> {
pub node: &'ast ast::StmtImport,
pub alias_index: usize,
pub is_reexported: bool,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct StarImportDefinitionNodeRef<'ast> {
pub(crate) node: &'ast ast::StmtImportFrom,
pub(crate) symbol_id: ScopedSymbolId,
pub struct StarImportDefinitionNodeRef<'ast> {
pub node: &'ast ast::StmtImportFrom,
pub symbol_id: ScopedSymbolId,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct ImportFromDefinitionNodeRef<'ast> {
pub(crate) node: &'ast ast::StmtImportFrom,
pub(crate) alias_index: usize,
pub(crate) is_reexported: bool,
pub struct ImportFromDefinitionNodeRef<'ast> {
pub node: &'ast ast::StmtImportFrom,
pub alias_index: usize,
pub is_reexported: bool,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct ImportFromSubmoduleDefinitionNodeRef<'ast> {
pub(crate) node: &'ast ast::StmtImportFrom,
pub struct ImportFromSubmoduleDefinitionNodeRef<'ast> {
pub node: &'ast ast::StmtImportFrom,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct AssignmentDefinitionNodeRef<'ast, 'db> {
pub(crate) unpack: Option<(UnpackPosition, Unpack<'db>)>,
pub(crate) value: &'ast ast::Expr,
pub(crate) target: &'ast ast::Expr,
pub struct AssignmentDefinitionNodeRef<'ast, 'db> {
pub unpack: Option<(UnpackPosition, Unpack<'db>)>,
pub value: &'ast ast::Expr,
pub target: &'ast ast::Expr,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct AnnotatedAssignmentDefinitionNodeRef<'ast> {
pub(crate) node: &'ast ast::StmtAnnAssign,
pub(crate) annotation: &'ast ast::Expr,
pub(crate) value: Option<&'ast ast::Expr>,
pub(crate) target: &'ast ast::Expr,
pub struct AnnotatedAssignmentDefinitionNodeRef<'ast> {
pub node: &'ast ast::StmtAnnAssign,
pub annotation: &'ast ast::Expr,
pub value: Option<&'ast ast::Expr>,
pub target: &'ast ast::Expr,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct WithItemDefinitionNodeRef<'ast, 'db> {
pub(crate) unpack: Option<(UnpackPosition, Unpack<'db>)>,
pub(crate) context_expr: &'ast ast::Expr,
pub(crate) target: &'ast ast::Expr,
pub(crate) is_async: bool,
pub struct WithItemDefinitionNodeRef<'ast, 'db> {
pub unpack: Option<(UnpackPosition, Unpack<'db>)>,
pub context_expr: &'ast ast::Expr,
pub target: &'ast ast::Expr,
pub is_async: bool,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct ForStmtDefinitionNodeRef<'ast, 'db> {
pub(crate) unpack: Option<(UnpackPosition, Unpack<'db>)>,
pub(crate) iterable: &'ast ast::Expr,
pub(crate) target: &'ast ast::Expr,
pub(crate) is_async: bool,
pub struct ForStmtDefinitionNodeRef<'ast, 'db> {
pub unpack: Option<(UnpackPosition, Unpack<'db>)>,
pub iterable: &'ast ast::Expr,
pub target: &'ast ast::Expr,
pub is_async: bool,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct ExceptHandlerDefinitionNodeRef<'ast> {
pub(crate) handler: &'ast ast::ExceptHandlerExceptHandler,
pub(crate) is_star: bool,
pub struct ExceptHandlerDefinitionNodeRef<'ast> {
pub handler: &'ast ast::ExceptHandlerExceptHandler,
pub is_star: bool,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct ComprehensionDefinitionNodeRef<'ast, 'db> {
pub(crate) unpack: Option<(UnpackPosition, Unpack<'db>)>,
pub(crate) iterable: &'ast ast::Expr,
pub(crate) target: &'ast ast::Expr,
pub(crate) first: bool,
pub(crate) is_async: bool,
pub struct ComprehensionDefinitionNodeRef<'ast, 'db> {
pub unpack: Option<(UnpackPosition, Unpack<'db>)>,
pub iterable: &'ast ast::Expr,
pub target: &'ast ast::Expr,
pub first: bool,
pub is_async: bool,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct MatchPatternDefinitionNodeRef<'ast> {
pub struct MatchPatternDefinitionNodeRef<'ast> {
/// The outermost pattern node in which the identifier being defined occurs.
pub(crate) pattern: &'ast ast::Pattern,
pub pattern: &'ast ast::Pattern,
/// The identifier being defined.
pub(crate) identifier: &'ast ast::Identifier,
pub identifier: &'ast ast::Identifier,
/// The index of the identifier in the pattern when visiting the `pattern` node in evaluation
/// order.
pub(crate) index: u32,
pub index: u32,
}
impl<'db> DefinitionNodeRef<'_, 'db> {
@@ -688,7 +688,7 @@ impl<'db> DefinitionNodeRef<'_, 'db> {
}
#[derive(Clone, Copy, Debug)]
pub(crate) enum DefinitionCategory {
pub enum DefinitionCategory {
/// A Definition which binds a value to a name (e.g. `x = 1`).
Binding,
/// A Definition which declares the upper-bound of acceptable types for this name (`x: int`).
@@ -704,7 +704,7 @@ impl DefinitionCategory {
/// type not assignable to the declared type.
///
/// Annotations establish a declared type. So do function and class definitions, and imports.
pub(crate) fn is_declaration(self) -> bool {
pub fn is_declaration(self) -> bool {
matches!(
self,
DefinitionCategory::Declaration | DefinitionCategory::DeclarationAndBinding
@@ -714,7 +714,7 @@ impl DefinitionCategory {
/// True if this definition assigns a value to the place.
///
/// False only for annotated assignments without a RHS.
pub(crate) fn is_binding(self) -> bool {
pub fn is_binding(self) -> bool {
matches!(
self,
DefinitionCategory::Binding | DefinitionCategory::DeclarationAndBinding
@@ -756,7 +756,7 @@ pub enum DefinitionKind<'db> {
}
impl DefinitionKind<'_> {
pub(crate) fn is_reexported(&self) -> bool {
pub fn is_reexported(&self) -> bool {
match self {
DefinitionKind::Import(import) => import.is_reexported(),
DefinitionKind::ImportFrom(import) => import.is_reexported(),
@@ -765,21 +765,21 @@ impl DefinitionKind<'_> {
}
}
pub(crate) const fn as_star_import(&self) -> Option<&StarImportDefinitionKind> {
pub const fn as_star_import(&self) -> Option<&StarImportDefinitionKind> {
match self {
DefinitionKind::StarImport(import) => Some(import),
_ => None,
}
}
pub(crate) const fn as_class(&self) -> Option<&AstNodeRef<ast::StmtClassDef>> {
pub const fn as_class(&self) -> Option<&AstNodeRef<ast::StmtClassDef>> {
match self {
DefinitionKind::Class(class) => Some(class),
_ => None,
}
}
pub(crate) fn is_import(&self) -> bool {
pub fn is_import(&self) -> bool {
matches!(
self,
DefinitionKind::Import(_)
@@ -789,11 +789,11 @@ impl DefinitionKind<'_> {
)
}
pub(crate) const fn is_unannotated_assignment(&self) -> bool {
pub const fn is_unannotated_assignment(&self) -> bool {
matches!(self, DefinitionKind::Assignment(_))
}
pub(crate) const fn is_function_def(&self) -> bool {
pub const fn is_function_def(&self) -> bool {
matches!(self, DefinitionKind::Function(_))
}
@@ -801,7 +801,7 @@ impl DefinitionKind<'_> {
///
/// A definition target would mainly be the node representing the place being defined i.e.,
/// [`ast::ExprName`], [`ast::Identifier`], [`ast::ExprAttribute`] or [`ast::ExprSubscript`] but could also be other nodes.
pub(crate) fn target_range(&self, module: &ParsedModuleRef) -> TextRange {
pub fn target_range(&self, module: &ParsedModuleRef) -> TextRange {
match self {
DefinitionKind::Import(import) => import.alias(module).range(),
DefinitionKind::ImportFrom(import) => import.alias(module).range(),
@@ -839,7 +839,7 @@ impl DefinitionKind<'_> {
}
/// Returns the [`TextRange`] of the entire definition.
pub(crate) fn full_range(&self, module: &ParsedModuleRef) -> TextRange {
pub fn full_range(&self, module: &ParsedModuleRef) -> TextRange {
match self {
DefinitionKind::Import(import) => import.alias(module).range(),
DefinitionKind::ImportFrom(import) => import.alias(module).range(),
@@ -883,7 +883,7 @@ impl DefinitionKind<'_> {
}
}
pub(crate) fn category(&self, in_stub: bool, module: &ParsedModuleRef) -> DefinitionCategory {
pub fn category(&self, in_stub: bool, module: &ParsedModuleRef) -> DefinitionCategory {
match self {
// functions, classes, and imports always bind, and we consider them declarations
DefinitionKind::Function(_)
@@ -941,7 +941,7 @@ impl DefinitionKind<'_> {
}
#[derive(Copy, Clone, Debug, PartialEq, Hash, get_size2::GetSize)]
pub(crate) enum TargetKind<'db> {
pub enum TargetKind<'db> {
Sequence(UnpackPosition, Unpack<'db>),
/// Name, attribute, or subscript.
Single,
@@ -967,7 +967,7 @@ impl StarImportDefinitionKind {
self.node.node(module)
}
pub(crate) fn alias<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Alias {
pub fn alias<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Alias {
// INVARIANT: for an invalid-syntax statement such as `from foo import *, bar, *`,
// we only create a `StarImportDefinitionKind` for the *first* `*` alias in the names list.
self.node
@@ -981,7 +981,7 @@ impl StarImportDefinitionKind {
)
}
pub(crate) fn symbol_id(&self) -> ScopedSymbolId {
pub fn symbol_id(&self) -> ScopedSymbolId {
self.symbol_id
}
}
@@ -994,11 +994,11 @@ pub struct MatchPatternDefinitionKind {
}
impl MatchPatternDefinitionKind {
pub(crate) fn pattern<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Pattern {
pub fn pattern<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Pattern {
self.pattern.node(module)
}
pub(crate) fn index(&self) -> u32 {
pub fn index(&self) -> u32 {
self.index
}
}
@@ -1018,23 +1018,23 @@ pub struct ComprehensionDefinitionKind<'db> {
}
impl<'db> ComprehensionDefinitionKind<'db> {
pub(crate) fn iterable<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
pub fn iterable<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
self.iterable.node(module)
}
pub(crate) fn target_kind(&self) -> TargetKind<'db> {
pub fn target_kind(&self) -> TargetKind<'db> {
self.target_kind
}
pub(crate) fn target<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
pub fn target<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
self.target.node(module)
}
pub(crate) fn is_first(&self) -> bool {
pub fn is_first(&self) -> bool {
self.first
}
pub(crate) fn is_async(&self) -> bool {
pub fn is_async(&self) -> bool {
self.is_async
}
}
@@ -1051,11 +1051,11 @@ impl ImportDefinitionKind {
self.node.node(module)
}
pub(crate) fn alias<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Alias {
pub fn alias<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Alias {
&self.node.node(module).names[self.alias_index]
}
pub(crate) fn is_reexported(&self) -> bool {
pub fn is_reexported(&self) -> bool {
self.is_reexported
}
}
@@ -1072,11 +1072,11 @@ impl ImportFromDefinitionKind {
self.node.node(module)
}
pub(crate) fn alias<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Alias {
pub fn alias<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Alias {
&self.node.node(module).names[self.alias_index]
}
pub(crate) fn is_reexported(&self) -> bool {
pub fn is_reexported(&self) -> bool {
self.is_reexported
}
}
@@ -1099,7 +1099,7 @@ pub struct AssignmentDefinitionKind<'db> {
}
impl<'db> AssignmentDefinitionKind<'db> {
pub(crate) fn target_kind(&self) -> TargetKind<'db> {
pub fn target_kind(&self) -> TargetKind<'db> {
self.target_kind
}
@@ -1107,7 +1107,7 @@ impl<'db> AssignmentDefinitionKind<'db> {
self.value.node(module)
}
pub(crate) fn target<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
pub fn target<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
self.target.node(module)
}
}
@@ -1120,15 +1120,15 @@ pub struct AnnotatedAssignmentDefinitionKind {
}
impl AnnotatedAssignmentDefinitionKind {
pub(crate) fn value<'ast>(&self, module: &'ast ParsedModuleRef) -> Option<&'ast ast::Expr> {
pub fn value<'ast>(&self, module: &'ast ParsedModuleRef) -> Option<&'ast ast::Expr> {
self.value.as_ref().map(|value| value.node(module))
}
pub(crate) fn annotation<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
pub fn annotation<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
self.annotation.node(module)
}
pub(crate) fn target<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
pub fn target<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
self.target.node(module)
}
}
@@ -1142,19 +1142,19 @@ pub struct WithItemDefinitionKind<'db> {
}
impl<'db> WithItemDefinitionKind<'db> {
pub(crate) fn context_expr<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
pub fn context_expr<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
self.context_expr.node(module)
}
pub(crate) fn target_kind(&self) -> TargetKind<'db> {
pub fn target_kind(&self) -> TargetKind<'db> {
self.target_kind
}
pub(crate) fn target<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
pub fn target<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
self.target.node(module)
}
pub(crate) const fn is_async(&self) -> bool {
pub const fn is_async(&self) -> bool {
self.is_async
}
}
@@ -1168,19 +1168,19 @@ pub struct ForStmtDefinitionKind<'db> {
}
impl<'db> ForStmtDefinitionKind<'db> {
pub(crate) fn iterable<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
pub fn iterable<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
self.iterable.node(module)
}
pub(crate) fn target_kind(&self) -> TargetKind<'db> {
pub fn target_kind(&self) -> TargetKind<'db> {
self.target_kind
}
pub(crate) fn target<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
pub fn target<'ast>(&self, module: &'ast ParsedModuleRef) -> &'ast ast::Expr {
self.target.node(module)
}
pub(crate) const fn is_async(&self) -> bool {
pub const fn is_async(&self) -> bool {
self.is_async
}
}
@@ -1192,27 +1192,27 @@ pub struct ExceptHandlerDefinitionKind {
}
impl ExceptHandlerDefinitionKind {
pub(crate) fn node<'ast>(
pub fn node<'ast>(
&self,
module: &'ast ParsedModuleRef,
) -> &'ast ast::ExceptHandlerExceptHandler {
self.handler.node(module)
}
pub(crate) fn handled_exceptions<'ast>(
pub fn handled_exceptions<'ast>(
&self,
module: &'ast ParsedModuleRef,
) -> Option<&'ast ast::Expr> {
self.node(module).type_.as_deref()
}
pub(crate) fn is_star(&self) -> bool {
pub fn is_star(&self) -> bool {
self.is_star
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, salsa::Update, get_size2::GetSize)]
pub(crate) struct DefinitionNodeKey(NodeKey);
pub struct DefinitionNodeKey(NodeKey);
impl From<&ast::Alias> for DefinitionNodeKey {
fn from(node: &ast::Alias) -> Self {

View File

@@ -11,7 +11,7 @@ use salsa;
/// `<annotation>` is inferred as a type expression, while `<value>` is inferred
/// as a normal expression.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize)]
pub(crate) enum ExpressionKind {
pub enum ExpressionKind {
Normal,
TypeExpression,
}
@@ -32,18 +32,18 @@ pub(crate) enum ExpressionKind {
/// * a field of a type that is a return type of a cross-module query
/// * an argument of a cross-module query
#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)]
pub(crate) struct Expression<'db> {
pub struct Expression<'db> {
/// The file in which the expression occurs.
pub(crate) file: File,
pub file: File,
/// The scope in which the expression occurs.
pub(crate) file_scope: FileScopeId,
pub file_scope: FileScopeId,
/// The expression node.
#[no_eq]
#[tracked]
#[returns(ref)]
pub(crate) _node_ref: AstNodeRef<ast::Expr>,
pub _node_ref: AstNodeRef<ast::Expr>,
/// An assignment statement, if this expression is immediately used as the rhs of that
/// assignment.
@@ -54,25 +54,21 @@ pub(crate) struct Expression<'db> {
/// to the target, and so have `None` for this field.)
#[no_eq]
#[tracked]
pub(crate) assigned_to: Option<AstNodeRef<ast::StmtAssign>>,
pub assigned_to: Option<AstNodeRef<ast::StmtAssign>>,
/// Should this expression be inferred as a normal expression or a type expression?
pub(crate) kind: ExpressionKind,
pub kind: ExpressionKind,
}
// The Salsa heap is tracked separately.
impl get_size2::GetSize for Expression<'_> {}
impl<'db> Expression<'db> {
pub(crate) fn node_ref<'ast>(
self,
db: &'db dyn Db,
parsed: &'ast ParsedModuleRef,
) -> &'ast ast::Expr {
pub fn node_ref<'ast>(self, db: &'db dyn Db, parsed: &'ast ParsedModuleRef) -> &'ast ast::Expr {
self._node_ref(db).node(parsed)
}
pub(crate) fn scope(self, db: &'db dyn Db) -> ScopeId<'db> {
pub fn scope(self, db: &'db dyn Db) -> ScopeId<'db> {
self.file_scope(db).to_scope_id(db, self.file(db))
}
}

View File

@@ -10,7 +10,7 @@ use std::ops::{Deref, DerefMut};
/// A member access, e.g. `x.y` or `x[1]` or `x["foo"]`.
#[derive(Clone, Debug, PartialEq, Eq, get_size2::GetSize)]
pub(crate) struct Member {
pub struct Member {
expression: MemberExpr,
flags: MemberFlags,
}
@@ -26,7 +26,7 @@ impl Member {
/// Returns the left most part of the member expression, e.g. `x` in `x.y.z`.
///
/// This is the symbol on which the member access is performed.
pub(crate) fn symbol_name(&self) -> &str {
pub fn symbol_name(&self) -> &str {
self.expression.symbol_name()
}
@@ -35,12 +35,12 @@ impl Member {
}
/// Is the place given a value in its containing scope?
pub(crate) const fn is_bound(&self) -> bool {
pub const fn is_bound(&self) -> bool {
self.flags.contains(MemberFlags::IS_BOUND)
}
/// Is the place declared in its containing scope?
pub(crate) fn is_declared(&self) -> bool {
pub fn is_declared(&self) -> bool {
self.flags.contains(MemberFlags::IS_DECLARED)
}
@@ -57,7 +57,7 @@ impl Member {
}
/// Is the place an instance attribute?
pub(crate) fn is_instance_attribute(&self) -> bool {
pub fn is_instance_attribute(&self) -> bool {
let is_instance_attribute = self.flags.contains(MemberFlags::IS_INSTANCE_ATTRIBUTE);
if is_instance_attribute {
debug_assert!(self.is_instance_attribute_candidate());
@@ -109,7 +109,7 @@ impl Member {
}
/// Return `Some(<ATTRIBUTE>)` if the place expression is an instance attribute.
pub(crate) fn as_instance_attribute(&self) -> Option<&str> {
pub fn as_instance_attribute(&self) -> Option<&str> {
if self.is_instance_attribute() {
debug_assert!(self.as_instance_attribute_candidate().is_some());
self.as_instance_attribute_candidate()
@@ -367,7 +367,7 @@ impl MemberTable {
/// ## Panics
/// If the ID is not valid for this table.
#[track_caller]
pub(crate) fn member(&self, id: ScopedMemberId) -> &Member {
pub(super) fn member(&self, id: ScopedMemberId) -> &Member {
&self.members[id]
}
@@ -381,7 +381,7 @@ impl MemberTable {
}
/// Returns an iterator over all members in the table.
pub(crate) fn iter(&self) -> std::slice::Iter<'_, Member> {
pub(super) fn iter(&self) -> std::slice::Iter<'_, Member> {
self.members.iter()
}
@@ -390,7 +390,7 @@ impl MemberTable {
}
/// Returns the ID of the member with the given expression, if it exists.
pub(crate) fn member_id<'a>(
pub(super) fn member_id<'a>(
&self,
member: impl Into<MemberExprRef<'a>>,
) -> Option<ScopedMemberId> {
@@ -401,7 +401,7 @@ impl MemberTable {
.copied()
}
pub(crate) fn place_id_by_instance_attribute_name(&self, name: &str) -> Option<ScopedMemberId> {
pub(super) fn place_id_by_instance_attribute_name(&self, name: &str) -> Option<ScopedMemberId> {
for (id, member) in self.members.iter_enumerated() {
if member.is_instance_attribute_named(name) {
return Some(id);

View File

@@ -38,10 +38,10 @@ use crate::semantic_index::scope::FileScopeId;
/// A constraint is a list of [`Predicate`]s that each constrain the type of the binding's place.
///
/// [`Predicate`]: crate::semantic_index::predicate::Predicate
pub(crate) type ScopedNarrowingConstraint = List<ScopedNarrowingConstraintPredicate>;
pub type ScopedNarrowingConstraint = List<ScopedNarrowingConstraintPredicate>;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum ConstraintKey {
pub enum ConstraintKey {
NarrowingConstraint(ScopedNarrowingConstraint),
NestedScope(FileScopeId),
UseId(ScopedUseId),
@@ -56,11 +56,11 @@ pub(crate) enum ConstraintKey {
///
/// [`Predicate`]: crate::semantic_index::predicate::Predicate
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, get_size2::GetSize)]
pub(crate) struct ScopedNarrowingConstraintPredicate(ScopedPredicateId);
pub struct ScopedNarrowingConstraintPredicate(ScopedPredicateId);
impl ScopedNarrowingConstraintPredicate {
/// Returns (the ID of) the `Predicate`
pub(crate) fn predicate(self) -> ScopedPredicateId {
pub fn predicate(self) -> ScopedPredicateId {
self.0
}
}
@@ -73,7 +73,7 @@ impl From<ScopedPredicateId> for ScopedNarrowingConstraintPredicate {
/// A collection of narrowing constraints for a given scope.
#[derive(Debug, Eq, PartialEq, get_size2::GetSize)]
pub(crate) struct NarrowingConstraints {
pub struct NarrowingConstraints {
lists: ListStorage<ScopedNarrowingConstraintPredicate>,
}
@@ -82,19 +82,19 @@ pub(crate) struct NarrowingConstraints {
/// A builder for creating narrowing constraints.
#[derive(Debug, Default, Eq, PartialEq)]
pub(crate) struct NarrowingConstraintsBuilder {
pub struct NarrowingConstraintsBuilder {
lists: ListBuilder<ScopedNarrowingConstraintPredicate>,
}
impl NarrowingConstraintsBuilder {
pub(crate) fn build(self) -> NarrowingConstraints {
pub fn build(self) -> NarrowingConstraints {
NarrowingConstraints {
lists: self.lists.build(),
}
}
/// Adds a predicate to an existing narrowing constraint.
pub(crate) fn add_predicate_to_constraint(
pub fn add_predicate_to_constraint(
&mut self,
constraint: ScopedNarrowingConstraint,
predicate: ScopedNarrowingConstraintPredicate,
@@ -104,7 +104,7 @@ impl NarrowingConstraintsBuilder {
/// Returns the intersection of two narrowing constraints. The result contains the predicates
/// that appear in both inputs.
pub(crate) fn intersect_constraints(
pub fn intersect_constraints(
&mut self,
a: ScopedNarrowingConstraint,
b: ScopedNarrowingConstraint,
@@ -137,7 +137,7 @@ mod tests {
use super::*;
impl ScopedNarrowingConstraintPredicate {
pub(crate) fn as_u32(self) -> u32 {
pub fn as_u32(self) -> u32 {
self.0.as_u32()
}
}

View File

@@ -11,7 +11,7 @@ use std::iter::FusedIterator;
/// An expression that can be the target of a `Definition`.
#[derive(Eq, PartialEq, Debug, get_size2::GetSize)]
pub(crate) enum PlaceExpr {
pub enum PlaceExpr {
/// A simple symbol, e.g. `x`.
Symbol(Symbol),
@@ -23,7 +23,7 @@ impl PlaceExpr {
/// Create a new `PlaceExpr` from a name.
///
/// This always returns a `PlaceExpr::Symbol` with empty flags and `name`.
pub(crate) fn from_expr_name(name: &ast::ExprName) -> Self {
pub fn from_expr_name(name: &ast::ExprName) -> Self {
PlaceExpr::Symbol(Symbol::new(name.id.clone()))
}
@@ -35,7 +35,7 @@ impl PlaceExpr {
/// * name: `x`
/// * attribute: `x.y`
/// * subscripts with integer or string literals: `x[0]`, `x['key']`
pub(crate) fn try_from_expr<'e>(expr: impl Into<ast::ExprRef<'e>>) -> Option<Self> {
pub fn try_from_expr<'e>(expr: impl Into<ast::ExprRef<'e>>) -> Option<Self> {
let expr = expr.into();
if let ast::ExprRef::Name(name) = expr {
@@ -60,14 +60,14 @@ impl std::fmt::Display for PlaceExpr {
///
/// Needed so that we can iterate over all places without cloning them.
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
pub(crate) enum PlaceExprRef<'a> {
pub enum PlaceExprRef<'a> {
Symbol(&'a Symbol),
Member(&'a Member),
}
impl<'a> PlaceExprRef<'a> {
/// Returns `Some` if the reference is a `Symbol`, otherwise `None`.
pub(crate) const fn as_symbol(self) -> Option<&'a Symbol> {
pub const fn as_symbol(self) -> Option<&'a Symbol> {
if let PlaceExprRef::Symbol(symbol) = self {
Some(symbol)
} else {
@@ -76,25 +76,25 @@ impl<'a> PlaceExprRef<'a> {
}
/// Returns `true` if the reference is a `Symbol`, otherwise `false`.
pub(crate) const fn is_symbol(self) -> bool {
pub const fn is_symbol(self) -> bool {
matches!(self, PlaceExprRef::Symbol(_))
}
pub(crate) fn is_declared(self) -> bool {
pub fn is_declared(self) -> bool {
match self {
Self::Symbol(symbol) => symbol.is_declared(),
Self::Member(member) => member.is_declared(),
}
}
pub(crate) const fn is_bound(self) -> bool {
pub const fn is_bound(self) -> bool {
match self {
PlaceExprRef::Symbol(symbol) => symbol.is_bound(),
PlaceExprRef::Member(member) => member.is_bound(),
}
}
pub(crate) fn num_member_segments(self) -> usize {
pub fn num_member_segments(self) -> usize {
match self {
PlaceExprRef::Symbol(_) => 0,
PlaceExprRef::Member(member) => member.expression().num_segments(),
@@ -140,7 +140,7 @@ pub enum ScopedPlaceId {
}
#[derive(Debug, Eq, PartialEq, salsa::Update, get_size2::GetSize)]
pub(crate) struct PlaceTable {
pub struct PlaceTable {
symbols: SymbolTable,
members: MemberTable,
}
@@ -149,10 +149,7 @@ impl PlaceTable {
/// Iterate over the "root" expressions of the place (e.g. `x.y.z`, `x.y`, `x` for `x.y.z[0]`).
///
/// Note, this iterator may skip some parents if they are not defined in the current scope.
pub(crate) fn parents<'a>(
&'a self,
place_expr: impl Into<PlaceExprRef<'a>>,
) -> ParentPlaceIter<'a> {
pub fn parents<'a>(&'a self, place_expr: impl Into<PlaceExprRef<'a>>) -> ParentPlaceIter<'a> {
match place_expr.into() {
PlaceExprRef::Symbol(_) => ParentPlaceIter::for_symbol(),
PlaceExprRef::Member(member) => {
@@ -162,12 +159,12 @@ impl PlaceTable {
}
/// Iterator over all symbols in this scope.
pub(crate) fn symbols(&self) -> std::slice::Iter<'_, Symbol> {
pub fn symbols(&self) -> std::slice::Iter<'_, Symbol> {
self.symbols.iter()
}
/// Iterator over all members in this scope.
pub(crate) fn members(&self) -> std::slice::Iter<'_, Member> {
pub fn members(&self) -> std::slice::Iter<'_, Member> {
self.members.iter()
}
@@ -176,14 +173,14 @@ impl PlaceTable {
/// ## Panics
/// If the symbol ID is not found in the table.
#[track_caller]
pub(crate) fn symbol(&self, id: ScopedSymbolId) -> &Symbol {
pub fn symbol(&self, id: ScopedSymbolId) -> &Symbol {
self.symbols.symbol(id)
}
/// Looks up a symbol by its name and returns a reference to it, if it exists.
///
/// This should only be used in diagnostics and tests.
pub(crate) fn symbol_by_name(&self, name: &str) -> Option<&Symbol> {
pub fn symbol_by_name(&self, name: &str) -> Option<&Symbol> {
self.symbols.symbol_id(name).map(|id| self.symbol(id))
}
@@ -192,20 +189,17 @@ impl PlaceTable {
/// ## Panics
/// If the member ID is not found in the table.
#[track_caller]
pub(crate) fn member(&self, id: ScopedMemberId) -> &Member {
pub fn member(&self, id: ScopedMemberId) -> &Member {
self.members.member(id)
}
/// Returns the [`ScopedSymbolId`] of the place named `name`.
pub(crate) fn symbol_id(&self, name: &str) -> Option<ScopedSymbolId> {
pub fn symbol_id(&self, name: &str) -> Option<ScopedSymbolId> {
self.symbols.symbol_id(name)
}
/// Returns the [`ScopedPlaceId`] of the place expression.
pub(crate) fn place_id<'e>(
&self,
place_expr: impl Into<PlaceExprRef<'e>>,
) -> Option<ScopedPlaceId> {
pub fn place_id<'e>(&self, place_expr: impl Into<PlaceExprRef<'e>>) -> Option<ScopedPlaceId> {
let place_expr = place_expr.into();
match place_expr {
@@ -221,23 +215,20 @@ impl PlaceTable {
/// ## Panics
/// If the place ID is not found in the table.
#[track_caller]
pub(crate) fn place(&self, place_id: impl Into<ScopedPlaceId>) -> PlaceExprRef<'_> {
pub fn place(&self, place_id: impl Into<ScopedPlaceId>) -> PlaceExprRef<'_> {
match place_id.into() {
ScopedPlaceId::Symbol(symbol) => self.symbol(symbol).into(),
ScopedPlaceId::Member(member) => self.member(member).into(),
}
}
pub(crate) fn member_id_by_instance_attribute_name(
&self,
name: &str,
) -> Option<ScopedMemberId> {
pub fn member_id_by_instance_attribute_name(&self, name: &str) -> Option<ScopedMemberId> {
self.members.place_id_by_instance_attribute_name(name)
}
}
#[derive(Default)]
pub(crate) struct PlaceTableBuilder {
pub struct PlaceTableBuilder {
symbols: SymbolTableBuilder,
member: MemberTableBuilder,
@@ -276,32 +267,32 @@ impl PlaceTableBuilder {
}
#[track_caller]
pub(crate) fn place(&self, place_id: impl Into<ScopedPlaceId>) -> PlaceExprRef<'_> {
pub fn place(&self, place_id: impl Into<ScopedPlaceId>) -> PlaceExprRef<'_> {
match place_id.into() {
ScopedPlaceId::Symbol(id) => PlaceExprRef::Symbol(self.symbols.symbol(id)),
ScopedPlaceId::Member(id) => PlaceExprRef::Member(self.member.member(id)),
}
}
pub(crate) fn associated_place_ids(&self, place: ScopedPlaceId) -> &[ScopedMemberId] {
pub fn associated_place_ids(&self, place: ScopedPlaceId) -> &[ScopedMemberId] {
match place {
ScopedPlaceId::Symbol(symbol) => &self.associated_symbol_members[symbol],
ScopedPlaceId::Member(member) => &self.associated_sub_members[member],
}
}
pub(crate) fn iter(&self) -> impl Iterator<Item = PlaceExprRef<'_>> {
pub fn iter(&self) -> impl Iterator<Item = PlaceExprRef<'_>> {
self.symbols
.iter()
.map(Into::into)
.chain(self.member.iter().map(PlaceExprRef::Member))
}
pub(crate) fn symbols(&self) -> impl Iterator<Item = &Symbol> {
pub fn symbols(&self) -> impl Iterator<Item = &Symbol> {
self.symbols.iter()
}
pub(crate) fn add_symbol(&mut self, symbol: Symbol) -> (ScopedSymbolId, bool) {
pub fn add_symbol(&mut self, symbol: Symbol) -> (ScopedSymbolId, bool) {
let (id, is_new) = self.symbols.add(symbol);
if is_new {
@@ -312,7 +303,7 @@ impl PlaceTableBuilder {
(id, is_new)
}
pub(crate) fn add_member(&mut self, member: Member) -> (ScopedMemberId, bool) {
pub fn add_member(&mut self, member: Member) -> (ScopedMemberId, bool) {
let (id, is_new) = self.member.add(member);
if is_new {
@@ -339,7 +330,7 @@ impl PlaceTableBuilder {
(id, is_new)
}
pub(crate) fn add_place(&mut self, place: PlaceExpr) -> (ScopedPlaceId, bool) {
pub fn add_place(&mut self, place: PlaceExpr) -> (ScopedPlaceId, bool) {
match place {
PlaceExpr::Symbol(symbol) => {
let (id, is_new) = self.add_symbol(symbol);
@@ -376,7 +367,7 @@ impl PlaceTableBuilder {
}
}
pub(crate) fn finish(self) -> PlaceTable {
pub fn finish(self) -> PlaceTable {
PlaceTable {
symbols: self.symbols.build(),
members: self.member.build(),
@@ -454,7 +445,7 @@ impl FilePlaceId {
self.scope
}
pub(crate) fn scoped_place_id(self) -> ScopedPlaceId {
pub fn scoped_place_id(self) -> ScopedPlaceId {
self.scoped_place_id
}
}
@@ -465,7 +456,7 @@ impl From<FilePlaceId> for ScopedPlaceId {
}
}
pub(crate) struct ParentPlaceIter<'a> {
pub struct ParentPlaceIter<'a> {
state: Option<ParentPlaceIterState<'a>>,
}

View File

@@ -19,14 +19,14 @@ use crate::semantic_index::symbol::ScopedSymbolId;
// A scoped identifier for each `Predicate` in a scope.
#[derive(Clone, Debug, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, get_size2::GetSize)]
pub(crate) struct ScopedPredicateId(u32);
pub struct ScopedPredicateId(u32);
impl ScopedPredicateId {
/// A special ID that is used for an "always true" predicate.
pub(crate) const ALWAYS_TRUE: ScopedPredicateId = ScopedPredicateId(0xffff_ffff);
pub const ALWAYS_TRUE: ScopedPredicateId = ScopedPredicateId(0xffff_ffff);
/// A special ID that is used for an "always false" predicate.
pub(crate) const ALWAYS_FALSE: ScopedPredicateId = ScopedPredicateId(0xffff_fffe);
pub const ALWAYS_FALSE: ScopedPredicateId = ScopedPredicateId(0xffff_fffe);
const SMALLEST_TERMINAL: ScopedPredicateId = Self::ALWAYS_FALSE;
@@ -35,7 +35,7 @@ impl ScopedPredicateId {
}
#[cfg(test)]
pub(crate) fn as_u32(self) -> u32 {
pub fn as_u32(self) -> u32 {
self.0
}
}
@@ -56,10 +56,10 @@ impl Idx for ScopedPredicateId {
}
// A collection of predicates for a given scope.
pub(crate) type Predicates<'db> = IndexVec<ScopedPredicateId, Predicate<'db>>;
pub type Predicates<'db> = IndexVec<ScopedPredicateId, Predicate<'db>>;
#[derive(Debug, Default)]
pub(crate) struct PredicatesBuilder<'db> {
pub struct PredicatesBuilder<'db> {
predicates: IndexVec<ScopedPredicateId, Predicate<'db>>,
}
@@ -78,13 +78,13 @@ impl<'db> PredicatesBuilder<'db> {
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update, get_size2::GetSize)]
pub(crate) struct Predicate<'db> {
pub(crate) node: PredicateNode<'db>,
pub(crate) is_positive: bool,
pub struct Predicate<'db> {
pub node: PredicateNode<'db>,
pub is_positive: bool,
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update, get_size2::GetSize)]
pub(crate) enum PredicateOrLiteral<'db> {
pub enum PredicateOrLiteral<'db> {
Literal(bool),
Predicate(Predicate<'db>),
}
@@ -104,13 +104,13 @@ impl PredicateOrLiteral<'_> {
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update, get_size2::GetSize)]
pub(crate) struct CallableAndCallExpr<'db> {
pub(crate) callable: Expression<'db>,
pub(crate) call_expr: Expression<'db>,
pub struct CallableAndCallExpr<'db> {
pub callable: Expression<'db>,
pub call_expr: Expression<'db>,
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update, get_size2::GetSize)]
pub(crate) enum PredicateNode<'db> {
pub enum PredicateNode<'db> {
Expression(Expression<'db>),
ReturnsNever(CallableAndCallExpr<'db>),
Pattern(PatternPredicate<'db>),
@@ -118,20 +118,20 @@ pub(crate) enum PredicateNode<'db> {
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, salsa::Update, get_size2::GetSize)]
pub(crate) enum ClassPatternKind {
pub enum ClassPatternKind {
Irrefutable,
Refutable,
}
impl ClassPatternKind {
pub(crate) fn is_irrefutable(self) -> bool {
pub fn is_irrefutable(self) -> bool {
matches!(self, ClassPatternKind::Irrefutable)
}
}
/// Pattern kinds for which we support type narrowing and/or static reachability analysis.
#[derive(Debug, Clone, Hash, PartialEq, salsa::Update, get_size2::GetSize)]
pub(crate) enum PatternPredicateKind<'db> {
pub enum PatternPredicateKind<'db> {
Singleton(Singleton),
Value(Expression<'db>),
Or(Vec<PatternPredicateKind<'db>>),
@@ -141,27 +141,27 @@ pub(crate) enum PatternPredicateKind<'db> {
}
#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)]
pub(crate) struct PatternPredicate<'db> {
pub(crate) file: File,
pub struct PatternPredicate<'db> {
pub file: File,
pub(crate) file_scope: FileScopeId,
pub file_scope: FileScopeId,
pub(crate) subject: Expression<'db>,
pub subject: Expression<'db>,
#[returns(ref)]
pub(crate) kind: PatternPredicateKind<'db>,
pub kind: PatternPredicateKind<'db>,
pub(crate) guard: Option<Expression<'db>>,
pub guard: Option<Expression<'db>>,
/// A reference to the pattern of the previous match case
pub(crate) previous_predicate: Option<Box<PatternPredicate<'db>>>,
pub previous_predicate: Option<Box<PatternPredicate<'db>>>,
}
// The Salsa heap is tracked separately.
impl get_size2::GetSize for PatternPredicate<'_> {}
impl<'db> PatternPredicate<'db> {
pub(crate) fn scope(self, db: &'db dyn Db) -> ScopeId<'db> {
pub fn scope(self, db: &'db dyn Db) -> ScopeId<'db> {
self.file_scope(db).to_scope_id(db, self.file(db))
}
}
@@ -207,8 +207,8 @@ impl<'db> PatternPredicate<'db> {
///
/// [Truthiness]: [crate::types::Truthiness]
#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)]
pub(crate) struct StarImportPlaceholderPredicate<'db> {
pub(crate) importing_file: File,
pub struct StarImportPlaceholderPredicate<'db> {
pub importing_file: File,
/// Each symbol imported by a `*` import has a separate predicate associated with it:
/// this field identifies which symbol that is.
@@ -219,16 +219,16 @@ pub(crate) struct StarImportPlaceholderPredicate<'db> {
/// for valid `*`-import definitions, and valid `*`-import definitions can only ever
/// exist in the global scope; thus, we know that the `symbol_id` here will be relative
/// to the global scope of the importing file.
pub(crate) symbol_id: ScopedSymbolId,
pub symbol_id: ScopedSymbolId,
pub(crate) referenced_file: File,
pub referenced_file: File,
}
// The Salsa heap is tracked separately.
impl get_size2::GetSize for StarImportPlaceholderPredicate<'_> {}
impl<'db> StarImportPlaceholderPredicate<'db> {
pub(crate) fn scope(self, db: &'db dyn Db) -> ScopeId<'db> {
pub fn scope(self, db: &'db dyn Db) -> ScopeId<'db> {
// See doc-comment above [`StarImportPlaceholderPredicate::symbol_id`]:
// valid `*`-import definitions can only take place in the global scope.
global_scope(db, self.importing_file(db))

View File

@@ -198,19 +198,8 @@ use std::cmp::Ordering;
use ruff_index::{Idx, IndexVec};
use rustc_hash::FxHashMap;
use crate::Db;
use crate::dunder_all::dunder_all_names;
use crate::place::{RequiresExplicitReExport, imported_symbol};
use crate::rank::RankBitBox;
use crate::semantic_index::place_table;
use crate::semantic_index::predicate::{
CallableAndCallExpr, PatternPredicate, PatternPredicateKind, Predicate, PredicateNode,
Predicates, ScopedPredicateId,
};
use crate::types::{
CallableTypes, IntersectionBuilder, Truthiness, Type, TypeContext, UnionBuilder, UnionType,
infer_expression_type, static_expression_truthiness,
};
use crate::semantic_index::predicate::ScopedPredicateId;
/// A ternary formula that defines under what conditions a binding is visible. (A ternary formula
/// is just like a boolean formula, but with `Ambiguous` as a third potential result. See the
@@ -231,7 +220,7 @@ use crate::types::{
/// reachability constraints are normalized, so equivalent constraints are guaranteed to have equal
/// IDs.
#[derive(Clone, Copy, Eq, Hash, PartialEq, get_size2::GetSize)]
pub(crate) struct ScopedReachabilityConstraintId(u32);
pub struct ScopedReachabilityConstraintId(u32);
impl std::fmt::Debug for ScopedReachabilityConstraintId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -259,35 +248,36 @@ impl std::fmt::Debug for ScopedReachabilityConstraintId {
// _Interior nodes_ provide the TDD structure for the formula. Interior nodes are stored in an
// arena Vec, with the constraint ID providing an index into the arena.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, get_size2::GetSize)]
struct InteriorNode {
/// An interior node in the TDD (Ternary Decision Diagram).
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, get_size2::GetSize)]
pub struct InteriorNode {
/// A "variable" that is evaluated as part of a TDD ternary function. For reachability
/// constraints, this is a `Predicate` that represents some runtime property of the Python
/// code that we are evaluating.
atom: ScopedPredicateId,
if_true: ScopedReachabilityConstraintId,
if_ambiguous: ScopedReachabilityConstraintId,
if_false: ScopedReachabilityConstraintId,
pub atom: ScopedPredicateId,
pub if_true: ScopedReachabilityConstraintId,
pub if_ambiguous: ScopedReachabilityConstraintId,
pub if_false: ScopedReachabilityConstraintId,
}
impl ScopedReachabilityConstraintId {
/// A special ID that is used for an "always true" / "always visible" constraint.
pub(crate) const ALWAYS_TRUE: ScopedReachabilityConstraintId =
pub const ALWAYS_TRUE: ScopedReachabilityConstraintId =
ScopedReachabilityConstraintId(0xffff_ffff);
/// A special ID that is used for an ambiguous constraint.
pub(crate) const AMBIGUOUS: ScopedReachabilityConstraintId =
pub const AMBIGUOUS: ScopedReachabilityConstraintId =
ScopedReachabilityConstraintId(0xffff_fffe);
/// A special ID that is used for an "always false" / "never visible" constraint.
pub(crate) const ALWAYS_FALSE: ScopedReachabilityConstraintId =
pub const ALWAYS_FALSE: ScopedReachabilityConstraintId =
ScopedReachabilityConstraintId(0xffff_fffd);
fn is_terminal(self) -> bool {
pub fn is_terminal(self) -> bool {
self.0 >= SMALLEST_TERMINAL.0
}
fn as_u32(self) -> u32 {
pub fn as_u32(self) -> u32 {
self.0
}
}
@@ -313,120 +303,9 @@ const AMBIGUOUS: ScopedReachabilityConstraintId = ScopedReachabilityConstraintId
const ALWAYS_FALSE: ScopedReachabilityConstraintId = ScopedReachabilityConstraintId::ALWAYS_FALSE;
const SMALLEST_TERMINAL: ScopedReachabilityConstraintId = ALWAYS_FALSE;
fn singleton_to_type(db: &dyn Db, singleton: ruff_python_ast::Singleton) -> Type<'_> {
let ty = match singleton {
ruff_python_ast::Singleton::None => Type::none(db),
ruff_python_ast::Singleton::True => Type::BooleanLiteral(true),
ruff_python_ast::Singleton::False => Type::BooleanLiteral(false),
};
debug_assert!(ty.is_singleton(db));
ty
}
/// Turn a `match` pattern kind into a type that represents the set of all values that would definitely
/// match that pattern.
fn pattern_kind_to_type<'db>(db: &'db dyn Db, kind: &PatternPredicateKind<'db>) -> Type<'db> {
match kind {
PatternPredicateKind::Singleton(singleton) => singleton_to_type(db, *singleton),
PatternPredicateKind::Value(value) => {
infer_expression_type(db, *value, TypeContext::default())
}
PatternPredicateKind::Class(class_expr, kind) => {
if kind.is_irrefutable() {
infer_expression_type(db, *class_expr, TypeContext::default())
.to_instance(db)
.unwrap_or(Type::Never)
.top_materialization(db)
} else {
Type::Never
}
}
PatternPredicateKind::Or(predicates) => {
UnionType::from_elements(db, predicates.iter().map(|p| pattern_kind_to_type(db, p)))
}
PatternPredicateKind::As(pattern, _) => pattern
.as_deref()
.map(|p| pattern_kind_to_type(db, p))
.unwrap_or_else(Type::object),
PatternPredicateKind::Unsupported => Type::Never,
}
}
/// Go through the list of previous match cases, and accumulate a union of all types that were already
/// matched by these patterns.
fn type_excluded_by_previous_patterns<'db>(
db: &'db dyn Db,
mut predicate: PatternPredicate<'db>,
) -> Type<'db> {
let mut builder = UnionBuilder::new(db);
while let Some(previous) = predicate.previous_predicate(db) {
predicate = *previous;
if predicate.guard(db).is_none() {
builder = builder.add(pattern_kind_to_type(db, predicate.kind(db)));
}
}
builder.build()
}
/// Analyze a pattern predicate to determine its static truthiness.
///
/// This is a Salsa tracked function to enable memoization. Without memoization, for a match
/// statement with N cases where each case references the subject (e.g., `self`), we would
/// re-analyze each pattern O(N) times (once per reference), leading to O(N²) total work.
/// With memoization, each pattern is analyzed exactly once.
#[salsa::tracked(cycle_initial = analyze_pattern_predicate_cycle_initial, heap_size = get_size2::GetSize::get_heap_size)]
fn analyze_pattern_predicate<'db>(db: &'db dyn Db, predicate: PatternPredicate<'db>) -> Truthiness {
let subject_ty = infer_expression_type(db, predicate.subject(db), TypeContext::default());
let narrowed_subject = IntersectionBuilder::new(db)
.add_positive(subject_ty)
.add_negative(type_excluded_by_previous_patterns(db, predicate));
let narrowed_subject_ty = narrowed_subject.clone().build();
// Consider a case where we match on a subject type of `Self` with an upper bound of `Answer`,
// where `Answer` is a {YES, NO} enum. After a previous pattern matching on `NO`, the narrowed
// subject type is `Self & ~Literal[NO]`. This type is *not* equivalent to `Literal[YES]`,
// because `Self` could also specialize to `Literal[NO]` or `Never`, making the intersection
// empty. However, if the current pattern matches on `YES`, the *next* narrowed subject type
// will be `Self & ~Literal[NO] & ~Literal[YES]`, which *is* always equivalent to `Never`. This
// means that subsequent patterns can never match. And we know that if we reach this point,
// the current pattern will have to match. We return `AlwaysTrue` here, since the call to
// `analyze_single_pattern_predicate_kind` below would return `Ambiguous` in this case.
let next_narrowed_subject_ty = narrowed_subject
.add_negative(pattern_kind_to_type(db, predicate.kind(db)))
.build();
if !narrowed_subject_ty.is_never() && next_narrowed_subject_ty.is_never() {
return Truthiness::AlwaysTrue;
}
let truthiness = ReachabilityConstraints::analyze_single_pattern_predicate_kind(
db,
predicate.kind(db),
narrowed_subject_ty,
);
if truthiness == Truthiness::AlwaysTrue && predicate.guard(db).is_some() {
// Fall back to ambiguous, the guard might change the result.
// TODO: actually analyze guard truthiness
Truthiness::Ambiguous
} else {
truthiness
}
}
fn analyze_pattern_predicate_cycle_initial<'db>(
_db: &'db dyn Db,
_id: salsa::Id,
_predicate: PatternPredicate<'db>,
) -> Truthiness {
Truthiness::Ambiguous
}
/// A collection of reachability constraints for a given scope.
#[derive(Debug, PartialEq, Eq, salsa::Update, get_size2::GetSize)]
pub(crate) struct ReachabilityConstraints {
pub struct ReachabilityConstraints {
/// The interior TDD nodes that were marked as used when being built.
used_interiors: Box<[InteriorNode]>,
/// A bit vector indicating which interior TDD nodes were marked as used. This is indexed by
@@ -435,6 +314,18 @@ pub(crate) struct ReachabilityConstraints {
used_indices: RankBitBox,
}
impl ReachabilityConstraints {
/// Returns a reference to the used interior nodes.
pub fn used_interiors(&self) -> &[InteriorNode] {
&self.used_interiors
}
/// Returns a reference to the used indices bit vector.
pub fn used_indices(&self) -> &RankBitBox {
&self.used_indices
}
}
#[derive(Debug, Default, PartialEq, Eq)]
pub(crate) struct ReachabilityConstraintsBuilder {
interiors: IndexVec<ScopedReachabilityConstraintId, InteriorNode>,
@@ -730,242 +621,3 @@ impl ReachabilityConstraintsBuilder {
result
}
}
impl ReachabilityConstraints {
/// Analyze the statically known reachability for a given constraint.
pub(crate) fn evaluate<'db>(
&self,
db: &'db dyn Db,
predicates: &Predicates<'db>,
mut id: ScopedReachabilityConstraintId,
) -> Truthiness {
loop {
let node = match id {
ALWAYS_TRUE => return Truthiness::AlwaysTrue,
AMBIGUOUS => return Truthiness::Ambiguous,
ALWAYS_FALSE => return Truthiness::AlwaysFalse,
_ => {
// `id` gives us the index of this node in the IndexVec that we used when
// constructing this BDD. When finalizing the builder, we threw away any
// interior nodes that weren't marked as used. The `used_indices` bit vector
// lets us verify that this node was marked as used, and the rank of that bit
// in the bit vector tells us where this node lives in the "condensed"
// `used_interiors` vector.
let raw_index = id.as_u32() as usize;
debug_assert!(
self.used_indices.get_bit(raw_index).unwrap_or(false),
"all used reachability constraints should have been marked as used",
);
let index = self.used_indices.rank(raw_index) as usize;
self.used_interiors[index]
}
};
let predicate = &predicates[node.atom];
match Self::analyze_single(db, predicate) {
Truthiness::AlwaysTrue => id = node.if_true,
Truthiness::Ambiguous => id = node.if_ambiguous,
Truthiness::AlwaysFalse => id = node.if_false,
}
}
}
fn analyze_single_pattern_predicate_kind<'db>(
db: &'db dyn Db,
predicate_kind: &PatternPredicateKind<'db>,
subject_ty: Type<'db>,
) -> Truthiness {
match predicate_kind {
PatternPredicateKind::Value(value) => {
let value_ty = infer_expression_type(db, *value, TypeContext::default());
if subject_ty.is_single_valued(db) {
Truthiness::from(subject_ty.is_equivalent_to(db, value_ty))
} else {
Truthiness::Ambiguous
}
}
PatternPredicateKind::Singleton(singleton) => {
let singleton_ty = singleton_to_type(db, *singleton);
if subject_ty.is_equivalent_to(db, singleton_ty) {
Truthiness::AlwaysTrue
} else if subject_ty.is_disjoint_from(db, singleton_ty) {
Truthiness::AlwaysFalse
} else {
Truthiness::Ambiguous
}
}
PatternPredicateKind::Or(predicates) => {
use std::ops::ControlFlow;
let mut excluded_types = vec![];
let (ControlFlow::Break(truthiness) | ControlFlow::Continue(truthiness)) =
predicates
.iter()
.map(|p| {
let narrowed_subject_ty = IntersectionBuilder::new(db)
.add_positive(subject_ty)
.add_negative(UnionType::from_elements(db, excluded_types.iter()))
.build();
excluded_types.push(pattern_kind_to_type(db, p));
Self::analyze_single_pattern_predicate_kind(db, p, narrowed_subject_ty)
})
// this is just a "max", but with a slight optimization: `AlwaysTrue` is the "greatest" possible element, so we short-circuit if we get there
.try_fold(Truthiness::AlwaysFalse, |acc, next| match (acc, next) {
(Truthiness::AlwaysTrue, _) | (_, Truthiness::AlwaysTrue) => {
ControlFlow::Break(Truthiness::AlwaysTrue)
}
(Truthiness::Ambiguous, _) | (_, Truthiness::Ambiguous) => {
ControlFlow::Continue(Truthiness::Ambiguous)
}
(Truthiness::AlwaysFalse, Truthiness::AlwaysFalse) => {
ControlFlow::Continue(Truthiness::AlwaysFalse)
}
});
truthiness
}
PatternPredicateKind::Class(class_expr, kind) => {
let class_ty = infer_expression_type(db, *class_expr, TypeContext::default())
.as_class_literal()
.map(|class| Type::instance(db, class.top_materialization(db)));
class_ty.map_or(Truthiness::Ambiguous, |class_ty| {
if subject_ty.is_subtype_of(db, class_ty) {
if kind.is_irrefutable() {
Truthiness::AlwaysTrue
} else {
// A class pattern like `case Point(x=0, y=0)` is not irrefutable,
// i.e. it does not match all instances of `Point`. This means that
// we can't tell for sure if this pattern will match or not.
Truthiness::Ambiguous
}
} else if subject_ty.is_disjoint_from(db, class_ty) {
Truthiness::AlwaysFalse
} else {
Truthiness::Ambiguous
}
})
}
PatternPredicateKind::As(pattern, _) => pattern
.as_deref()
.map(|p| Self::analyze_single_pattern_predicate_kind(db, p, subject_ty))
.unwrap_or(Truthiness::AlwaysTrue),
PatternPredicateKind::Unsupported => Truthiness::Ambiguous,
}
}
fn analyze_single(db: &dyn Db, predicate: &Predicate) -> Truthiness {
let _span = tracing::trace_span!("analyze_single", ?predicate).entered();
match predicate.node {
PredicateNode::Expression(test_expr) => {
static_expression_truthiness(db, test_expr).negate_if(!predicate.is_positive)
}
PredicateNode::ReturnsNever(CallableAndCallExpr {
callable,
call_expr,
}) => {
// We first infer just the type of the callable. In the most likely case that the
// function is not marked with `NoReturn`, or that it always returns `NoReturn`,
// doing so allows us to avoid the more expensive work of inferring the entire call
// expression (which could involve inferring argument types to possibly run the overload
// selection algorithm).
// Avoiding this on the happy-path is important because these constraints can be
// very large in number, since we add them on all statement level function calls.
let ty = infer_expression_type(db, callable, TypeContext::default());
// Short-circuit for well known types that are known not to return `Never` when called.
// Without the short-circuit, we've seen that threads keep blocking each other
// because they all try to acquire Salsa's `CallableType` lock that ensures each type
// is only interned once. The lock is so heavily congested because there are only
// very few dynamic types, in which case Salsa's sharding the locks by value
// doesn't help much.
// See <https://github.com/astral-sh/ty/issues/968>.
if matches!(ty, Type::Dynamic(_)) {
return Truthiness::AlwaysFalse.negate_if(!predicate.is_positive);
}
let overloads_iterator = if let Some(callable) = ty
.try_upcast_to_callable(db)
.and_then(CallableTypes::exactly_one)
{
callable.signatures(db).overloads.iter()
} else {
return Truthiness::AlwaysFalse.negate_if(!predicate.is_positive);
};
let (no_overloads_return_never, all_overloads_return_never) = overloads_iterator
.fold((true, true), |(none, all), overload| {
let overload_returns_never =
overload.return_ty.is_some_and(|return_type| {
return_type.is_equivalent_to(db, Type::Never)
});
(
none && !overload_returns_never,
all && overload_returns_never,
)
});
if no_overloads_return_never {
Truthiness::AlwaysFalse
} else if all_overloads_return_never {
Truthiness::AlwaysTrue
} else {
let call_expr_ty = infer_expression_type(db, call_expr, TypeContext::default());
if call_expr_ty.is_equivalent_to(db, Type::Never) {
Truthiness::AlwaysTrue
} else {
Truthiness::AlwaysFalse
}
}
.negate_if(!predicate.is_positive)
}
PredicateNode::Pattern(inner) => analyze_pattern_predicate(db, inner),
PredicateNode::StarImportPlaceholder(star_import) => {
let place_table = place_table(db, star_import.scope(db));
let symbol = place_table.symbol(star_import.symbol_id(db));
let referenced_file = star_import.referenced_file(db);
let requires_explicit_reexport = match dunder_all_names(db, referenced_file) {
Some(all_names) => {
if all_names.contains(symbol.name()) {
Some(RequiresExplicitReExport::No)
} else {
tracing::trace!(
"Symbol `{}` (via star import) not found in `__all__` of `{}`",
symbol.name(),
referenced_file.path(db)
);
return Truthiness::AlwaysFalse;
}
}
None => None,
};
match imported_symbol(
db,
referenced_file,
symbol.name(),
requires_explicit_reexport,
)
.place
{
crate::place::Place::Defined(
_,
_,
crate::place::Definedness::AlwaysDefined,
) => Truthiness::AlwaysTrue,
crate::place::Place::Defined(
_,
_,
crate::place::Definedness::PossiblyUndefined,
) => Truthiness::Ambiguous,
crate::place::Place::Undefined => Truthiness::AlwaysFalse,
}
}
}
}
}

View File

@@ -11,7 +11,6 @@ use crate::{
semantic_index::{
SemanticIndex, reachability_constraints::ScopedReachabilityConstraintId, semantic_index,
},
types::{GenericContext, binding_type, infer_definition_types},
};
/// A cross-module identifier of a scope that can be used as a salsa query parameter.
@@ -27,20 +26,20 @@ pub struct ScopeId<'db> {
impl get_size2::GetSize for ScopeId<'_> {}
impl<'db> ScopeId<'db> {
pub(crate) fn is_annotation(self, db: &'db dyn Db) -> bool {
pub fn is_annotation(self, db: &'db dyn Db) -> bool {
self.node(db).scope_kind().is_annotation()
}
pub(crate) fn node(self, db: &dyn Db) -> &NodeWithScopeKind {
pub fn node(self, db: &dyn Db) -> &NodeWithScopeKind {
self.scope(db).node()
}
pub(crate) fn scope(self, db: &dyn Db) -> &Scope {
pub fn scope(self, db: &dyn Db) -> &Scope {
semantic_index(db, self.file(db)).scope(self.file_scope_id(db))
}
#[cfg(test)]
pub(crate) fn name<'ast>(self, db: &'db dyn Db, module: &'ast ParsedModuleRef) -> &'ast str {
#[cfg(any(test, feature = "testing"))]
pub fn name<'ast>(self, db: &'db dyn Db, module: &'ast ParsedModuleRef) -> &'ast str {
match self.node(db) {
NodeWithScopeKind::Module => "<module>",
NodeWithScopeKind::Class(class) | NodeWithScopeKind::ClassTypeParameters(class) => {
@@ -86,13 +85,13 @@ impl FileScopeId {
index.scope_ids_by_scope[self]
}
pub(crate) fn is_generator_function(self, index: &SemanticIndex) -> bool {
pub fn is_generator_function(self, index: &SemanticIndex) -> bool {
index.generator_functions.contains(&self)
}
}
#[derive(Debug, salsa::Update, get_size2::GetSize)]
pub(crate) struct Scope {
pub struct Scope {
/// The parent scope, if any.
parent: Option<FileScopeId>,
@@ -126,23 +125,23 @@ impl Scope {
}
}
pub(crate) fn parent(&self) -> Option<FileScopeId> {
pub fn parent(&self) -> Option<FileScopeId> {
self.parent
}
pub(crate) fn node(&self) -> &NodeWithScopeKind {
pub fn node(&self) -> &NodeWithScopeKind {
&self.node
}
pub(crate) fn kind(&self) -> ScopeKind {
pub fn kind(&self) -> ScopeKind {
self.node().scope_kind()
}
pub(crate) fn visibility(&self) -> ScopeVisibility {
pub fn visibility(&self) -> ScopeVisibility {
self.kind().visibility()
}
pub(crate) fn descendants(&self) -> Range<FileScopeId> {
pub fn descendants(&self) -> Range<FileScopeId> {
self.descendants.clone()
}
@@ -150,21 +149,21 @@ impl Scope {
self.descendants = self.descendants.start..children_end;
}
pub(crate) fn is_eager(&self) -> bool {
pub fn is_eager(&self) -> bool {
self.kind().is_eager()
}
pub(crate) fn reachability(&self) -> ScopedReachabilityConstraintId {
pub fn reachability(&self) -> ScopedReachabilityConstraintId {
self.reachability
}
pub(crate) fn in_type_checking_block(&self) -> bool {
pub fn in_type_checking_block(&self) -> bool {
self.in_type_checking_block
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, get_size2::GetSize)]
pub(crate) enum ScopeVisibility {
pub enum ScopeVisibility {
/// The scope is private (e.g. function, type alias, comprehension scope).
Private,
/// The scope is public (e.g. module, class scope).
@@ -172,17 +171,17 @@ pub(crate) enum ScopeVisibility {
}
impl ScopeVisibility {
pub(crate) const fn is_public(self) -> bool {
pub const fn is_public(self) -> bool {
matches!(self, ScopeVisibility::Public)
}
pub(crate) const fn is_private(self) -> bool {
pub const fn is_private(self) -> bool {
matches!(self, ScopeVisibility::Private)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, get_size2::GetSize)]
pub(crate) enum ScopeLaziness {
pub enum ScopeLaziness {
/// The scope is evaluated lazily (e.g. function, type alias scope).
Lazy,
/// The scope is evaluated eagerly (e.g. module, class, comprehension scope).
@@ -190,17 +189,17 @@ pub(crate) enum ScopeLaziness {
}
impl ScopeLaziness {
pub(crate) const fn is_eager(self) -> bool {
pub const fn is_eager(self) -> bool {
matches!(self, ScopeLaziness::Eager)
}
pub(crate) const fn is_lazy(self) -> bool {
pub const fn is_lazy(self) -> bool {
matches!(self, ScopeLaziness::Lazy)
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum ScopeKind {
pub enum ScopeKind {
Module,
TypeParams,
Class,
@@ -211,11 +210,11 @@ pub(crate) enum ScopeKind {
}
impl ScopeKind {
pub(crate) const fn is_eager(self) -> bool {
pub const fn is_eager(self) -> bool {
self.laziness().is_eager()
}
pub(crate) const fn laziness(self) -> ScopeLaziness {
pub const fn laziness(self) -> ScopeLaziness {
match self {
ScopeKind::Module
| ScopeKind::Class
@@ -225,7 +224,7 @@ impl ScopeKind {
}
}
pub(crate) const fn visibility(self) -> ScopeVisibility {
pub const fn visibility(self) -> ScopeVisibility {
match self {
ScopeKind::Module | ScopeKind::Class => ScopeVisibility::Public,
ScopeKind::TypeParams
@@ -236,7 +235,7 @@ impl ScopeKind {
}
}
pub(crate) const fn is_function_like(self) -> bool {
pub const fn is_function_like(self) -> bool {
// Type parameter scopes behave like function scopes in terms of name resolution; CPython
// symbol table also uses the term "function-like" for these scopes.
matches!(
@@ -249,26 +248,26 @@ impl ScopeKind {
)
}
pub(crate) const fn is_class(self) -> bool {
pub const fn is_class(self) -> bool {
matches!(self, ScopeKind::Class)
}
pub(crate) const fn is_module(self) -> bool {
pub const fn is_module(self) -> bool {
matches!(self, ScopeKind::Module)
}
pub(crate) const fn is_annotation(self) -> bool {
pub const fn is_annotation(self) -> bool {
matches!(self, ScopeKind::TypeParams | ScopeKind::TypeAlias)
}
pub(crate) const fn is_non_lambda_function(self) -> bool {
pub const fn is_non_lambda_function(self) -> bool {
matches!(self, ScopeKind::Function)
}
}
/// Reference to a node that introduces a new scope.
#[derive(Copy, Clone, Debug)]
pub(crate) enum NodeWithScopeRef<'a> {
pub enum NodeWithScopeRef<'a> {
Module,
Class(&'a ast::StmtClassDef),
Function(&'a ast::StmtFunctionDef),
@@ -326,7 +325,7 @@ impl NodeWithScopeRef<'_> {
}
}
pub(crate) fn node_key(self) -> NodeWithScopeKey {
pub fn node_key(self) -> NodeWithScopeKey {
match self {
NodeWithScopeRef::Module => NodeWithScopeKey::Module,
NodeWithScopeRef::Class(class) => NodeWithScopeKey::Class(NodeKey::from_node(class)),
@@ -366,7 +365,7 @@ impl NodeWithScopeRef<'_> {
/// Node that introduces a new scope.
#[derive(Clone, Debug, salsa::Update, get_size2::GetSize)]
pub(crate) enum NodeWithScopeKind {
pub enum NodeWithScopeKind {
Module,
Class(AstNodeRef<ast::StmtClassDef>),
ClassTypeParameters(AstNodeRef<ast::StmtClassDef>),
@@ -382,7 +381,7 @@ pub(crate) enum NodeWithScopeKind {
}
impl NodeWithScopeKind {
pub(crate) const fn scope_kind(&self) -> ScopeKind {
pub const fn scope_kind(&self) -> ScopeKind {
match self {
Self::Module => ScopeKind::Module,
Self::Class(_) => ScopeKind::Class,
@@ -399,74 +398,42 @@ impl NodeWithScopeKind {
}
}
pub(crate) fn as_class(&self) -> Option<&AstNodeRef<ast::StmtClassDef>> {
pub fn as_class(&self) -> Option<&AstNodeRef<ast::StmtClassDef>> {
match self {
Self::Class(class) => Some(class),
_ => None,
}
}
pub(crate) fn expect_class(&self) -> &AstNodeRef<ast::StmtClassDef> {
pub fn expect_class(&self) -> &AstNodeRef<ast::StmtClassDef> {
self.as_class().expect("expected class")
}
pub(crate) fn as_function(&self) -> Option<&AstNodeRef<ast::StmtFunctionDef>> {
pub fn as_function(&self) -> Option<&AstNodeRef<ast::StmtFunctionDef>> {
match self {
Self::Function(function) => Some(function),
_ => None,
}
}
pub(crate) fn expect_function(&self) -> &AstNodeRef<ast::StmtFunctionDef> {
pub fn expect_function(&self) -> &AstNodeRef<ast::StmtFunctionDef> {
self.as_function().expect("expected function")
}
pub(crate) fn as_type_alias(&self) -> Option<&AstNodeRef<ast::StmtTypeAlias>> {
pub fn as_type_alias(&self) -> Option<&AstNodeRef<ast::StmtTypeAlias>> {
match self {
Self::TypeAlias(type_alias) => Some(type_alias),
_ => None,
}
}
pub(crate) fn expect_type_alias(&self) -> &AstNodeRef<ast::StmtTypeAlias> {
pub fn expect_type_alias(&self) -> &AstNodeRef<ast::StmtTypeAlias> {
self.as_type_alias().expect("expected type alias")
}
pub(crate) fn generic_context<'db>(
&self,
db: &'db dyn Db,
index: &SemanticIndex<'db>,
) -> Option<GenericContext<'db>> {
match self {
NodeWithScopeKind::Class(class) => {
let definition = index.expect_single_definition(class);
binding_type(db, definition)
.as_class_literal()?
.generic_context(db)
}
NodeWithScopeKind::Function(function) => {
let definition = index.expect_single_definition(function);
infer_definition_types(db, definition)
.undecorated_type()
.expect("function should have undecorated type")
.as_function_literal()?
.last_definition_signature(db)
.generic_context
}
NodeWithScopeKind::TypeAlias(type_alias) => {
let definition = index.expect_single_definition(type_alias);
binding_type(db, definition)
.as_type_alias()?
.as_pep_695_type_alias()?
.generic_context(db)
}
_ => None,
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, get_size2::GetSize)]
pub(crate) enum NodeWithScopeKey {
pub enum NodeWithScopeKey {
Module,
Class(NodeKey),
ClassTypeParameters(NodeKey),

View File

@@ -13,7 +13,7 @@ pub struct ScopedSymbolId;
/// A symbol in a given scope.
#[derive(Debug, Clone, PartialEq, Eq, get_size2::GetSize, salsa::Update)]
pub(crate) struct Symbol {
pub struct Symbol {
name: Name,
flags: SymbolFlags,
}
@@ -52,32 +52,32 @@ impl Symbol {
}
}
pub(crate) fn name(&self) -> &Name {
pub fn name(&self) -> &Name {
&self.name
}
/// Is the symbol used in its containing scope?
pub(crate) fn is_used(&self) -> bool {
pub fn is_used(&self) -> bool {
self.flags.contains(SymbolFlags::IS_USED)
}
/// Is the symbol given a value in its containing scope?
pub(crate) const fn is_bound(&self) -> bool {
pub const fn is_bound(&self) -> bool {
self.flags.contains(SymbolFlags::IS_BOUND)
}
/// Is the symbol declared in its containing scope?
pub(crate) fn is_declared(&self) -> bool {
pub fn is_declared(&self) -> bool {
self.flags.contains(SymbolFlags::IS_DECLARED)
}
/// Is the symbol `global` its containing scope?
pub(crate) fn is_global(&self) -> bool {
pub fn is_global(&self) -> bool {
self.flags.contains(SymbolFlags::MARKED_GLOBAL)
}
/// Is the symbol `nonlocal` its containing scope?
pub(crate) fn is_nonlocal(&self) -> bool {
pub fn is_nonlocal(&self) -> bool {
self.flags.contains(SymbolFlags::MARKED_NONLOCAL)
}
@@ -109,7 +109,7 @@ impl Symbol {
/// In cases like this, the resolution isn't known until runtime, and in fact it varies from
/// one use to the next. The semantic index alone can't resolve this, and instead it's a
/// special case in type inference (see `infer_place_load`).
pub(crate) fn is_local(&self) -> bool {
pub fn is_local(&self) -> bool {
!self.is_global() && !self.is_nonlocal() && (self.is_bound() || self.is_declared())
}
@@ -117,7 +117,7 @@ impl Symbol {
self.flags.contains(SymbolFlags::IS_REASSIGNED)
}
pub(crate) fn is_parameter(&self) -> bool {
pub fn is_parameter(&self) -> bool {
self.flags.contains(SymbolFlags::IS_PARAMETER)
}
@@ -173,7 +173,7 @@ impl SymbolTable {
/// ## Panics
/// If the ID is not valid for this symbol table.
#[track_caller]
pub(crate) fn symbol(&self, id: ScopedSymbolId) -> &Symbol {
pub(super) fn symbol(&self, id: ScopedSymbolId) -> &Symbol {
&self.symbols[id]
}
@@ -182,19 +182,19 @@ impl SymbolTable {
/// ## Panics
/// If the ID is not valid for this symbol table.
#[track_caller]
pub(crate) fn symbol_mut(&mut self, id: ScopedSymbolId) -> &mut Symbol {
pub(super) fn symbol_mut(&mut self, id: ScopedSymbolId) -> &mut Symbol {
&mut self.symbols[id]
}
/// Look up the ID of a symbol by its name.
pub(crate) fn symbol_id(&self, name: &str) -> Option<ScopedSymbolId> {
pub(super) fn symbol_id(&self, name: &str) -> Option<ScopedSymbolId> {
self.map
.find(Self::hash_name(name), |id| self.symbols[*id].name == name)
.copied()
}
/// Iterate over the symbols in this symbol table.
pub(crate) fn iter(&self) -> std::slice::Iter<'_, Symbol> {
pub(super) fn iter(&self) -> std::slice::Iter<'_, Symbol> {
self.symbols.iter()
}

View File

@@ -244,7 +244,7 @@ use ruff_index::{IndexVec, newtype_index};
use rustc_hash::FxHashMap;
use crate::node_key::NodeKey;
use crate::place::BoundnessAnalysis;
use crate::place_qualifiers::BoundnessAnalysis;
use crate::semantic_index::ast_ids::ScopedUseId;
use crate::semantic_index::definition::{Definition, DefinitionState};
use crate::semantic_index::member::ScopedMemberId;
@@ -266,13 +266,12 @@ use crate::semantic_index::use_def::place_state::{
LiveDeclarationsIterator, PlaceState, PreviousDefinitions, ScopedDefinitionId,
};
use crate::semantic_index::{EnclosingSnapshotResult, SemanticIndex};
use crate::types::{IntersectionBuilder, Truthiness, Type, infer_narrowing_constraint};
mod place_state;
/// Applicable definitions and constraints for every use of a name.
#[derive(Debug, PartialEq, Eq, salsa::Update, get_size2::GetSize)]
pub(crate) struct UseDefMap<'db> {
pub struct UseDefMap<'db> {
/// Array of [`Definition`] in this scope. Only the first entry should be [`DefinitionState::Undefined`];
/// this represents the implicit "unbound"/"undeclared" definition of every place.
all_definitions: IndexVec<ScopedDefinitionId, DefinitionState<'db>>,
@@ -348,23 +347,20 @@ pub(crate) struct UseDefMap<'db> {
end_of_scope_reachability: ScopedReachabilityConstraintId,
}
pub(crate) enum ApplicableConstraints<'map, 'db> {
pub enum ApplicableConstraints<'map, 'db> {
UnboundBinding(ConstraintsIterator<'map, 'db>),
ConstrainedBindings(BindingWithConstraintsIterator<'map, 'db>),
}
impl<'db> UseDefMap<'db> {
pub(crate) fn bindings_at_use(
&self,
use_id: ScopedUseId,
) -> BindingWithConstraintsIterator<'_, 'db> {
pub fn bindings_at_use(&self, use_id: ScopedUseId) -> BindingWithConstraintsIterator<'_, 'db> {
self.bindings_iterator(
&self.bindings_by_use[use_id],
BoundnessAnalysis::BasedOnUnboundVisibility,
)
}
pub(crate) fn applicable_constraints(
pub fn applicable_constraints(
&self,
constraint_key: ConstraintKey,
enclosing_scope: FileScopeId,
@@ -394,36 +390,33 @@ impl<'db> UseDefMap<'db> {
}
}
pub(super) fn is_reachable(
&self,
db: &dyn crate::Db,
reachability: ScopedReachabilityConstraintId,
) -> bool {
self.reachability_constraints
.evaluate(db, &self.predicates, reachability)
.may_be_true()
/// Returns a reference to the reachability constraints.
pub fn reachability_constraints(&self) -> &ReachabilityConstraints {
&self.reachability_constraints
}
/// Check whether or not a given expression is reachable from the start of the scope. This
/// is a local analysis which does not capture the possibility that the entire scope might
/// be unreachable. Use [`super::SemanticIndex::is_node_reachable`] for the global
/// analysis.
/// Returns a reference to the predicates.
pub fn predicates(&self) -> &Predicates<'db> {
&self.predicates
}
/// Returns the end-of-scope reachability constraint ID.
pub fn end_of_scope_reachability(&self) -> ScopedReachabilityConstraintId {
self.end_of_scope_reachability
}
/// Returns the reachability constraint ID for a given node.
///
/// # Panics
/// Panics if the node key is not in the reachability map.
#[track_caller]
pub(super) fn is_node_reachable(&self, db: &dyn crate::Db, node_key: NodeKey) -> bool {
self
.reachability_constraints
.evaluate(
db,
&self.predicates,
*self
.node_reachability
.get(&node_key)
.expect("`is_node_reachable` should only be called on AST nodes with recorded reachability"),
)
.may_be_true()
pub fn node_reachability(&self, node_key: NodeKey) -> ScopedReachabilityConstraintId {
*self.node_reachability.get(&node_key).expect(
"`node_reachability` should only be called on AST nodes with recorded reachability",
)
}
pub(crate) fn end_of_scope_bindings(
pub fn end_of_scope_bindings(
&self,
place: ScopedPlaceId,
) -> BindingWithConstraintsIterator<'_, 'db> {
@@ -433,7 +426,7 @@ impl<'db> UseDefMap<'db> {
}
}
pub(crate) fn end_of_scope_symbol_bindings(
pub fn end_of_scope_symbol_bindings(
&self,
symbol: ScopedSymbolId,
) -> BindingWithConstraintsIterator<'_, 'db> {
@@ -453,7 +446,7 @@ impl<'db> UseDefMap<'db> {
)
}
pub(crate) fn reachable_bindings(
pub fn reachable_bindings(
&self,
place: ScopedPlaceId,
) -> BindingWithConstraintsIterator<'_, 'db> {
@@ -463,7 +456,7 @@ impl<'db> UseDefMap<'db> {
}
}
pub(crate) fn reachable_symbol_bindings(
pub fn reachable_symbol_bindings(
&self,
symbol: ScopedSymbolId,
) -> BindingWithConstraintsIterator<'_, 'db> {
@@ -471,7 +464,7 @@ impl<'db> UseDefMap<'db> {
self.bindings_iterator(bindings, BoundnessAnalysis::AssumeBound)
}
pub(crate) fn reachable_member_bindings(
pub fn reachable_member_bindings(
&self,
symbol: ScopedMemberId,
) -> BindingWithConstraintsIterator<'_, 'db> {
@@ -501,7 +494,7 @@ impl<'db> UseDefMap<'db> {
}
}
pub(crate) fn bindings_at_definition(
pub fn bindings_at_definition(
&self,
definition: Definition<'db>,
) -> BindingWithConstraintsIterator<'_, 'db> {
@@ -511,7 +504,7 @@ impl<'db> UseDefMap<'db> {
)
}
pub(crate) fn declarations_at_binding(
pub fn declarations_at_binding(
&self,
binding: Definition<'db>,
) -> DeclarationsIterator<'_, 'db> {
@@ -521,7 +514,7 @@ impl<'db> UseDefMap<'db> {
)
}
pub(crate) fn end_of_scope_declarations<'map>(
pub fn end_of_scope_declarations<'map>(
&'map self,
place: ScopedPlaceId,
) -> DeclarationsIterator<'map, 'db> {
@@ -531,7 +524,7 @@ impl<'db> UseDefMap<'db> {
}
}
pub(crate) fn end_of_scope_symbol_declarations<'map>(
pub fn end_of_scope_symbol_declarations<'map>(
&'map self,
symbol: ScopedSymbolId,
) -> DeclarationsIterator<'map, 'db> {
@@ -547,7 +540,7 @@ impl<'db> UseDefMap<'db> {
self.declarations_iterator(declarations, BoundnessAnalysis::BasedOnUnboundVisibility)
}
pub(crate) fn reachable_symbol_declarations(
pub fn reachable_symbol_declarations(
&self,
symbol: ScopedSymbolId,
) -> DeclarationsIterator<'_, 'db> {
@@ -555,7 +548,7 @@ impl<'db> UseDefMap<'db> {
self.declarations_iterator(declarations, BoundnessAnalysis::AssumeBound)
}
pub(crate) fn reachable_member_declarations(
pub fn reachable_member_declarations(
&self,
member: ScopedMemberId,
) -> DeclarationsIterator<'_, 'db> {
@@ -563,17 +556,14 @@ impl<'db> UseDefMap<'db> {
self.declarations_iterator(declarations, BoundnessAnalysis::AssumeBound)
}
pub(crate) fn reachable_declarations(
&self,
place: ScopedPlaceId,
) -> DeclarationsIterator<'_, 'db> {
pub fn reachable_declarations(&self, place: ScopedPlaceId) -> DeclarationsIterator<'_, 'db> {
match place {
ScopedPlaceId::Symbol(symbol) => self.reachable_symbol_declarations(symbol),
ScopedPlaceId::Member(member) => self.reachable_member_declarations(member),
}
}
pub(crate) fn all_end_of_scope_symbol_declarations<'map>(
pub fn all_end_of_scope_symbol_declarations<'map>(
&'map self,
) -> impl Iterator<Item = (ScopedSymbolId, DeclarationsIterator<'map, 'db>)> + 'map {
self.end_of_scope_symbols
@@ -581,7 +571,7 @@ impl<'db> UseDefMap<'db> {
.map(|symbol_id| (symbol_id, self.end_of_scope_symbol_declarations(symbol_id)))
}
pub(crate) fn all_end_of_scope_symbol_bindings<'map>(
pub fn all_end_of_scope_symbol_bindings<'map>(
&'map self,
) -> impl Iterator<Item = (ScopedSymbolId, BindingWithConstraintsIterator<'map, 'db>)> + 'map
{
@@ -590,7 +580,7 @@ impl<'db> UseDefMap<'db> {
.map(|symbol_id| (symbol_id, self.end_of_scope_symbol_bindings(symbol_id)))
}
pub(crate) fn all_reachable_symbols<'map>(
pub fn all_reachable_symbols<'map>(
&'map self,
) -> impl Iterator<
Item = (
@@ -614,26 +604,6 @@ impl<'db> UseDefMap<'db> {
)
}
/// This function is intended to be called only once inside `TypeInferenceBuilder::infer_function_body`.
pub(crate) fn can_implicitly_return_none(&self, db: &dyn crate::Db) -> bool {
!self
.reachability_constraints
.evaluate(db, &self.predicates, self.end_of_scope_reachability)
.is_always_false()
}
pub(crate) fn binding_reachability(
&self,
db: &dyn crate::Db,
binding: &BindingWithConstraints<'_, 'db>,
) -> Truthiness {
self.reachability_constraints.evaluate(
db,
&self.predicates,
binding.reachability_constraint,
)
}
fn bindings_iterator<'map>(
&'map self,
bindings: &'map Bindings,
@@ -674,10 +644,10 @@ impl<'db> UseDefMap<'db> {
/// There is a unique ID for each distinct [`EnclosingSnapshotKey`] in the file.
#[newtype_index]
#[derive(get_size2::GetSize)]
pub(crate) struct ScopedEnclosingSnapshotId;
pub struct ScopedEnclosingSnapshotId;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, get_size2::GetSize)]
pub(crate) struct EnclosingSnapshotKey {
pub struct EnclosingSnapshotKey {
/// The enclosing scope containing the bindings
pub(crate) enclosing_scope: FileScopeId,
/// The referenced place (in the enclosing scope)
@@ -695,12 +665,12 @@ pub(crate) struct EnclosingSnapshotKey {
type EnclosingSnapshots = IndexVec<ScopedEnclosingSnapshotId, EnclosingSnapshot>;
#[derive(Clone, Debug)]
pub(crate) struct BindingWithConstraintsIterator<'map, 'db> {
pub struct BindingWithConstraintsIterator<'map, 'db> {
all_definitions: &'map IndexVec<ScopedDefinitionId, DefinitionState<'db>>,
pub(crate) predicates: &'map Predicates<'db>,
pub(crate) narrowing_constraints: &'map NarrowingConstraints,
pub(crate) reachability_constraints: &'map ReachabilityConstraints,
pub(crate) boundness_analysis: BoundnessAnalysis,
pub predicates: &'map Predicates<'db>,
pub narrowing_constraints: &'map NarrowingConstraints,
pub reachability_constraints: &'map ReachabilityConstraints,
pub boundness_analysis: BoundnessAnalysis,
inner: LiveBindingsIterator<'map>,
}
@@ -727,13 +697,13 @@ impl<'map, 'db> Iterator for BindingWithConstraintsIterator<'map, 'db> {
impl std::iter::FusedIterator for BindingWithConstraintsIterator<'_, '_> {}
pub(crate) struct BindingWithConstraints<'map, 'db> {
pub(crate) binding: DefinitionState<'db>,
pub(crate) narrowing_constraint: ConstraintsIterator<'map, 'db>,
pub(crate) reachability_constraint: ScopedReachabilityConstraintId,
pub struct BindingWithConstraints<'map, 'db> {
pub binding: DefinitionState<'db>,
pub narrowing_constraint: ConstraintsIterator<'map, 'db>,
pub reachability_constraint: ScopedReachabilityConstraintId,
}
pub(crate) struct ConstraintsIterator<'map, 'db> {
pub struct ConstraintsIterator<'map, 'db> {
predicates: &'map Predicates<'db>,
constraint_ids: NarrowingConstraintsIterator<'map>,
}
@@ -750,45 +720,21 @@ impl<'db> Iterator for ConstraintsIterator<'_, 'db> {
impl std::iter::FusedIterator for ConstraintsIterator<'_, '_> {}
impl<'db> ConstraintsIterator<'_, 'db> {
pub(crate) fn narrow(
self,
db: &'db dyn crate::Db,
base_ty: Type<'db>,
place: ScopedPlaceId,
) -> Type<'db> {
let constraint_tys: Vec<_> = self
.filter_map(|constraint| infer_narrowing_constraint(db, constraint, place))
.collect();
if constraint_tys.is_empty() {
base_ty
} else {
constraint_tys
.into_iter()
.rev()
.fold(
IntersectionBuilder::new(db).add_positive(base_ty),
IntersectionBuilder::add_positive,
)
.build()
}
}
}
// The narrow() method was moved to ty_python_types::place as it depends on type inference
#[derive(Clone)]
pub(crate) struct DeclarationsIterator<'map, 'db> {
pub struct DeclarationsIterator<'map, 'db> {
all_definitions: &'map IndexVec<ScopedDefinitionId, DefinitionState<'db>>,
pub(crate) predicates: &'map Predicates<'db>,
pub(crate) reachability_constraints: &'map ReachabilityConstraints,
pub(crate) boundness_analysis: BoundnessAnalysis,
pub predicates: &'map Predicates<'db>,
pub reachability_constraints: &'map ReachabilityConstraints,
pub boundness_analysis: BoundnessAnalysis,
inner: LiveDeclarationsIterator<'map>,
}
#[derive(Debug)]
pub(crate) struct DeclarationWithConstraint<'db> {
pub(crate) declaration: DefinitionState<'db>,
pub(crate) reachability_constraint: ScopedReachabilityConstraintId,
pub struct DeclarationWithConstraint<'db> {
pub declaration: DefinitionState<'db>,
pub reachability_constraint: ScopedReachabilityConstraintId,
}
impl<'db> Iterator for DeclarationsIterator<'_, 'db> {

View File

@@ -7,9 +7,9 @@ use itertools::Either;
use crate::Db;
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) struct OutOfBoundsError;
pub struct OutOfBoundsError;
pub(crate) trait PyIndex<'db> {
pub trait PyIndex<'db> {
type Item: 'db;
fn py_index(self, db: &'db dyn Db, index: i32) -> Result<Self::Item, OutOfBoundsError>;
@@ -41,13 +41,13 @@ enum Position {
AfterEnd,
}
pub(crate) enum Nth {
pub enum Nth {
FromStart(usize),
FromEnd(usize),
}
impl Nth {
pub(crate) fn from_index(index: i32) -> Self {
pub fn from_index(index: i32) -> Self {
if index >= 0 {
Nth::FromStart(from_nonnegative_i32(index))
} else {
@@ -105,9 +105,9 @@ where
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) struct StepSizeZeroError;
pub struct StepSizeZeroError;
pub(crate) trait PySlice<'db> {
pub trait PySlice<'db> {
type Item: 'db;
fn py_slice(

View File

@@ -19,7 +19,6 @@ use crate::suppression::parser::{
ParseError, ParseErrorKind, SuppressionComment, SuppressionParser,
};
use crate::suppression::unused::check_unused_suppressions;
use crate::types::TypeCheckDiagnostics;
use crate::{Db, declare_lint, lint::LintId};
declare_lint! {
@@ -97,7 +96,7 @@ declare_lint! {
}
#[salsa::tracked(returns(ref), heap_size=ruff_memory_usage::heap_size)]
pub(crate) fn suppressions(db: &dyn Db, file: File) -> Suppressions {
pub fn suppressions(db: &dyn Db, file: File) -> Suppressions {
let parsed = parsed_module(db, file).load(db);
let source = source_text(db, file);
@@ -152,7 +151,7 @@ pub(crate) fn suppressions(db: &dyn Db, file: File) -> Suppressions {
builder.finish()
}
pub(crate) fn check_suppressions(
pub fn check_suppressions(
db: &dyn Db,
file: File,
diagnostics: TypeCheckDiagnostics,
@@ -252,7 +251,7 @@ impl<'a> CheckSuppressionsContext<'a> {
///
/// This type exists to separate the phases of "check if a diagnostic should
/// be reported" and "build the actual diagnostic."
pub(crate) struct SuppressionDiagnosticGuardBuilder<'ctx, 'db> {
pub struct SuppressionDiagnosticGuardBuilder<'ctx, 'db> {
ctx: &'ctx CheckSuppressionsContext<'db>,
id: DiagnosticId,
range: TextRange,
@@ -296,7 +295,7 @@ impl<'ctx, 'db> SuppressionDiagnosticGuardBuilder<'ctx, 'db> {
/// The suppressions of a single file.
#[derive(Debug, Eq, PartialEq, get_size2::GetSize)]
pub(crate) struct Suppressions {
pub struct Suppressions {
/// Suppressions that apply to the entire file.
///
/// The suppressions are sorted by [`Suppression::comment_range`] and the [`Suppression::suppressed_range`]
@@ -320,7 +319,7 @@ pub(crate) struct Suppressions {
}
impl Suppressions {
pub(crate) fn find_suppression(&self, range: TextRange, id: LintId) -> Option<&Suppression> {
pub fn find_suppression(&self, range: TextRange, id: LintId) -> Option<&Suppression> {
self.lint_suppressions(range, id).next()
}
@@ -371,7 +370,7 @@ impl Suppressions {
}
}
pub(crate) type SuppressionsIter<'a> =
pub type SuppressionsIter<'a> =
std::iter::Chain<std::slice::Iter<'a, Suppression>, std::slice::Iter<'a, Suppression>>;
impl<'a> IntoIterator for &'a Suppressions {
@@ -389,7 +388,7 @@ impl<'a> IntoIterator for &'a Suppressions {
/// create multiple suppressions: one for every code.
/// They all share the same `comment_range`.
#[derive(Clone, Debug, Eq, PartialEq, get_size2::GetSize)]
pub(crate) struct Suppression {
pub struct Suppression {
target: SuppressionTarget,
kind: SuppressionKind,
@@ -429,7 +428,7 @@ impl Suppression {
}
}
pub(crate) fn id(&self) -> FileSuppressionId {
pub fn id(&self) -> FileSuppressionId {
FileSuppressionId(self.range)
}
}
@@ -469,7 +468,90 @@ impl fmt::Display for SuppressionKind {
/// This is unique enough because it is its exact
/// location in the source.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, get_size2::GetSize)]
pub(crate) struct FileSuppressionId(TextRange);
pub struct FileSuppressionId(TextRange);
/// A collection of type check diagnostics.
///
/// This struct is kept in `ty_python_semantic` to avoid circular dependencies,
/// since it's used by the suppression system.
#[derive(Default, Eq, PartialEq, get_size2::GetSize)]
pub struct TypeCheckDiagnostics {
diagnostics: Vec<Diagnostic>,
used_suppressions: rustc_hash::FxHashSet<FileSuppressionId>,
}
impl TypeCheckDiagnostics {
pub fn push(&mut self, diagnostic: Diagnostic) {
self.diagnostics.push(diagnostic);
}
pub fn extend(&mut self, other: &TypeCheckDiagnostics) {
self.diagnostics.extend_from_slice(&other.diagnostics);
self.used_suppressions.extend(&other.used_suppressions);
}
pub fn extend_diagnostics(&mut self, diagnostics: impl IntoIterator<Item = Diagnostic>) {
self.diagnostics.extend(diagnostics);
}
pub fn mark_used(&mut self, suppression_id: FileSuppressionId) {
self.used_suppressions.insert(suppression_id);
}
pub fn is_used(&self, suppression_id: FileSuppressionId) -> bool {
self.used_suppressions.contains(&suppression_id)
}
pub fn used_len(&self) -> usize {
self.used_suppressions.len()
}
pub fn shrink_to_fit(&mut self) {
self.used_suppressions.shrink_to_fit();
self.diagnostics.shrink_to_fit();
}
pub fn into_diagnostics(self) -> Vec<Diagnostic> {
self.diagnostics
}
pub fn is_empty(&self) -> bool {
self.diagnostics.is_empty() && self.used_suppressions.is_empty()
}
pub fn iter(&self) -> std::slice::Iter<'_, Diagnostic> {
self.diagnostics().iter()
}
fn diagnostics(&self) -> &[Diagnostic] {
self.diagnostics.as_slice()
}
}
impl std::fmt::Debug for TypeCheckDiagnostics {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.diagnostics().fmt(f)
}
}
impl IntoIterator for TypeCheckDiagnostics {
type Item = Diagnostic;
type IntoIter = std::vec::IntoIter<Diagnostic>;
fn into_iter(self) -> Self::IntoIter {
self.into_diagnostics().into_iter()
}
}
impl<'a> IntoIterator for &'a TypeCheckDiagnostics {
type Item = &'a Diagnostic;
type IntoIter = std::slice::Iter<'a, Diagnostic>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, get_size2::GetSize)]
enum SuppressionTarget {

View File

@@ -0,0 +1,65 @@
//! The `Truthiness` enum represents the possible boolean evaluation of a value.
/// The possible boolean values of a value.
///
/// This is used for type narrowing and reachability analysis.
#[derive(Debug, Copy, Clone, PartialEq, Eq, get_size2::GetSize)]
pub enum Truthiness {
/// For an object `x`, `bool(x)` will always return `True`
AlwaysTrue,
/// For an object `x`, `bool(x)` will always return `False`
AlwaysFalse,
/// For an object `x`, `bool(x)` could return either `True` or `False`
Ambiguous,
}
impl Truthiness {
pub const fn is_ambiguous(self) -> bool {
matches!(self, Truthiness::Ambiguous)
}
pub const fn is_always_false(self) -> bool {
matches!(self, Truthiness::AlwaysFalse)
}
pub const fn may_be_true(self) -> bool {
!self.is_always_false()
}
pub const fn is_always_true(self) -> bool {
matches!(self, Truthiness::AlwaysTrue)
}
#[must_use]
pub const fn negate(self) -> Self {
match self {
Self::AlwaysTrue => Self::AlwaysFalse,
Self::AlwaysFalse => Self::AlwaysTrue,
Self::Ambiguous => Self::Ambiguous,
}
}
#[must_use]
pub const fn negate_if(self, condition: bool) -> Self {
if condition { self.negate() } else { self }
}
#[must_use]
pub fn or(self, other: Self) -> Self {
match (self, other) {
(Truthiness::AlwaysFalse, Truthiness::AlwaysFalse) => Truthiness::AlwaysFalse,
(Truthiness::AlwaysTrue, _) | (_, Truthiness::AlwaysTrue) => Truthiness::AlwaysTrue,
_ => Truthiness::Ambiguous,
}
}
}
impl From<bool> for Truthiness {
fn from(value: bool) -> Self {
if value {
Truthiness::AlwaysTrue
} else {
Truthiness::AlwaysFalse
}
}
}

View File

@@ -27,51 +27,47 @@ use crate::semantic_index::scope::{FileScopeId, ScopeId};
/// * a field of a type that is a return type of a cross-module query
/// * an argument of a cross-module query
#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)]
pub(crate) struct Unpack<'db> {
pub(crate) file: File,
pub struct Unpack<'db> {
pub file: File,
pub(crate) value_file_scope: FileScopeId,
pub value_file_scope: FileScopeId,
pub(crate) target_file_scope: FileScopeId,
pub target_file_scope: FileScopeId,
/// The target expression that is being unpacked. For example, in `(a, b) = (1, 2)`, the target
/// expression is `(a, b)`.
#[no_eq]
#[tracked]
#[returns(ref)]
pub(crate) _target: AstNodeRef<ast::Expr>,
pub _target: AstNodeRef<ast::Expr>,
/// The ingredient representing the value expression of the unpacking. For example, in
/// `(a, b) = (1, 2)`, the value expression is `(1, 2)`.
pub(crate) value: UnpackValue<'db>,
pub value: UnpackValue<'db>,
}
// The Salsa heap is tracked separately.
impl get_size2::GetSize for Unpack<'_> {}
impl<'db> Unpack<'db> {
pub(crate) fn target<'ast>(
self,
db: &'db dyn Db,
parsed: &'ast ParsedModuleRef,
) -> &'ast ast::Expr {
pub fn target<'ast>(self, db: &'db dyn Db, parsed: &'ast ParsedModuleRef) -> &'ast ast::Expr {
self._target(db).node(parsed)
}
/// Returns the scope where the unpack target expression belongs to.
pub(crate) fn target_scope(self, db: &'db dyn Db) -> ScopeId<'db> {
pub fn target_scope(self, db: &'db dyn Db) -> ScopeId<'db> {
self.target_file_scope(db).to_scope_id(db, self.file(db))
}
/// Returns the range of the unpack target expression.
pub(crate) fn range(self, db: &'db dyn Db, module: &ParsedModuleRef) -> TextRange {
pub fn range(self, db: &'db dyn Db, module: &ParsedModuleRef) -> TextRange {
self.target(db, module).range()
}
}
/// The expression that is being unpacked.
#[derive(Clone, Copy, Debug, Hash, salsa::Update, get_size2::GetSize)]
pub(crate) struct UnpackValue<'db> {
pub struct UnpackValue<'db> {
/// The kind of unpack expression
kind: UnpackKind,
/// The expression we are unpacking
@@ -79,17 +75,17 @@ pub(crate) struct UnpackValue<'db> {
}
impl<'db> UnpackValue<'db> {
pub(crate) fn new(kind: UnpackKind, expression: Expression<'db>) -> Self {
pub fn new(kind: UnpackKind, expression: Expression<'db>) -> Self {
Self { kind, expression }
}
/// Returns the underlying [`Expression`] that is being unpacked.
pub(crate) const fn expression(self) -> Expression<'db> {
pub const fn expression(self) -> Expression<'db> {
self.expression
}
/// Returns the expression as an [`AnyNodeRef`].
pub(crate) fn as_any_node_ref<'ast>(
pub fn as_any_node_ref<'ast>(
self,
db: &'db dyn Db,
module: &'ast ParsedModuleRef,
@@ -97,19 +93,19 @@ impl<'db> UnpackValue<'db> {
self.expression().node_ref(db, module).into()
}
pub(crate) const fn kind(self) -> UnpackKind {
pub const fn kind(self) -> UnpackKind {
self.kind
}
}
#[derive(Clone, Copy, Debug, Hash, salsa::Update, get_size2::GetSize)]
pub(crate) enum EvaluationMode {
pub enum EvaluationMode {
Sync,
Async,
}
impl EvaluationMode {
pub(crate) const fn from_is_async(is_async: bool) -> Self {
pub const fn from_is_async(is_async: bool) -> Self {
if is_async {
EvaluationMode::Async
} else {
@@ -117,13 +113,13 @@ impl EvaluationMode {
}
}
pub(crate) const fn is_async(self) -> bool {
pub const fn is_async(self) -> bool {
matches!(self, EvaluationMode::Async)
}
}
#[derive(Clone, Copy, Debug, Hash, salsa::Update, get_size2::GetSize)]
pub(crate) enum UnpackKind {
pub enum UnpackKind {
/// An iterable expression like the one in a `for` loop or a comprehension.
Iterable { mode: EvaluationMode },
/// An context manager expression like the one in a `with` statement.
@@ -134,7 +130,7 @@ pub(crate) enum UnpackKind {
/// The position of the target element in an unpacking.
#[derive(Clone, Copy, Debug, Hash, PartialEq, salsa::Update, get_size2::GetSize)]
pub(crate) enum UnpackPosition {
pub enum UnpackPosition {
/// The target element is in the first position of the unpacking.
First,
/// The target element is in the position other than the first position of the unpacking.

View File

@@ -0,0 +1,80 @@
[package]
name = "ty_python_types"
version = "0.0.0"
publish = false
authors = { workspace = true }
edition = { workspace = true }
rust-version = { workspace = true }
homepage = { workspace = true }
documentation = { workspace = true }
repository = { workspace = true }
license = { workspace = true }
[dependencies]
ty_python_semantic = { workspace = true }
ty_module_resolver = { workspace = true }
ruff_db = { workspace = true }
ruff_diagnostics = { workspace = true }
ruff_macros = { workspace = true }
ruff_memory_usage = { workspace = true }
ruff_python_ast = { workspace = true, features = ["salsa"] }
ruff_python_parser = { workspace = true }
ruff_python_stdlib = { workspace = true }
ruff_source_file = { workspace = true }
ruff_text_size = { workspace = true }
ruff_python_literal = { workspace = true }
bitflags = { workspace = true }
compact_str = { workspace = true }
drop_bomb = { workspace = true }
get-size2 = { workspace = true, features = ["indexmap", "ordermap"] }
indexmap = { workspace = true }
itertools = { workspace = true }
ordermap = { workspace = true }
salsa = { workspace = true, features = ["compact_str", "ordermap"] }
tracing = { workspace = true }
rustc-hash = { workspace = true }
schemars = { workspace = true, optional = true }
serde = { workspace = true, optional = true }
serde_json = { workspace = true, optional = true }
smallvec = { workspace = true }
static_assertions = { workspace = true }
test-case = { workspace = true }
memchr = { workspace = true }
strum = { workspace = true }
strum_macros = { workspace = true }
[dev-dependencies]
ruff_db = { workspace = true, features = ["testing", "os"] }
ruff_python_parser = { workspace = true }
ty_python_semantic = { workspace = true, features = ["testing"] }
ty_static = { workspace = true }
ty_test = { workspace = true }
ty_vendored = { workspace = true }
anyhow = { workspace = true }
datatest-stable = { workspace = true }
glob = { workspace = true }
indoc = { workspace = true }
insta = { workspace = true }
pretty_assertions = { workspace = true }
quickcheck = { version = "1.0.3", default-features = false }
quickcheck_macros = { version = "1.0.0" }
camino = { workspace = true }
[features]
schemars = ["dep:schemars", "dep:serde_json"]
serde = ["ruff_db/serde", "dep:serde", "ruff_python_ast/serde", "ty_python_semantic/serde"]
testing = ["ty_python_semantic/testing"]
[[test]]
name = "mdtest"
harness = false
[lints]
workspace = true
[package.metadata.cargo-shear]
# Used via macro expansion.
ignored = ["ruff_macros"]

Some files were not shown because too many files have changed in this diff Show More