mirror of https://github.com/astral-sh/ruff
Merge branch 'main' into alex/protocol-property-check-2
This commit is contained in:
commit
f57ea60d05
|
|
@ -213,3 +213,17 @@ async def get_id_pydantic_full(
|
|||
async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()]): ...
|
||||
@app.get("/{my_id}")
|
||||
async def get_id_init_not_annotated(params = Depends(InitParams)): ...
|
||||
|
||||
@app.get("/things/{ thing_id }")
|
||||
async def read_thing(query: str):
|
||||
return {"query": query}
|
||||
|
||||
|
||||
@app.get("/things/{ thing_id : path }")
|
||||
async def read_thing(query: str):
|
||||
return {"query": query}
|
||||
|
||||
|
||||
@app.get("/things/{ thing_id : str }")
|
||||
async def read_thing(query: str):
|
||||
return {"query": query}
|
||||
|
|
|
|||
|
|
@ -192,3 +192,24 @@ def issue_19005_3():
|
|||
c = {}
|
||||
for a[0], a[1] in ():
|
||||
c[a[0]] = a[1]
|
||||
|
||||
|
||||
def issue_19153_1():
|
||||
v = {}
|
||||
for o, (x,) in ["ox"]:
|
||||
v[x,] = o
|
||||
return v
|
||||
|
||||
|
||||
def issue_19153_2():
|
||||
v = {}
|
||||
for (o, p), x in [("op", "x")]:
|
||||
v[x] = o, p
|
||||
return v
|
||||
|
||||
|
||||
def issue_19153_3():
|
||||
v = {}
|
||||
for o, (x,) in ["ox"]:
|
||||
v[(x,)] = o
|
||||
return v
|
||||
|
|
@ -4,6 +4,9 @@ print("שלום")
|
|||
# E2502
|
||||
example = "x" * 100 # "x" is assigned
|
||||
|
||||
# E2502
|
||||
another = "x" * 50 # "x" is assigned
|
||||
|
||||
# E2502
|
||||
if access_level != "none": # Check if admin ' and access_level != 'user
|
||||
print("You are an admin.")
|
||||
|
|
|
|||
|
|
@ -107,3 +107,6 @@ deque(f"{x}" "") # OK
|
|||
deque(t"")
|
||||
deque(t"" t"")
|
||||
deque(t"{""}") # OK
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/20050
|
||||
deque(f"{""}") # RUF037
|
||||
|
|
|
|||
|
|
@ -255,3 +255,8 @@ pub(crate) const fn is_trailing_comma_type_params_enabled(settings: &LinterSetti
|
|||
pub(crate) const fn is_maxsplit_without_separator_fix_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/20106
|
||||
pub(crate) const fn is_bidi_forbid_arabic_letter_mark_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -457,6 +457,9 @@ fn parameter_alias<'a>(parameter: &'a Parameter, semantic: &SemanticModel) -> Op
|
|||
///
|
||||
/// The iterator yields tuples of the parameter name and the range of the parameter in the input,
|
||||
/// inclusive of curly braces.
|
||||
///
|
||||
/// FastAPI only recognizes path parameters when there are no leading or trailing spaces around
|
||||
/// the parameter name. For example, `/{x}` is a valid parameter, but `/{ x }` is treated literally.
|
||||
#[derive(Debug)]
|
||||
struct PathParamIterator<'a> {
|
||||
input: &'a str,
|
||||
|
|
@ -483,7 +486,7 @@ impl<'a> Iterator for PathParamIterator<'a> {
|
|||
// We ignore text after a colon, since those are path converters
|
||||
// See also: https://fastapi.tiangolo.com/tutorial/path-params/?h=path#path-convertor
|
||||
let param_name_end = param_content.find(':').unwrap_or(param_content.len());
|
||||
let param_name = ¶m_content[..param_name_end].trim();
|
||||
let param_name = ¶m_content[..param_name_end];
|
||||
|
||||
#[expect(clippy::range_plus_one)]
|
||||
return Some((param_name, start..end + 1));
|
||||
|
|
|
|||
|
|
@ -59,25 +59,6 @@ help: Add `thing_id` to function signature
|
|||
23 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
FAST003 [*] Parameter `thing_id` appears in route path, but not in `read_thing` signature
|
||||
--> FAST003.py:24:19
|
||||
|
|
||||
24 | @app.get("/things/{thing_id : path}")
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
25 | async def read_thing(query: str):
|
||||
26 | return {"query": query}
|
||||
|
|
||||
help: Add `thing_id` to function signature
|
||||
22 |
|
||||
23 |
|
||||
24 | @app.get("/things/{thing_id : path}")
|
||||
- async def read_thing(query: str):
|
||||
25 + async def read_thing(query: str, thing_id):
|
||||
26 | return {"query": query}
|
||||
27 |
|
||||
28 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
FAST003 [*] Parameter `title` appears in route path, but not in `read_thing` signature
|
||||
--> FAST003.py:29:27
|
||||
|
|
||||
|
|
|
|||
|
|
@ -354,21 +354,37 @@ fn convert_to_dict_comprehension(
|
|||
"for"
|
||||
};
|
||||
// Handles the case where `key` has a trailing comma, e.g, `dict[x,] = y`
|
||||
let key_range = if let Expr::Tuple(ast::ExprTuple { elts, .. }) = key {
|
||||
let [expr] = elts.as_slice() else {
|
||||
let key_str = if let Expr::Tuple(ast::ExprTuple {
|
||||
elts,
|
||||
parenthesized,
|
||||
..
|
||||
}) = key
|
||||
{
|
||||
if elts.len() != 1 {
|
||||
return None;
|
||||
};
|
||||
expr.range()
|
||||
}
|
||||
if *parenthesized {
|
||||
locator.slice(key).to_string()
|
||||
} else {
|
||||
key.range()
|
||||
format!("({})", locator.slice(key))
|
||||
}
|
||||
} else {
|
||||
locator.slice(key).to_string()
|
||||
};
|
||||
let elt_str = format!(
|
||||
"{}: {}",
|
||||
locator.slice(key_range),
|
||||
locator.slice(value.range())
|
||||
);
|
||||
|
||||
let comprehension_str = format!("{{{elt_str} {for_type} {target_str} in {iter_str}{if_str}}}");
|
||||
// If the value is a tuple without parentheses, add them
|
||||
let value_str = if let Expr::Tuple(ast::ExprTuple {
|
||||
parenthesized: false,
|
||||
..
|
||||
}) = value
|
||||
{
|
||||
format!("({})", locator.slice(value))
|
||||
} else {
|
||||
locator.slice(value).to_string()
|
||||
};
|
||||
|
||||
let comprehension_str =
|
||||
format!("{{{key_str}: {value_str} {for_type} {target_str} in {iter_str}{if_str}}}");
|
||||
|
||||
let for_loop_inline_comments = comment_strings_in_range(
|
||||
checker,
|
||||
|
|
|
|||
|
|
@ -176,3 +176,36 @@ PERF403 Use a dictionary comprehension instead of a for-loop
|
|||
| ^^^^^^^
|
||||
|
|
||||
help: Replace for loop with dict comprehension
|
||||
|
||||
PERF403 Use a dictionary comprehension instead of a for-loop
|
||||
--> PERF403.py:200:9
|
||||
|
|
||||
198 | v = {}
|
||||
199 | for o, (x,) in ["ox"]:
|
||||
200 | v[x,] = o
|
||||
| ^^^^^^^^^
|
||||
201 | return v
|
||||
|
|
||||
help: Replace for loop with dict comprehension
|
||||
|
||||
PERF403 Use a dictionary comprehension instead of a for-loop
|
||||
--> PERF403.py:207:9
|
||||
|
|
||||
205 | v = {}
|
||||
206 | for (o, p), x in [("op", "x")]:
|
||||
207 | v[x] = o, p
|
||||
| ^^^^^^^^^^^
|
||||
208 | return v
|
||||
|
|
||||
help: Replace for loop with dict comprehension
|
||||
|
||||
PERF403 Use a dictionary comprehension instead of a for-loop
|
||||
--> PERF403.py:214:9
|
||||
|
|
||||
212 | v = {}
|
||||
213 | for o, (x,) in ["ox"]:
|
||||
214 | v[(x,)] = o
|
||||
| ^^^^^^^^^^^
|
||||
215 | return v
|
||||
|
|
||||
help: Replace for loop with dict comprehension
|
||||
|
|
|
|||
|
|
@ -372,8 +372,72 @@ help: Replace for loop with dict comprehension
|
|||
- v = {}
|
||||
- for o,(x,)in():
|
||||
- v[x,]=o
|
||||
170 + v = {x: o for o,(x,) in ()}
|
||||
170 + v = {(x,): o for o,(x,) in ()}
|
||||
171 |
|
||||
172 |
|
||||
173 | # https://github.com/astral-sh/ruff/issues/19005
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
PERF403 [*] Use a dictionary comprehension instead of a for-loop
|
||||
--> PERF403.py:200:9
|
||||
|
|
||||
198 | v = {}
|
||||
199 | for o, (x,) in ["ox"]:
|
||||
200 | v[x,] = o
|
||||
| ^^^^^^^^^
|
||||
201 | return v
|
||||
|
|
||||
help: Replace for loop with dict comprehension
|
||||
195 |
|
||||
196 |
|
||||
197 | def issue_19153_1():
|
||||
- v = {}
|
||||
- for o, (x,) in ["ox"]:
|
||||
- v[x,] = o
|
||||
198 + v = {(x,): o for o, (x,) in ["ox"]}
|
||||
199 | return v
|
||||
200 |
|
||||
201 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
PERF403 [*] Use a dictionary comprehension instead of a for-loop
|
||||
--> PERF403.py:207:9
|
||||
|
|
||||
205 | v = {}
|
||||
206 | for (o, p), x in [("op", "x")]:
|
||||
207 | v[x] = o, p
|
||||
| ^^^^^^^^^^^
|
||||
208 | return v
|
||||
|
|
||||
help: Replace for loop with dict comprehension
|
||||
202 |
|
||||
203 |
|
||||
204 | def issue_19153_2():
|
||||
- v = {}
|
||||
- for (o, p), x in [("op", "x")]:
|
||||
- v[x] = o, p
|
||||
205 + v = {x: (o, p) for (o, p), x in [("op", "x")]}
|
||||
206 | return v
|
||||
207 |
|
||||
208 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
PERF403 [*] Use a dictionary comprehension instead of a for-loop
|
||||
--> PERF403.py:214:9
|
||||
|
|
||||
212 | v = {}
|
||||
213 | for o, (x,) in ["ox"]:
|
||||
214 | v[(x,)] = o
|
||||
| ^^^^^^^^^^^
|
||||
215 | return v
|
||||
|
|
||||
help: Replace for loop with dict comprehension
|
||||
209 |
|
||||
210 |
|
||||
211 | def issue_19153_3():
|
||||
- v = {}
|
||||
- for o, (x,) in ["ox"]:
|
||||
- v[(x,)] = o
|
||||
212 + v = {(x,): o for o, (x,) in ["ox"]}
|
||||
213 | return v
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
|
|
|||
|
|
@ -252,6 +252,30 @@ mod tests {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(Rule::BidirectionalUnicode, Path::new("bidirectional_unicode.py"))]
|
||||
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!(
|
||||
"preview__{}_{}",
|
||||
rule_code.noqa_code(),
|
||||
path.to_string_lossy()
|
||||
);
|
||||
let diagnostics = test_path(
|
||||
Path::new("pylint").join(path).as_path(),
|
||||
&LinterSettings {
|
||||
pylint: pylint::settings::Settings {
|
||||
allow_dunder_method_names: FxHashSet::from_iter([
|
||||
"__special_custom_magic__".to_string()
|
||||
]),
|
||||
..pylint::settings::Settings::default()
|
||||
},
|
||||
preview: PreviewMode::Enabled,
|
||||
..LinterSettings::for_rule(rule_code)
|
||||
},
|
||||
)?;
|
||||
assert_diagnostics!(snapshot, diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn continue_in_finally() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_source_file::Line;
|
||||
|
||||
use crate::{Violation, checkers::ast::LintContext};
|
||||
use crate::{
|
||||
Violation, checkers::ast::LintContext, preview::is_bidi_forbid_arabic_letter_mark_enabled,
|
||||
};
|
||||
|
||||
const BIDI_UNICODE: [char; 10] = [
|
||||
'\u{202A}', //{LEFT-TO-RIGHT EMBEDDING}
|
||||
|
|
@ -60,7 +62,12 @@ impl Violation for BidirectionalUnicode {
|
|||
|
||||
/// PLE2502
|
||||
pub(crate) fn bidirectional_unicode(line: &Line, context: &LintContext) {
|
||||
if line.contains(BIDI_UNICODE) {
|
||||
if line.contains(BIDI_UNICODE)
|
||||
|| (is_bidi_forbid_arabic_letter_mark_enabled(context.settings())
|
||||
&& line.contains(
|
||||
'\u{061C}', //{ARABIC LETTER MARK}
|
||||
))
|
||||
{
|
||||
context.report_diagnostic(BidirectionalUnicode, line.full_range());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,21 +22,21 @@ PLE2502 Contains control characters that can permit obfuscated code
|
|||
|
|
||||
|
||||
PLE2502 Contains control characters that can permit obfuscated code
|
||||
--> bidirectional_unicode.py:8:1
|
||||
--> bidirectional_unicode.py:11:1
|
||||
|
|
||||
7 | # E2502
|
||||
8 | if access_level != "none": # Check if admin ' and access_level != 'user
|
||||
10 | # E2502
|
||||
11 | if access_level != "none": # Check if admin ' and access_level != 'user
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
9 | print("You are an admin.")
|
||||
12 | print("You are an admin.")
|
||||
|
|
||||
|
||||
PLE2502 Contains control characters that can permit obfuscated code
|
||||
--> bidirectional_unicode.py:14:1
|
||||
--> bidirectional_unicode.py:17:1
|
||||
|
|
||||
12 | # E2502
|
||||
13 | def subtract_funds(account: str, amount: int):
|
||||
14 | """Subtract funds from bank account then """
|
||||
15 | # E2502
|
||||
16 | def subtract_funds(account: str, amount: int):
|
||||
17 | """Subtract funds from bank account then """
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
15 | return
|
||||
16 | bank[account] -= amount
|
||||
18 | return
|
||||
19 | bank[account] -= amount
|
||||
|
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/pylint/mod.rs
|
||||
---
|
||||
PLE2502 Contains control characters that can permit obfuscated code
|
||||
--> bidirectional_unicode.py:2:1
|
||||
|
|
||||
1 | # E2502
|
||||
2 | print("שלום")
|
||||
| ^^^^^^^^^^^^^
|
||||
3 |
|
||||
4 | # E2502
|
||||
|
|
||||
|
||||
PLE2502 Contains control characters that can permit obfuscated code
|
||||
--> bidirectional_unicode.py:5:1
|
||||
|
|
||||
4 | # E2502
|
||||
5 | example = "x" * 100 # "x" is assigned
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
6 |
|
||||
7 | # E2502
|
||||
|
|
||||
|
||||
PLE2502 Contains control characters that can permit obfuscated code
|
||||
--> bidirectional_unicode.py:8:1
|
||||
|
|
||||
7 | # E2502
|
||||
8 | another = "x" * 50 # "x" is assigned
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
9 |
|
||||
10 | # E2502
|
||||
|
|
||||
|
||||
PLE2502 Contains control characters that can permit obfuscated code
|
||||
--> bidirectional_unicode.py:11:1
|
||||
|
|
||||
10 | # E2502
|
||||
11 | if access_level != "none": # Check if admin ' and access_level != 'user
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
12 | print("You are an admin.")
|
||||
|
|
||||
|
||||
PLE2502 Contains control characters that can permit obfuscated code
|
||||
--> bidirectional_unicode.py:17:1
|
||||
|
|
||||
15 | # E2502
|
||||
16 | def subtract_funds(account: str, amount: int):
|
||||
17 | """Subtract funds from bank account then """
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
18 | return
|
||||
19 | bank[account] -= amount
|
||||
|
|
||||
|
|
@ -30,6 +30,11 @@ use crate::{Applicability, Edit, Fix, FixAvailability, Violation};
|
|||
/// print()
|
||||
/// ```
|
||||
///
|
||||
/// ## Fix safety
|
||||
/// This fix is marked as unsafe if it removes an unused `sep` keyword argument
|
||||
/// that may have side effects. Removing such arguments may change the program's
|
||||
/// behavior by skipping the execution of those side effects.
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: `print`](https://docs.python.org/3/library/functions.html#print)
|
||||
#[derive(ViolationMetadata)]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use ruff_diagnostics::{Applicability, Edit};
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
|
||||
use ruff_python_ast::helpers::is_empty_f_string;
|
||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
||||
use ruff_python_ast::{self as ast, Expr};
|
||||
use ruff_text_size::Ranged;
|
||||
|
|
@ -102,7 +104,7 @@ pub(crate) fn unnecessary_literal_within_deque_call(checker: &Checker, deque: &a
|
|||
}
|
||||
Expr::StringLiteral(string) => string.value.is_empty(),
|
||||
Expr::BytesLiteral(bytes) => bytes.value.is_empty(),
|
||||
Expr::FString(fstring) => fstring.value.is_empty_literal(),
|
||||
Expr::FString(fstring) => is_empty_f_string(fstring),
|
||||
Expr::TString(tstring) => tstring.value.is_empty_iterable(),
|
||||
_ => false,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -369,6 +369,7 @@ help: Replace with `deque()`
|
|||
107 + deque()
|
||||
108 | deque(t"" t"")
|
||||
109 | deque(t"{""}") # OK
|
||||
110 |
|
||||
|
||||
RUF037 [*] Unnecessary empty iterable within a deque call
|
||||
--> RUF037.py:108:1
|
||||
|
|
@ -386,3 +387,19 @@ help: Replace with `deque()`
|
|||
- deque(t"" t"")
|
||||
108 + deque()
|
||||
109 | deque(t"{""}") # OK
|
||||
110 |
|
||||
111 | # https://github.com/astral-sh/ruff/issues/20050
|
||||
|
||||
RUF037 [*] Unnecessary empty iterable within a deque call
|
||||
--> RUF037.py:112:1
|
||||
|
|
||||
111 | # https://github.com/astral-sh/ruff/issues/20050
|
||||
112 | deque(f"{""}") # RUF037
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
help: Replace with `deque()`
|
||||
109 | deque(t"{""}") # OK
|
||||
110 |
|
||||
111 | # https://github.com/astral-sh/ruff/issues/20050
|
||||
- deque(f"{""}") # RUF037
|
||||
112 + deque() # RUF037
|
||||
|
|
|
|||
|
|
@ -1406,7 +1406,7 @@ fn is_non_empty_f_string(expr: &ast::ExprFString) -> bool {
|
|||
|
||||
/// Returns `true` if the expression definitely resolves to the empty string, when used as an f-string
|
||||
/// expression.
|
||||
fn is_empty_f_string(expr: &ast::ExprFString) -> bool {
|
||||
pub fn is_empty_f_string(expr: &ast::ExprFString) -> bool {
|
||||
fn inner(expr: &Expr) -> bool {
|
||||
match expr {
|
||||
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => value.is_empty(),
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ def test(): -> "int":
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20call-non-callable) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L112)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L113)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -58,7 +58,7 @@ Calling a non-callable object will raise a `TypeError` at runtime.
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-argument-forms) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L156)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L157)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -88,7 +88,7 @@ f(int) # error
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-declarations) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L182)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L183)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -117,7 +117,7 @@ a = 1
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-metaclass) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L207)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L208)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -147,7 +147,7 @@ class C(A, B): ...
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20cyclic-class-definition) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L233)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L234)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -177,7 +177,7 @@ class B(A): ...
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20duplicate-base) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L298)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L299)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -202,7 +202,7 @@ class B(A, A): ...
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20duplicate-kw-only) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L319)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L320)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -306,7 +306,7 @@ def test(): -> "Literal[5]":
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20inconsistent-mro) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L522)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L523)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -334,7 +334,7 @@ class C(A, B): ...
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20index-out-of-bounds) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L546)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L547)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -358,7 +358,7 @@ t[3] # IndexError: tuple index out of range
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20instance-layout-conflict) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L351)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L352)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -445,7 +445,7 @@ an atypical memory layout.
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-argument-type) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L591)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L592)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -470,7 +470,7 @@ func("foo") # error: [invalid-argument-type]
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-assignment) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L631)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L632)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -496,7 +496,7 @@ a: int = ''
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-attribute-access) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1665)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1666)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -528,7 +528,7 @@ C.instance_var = 3 # error: Cannot assign to instance variable
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-await) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L653)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L654)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -562,7 +562,7 @@ asyncio.run(main())
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-base) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L683)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L684)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -584,7 +584,7 @@ class A(42): ... # error: [invalid-base]
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-context-manager) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L734)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L735)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -609,7 +609,7 @@ with 1:
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-declaration) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L755)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L756)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -636,7 +636,7 @@ a: str
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-exception-caught) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L778)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L779)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -678,7 +678,7 @@ except ZeroDivisionError:
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-generic-class) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L814)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L815)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -709,7 +709,7 @@ class C[U](Generic[T]): ...
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-key) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L566)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L567)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -738,7 +738,7 @@ alice["height"] # KeyError: 'height'
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-legacy-type-variable) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L840)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L841)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -771,7 +771,7 @@ def f(t: TypeVar("U")): ...
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-metaclass) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L889)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L890)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -803,7 +803,7 @@ class B(metaclass=f): ...
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-named-tuple) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L496)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L497)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -833,7 +833,7 @@ TypeError: can only inherit from a NamedTuple type and Generic
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-overload) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L916)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L917)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -881,7 +881,7 @@ def foo(x: int) -> int: ...
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-parameter-default) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L959)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L960)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -905,7 +905,7 @@ def f(a: int = ''): ...
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-protocol) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L433)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L434)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -937,7 +937,7 @@ TypeError: Protocols can only inherit from other protocols, got <class 'int'>
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-raise) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L979)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L980)
|
||||
</small>
|
||||
|
||||
Checks for `raise` statements that raise non-exceptions or use invalid
|
||||
|
|
@ -984,7 +984,7 @@ def g():
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-return-type) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L612)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L613)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1007,7 +1007,7 @@ def func() -> int:
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-super-argument) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1022)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1023)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1061,7 +1061,7 @@ TODO #14889
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-alias-type) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L868)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L869)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1086,7 +1086,7 @@ NewAlias = TypeAliasType(get_name(), int) # error: TypeAliasType name mus
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-checking-constant) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1061)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1062)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1114,7 +1114,7 @@ TYPE_CHECKING = ''
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-form) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1085)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1086)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1142,7 +1142,7 @@ b: Annotated[int] # `Annotated` expects at least two arguments
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-guard-call) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1137)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1138)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1174,7 +1174,7 @@ f(10) # Error
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-guard-definition) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1109)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1110)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1206,7 +1206,7 @@ class C:
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-variable-constraints) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1165)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1166)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1239,7 +1239,7 @@ T = TypeVar('T', bound=str) # valid bound TypeVar
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20missing-argument) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1194)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1195)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1262,7 +1262,7 @@ func() # TypeError: func() missing 1 required positional argument: 'x'
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20missing-typed-dict-key) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1764)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1765)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1293,7 +1293,7 @@ alice["age"] # KeyError
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20no-matching-overload) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1213)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1214)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1320,7 +1320,7 @@ func("string") # error: [no-matching-overload]
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20non-subscriptable) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1236)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1237)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1342,7 +1342,7 @@ Subscripting an object that does not support it will raise a `TypeError` at runt
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20not-iterable) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1254)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1255)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1366,7 +1366,7 @@ for i in 34: # TypeError: 'int' object is not iterable
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20parameter-already-assigned) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1305)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1306)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1420,7 +1420,7 @@ def test(): -> "int":
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20static-assert-error) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1641)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1642)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1448,7 +1448,7 @@ static_assert(int(2.0 * 3.0) == 6) # error: does not have a statically known tr
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20subclass-of-final-class) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1396)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1397)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1475,7 +1475,7 @@ class B(A): ... # Error raised here
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20too-many-positional-arguments) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1441)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1442)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1500,7 +1500,7 @@ f("foo") # Error raised here
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20type-assertion-failure) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1419)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1420)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1526,7 +1526,7 @@ def _(x: int):
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unavailable-implicit-super-arguments) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1462)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1463)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1570,7 +1570,7 @@ class A:
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unknown-argument) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1519)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1520)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1595,7 +1595,7 @@ f(x=1, y=2) # Error raised here
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-attribute) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1540)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1541)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1621,7 +1621,7 @@ A().foo # AttributeError: 'A' object has no attribute 'foo'
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-import) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1562)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1563)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1644,7 +1644,7 @@ import foo # ModuleNotFoundError: No module named 'foo'
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-reference) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1581)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1582)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1667,7 +1667,7 @@ print(x) # NameError: name 'x' is not defined
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-bool-conversion) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1274)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1275)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1702,7 +1702,7 @@ b1 < b2 < b1 # exception raised here
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-operator) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1600)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1601)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1728,7 +1728,7 @@ A() + A() # TypeError: unsupported operand type(s) for +: 'A' and 'A'
|
|||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20zero-stepsize-in-slice) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1622)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1623)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1751,7 +1751,7 @@ l[1:10:0] # ValueError: slice step cannot be zero
|
|||
<small>
|
||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20ambiguous-protocol-member) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L461)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L462)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1790,7 +1790,7 @@ class SubProto(BaseProto, Protocol):
|
|||
<small>
|
||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20deprecated) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L277)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L278)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1843,7 +1843,7 @@ a = 20 / 0 # type: ignore
|
|||
<small>
|
||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unbound-attribute) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1326)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1327)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1869,7 +1869,7 @@ A.c # AttributeError: type object 'A' has no attribute 'c'
|
|||
<small>
|
||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unbound-implicit-call) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L130)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L131)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1899,7 +1899,7 @@ A()[0] # TypeError: 'A' object is not subscriptable
|
|||
<small>
|
||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unbound-import) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1348)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1349)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1929,7 +1929,7 @@ from module import a # ImportError: cannot import name 'a' from 'module'
|
|||
<small>
|
||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20redundant-cast) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1693)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1694)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -1954,7 +1954,7 @@ cast(int, f()) # Redundant
|
|||
<small>
|
||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20undefined-reveal) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1501)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1502)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -2005,7 +2005,7 @@ a = 20 / 0 # ty: ignore[division-by-zero]
|
|||
<small>
|
||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-global) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1714)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1715)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -2059,7 +2059,7 @@ def g():
|
|||
<small>
|
||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-base) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L701)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L702)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -2096,7 +2096,7 @@ class D(C): ... # error: [unsupported-base]
|
|||
<small>
|
||||
Default level: [`ignore`](../rules.md#rule-levels "This lint has a default level of 'ignore'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20division-by-zero) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L259)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L260)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
@ -2118,7 +2118,7 @@ Dividing by zero raises a `ZeroDivisionError` at runtime.
|
|||
<small>
|
||||
Default level: [`ignore`](../rules.md#rule-levels "This lint has a default level of 'ignore'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unresolved-reference) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1374)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1375)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
|
|
|||
|
|
@ -290,16 +290,10 @@ from overloaded import A, f
|
|||
|
||||
def _(x: int, y: A | int):
|
||||
reveal_type(f(x)) # revealed: int
|
||||
# TODO: revealed: int
|
||||
# TODO: no error
|
||||
# error: [no-matching-overload]
|
||||
reveal_type(f(*(x,))) # revealed: Unknown
|
||||
reveal_type(f(*(x,))) # revealed: int
|
||||
|
||||
reveal_type(f(y)) # revealed: A | int
|
||||
# TODO: revealed: A | int
|
||||
# TODO: no error
|
||||
# error: [no-matching-overload]
|
||||
reveal_type(f(*(y,))) # revealed: Unknown
|
||||
reveal_type(f(*(y,))) # revealed: A | int
|
||||
```
|
||||
|
||||
### Generics (PEP 695)
|
||||
|
|
@ -328,16 +322,10 @@ from overloaded import B, f
|
|||
|
||||
def _(x: int, y: B | int):
|
||||
reveal_type(f(x)) # revealed: int
|
||||
# TODO: revealed: int
|
||||
# TODO: no error
|
||||
# error: [no-matching-overload]
|
||||
reveal_type(f(*(x,))) # revealed: Unknown
|
||||
reveal_type(f(*(x,))) # revealed: int
|
||||
|
||||
reveal_type(f(y)) # revealed: B | int
|
||||
# TODO: revealed: B | int
|
||||
# TODO: no error
|
||||
# error: [no-matching-overload]
|
||||
reveal_type(f(*(y,))) # revealed: Unknown
|
||||
reveal_type(f(*(y,))) # revealed: B | int
|
||||
```
|
||||
|
||||
### Expanding `bool`
|
||||
|
|
@ -1236,21 +1224,14 @@ def _(integer: int, string: str, any: Any, list_any: list[Any]):
|
|||
reveal_type(f(*(integer, string))) # revealed: int
|
||||
|
||||
reveal_type(f(string, integer)) # revealed: int
|
||||
# TODO: revealed: int
|
||||
# TODO: no error
|
||||
# error: [no-matching-overload]
|
||||
reveal_type(f(*(string, integer))) # revealed: Unknown
|
||||
reveal_type(f(*(string, integer))) # revealed: int
|
||||
|
||||
# This matches the second overload and is _not_ the case of ambiguous overload matching.
|
||||
reveal_type(f(string, any)) # revealed: Any
|
||||
# TODO: Any
|
||||
reveal_type(f(*(string, any))) # revealed: tuple[str, Any]
|
||||
reveal_type(f(*(string, any))) # revealed: Any
|
||||
|
||||
reveal_type(f(string, list_any)) # revealed: list[Any]
|
||||
# TODO: revealed: list[Any]
|
||||
# TODO: no error
|
||||
# error: [no-matching-overload]
|
||||
reveal_type(f(*(string, list_any))) # revealed: Unknown
|
||||
reveal_type(f(*(string, list_any))) # revealed: list[Any]
|
||||
```
|
||||
|
||||
### Generic `self`
|
||||
|
|
|
|||
|
|
@ -522,6 +522,22 @@ c.name = None
|
|||
c.name = 42
|
||||
```
|
||||
|
||||
### Properties with no setters
|
||||
|
||||
<!-- snapshot-diagnostics -->
|
||||
|
||||
If a property has no setter, we emit a bespoke error message when a user attempts to set that
|
||||
attribute, since this is a common error.
|
||||
|
||||
```py
|
||||
class DontAssignToMe:
|
||||
@property
|
||||
def immutable(self): ...
|
||||
|
||||
# error: [invalid-assignment]
|
||||
DontAssignToMe().immutable = "the properties, they are a-changing"
|
||||
```
|
||||
|
||||
### Built-in `classmethod` descriptor
|
||||
|
||||
Similarly to `property`, `classmethod` decorator creates an implicit descriptor that binds the first
|
||||
|
|
|
|||
|
|
@ -260,6 +260,11 @@ def f(cond: bool) -> int:
|
|||
|
||||
<!-- snapshot-diagnostics -->
|
||||
|
||||
```toml
|
||||
[environment]
|
||||
python-version = "3.12"
|
||||
```
|
||||
|
||||
```py
|
||||
# error: [invalid-return-type]
|
||||
def f() -> int:
|
||||
|
|
@ -279,6 +284,18 @@ T = TypeVar("T")
|
|||
|
||||
# error: [invalid-return-type]
|
||||
def m(x: T) -> T: ...
|
||||
|
||||
class A[T]: ...
|
||||
|
||||
def f() -> A[int]:
|
||||
class A[T]: ...
|
||||
return A[int]() # error: [invalid-return-type]
|
||||
|
||||
class B: ...
|
||||
|
||||
def g() -> B:
|
||||
class B: ...
|
||||
return B() # error: [invalid-return-type]
|
||||
```
|
||||
|
||||
## Invalid return type in stub file
|
||||
|
|
|
|||
|
|
@ -78,10 +78,7 @@ reveal_type(Person.id) # revealed: property
|
|||
reveal_type(Person.name) # revealed: property
|
||||
reveal_type(Person.age) # revealed: property
|
||||
|
||||
# TODO... the error is correct, but this is not the friendliest error message
|
||||
# for assigning to a read-only property :-)
|
||||
#
|
||||
# error: [invalid-assignment] "Invalid assignment to data descriptor attribute `id` on type `Person` with custom `__set__` method"
|
||||
# error: [invalid-assignment] "Attribute `id` on object of type `Person` is read-only"
|
||||
alice.id = 42
|
||||
# error: [invalid-assignment]
|
||||
bob.age = None
|
||||
|
|
@ -221,10 +218,7 @@ james = SuperUser(0, "James", 42, "Jimmy")
|
|||
# on the subclass
|
||||
james.name = "Robert"
|
||||
|
||||
# TODO: the error is correct (can't assign to the read-only property inherited from the superclass)
|
||||
# but the error message could be friendlier :-)
|
||||
#
|
||||
# error: [invalid-assignment] "Invalid assignment to data descriptor attribute `nickname` on type `SuperUser` with custom `__set__` method"
|
||||
# error: [invalid-assignment] "Attribute `nickname` on object of type `SuperUser` is read-only"
|
||||
james.nickname = "Bob"
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -1694,7 +1694,11 @@ class NotSubtype:
|
|||
def m(self, x: int) -> int:
|
||||
return 42
|
||||
|
||||
class DefinitelyNotSubtype:
|
||||
m = None
|
||||
|
||||
static_assert(is_subtype_of(NominalSubtype, P))
|
||||
static_assert(not is_subtype_of(DefinitelyNotSubtype, P))
|
||||
|
||||
# TODO: should pass
|
||||
static_assert(not is_subtype_of(NotSubtype, P)) # error: [static-assert-error]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
---
|
||||
source: crates/ty_test/src/lib.rs
|
||||
expression: snapshot
|
||||
---
|
||||
---
|
||||
mdtest name: descriptor_protocol.md - Descriptor protocol - Special descriptors - Properties with no setters
|
||||
mdtest path: crates/ty_python_semantic/resources/mdtest/descriptor_protocol.md
|
||||
---
|
||||
|
||||
# Python source files
|
||||
|
||||
## mdtest_snippet.py
|
||||
|
||||
```
|
||||
1 | class DontAssignToMe:
|
||||
2 | @property
|
||||
3 | def immutable(self): ...
|
||||
4 |
|
||||
5 | # error: [invalid-assignment]
|
||||
6 | DontAssignToMe().immutable = "the properties, they are a-changing"
|
||||
```
|
||||
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error[invalid-assignment]: Cannot assign to read-only property `immutable` on object of type `DontAssignToMe`
|
||||
--> src/mdtest_snippet.py:3:9
|
||||
|
|
||||
1 | class DontAssignToMe:
|
||||
2 | @property
|
||||
3 | def immutable(self): ...
|
||||
| --------- Property `DontAssignToMe.immutable` defined here with no setter
|
||||
4 |
|
||||
5 | # error: [invalid-assignment]
|
||||
6 | DontAssignToMe().immutable = "the properties, they are a-changing"
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ Attempted assignment to `DontAssignToMe.immutable` here
|
||||
|
|
||||
info: rule `invalid-assignment` is enabled by default
|
||||
|
||||
```
|
||||
|
|
@ -30,6 +30,18 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/function/return_type.md
|
|||
16 |
|
||||
17 | # error: [invalid-return-type]
|
||||
18 | def m(x: T) -> T: ...
|
||||
19 |
|
||||
20 | class A[T]: ...
|
||||
21 |
|
||||
22 | def f() -> A[int]:
|
||||
23 | class A[T]: ...
|
||||
24 | return A[int]() # error: [invalid-return-type]
|
||||
25 |
|
||||
26 | class B: ...
|
||||
27 |
|
||||
28 | def g() -> B:
|
||||
29 | class B: ...
|
||||
30 | return B() # error: [invalid-return-type]
|
||||
```
|
||||
|
||||
# Diagnostics
|
||||
|
|
@ -91,9 +103,45 @@ error[invalid-return-type]: Function always implicitly returns `None`, which is
|
|||
17 | # error: [invalid-return-type]
|
||||
18 | def m(x: T) -> T: ...
|
||||
| ^
|
||||
19 |
|
||||
20 | class A[T]: ...
|
||||
|
|
||||
info: Consider changing the return annotation to `-> None` or adding a `return` statement
|
||||
info: Only functions in stub files, methods on protocol classes, or methods with `@abstractmethod` are permitted to have empty bodies
|
||||
info: rule `invalid-return-type` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[invalid-return-type]: Return type does not match returned value
|
||||
--> src/mdtest_snippet.py:22:12
|
||||
|
|
||||
20 | class A[T]: ...
|
||||
21 |
|
||||
22 | def f() -> A[int]:
|
||||
| ------ Expected `mdtest_snippet.A[int]` because of return type
|
||||
23 | class A[T]: ...
|
||||
24 | return A[int]() # error: [invalid-return-type]
|
||||
| ^^^^^^^^ expected `mdtest_snippet.A[int]`, found `mdtest_snippet.<locals of function 'f'>.A[int]`
|
||||
25 |
|
||||
26 | class B: ...
|
||||
|
|
||||
info: rule `invalid-return-type` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[invalid-return-type]: Return type does not match returned value
|
||||
--> src/mdtest_snippet.py:28:12
|
||||
|
|
||||
26 | class B: ...
|
||||
27 |
|
||||
28 | def g() -> B:
|
||||
| - Expected `mdtest_snippet.B` because of return type
|
||||
29 | class B: ...
|
||||
30 | return B() # error: [invalid-return-type]
|
||||
| ^^^ expected `mdtest_snippet.B`, found `mdtest_snippet.<locals of function 'g'>.B`
|
||||
|
|
||||
info: rule `invalid-return-type` is enabled by default
|
||||
|
||||
```
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ use crate::suppression::check_suppressions;
|
|||
use crate::types::call::{Binding, Bindings, CallArguments, CallableBinding};
|
||||
pub(crate) use crate::types::class_base::ClassBase;
|
||||
use crate::types::constraints::{
|
||||
Constraints, IteratorConstraintsExtension, OptionConstraintsExtension,
|
||||
ConstraintSet, Constraints, IteratorConstraintsExtension, OptionConstraintsExtension,
|
||||
};
|
||||
use crate::types::context::{LintDiagnosticGuard, LintDiagnosticGuardBuilder};
|
||||
use crate::types::diagnostic::{INVALID_AWAIT, INVALID_TYPE_FORM, UNSUPPORTED_BOOL_CONVERSION};
|
||||
|
|
@ -298,7 +298,7 @@ pub(crate) enum AttributeAssignmentError<'db> {
|
|||
CannotAssignToInstanceAttr,
|
||||
CannotAssignToFinal,
|
||||
CannotAssignToUnresolved,
|
||||
ReadOnlyProperty,
|
||||
ReadOnlyProperty(Option<PropertyInstanceType<'db>>),
|
||||
FailToSet,
|
||||
FailToSetAttr,
|
||||
SetAttrReturnsNeverOrNoReturn,
|
||||
|
|
@ -1428,7 +1428,8 @@ impl<'db> Type<'db> {
|
|||
/// intersection simplification dependent on the order in which elements are added), so we do
|
||||
/// not use this more general definition of subtyping.
|
||||
pub(crate) fn is_subtype_of(self, db: &'db dyn Db, target: Type<'db>) -> bool {
|
||||
self.when_subtype_of(db, target)
|
||||
self.when_subtype_of::<ConstraintSet>(db, target)
|
||||
.is_always_satisfied(db)
|
||||
}
|
||||
|
||||
fn when_subtype_of<C: Constraints<'db>>(self, db: &'db dyn Db, target: Type<'db>) -> C {
|
||||
|
|
@ -1439,7 +1440,8 @@ impl<'db> Type<'db> {
|
|||
///
|
||||
/// [assignable to]: https://typing.python.org/en/latest/spec/concepts.html#the-assignable-to-or-consistent-subtyping-relation
|
||||
pub(crate) fn is_assignable_to(self, db: &'db dyn Db, target: Type<'db>) -> bool {
|
||||
self.when_assignable_to(db, target)
|
||||
self.when_assignable_to::<ConstraintSet>(db, target)
|
||||
.is_always_satisfied(db)
|
||||
}
|
||||
|
||||
fn when_assignable_to<C: Constraints<'db>>(self, db: &'db dyn Db, target: Type<'db>) -> C {
|
||||
|
|
@ -1917,7 +1919,8 @@ impl<'db> Type<'db> {
|
|||
///
|
||||
/// [equivalent to]: https://typing.python.org/en/latest/spec/glossary.html#term-equivalent
|
||||
pub(crate) fn is_equivalent_to(self, db: &'db dyn Db, other: Type<'db>) -> bool {
|
||||
self.when_equivalent_to(db, other)
|
||||
self.when_equivalent_to::<ConstraintSet>(db, other)
|
||||
.is_always_satisfied(db)
|
||||
}
|
||||
|
||||
fn when_equivalent_to<C: Constraints<'db>>(self, db: &'db dyn Db, other: Type<'db>) -> C {
|
||||
|
|
@ -2017,7 +2020,8 @@ impl<'db> Type<'db> {
|
|||
/// Note: This function aims to have no false positives, but might return
|
||||
/// wrong `false` answers in some cases.
|
||||
pub(crate) fn is_disjoint_from(self, db: &'db dyn Db, other: Type<'db>) -> bool {
|
||||
self.when_disjoint_from(db, other)
|
||||
self.when_disjoint_from::<ConstraintSet>(db, other)
|
||||
.is_always_satisfied(db)
|
||||
}
|
||||
|
||||
fn when_disjoint_from<C: Constraints<'db>>(self, db: &'db dyn Db, other: Type<'db>) -> C {
|
||||
|
|
@ -5058,7 +5062,7 @@ impl<'db> Type<'db> {
|
|||
Err(if !member_exists {
|
||||
AttributeAssignmentError::CannotAssignToUnresolved
|
||||
} else if is_setattr_synthesized {
|
||||
AttributeAssignmentError::ReadOnlyProperty
|
||||
AttributeAssignmentError::ReadOnlyProperty(None)
|
||||
} else {
|
||||
AttributeAssignmentError::SetAttrReturnsNeverOrNoReturn
|
||||
})
|
||||
|
|
@ -5092,19 +5096,23 @@ impl<'db> Type<'db> {
|
|||
if let Place::Type(meta_dunder_set, _) =
|
||||
meta_attr_ty.class_member(db, "__set__".into()).place
|
||||
{
|
||||
let successful_call = meta_dunder_set
|
||||
.try_call(
|
||||
let dunder_set_result = meta_dunder_set.try_call(
|
||||
db,
|
||||
&CallArguments::positional([
|
||||
meta_attr_ty,
|
||||
self,
|
||||
value_ty,
|
||||
]),
|
||||
)
|
||||
.is_ok();
|
||||
&CallArguments::positional([meta_attr_ty, self, value_ty]),
|
||||
);
|
||||
|
||||
if !successful_call {
|
||||
results.insert(AttributeAssignmentError::FailToSet);
|
||||
if let Err(dunder_set_error) = dunder_set_result {
|
||||
results.insert(
|
||||
if let Some(property) = dunder_set_error
|
||||
.as_attempt_to_set_property_with_no_setter()
|
||||
{
|
||||
AttributeAssignmentError::ReadOnlyProperty(Some(
|
||||
property,
|
||||
))
|
||||
} else {
|
||||
AttributeAssignmentError::FailToSet
|
||||
},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
results.insert_if_error(ensure_assignable_to(meta_attr_ty));
|
||||
|
|
@ -5178,15 +5186,21 @@ impl<'db> Type<'db> {
|
|||
if let Place::Type(meta_dunder_set, _) =
|
||||
meta_attr_ty.class_member(db, "__set__".into()).place
|
||||
{
|
||||
let successful_call = meta_dunder_set
|
||||
.try_call(
|
||||
let dunder_set_result = meta_dunder_set.try_call(
|
||||
db,
|
||||
&CallArguments::positional([meta_attr_ty, self, value_ty]),
|
||||
)
|
||||
.is_ok();
|
||||
);
|
||||
|
||||
if !successful_call {
|
||||
results.insert(AttributeAssignmentError::FailToSet);
|
||||
if let Err(dunder_set_error) = dunder_set_result {
|
||||
results.insert(
|
||||
if let Some(property) =
|
||||
dunder_set_error.as_attempt_to_set_property_with_no_setter()
|
||||
{
|
||||
AttributeAssignmentError::ReadOnlyProperty(Some(property))
|
||||
} else {
|
||||
AttributeAssignmentError::FailToSet
|
||||
},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
results.insert_if_error(ensure_assignable_to(meta_attr_ty));
|
||||
|
|
@ -5278,7 +5292,7 @@ impl<'db> Type<'db> {
|
|||
argument_types: &CallArguments<'_, 'db>,
|
||||
) -> Result<Bindings<'db>, CallError<'db>> {
|
||||
self.bindings(db)
|
||||
.match_parameters(argument_types)
|
||||
.match_parameters(db, argument_types)
|
||||
.check_types(db, argument_types)
|
||||
}
|
||||
|
||||
|
|
@ -5327,7 +5341,7 @@ impl<'db> Type<'db> {
|
|||
Place::Type(dunder_callable, boundness) => {
|
||||
let bindings = dunder_callable
|
||||
.bindings(db)
|
||||
.match_parameters(argument_types)
|
||||
.match_parameters(db, argument_types)
|
||||
.check_types(db, argument_types)?;
|
||||
if boundness == Boundness::PossiblyUnbound {
|
||||
return Err(CallDunderError::PossiblyUnbound(Box::new(bindings)));
|
||||
|
|
@ -10140,6 +10154,16 @@ pub(super) fn walk_intersection_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>
|
|||
}
|
||||
|
||||
impl<'db> IntersectionType<'db> {
|
||||
pub(crate) fn from_elements<I, T>(db: &'db dyn Db, elements: I) -> Type<'db>
|
||||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
T: Into<Type<'db>>,
|
||||
{
|
||||
IntersectionBuilder::new(db)
|
||||
.positive_elements(elements)
|
||||
.build()
|
||||
}
|
||||
|
||||
/// Return a new `IntersectionType` instance with the positive and negative types sorted
|
||||
/// according to a canonical ordering, and other normalizations applied to each element as applicable.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use super::context::InferContext;
|
||||
use super::{Signature, Type};
|
||||
use crate::Db;
|
||||
use crate::types::PropertyInstanceType;
|
||||
use crate::types::call::bind::BindingError;
|
||||
|
||||
mod arguments;
|
||||
pub(crate) mod bind;
|
||||
|
|
@ -14,6 +16,26 @@ pub(super) use bind::{Binding, Bindings, CallableBinding, MatchedArgument};
|
|||
#[derive(Debug)]
|
||||
pub(crate) struct CallError<'db>(pub(crate) CallErrorKind, pub(crate) Box<Bindings<'db>>);
|
||||
|
||||
impl<'db> CallError<'db> {
|
||||
/// Returns `Some(property)` if the call error was caused by an attempt to set a property
|
||||
/// that has no setter, and `None` otherwise.
|
||||
pub(crate) fn as_attempt_to_set_property_with_no_setter(
|
||||
&self,
|
||||
) -> Option<PropertyInstanceType<'db>> {
|
||||
if self.0 != CallErrorKind::BindingError {
|
||||
return None;
|
||||
}
|
||||
self.1
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.flat_map(bind::Binding::errors)
|
||||
.find_map(|error| match error {
|
||||
BindingError::PropertyHasNoSetter(property) => Some(*property),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The reason why calling a type failed.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) enum CallErrorKind {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use std::borrow::Cow;
|
|||
use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
|
||||
use itertools::Itertools;
|
||||
use itertools::{Either, Itertools};
|
||||
use ruff_db::parsed::parsed_module;
|
||||
use smallvec::{SmallVec, smallvec, smallvec_inline};
|
||||
|
||||
|
|
@ -17,6 +17,7 @@ use crate::db::Db;
|
|||
use crate::dunder_all::dunder_all_names;
|
||||
use crate::place::{Boundness, Place};
|
||||
use crate::types::call::arguments::{Expansion, is_expandable_type};
|
||||
use crate::types::constraints::{ConstraintSet, Constraints};
|
||||
use crate::types::diagnostic::{
|
||||
CALL_NON_CALLABLE, CONFLICTING_ARGUMENT_FORMS, INVALID_ARGUMENT_TYPE, MISSING_ARGUMENT,
|
||||
NO_MATCHING_OVERLOAD, PARAMETER_ALREADY_ASSIGNED, TOO_MANY_POSITIONAL_ARGUMENTS,
|
||||
|
|
@ -100,11 +101,15 @@ impl<'db> Bindings<'db> {
|
|||
///
|
||||
/// Once you have argument types available, you can call [`check_types`][Self::check_types] to
|
||||
/// verify that each argument type is assignable to the corresponding parameter type.
|
||||
pub(crate) fn match_parameters(mut self, arguments: &CallArguments<'_, 'db>) -> Self {
|
||||
pub(crate) fn match_parameters(
|
||||
mut self,
|
||||
db: &'db dyn Db,
|
||||
arguments: &CallArguments<'_, 'db>,
|
||||
) -> Self {
|
||||
let mut argument_forms = vec![None; arguments.len()];
|
||||
let mut conflicting_forms = vec![false; arguments.len()];
|
||||
for binding in &mut self.elements {
|
||||
binding.match_parameters(arguments, &mut argument_forms, &mut conflicting_forms);
|
||||
binding.match_parameters(db, arguments, &mut argument_forms, &mut conflicting_forms);
|
||||
}
|
||||
self.argument_forms = argument_forms.into();
|
||||
self.conflicting_forms = conflicting_forms.into();
|
||||
|
|
@ -421,9 +426,9 @@ impl<'db> Bindings<'db> {
|
|||
overload.set_return_type(Type::unknown());
|
||||
}
|
||||
} else {
|
||||
overload.errors.push(BindingError::InternalCallError(
|
||||
"property has no getter",
|
||||
));
|
||||
overload
|
||||
.errors
|
||||
.push(BindingError::PropertyHasNoSetter(*property));
|
||||
overload.set_return_type(Type::Never);
|
||||
}
|
||||
}
|
||||
|
|
@ -477,9 +482,9 @@ impl<'db> Bindings<'db> {
|
|||
));
|
||||
}
|
||||
} else {
|
||||
overload.errors.push(BindingError::InternalCallError(
|
||||
"property has no setter",
|
||||
));
|
||||
overload
|
||||
.errors
|
||||
.push(BindingError::PropertyHasNoSetter(*property));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -495,9 +500,9 @@ impl<'db> Bindings<'db> {
|
|||
));
|
||||
}
|
||||
} else {
|
||||
overload.errors.push(BindingError::InternalCallError(
|
||||
"property has no setter",
|
||||
));
|
||||
overload
|
||||
.errors
|
||||
.push(BindingError::PropertyHasNoSetter(property));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1242,6 +1247,7 @@ impl<'db> CallableBinding<'db> {
|
|||
|
||||
fn match_parameters(
|
||||
&mut self,
|
||||
db: &'db dyn Db,
|
||||
arguments: &CallArguments<'_, 'db>,
|
||||
argument_forms: &mut [Option<ParameterForm>],
|
||||
conflicting_forms: &mut [bool],
|
||||
|
|
@ -1251,7 +1257,7 @@ impl<'db> CallableBinding<'db> {
|
|||
let arguments = arguments.with_self(self.bound_type);
|
||||
|
||||
for overload in &mut self.overloads {
|
||||
overload.match_parameters(arguments.as_ref(), argument_forms, conflicting_forms);
|
||||
overload.match_parameters(db, arguments.as_ref(), argument_forms, conflicting_forms);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1902,7 +1908,7 @@ struct ArgumentMatcher<'a, 'db> {
|
|||
conflicting_forms: &'a mut [bool],
|
||||
errors: &'a mut Vec<BindingError<'db>>,
|
||||
|
||||
argument_matches: Vec<MatchedArgument>,
|
||||
argument_matches: Vec<MatchedArgument<'db>>,
|
||||
parameter_matched: Vec<bool>,
|
||||
next_positional: usize,
|
||||
first_excess_positional: Option<usize>,
|
||||
|
|
@ -1946,6 +1952,7 @@ impl<'a, 'db> ArgumentMatcher<'a, 'db> {
|
|||
&mut self,
|
||||
argument_index: usize,
|
||||
argument: Argument<'a>,
|
||||
argument_type: Option<Type<'db>>,
|
||||
parameter_index: usize,
|
||||
parameter: &Parameter<'db>,
|
||||
positional: bool,
|
||||
|
|
@ -1969,6 +1976,7 @@ impl<'a, 'db> ArgumentMatcher<'a, 'db> {
|
|||
}
|
||||
let matched_argument = &mut self.argument_matches[argument_index];
|
||||
matched_argument.parameters.push(parameter_index);
|
||||
matched_argument.types.push(argument_type);
|
||||
matched_argument.matched = true;
|
||||
self.parameter_matched[parameter_index] = true;
|
||||
}
|
||||
|
|
@ -1977,6 +1985,7 @@ impl<'a, 'db> ArgumentMatcher<'a, 'db> {
|
|||
&mut self,
|
||||
argument_index: usize,
|
||||
argument: Argument<'a>,
|
||||
argument_type: Option<Type<'db>>,
|
||||
) -> Result<(), ()> {
|
||||
if matches!(argument, Argument::Synthetic) {
|
||||
self.num_synthetic_args += 1;
|
||||
|
|
@ -1995,6 +2004,7 @@ impl<'a, 'db> ArgumentMatcher<'a, 'db> {
|
|||
self.assign_argument(
|
||||
argument_index,
|
||||
argument,
|
||||
argument_type,
|
||||
parameter_index,
|
||||
parameter,
|
||||
!parameter.is_variadic(),
|
||||
|
|
@ -2019,20 +2029,35 @@ impl<'a, 'db> ArgumentMatcher<'a, 'db> {
|
|||
});
|
||||
return Err(());
|
||||
};
|
||||
self.assign_argument(argument_index, argument, parameter_index, parameter, false);
|
||||
self.assign_argument(
|
||||
argument_index,
|
||||
argument,
|
||||
None,
|
||||
parameter_index,
|
||||
parameter,
|
||||
false,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn match_variadic(
|
||||
&mut self,
|
||||
db: &'db dyn Db,
|
||||
argument_index: usize,
|
||||
argument: Argument<'a>,
|
||||
argument_type: Option<Type<'db>>,
|
||||
length: TupleLength,
|
||||
) -> Result<(), ()> {
|
||||
let tuple = argument_type.map(|ty| ty.iterate(db));
|
||||
let mut argument_types = match tuple.as_ref() {
|
||||
Some(tuple) => Either::Left(tuple.all_elements().copied()),
|
||||
None => Either::Right(std::iter::empty()),
|
||||
};
|
||||
|
||||
// We must be able to match up the fixed-length portion of the argument with positional
|
||||
// parameters, so we pass on any errors that occur.
|
||||
for _ in 0..length.minimum() {
|
||||
self.match_positional(argument_index, argument)?;
|
||||
self.match_positional(argument_index, argument, argument_types.next())?;
|
||||
}
|
||||
|
||||
// If the tuple is variable-length, we assume that it will soak up all remaining positional
|
||||
|
|
@ -2043,14 +2068,14 @@ impl<'a, 'db> ArgumentMatcher<'a, 'db> {
|
|||
.get_positional(self.next_positional)
|
||||
.is_some()
|
||||
{
|
||||
self.match_positional(argument_index, argument)?;
|
||||
self.match_positional(argument_index, argument, argument_types.next())?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn finish(self) -> Box<[MatchedArgument]> {
|
||||
fn finish(self) -> Box<[MatchedArgument<'db>]> {
|
||||
if let Some(first_excess_argument_index) = self.first_excess_positional {
|
||||
self.errors.push(BindingError::TooManyPositionalArguments {
|
||||
first_excess_argument_index: self.get_argument_index(first_excess_argument_index),
|
||||
|
|
@ -2087,7 +2112,7 @@ struct ArgumentTypeChecker<'a, 'db> {
|
|||
db: &'db dyn Db,
|
||||
signature: &'a Signature<'db>,
|
||||
arguments: &'a CallArguments<'a, 'db>,
|
||||
argument_matches: &'a [MatchedArgument],
|
||||
argument_matches: &'a [MatchedArgument<'db>],
|
||||
parameter_tys: &'a mut [Option<Type<'db>>],
|
||||
errors: &'a mut Vec<BindingError<'db>>,
|
||||
|
||||
|
|
@ -2100,7 +2125,7 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
|
|||
db: &'db dyn Db,
|
||||
signature: &'a Signature<'db>,
|
||||
arguments: &'a CallArguments<'a, 'db>,
|
||||
argument_matches: &'a [MatchedArgument],
|
||||
argument_matches: &'a [MatchedArgument<'db>],
|
||||
parameter_tys: &'a mut [Option<Type<'db>>],
|
||||
errors: &'a mut Vec<BindingError<'db>>,
|
||||
) -> Self {
|
||||
|
|
@ -2155,12 +2180,17 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
|
|||
for (argument_index, adjusted_argument_index, _, argument_type) in
|
||||
self.enumerate_argument_types()
|
||||
{
|
||||
for parameter_index in &self.argument_matches[argument_index].parameters {
|
||||
let parameter = ¶meters[*parameter_index];
|
||||
for (parameter_index, variadic_argument_type) in
|
||||
self.argument_matches[argument_index].iter()
|
||||
{
|
||||
let parameter = ¶meters[parameter_index];
|
||||
let Some(expected_type) = parameter.annotated_type() else {
|
||||
continue;
|
||||
};
|
||||
if let Err(error) = builder.infer(expected_type, argument_type) {
|
||||
if let Err(error) = builder.infer(
|
||||
expected_type,
|
||||
variadic_argument_type.unwrap_or(argument_type),
|
||||
) {
|
||||
self.errors.push(BindingError::SpecializationError {
|
||||
error,
|
||||
argument_index: adjusted_argument_index,
|
||||
|
|
@ -2198,7 +2228,16 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
|
|||
argument_type.apply_specialization(self.db, inherited_specialization);
|
||||
expected_ty = expected_ty.apply_specialization(self.db, inherited_specialization);
|
||||
}
|
||||
if !argument_type.is_assignable_to(self.db, expected_ty) {
|
||||
// This is one of the few places where we want to check if there's _any_ specialization
|
||||
// where assignability holds; normally we want to check that assignability holds for
|
||||
// _all_ specializations.
|
||||
// TODO: Soon we will go further, and build the actual specializations from the
|
||||
// constraint set that we get from this assignability check, instead of inferring and
|
||||
// building them in an earlier separate step.
|
||||
if argument_type
|
||||
.when_assignable_to::<ConstraintSet>(self.db, expected_ty)
|
||||
.is_never_satisfied(self.db)
|
||||
{
|
||||
let positional = matches!(argument, Argument::Positional | Argument::Synthetic)
|
||||
&& !parameter.is_variadic();
|
||||
self.errors.push(BindingError::InvalidArgumentType {
|
||||
|
|
@ -2295,7 +2334,7 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
|
|||
/// Information about which parameter(s) an argument was matched against. This is tracked
|
||||
/// separately for each overload.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct MatchedArgument {
|
||||
pub struct MatchedArgument<'db> {
|
||||
/// The index of the parameter(s) that an argument was matched against. A splatted argument
|
||||
/// might be matched against multiple parameters.
|
||||
pub parameters: SmallVec<[usize; 1]>,
|
||||
|
|
@ -2304,6 +2343,33 @@ pub struct MatchedArgument {
|
|||
/// elements must have been successfully matched. (That means that this can be `false` while
|
||||
/// the `parameters` field is non-empty.)
|
||||
pub matched: bool,
|
||||
|
||||
/// The types of a variadic argument when it's unpacked.
|
||||
///
|
||||
/// The length of this vector is always the same as the `parameters` vector i.e., these are the
|
||||
/// types assigned to each matched parameter. This isn't necessarily the same as the number of
|
||||
/// types in the argument type which might not be a fixed-length iterable.
|
||||
///
|
||||
/// Another thing to note is that the way this is populated means that for any other argument
|
||||
/// kind (synthetic, positional, keyword, keyword-variadic), this will be a single-element
|
||||
/// vector containing `None`, since we don't know the type of the argument when this is
|
||||
/// constructed. So, this field is populated only for variadic arguments.
|
||||
///
|
||||
/// For example, given a `*args` whose type is `tuple[A, B, C]` and the following parameters:
|
||||
/// - `(x, *args)`: the `types` field will only have two elements (`B`, `C`) since `A` has been
|
||||
/// matched with `x`.
|
||||
/// - `(*args)`: the `types` field will have all the three elements (`A`, `B`, `C`)
|
||||
types: SmallVec<[Option<Type<'db>>; 1]>,
|
||||
}
|
||||
|
||||
impl<'db> MatchedArgument<'db> {
|
||||
/// Returns an iterator over the parameter indices and the corresponding argument type.
|
||||
pub fn iter(&self) -> impl Iterator<Item = (usize, Option<Type<'db>>)> + '_ {
|
||||
self.parameters
|
||||
.iter()
|
||||
.copied()
|
||||
.zip(self.types.iter().copied())
|
||||
}
|
||||
}
|
||||
|
||||
/// Binding information for one of the overloads of a callable.
|
||||
|
|
@ -2331,7 +2397,7 @@ pub(crate) struct Binding<'db> {
|
|||
|
||||
/// Information about which parameter(s) each argument was matched with, in argument source
|
||||
/// order.
|
||||
argument_matches: Box<[MatchedArgument]>,
|
||||
argument_matches: Box<[MatchedArgument<'db>]>,
|
||||
|
||||
/// Bound types for parameters, in parameter source order, or `None` if no argument was matched
|
||||
/// to that parameter.
|
||||
|
|
@ -2364,6 +2430,7 @@ impl<'db> Binding<'db> {
|
|||
|
||||
pub(crate) fn match_parameters(
|
||||
&mut self,
|
||||
db: &'db dyn Db,
|
||||
arguments: &CallArguments<'_, 'db>,
|
||||
argument_forms: &mut [Option<ParameterForm>],
|
||||
conflicting_forms: &mut [bool],
|
||||
|
|
@ -2376,16 +2443,17 @@ impl<'db> Binding<'db> {
|
|||
conflicting_forms,
|
||||
&mut self.errors,
|
||||
);
|
||||
for (argument_index, (argument, _)) in arguments.iter().enumerate() {
|
||||
for (argument_index, (argument, argument_type)) in arguments.iter().enumerate() {
|
||||
match argument {
|
||||
Argument::Positional | Argument::Synthetic => {
|
||||
let _ = matcher.match_positional(argument_index, argument);
|
||||
let _ = matcher.match_positional(argument_index, argument, None);
|
||||
}
|
||||
Argument::Keyword(name) => {
|
||||
let _ = matcher.match_keyword(argument_index, argument, name);
|
||||
}
|
||||
Argument::Variadic(length) => {
|
||||
let _ = matcher.match_variadic(argument_index, argument, length);
|
||||
let _ =
|
||||
matcher.match_variadic(db, argument_index, argument, argument_type, length);
|
||||
}
|
||||
Argument::Keywords => {
|
||||
// TODO
|
||||
|
|
@ -2522,9 +2590,13 @@ impl<'db> Binding<'db> {
|
|||
|
||||
/// Returns a vector where each index corresponds to an argument position,
|
||||
/// and the value is the parameter index that argument maps to (if any).
|
||||
pub(crate) fn argument_matches(&self) -> &[MatchedArgument] {
|
||||
pub(crate) fn argument_matches(&self) -> &[MatchedArgument<'db>] {
|
||||
&self.argument_matches
|
||||
}
|
||||
|
||||
pub(crate) fn errors(&self) -> &[BindingError<'db>] {
|
||||
&self.errors
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
@ -2532,7 +2604,7 @@ struct BindingSnapshot<'db> {
|
|||
return_ty: Type<'db>,
|
||||
specialization: Option<Specialization<'db>>,
|
||||
inherited_specialization: Option<Specialization<'db>>,
|
||||
argument_matches: Box<[MatchedArgument]>,
|
||||
argument_matches: Box<[MatchedArgument<'db>]>,
|
||||
parameter_tys: Box<[Option<Type<'db>>]>,
|
||||
errors: Vec<BindingError<'db>>,
|
||||
}
|
||||
|
|
@ -2743,7 +2815,9 @@ pub(crate) enum BindingError<'db> {
|
|||
provided_ty: Type<'db>,
|
||||
},
|
||||
/// One or more required parameters (that is, with no default) is not supplied by any argument.
|
||||
MissingArguments { parameters: ParameterContexts },
|
||||
MissingArguments {
|
||||
parameters: ParameterContexts,
|
||||
},
|
||||
/// A call argument can't be matched to any parameter.
|
||||
UnknownArgument {
|
||||
argument_name: ast::name::Name,
|
||||
|
|
@ -2765,6 +2839,7 @@ pub(crate) enum BindingError<'db> {
|
|||
error: SpecializationError<'db>,
|
||||
argument_index: Option<usize>,
|
||||
},
|
||||
PropertyHasNoSetter(PropertyInstanceType<'db>),
|
||||
/// The call itself might be well constructed, but an error occurred while evaluating the call.
|
||||
/// We use this variant to report errors in `property.__get__` and `property.__set__`, which
|
||||
/// can occur when the call to the underlying getter/setter fails.
|
||||
|
|
@ -3031,6 +3106,17 @@ impl<'db> BindingError<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
Self::PropertyHasNoSetter(_) => {
|
||||
BindingError::InternalCallError("property has no setter").report_diagnostic(
|
||||
context,
|
||||
node,
|
||||
callable_ty,
|
||||
callable_description,
|
||||
union_diag,
|
||||
matching_overload,
|
||||
);
|
||||
}
|
||||
|
||||
Self::InternalCallError(reason) => {
|
||||
let node = Self::get_node(node, None);
|
||||
if let Some(builder) = context.report_lint(&CALL_NON_CALLABLE, node) {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use crate::semantic_index::{
|
|||
BindingWithConstraints, DeclarationWithConstraint, SemanticIndex, attribute_declarations,
|
||||
attribute_scopes,
|
||||
};
|
||||
use crate::types::constraints::{Constraints, IteratorConstraintsExtension};
|
||||
use crate::types::constraints::{ConstraintSet, Constraints, IteratorConstraintsExtension};
|
||||
use crate::types::context::InferContext;
|
||||
use crate::types::diagnostic::{INVALID_LEGACY_TYPE_VARIABLE, INVALID_TYPE_ALIAS_TYPE};
|
||||
use crate::types::enums::enum_metadata;
|
||||
|
|
@ -552,7 +552,8 @@ impl<'db> ClassType<'db> {
|
|||
|
||||
/// Return `true` if `other` is present in this class's MRO.
|
||||
pub(super) fn is_subclass_of(self, db: &'db dyn Db, other: ClassType<'db>) -> bool {
|
||||
self.when_subclass_of(db, other)
|
||||
self.when_subclass_of::<ConstraintSet>(db, other)
|
||||
.is_always_satisfied(db)
|
||||
}
|
||||
|
||||
pub(super) fn when_subclass_of<C: Constraints<'db>>(
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -10,7 +10,7 @@ use crate::semantic_index::SemanticIndex;
|
|||
use crate::semantic_index::definition::Definition;
|
||||
use crate::semantic_index::place::{PlaceTable, ScopedPlaceId};
|
||||
use crate::suppression::FileSuppressionId;
|
||||
use crate::types::class::{ClassType, DisjointBase, DisjointBaseKind, Field};
|
||||
use crate::types::class::{DisjointBase, DisjointBaseKind, Field};
|
||||
use crate::types::function::KnownFunction;
|
||||
use crate::types::string_annotation::{
|
||||
BYTE_STRING_TYPE_ANNOTATION, ESCAPE_CHARACTER_IN_FORWARD_ANNOTATION, FSTRING_TYPE_ANNOTATION,
|
||||
|
|
@ -18,7 +18,8 @@ use crate::types::string_annotation::{
|
|||
RAW_STRING_TYPE_ANNOTATION,
|
||||
};
|
||||
use crate::types::{
|
||||
DynamicType, LintDiagnosticGuard, Protocol, ProtocolInstanceType, SubclassOfInner, binding_type,
|
||||
DynamicType, LintDiagnosticGuard, PropertyInstanceType, Protocol, ProtocolInstanceType,
|
||||
SubclassOfInner, binding_type,
|
||||
};
|
||||
use crate::types::{SpecialFormType, Type, protocol_class::ProtocolClass};
|
||||
use crate::util::diagnostics::format_enumeration;
|
||||
|
|
@ -26,7 +27,7 @@ use crate::{
|
|||
Db, DisplaySettings, FxIndexMap, FxOrderMap, Module, ModuleName, Program, declare_lint,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use ruff_db::diagnostic::{Annotation, Diagnostic, SubDiagnostic, SubDiagnosticSeverity};
|
||||
use ruff_db::diagnostic::{Annotation, Diagnostic, Span, SubDiagnostic, SubDiagnosticSeverity};
|
||||
use ruff_python_ast::name::Name;
|
||||
use ruff_python_ast::{self as ast, AnyNodeRef};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
|
@ -1945,18 +1946,8 @@ pub(super) fn report_invalid_assignment(
|
|||
target_ty: Type,
|
||||
source_ty: Type,
|
||||
) {
|
||||
let mut settings = DisplaySettings::default();
|
||||
// Handles the situation where the report naming is confusing, such as class with the same Name,
|
||||
// but from different scopes.
|
||||
if let Some(target_class) = type_to_class_literal(target_ty, context.db()) {
|
||||
if let Some(source_class) = type_to_class_literal(source_ty, context.db()) {
|
||||
if target_class != source_class
|
||||
&& target_class.name(context.db()) == source_class.name(context.db())
|
||||
{
|
||||
settings = settings.qualified();
|
||||
}
|
||||
}
|
||||
}
|
||||
let settings =
|
||||
DisplaySettings::from_possibly_ambiguous_type_pair(context.db(), target_ty, source_ty);
|
||||
|
||||
report_invalid_assignment_with_message(
|
||||
context,
|
||||
|
|
@ -1970,36 +1961,6 @@ pub(super) fn report_invalid_assignment(
|
|||
);
|
||||
}
|
||||
|
||||
// TODO: generalize this to a method that takes any two types, walks them recursively, and returns
|
||||
// a set of types with ambiguous names whose display should be qualified. Then we can use this in
|
||||
// any diagnostic that displays two types.
|
||||
fn type_to_class_literal<'db>(ty: Type<'db>, db: &'db dyn crate::Db) -> Option<ClassLiteral<'db>> {
|
||||
match ty {
|
||||
Type::ClassLiteral(class) => Some(class),
|
||||
Type::NominalInstance(instance) => match instance.class(db) {
|
||||
crate::types::class::ClassType::NonGeneric(class) => Some(class),
|
||||
crate::types::class::ClassType::Generic(alias) => Some(alias.origin(db)),
|
||||
},
|
||||
Type::EnumLiteral(enum_literal) => Some(enum_literal.enum_class(db)),
|
||||
Type::GenericAlias(alias) => Some(alias.origin(db)),
|
||||
Type::ProtocolInstance(ProtocolInstanceType {
|
||||
inner: Protocol::FromClass(class),
|
||||
..
|
||||
}) => match class {
|
||||
ClassType::NonGeneric(class) => Some(class),
|
||||
ClassType::Generic(alias) => Some(alias.origin(db)),
|
||||
},
|
||||
Type::TypedDict(typed_dict) => match typed_dict.defining_class() {
|
||||
ClassType::NonGeneric(class) => Some(class),
|
||||
ClassType::Generic(alias) => Some(alias.origin(db)),
|
||||
},
|
||||
Type::SubclassOf(subclass_of) => {
|
||||
type_to_class_literal(Type::from(subclass_of.subclass_of().into_class()?), db)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn report_invalid_attribute_assignment(
|
||||
context: &InferContext,
|
||||
node: AnyNodeRef,
|
||||
|
|
@ -2019,6 +1980,42 @@ pub(super) fn report_invalid_attribute_assignment(
|
|||
);
|
||||
}
|
||||
|
||||
pub(super) fn report_attempted_write_to_read_only_property<'db>(
|
||||
context: &InferContext<'db, '_>,
|
||||
property: Option<PropertyInstanceType<'db>>,
|
||||
attribute: &str,
|
||||
object_type: Type<'db>,
|
||||
target: &ast::ExprAttribute,
|
||||
) {
|
||||
let Some(builder) = context.report_lint(&INVALID_ASSIGNMENT, target) else {
|
||||
return;
|
||||
};
|
||||
let db = context.db();
|
||||
let object_type = object_type.display(db);
|
||||
|
||||
if let Some(file_range) = property
|
||||
.and_then(|property| property.getter(db))
|
||||
.and_then(|getter| getter.definition(db))
|
||||
.and_then(|definition| definition.focus_range(db))
|
||||
{
|
||||
let mut diagnostic = builder.into_diagnostic(format_args!(
|
||||
"Cannot assign to read-only property `{attribute}` on object of type `{object_type}`",
|
||||
));
|
||||
diagnostic.annotate(
|
||||
Annotation::secondary(Span::from(file_range)).message(format_args!(
|
||||
"Property `{object_type}.{attribute}` defined here with no setter"
|
||||
)),
|
||||
);
|
||||
diagnostic.set_primary_message(format_args!(
|
||||
"Attempted assignment to `{object_type}.{attribute}` here"
|
||||
));
|
||||
} else {
|
||||
builder.into_diagnostic(format_args!(
|
||||
"Attribute `{attribute}` on object of type `{object_type}` is read-only",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn report_invalid_return_type(
|
||||
context: &InferContext,
|
||||
object_range: impl Ranged,
|
||||
|
|
@ -2030,18 +2027,20 @@ pub(super) fn report_invalid_return_type(
|
|||
return;
|
||||
};
|
||||
|
||||
let settings =
|
||||
DisplaySettings::from_possibly_ambiguous_type_pair(context.db(), expected_ty, actual_ty);
|
||||
let return_type_span = context.span(return_type_range);
|
||||
|
||||
let mut diag = builder.into_diagnostic("Return type does not match returned value");
|
||||
diag.set_primary_message(format_args!(
|
||||
"expected `{expected_ty}`, found `{actual_ty}`",
|
||||
expected_ty = expected_ty.display(context.db()),
|
||||
actual_ty = actual_ty.display(context.db()),
|
||||
expected_ty = expected_ty.display_with(context.db(), settings),
|
||||
actual_ty = actual_ty.display_with(context.db(), settings),
|
||||
));
|
||||
diag.annotate(
|
||||
Annotation::secondary(return_type_span).message(format_args!(
|
||||
"Expected `{expected_ty}` because of return type",
|
||||
expected_ty = expected_ty.display(context.db()),
|
||||
expected_ty = expected_ty.display_with(context.db(), settings),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,9 @@ use crate::types::generics::{GenericContext, Specialization};
|
|||
use crate::types::signatures::{CallableSignature, Parameter, Parameters, Signature};
|
||||
use crate::types::tuple::TupleSpec;
|
||||
use crate::types::{
|
||||
CallableType, IntersectionType, KnownClass, MaterializationKind, MethodWrapperKind, Protocol,
|
||||
StringLiteralType, SubclassOfInner, Type, UnionType, WrapperDescriptorKind,
|
||||
BoundTypeVarInstance, CallableType, IntersectionType, KnownClass, MaterializationKind,
|
||||
MethodWrapperKind, Protocol, ProtocolInstanceType, StringLiteralType, SubclassOfInner, Type,
|
||||
UnionType, WrapperDescriptorKind,
|
||||
};
|
||||
use ruff_db::parsed::parsed_module;
|
||||
|
||||
|
|
@ -54,6 +55,58 @@ impl DisplaySettings {
|
|||
..self
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn from_possibly_ambiguous_type_pair<'db>(
|
||||
db: &'db dyn Db,
|
||||
type_1: Type<'db>,
|
||||
type_2: Type<'db>,
|
||||
) -> Self {
|
||||
let result = Self::default();
|
||||
|
||||
let Some(class_1) = type_to_class_literal(db, type_1) else {
|
||||
return result;
|
||||
};
|
||||
|
||||
let Some(class_2) = type_to_class_literal(db, type_2) else {
|
||||
return result;
|
||||
};
|
||||
|
||||
if class_1 == class_2 {
|
||||
return result;
|
||||
}
|
||||
|
||||
if class_1.name(db) == class_2.name(db) {
|
||||
result.qualified()
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: generalize this to a method that takes any two types, walks them recursively, and returns
|
||||
// a set of types with ambiguous names whose display should be qualified. Then we can use this in
|
||||
// any diagnostic that displays two types.
|
||||
fn type_to_class_literal<'db>(db: &'db dyn Db, ty: Type<'db>) -> Option<ClassLiteral<'db>> {
|
||||
match ty {
|
||||
Type::ClassLiteral(class) => Some(class),
|
||||
Type::NominalInstance(instance) => {
|
||||
type_to_class_literal(db, Type::from(instance.class(db)))
|
||||
}
|
||||
Type::EnumLiteral(enum_literal) => Some(enum_literal.enum_class(db)),
|
||||
Type::GenericAlias(alias) => Some(alias.origin(db)),
|
||||
Type::ProtocolInstance(ProtocolInstanceType {
|
||||
inner: Protocol::FromClass(class),
|
||||
..
|
||||
}) => type_to_class_literal(db, Type::from(class)),
|
||||
Type::TypedDict(typed_dict) => {
|
||||
type_to_class_literal(db, Type::from(typed_dict.defining_class()))
|
||||
}
|
||||
Type::SubclassOf(subclass_of) => {
|
||||
type_to_class_literal(db, Type::from(subclass_of.subclass_of().into_class()?))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> Type<'db> {
|
||||
|
|
@ -113,18 +166,25 @@ impl fmt::Debug for DisplayType<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Writes the string representation of a type, which is the value displayed either as
|
||||
/// `Literal[<repr>]` or `Literal[<repr1>, <repr2>]` for literal types or as `<repr>` for
|
||||
/// non literals
|
||||
struct DisplayRepresentation<'db> {
|
||||
ty: Type<'db>,
|
||||
impl<'db> ClassLiteral<'db> {
|
||||
fn display_with(self, db: &'db dyn Db, settings: DisplaySettings) -> ClassDisplay<'db> {
|
||||
ClassDisplay {
|
||||
db,
|
||||
class: self,
|
||||
settings,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ClassDisplay<'db> {
|
||||
db: &'db dyn Db,
|
||||
class: ClassLiteral<'db>,
|
||||
settings: DisplaySettings,
|
||||
}
|
||||
|
||||
impl DisplayRepresentation<'_> {
|
||||
fn class_parents(&self, class: ClassLiteral) -> Vec<String> {
|
||||
let body_scope = class.body_scope(self.db);
|
||||
impl ClassDisplay<'_> {
|
||||
fn class_parents(&self) -> Vec<String> {
|
||||
let body_scope = self.class.body_scope(self.db);
|
||||
let file = body_scope.file(self.db);
|
||||
let module_ast = parsed_module(self.db, file).load(self.db);
|
||||
let index = semantic_index(self.db, file);
|
||||
|
|
@ -164,23 +224,29 @@ impl DisplayRepresentation<'_> {
|
|||
name_parts.reverse();
|
||||
name_parts
|
||||
}
|
||||
}
|
||||
|
||||
fn write_maybe_qualified_class(
|
||||
&self,
|
||||
f: &mut Formatter<'_>,
|
||||
class: ClassLiteral,
|
||||
) -> fmt::Result {
|
||||
impl Display for ClassDisplay<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
if self.settings.qualified {
|
||||
let parents = self.class_parents(class);
|
||||
if !parents.is_empty() {
|
||||
f.write_str(&parents.join("."))?;
|
||||
for parent in self.class_parents() {
|
||||
f.write_str(&parent)?;
|
||||
f.write_char('.')?;
|
||||
}
|
||||
}
|
||||
f.write_str(class.name(self.db))
|
||||
f.write_str(self.class.name(self.db))
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes the string representation of a type, which is the value displayed either as
|
||||
/// `Literal[<repr>]` or `Literal[<repr1>, <repr2>]` for literal types or as `<repr>` for
|
||||
/// non literals
|
||||
struct DisplayRepresentation<'db> {
|
||||
ty: Type<'db>,
|
||||
db: &'db dyn Db,
|
||||
settings: DisplaySettings,
|
||||
}
|
||||
|
||||
impl Display for DisplayRepresentation<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self.ty {
|
||||
|
|
@ -199,14 +265,14 @@ impl Display for DisplayRepresentation<'_> {
|
|||
.display_with(self.db, self.settings)
|
||||
.fmt(f),
|
||||
(ClassType::NonGeneric(class), _) => {
|
||||
self.write_maybe_qualified_class(f, class)
|
||||
class.display_with(self.db, self.settings).fmt(f)
|
||||
},
|
||||
(ClassType::Generic(alias), _) => alias.display_with(self.db, self.settings).fmt(f),
|
||||
}
|
||||
}
|
||||
Type::ProtocolInstance(protocol) => match protocol.inner {
|
||||
Protocol::FromClass(ClassType::NonGeneric(class)) => {
|
||||
self.write_maybe_qualified_class(f, class)
|
||||
class.display_with(self.db, self.settings).fmt(f)
|
||||
}
|
||||
Protocol::FromClass(ClassType::Generic(alias)) => {
|
||||
alias.display_with(self.db, self.settings).fmt(f)
|
||||
|
|
@ -230,11 +296,11 @@ impl Display for DisplayRepresentation<'_> {
|
|||
Type::ModuleLiteral(module) => {
|
||||
write!(f, "<module '{}'>", module.module(self.db).name(self.db))
|
||||
}
|
||||
Type::ClassLiteral(class) => {
|
||||
write!(f, "<class '")?;
|
||||
self.write_maybe_qualified_class(f, class)?;
|
||||
write!(f, "'>")
|
||||
}
|
||||
Type::ClassLiteral(class) => write!(
|
||||
f,
|
||||
"<class '{}'>",
|
||||
class.display_with(self.db, self.settings)
|
||||
),
|
||||
Type::GenericAlias(generic) => write!(
|
||||
f,
|
||||
"<class '{}'>",
|
||||
|
|
@ -242,9 +308,7 @@ impl Display for DisplayRepresentation<'_> {
|
|||
),
|
||||
Type::SubclassOf(subclass_of_ty) => match subclass_of_ty.subclass_of() {
|
||||
SubclassOfInner::Class(ClassType::NonGeneric(class)) => {
|
||||
write!(f, "type[")?;
|
||||
self.write_maybe_qualified_class(f, class)?;
|
||||
write!(f, "]")
|
||||
write!(f, "type[{}]", class.display_with(self.db, self.settings))
|
||||
}
|
||||
SubclassOfInner::Class(ClassType::Generic(alias)) => {
|
||||
write!(
|
||||
|
|
@ -319,13 +383,13 @@ impl Display for DisplayRepresentation<'_> {
|
|||
)
|
||||
}
|
||||
Type::MethodWrapper(MethodWrapperKind::PropertyDunderGet(_)) => {
|
||||
write!(f, "<method-wrapper `__get__` of `property` object>",)
|
||||
f.write_str("<method-wrapper `__get__` of `property` object>")
|
||||
}
|
||||
Type::MethodWrapper(MethodWrapperKind::PropertyDunderSet(_)) => {
|
||||
write!(f, "<method-wrapper `__set__` of `property` object>",)
|
||||
f.write_str("<method-wrapper `__set__` of `property` object>")
|
||||
}
|
||||
Type::MethodWrapper(MethodWrapperKind::StrStartswith(_)) => {
|
||||
write!(f, "<method-wrapper `startswith` of `str` object>",)
|
||||
f.write_str("<method-wrapper `startswith` of `str` object>")
|
||||
}
|
||||
Type::WrapperDescriptor(kind) => {
|
||||
let (method, object) = match kind {
|
||||
|
|
@ -354,18 +418,16 @@ impl Display for DisplayRepresentation<'_> {
|
|||
|
||||
escape.bytes_repr(TripleQuotes::No).write(f)
|
||||
}
|
||||
Type::EnumLiteral(enum_literal) => {
|
||||
self.write_maybe_qualified_class(f, enum_literal.enum_class(self.db))?;
|
||||
f.write_char('.')?;
|
||||
f.write_str(enum_literal.name(self.db))
|
||||
}
|
||||
Type::EnumLiteral(enum_literal) => write!(
|
||||
f,
|
||||
"{enum_class}.{literal_name}",
|
||||
enum_class = enum_literal
|
||||
.enum_class(self.db)
|
||||
.display_with(self.db, self.settings),
|
||||
literal_name = enum_literal.name(self.db)
|
||||
),
|
||||
Type::NonInferableTypeVar(bound_typevar) | Type::TypeVar(bound_typevar) => {
|
||||
f.write_str(bound_typevar.typevar(self.db).name(self.db))?;
|
||||
if let Some(binding_context) = bound_typevar.binding_context(self.db).name(self.db)
|
||||
{
|
||||
write!(f, "@{binding_context}")?;
|
||||
}
|
||||
Ok(())
|
||||
bound_typevar.display(self.db).fmt(f)
|
||||
}
|
||||
Type::AlwaysTruthy => f.write_str("AlwaysTruthy"),
|
||||
Type::AlwaysFalsy => f.write_str("AlwaysFalsy"),
|
||||
|
|
@ -393,15 +455,41 @@ impl Display for DisplayRepresentation<'_> {
|
|||
}
|
||||
f.write_str("]")
|
||||
}
|
||||
Type::TypedDict(typed_dict) => self.write_maybe_qualified_class(
|
||||
f,
|
||||
typed_dict.defining_class().class_literal(self.db).0,
|
||||
),
|
||||
Type::TypedDict(typed_dict) => typed_dict
|
||||
.defining_class()
|
||||
.class_literal(self.db)
|
||||
.0
|
||||
.display_with(self.db, self.settings)
|
||||
.fmt(f),
|
||||
Type::TypeAlias(alias) => f.write_str(alias.name(self.db)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> BoundTypeVarInstance<'db> {
|
||||
pub(crate) fn display(self, db: &'db dyn Db) -> impl Display {
|
||||
DisplayBoundTypeVarInstance {
|
||||
bound_typevar: self,
|
||||
db,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DisplayBoundTypeVarInstance<'db> {
|
||||
bound_typevar: BoundTypeVarInstance<'db>,
|
||||
db: &'db dyn Db,
|
||||
}
|
||||
|
||||
impl Display for DisplayBoundTypeVarInstance<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.bound_typevar.typevar(self.db).name(self.db))?;
|
||||
if let Some(binding_context) = self.bound_typevar.binding_context(self.db).name(self.db) {
|
||||
write!(f, "@{binding_context}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> TupleSpec<'db> {
|
||||
pub(crate) fn display_with(
|
||||
&'db self,
|
||||
|
|
@ -627,7 +715,7 @@ impl Display for DisplayGenericAlias<'_> {
|
|||
f,
|
||||
"{prefix}{origin}{specialization}{suffix}",
|
||||
prefix = prefix,
|
||||
origin = self.origin.name(self.db),
|
||||
origin = self.origin.display_with(self.db, self.settings),
|
||||
specialization = self.specialization.display_short(
|
||||
self.db,
|
||||
TupleSpecialization::from_class(self.db, self.origin)
|
||||
|
|
|
|||
|
|
@ -801,7 +801,7 @@ pub struct CallSignatureDetails<'db> {
|
|||
|
||||
/// Mapping from argument indices to parameter indices. This helps
|
||||
/// determine which parameter corresponds to which argument position.
|
||||
pub argument_to_parameter_mapping: Vec<MatchedArgument>,
|
||||
pub argument_to_parameter_mapping: Vec<MatchedArgument<'db>>,
|
||||
}
|
||||
|
||||
/// Extract signature details from a function call expression.
|
||||
|
|
@ -821,7 +821,9 @@ pub fn call_signature_details<'db>(
|
|||
CallArguments::from_arguments(db, &call_expr.arguments, |_, splatted_value| {
|
||||
splatted_value.inferred_type(model)
|
||||
});
|
||||
let bindings = callable_type.bindings(db).match_parameters(&call_arguments);
|
||||
let bindings = callable_type
|
||||
.bindings(db)
|
||||
.match_parameters(db, &call_arguments);
|
||||
|
||||
// Extract signature details from all callable bindings
|
||||
bindings
|
||||
|
|
|
|||
|
|
@ -102,12 +102,12 @@ use crate::types::diagnostic::{
|
|||
INVALID_TYPE_VARIABLE_CONSTRAINTS, IncompatibleBases, POSSIBLY_UNBOUND_IMPLICIT_CALL,
|
||||
POSSIBLY_UNBOUND_IMPORT, TypeCheckDiagnostics, UNDEFINED_REVEAL, UNRESOLVED_ATTRIBUTE,
|
||||
UNRESOLVED_GLOBAL, UNRESOLVED_IMPORT, UNRESOLVED_REFERENCE, UNSUPPORTED_OPERATOR,
|
||||
report_implicit_return_type, report_instance_layout_conflict,
|
||||
report_invalid_argument_number_to_special_form, report_invalid_arguments_to_annotated,
|
||||
report_invalid_arguments_to_callable, report_invalid_assignment,
|
||||
report_invalid_attribute_assignment, report_invalid_generator_function_return_type,
|
||||
report_invalid_key_on_typed_dict, report_invalid_return_type,
|
||||
report_namedtuple_field_without_default_after_field_with_default,
|
||||
report_attempted_write_to_read_only_property, report_implicit_return_type,
|
||||
report_instance_layout_conflict, report_invalid_argument_number_to_special_form,
|
||||
report_invalid_arguments_to_annotated, report_invalid_arguments_to_callable,
|
||||
report_invalid_assignment, report_invalid_attribute_assignment,
|
||||
report_invalid_generator_function_return_type, report_invalid_key_on_typed_dict,
|
||||
report_invalid_return_type, report_namedtuple_field_without_default_after_field_with_default,
|
||||
report_possibly_unbound_attribute,
|
||||
};
|
||||
use crate::types::enums::is_enum_class;
|
||||
|
|
@ -4033,13 +4033,14 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
));
|
||||
}
|
||||
}
|
||||
AttributeAssignmentError::ReadOnlyProperty => {
|
||||
if let Some(builder) = self.context.report_lint(&INVALID_ASSIGNMENT, target) {
|
||||
builder.into_diagnostic(format_args!(
|
||||
"Property `{attribute}` defined in `{ty}` is read-only",
|
||||
ty = object_ty.display(self.db()),
|
||||
));
|
||||
}
|
||||
AttributeAssignmentError::ReadOnlyProperty(property) => {
|
||||
report_attempted_write_to_read_only_property(
|
||||
&self.context,
|
||||
property,
|
||||
attribute,
|
||||
object_ty,
|
||||
target,
|
||||
);
|
||||
}
|
||||
AttributeAssignmentError::FailToSet => {
|
||||
if let Some(builder) = self.context.report_lint(&INVALID_ASSIGNMENT, target) {
|
||||
|
|
@ -5944,7 +5945,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
|
||||
let bindings = callable_type
|
||||
.bindings(self.db())
|
||||
.match_parameters(&call_arguments);
|
||||
.match_parameters(self.db(), &call_arguments);
|
||||
self.infer_argument_types(arguments, &mut call_arguments, &bindings.argument_forms);
|
||||
|
||||
// Validate `TypedDict` constructor calls after argument type inference
|
||||
|
|
@ -8396,7 +8397,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
};
|
||||
let binding = Binding::single(value_ty, generic_context.signature(self.db()));
|
||||
let bindings = match Bindings::from(binding)
|
||||
.match_parameters(&call_argument_types)
|
||||
.match_parameters(self.db(), &call_argument_types)
|
||||
.check_types(self.db(), &call_argument_types)
|
||||
{
|
||||
Ok(bindings) => bindings,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use super::protocol_class::ProtocolInterface;
|
|||
use super::{BoundTypeVarInstance, ClassType, KnownClass, SubclassOfType, Type, TypeVarVariance};
|
||||
use crate::place::PlaceAndQualifiers;
|
||||
use crate::semantic_index::definition::Definition;
|
||||
use crate::types::constraints::{Constraints, IteratorConstraintsExtension};
|
||||
use crate::types::constraints::{ConstraintSet, Constraints, IteratorConstraintsExtension};
|
||||
use crate::types::enums::is_single_member_enum;
|
||||
use crate::types::protocol_class::walk_protocol_interface;
|
||||
use crate::types::tuple::{TupleSpec, TupleType};
|
||||
|
|
@ -513,12 +513,15 @@ impl<'db> ProtocolInstanceType<'db> {
|
|||
visitor: &NormalizedVisitor<'db>,
|
||||
) -> Type<'db> {
|
||||
let object = Type::object(db);
|
||||
if object.satisfies_protocol(
|
||||
if object
|
||||
.satisfies_protocol(
|
||||
db,
|
||||
self,
|
||||
TypeRelation::Subtyping,
|
||||
&HasRelationToVisitor::new(true),
|
||||
) {
|
||||
&HasRelationToVisitor::new(ConstraintSet::always_satisfiable(db)),
|
||||
)
|
||||
.is_always_satisfied(db)
|
||||
{
|
||||
return object;
|
||||
}
|
||||
match self.inner {
|
||||
|
|
|
|||
|
|
@ -638,7 +638,7 @@ impl<'a, 'db> ProtocolMember<'a, 'db> {
|
|||
pub(super) fn instance_set_type(&self) -> Result<Type<'db>, AttributeAssignmentError<'db>> {
|
||||
match self.kind {
|
||||
ProtocolMemberKind::Property { set_type, .. } => {
|
||||
set_type.ok_or(AttributeAssignmentError::ReadOnlyProperty)
|
||||
set_type.ok_or(AttributeAssignmentError::ReadOnlyProperty(None))
|
||||
}
|
||||
ProtocolMemberKind::Method(_) => Err(AttributeAssignmentError::CannotAssign),
|
||||
ProtocolMemberKind::Attribute(ty) => {
|
||||
|
|
@ -689,7 +689,8 @@ impl<'a, 'db> ProtocolMember<'a, 'db> {
|
|||
db,
|
||||
matches!(
|
||||
other.to_meta_type(db).member(db, self.name).place,
|
||||
Place::Type(_, Boundness::Bound)
|
||||
Place::Type(ty, Boundness::Bound)
|
||||
if ty.is_assignable_to(db, CallableType::single(db, Signature::dynamic(Type::any())))
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ use smallvec::{SmallVec, smallvec_inline};
|
|||
|
||||
use super::{DynamicType, Type, TypeVarVariance, definition_expression_type};
|
||||
use crate::semantic_index::definition::Definition;
|
||||
use crate::types::constraints::{Constraints, IteratorConstraintsExtension};
|
||||
use crate::types::constraints::{ConstraintSet, Constraints, IteratorConstraintsExtension};
|
||||
use crate::types::generics::{GenericContext, walk_generic_context};
|
||||
use crate::types::{
|
||||
BindingContext, BoundTypeVarInstance, FindLegacyTypeVarsVisitor, HasRelationToVisitor,
|
||||
|
|
@ -123,7 +123,8 @@ impl<'db> CallableSignature<'db> {
|
|||
///
|
||||
/// See [`Type::is_subtype_of`] for more details.
|
||||
pub(crate) fn is_subtype_of(&self, db: &'db dyn Db, other: &Self) -> bool {
|
||||
self.is_subtype_of_impl(db, other)
|
||||
self.is_subtype_of_impl::<ConstraintSet>(db, other)
|
||||
.is_always_satisfied(db)
|
||||
}
|
||||
|
||||
fn is_subtype_of_impl<C: Constraints<'db>>(&self, db: &'db dyn Db, other: &Self) -> C {
|
||||
|
|
@ -143,8 +144,9 @@ impl<'db> CallableSignature<'db> {
|
|||
db,
|
||||
other,
|
||||
TypeRelation::Assignability,
|
||||
&HasRelationToVisitor::new(true),
|
||||
&HasRelationToVisitor::new(ConstraintSet::always_satisfiable(db)),
|
||||
)
|
||||
.is_always_satisfied(db)
|
||||
}
|
||||
|
||||
pub(crate) fn has_relation_to_impl<C: Constraints<'db>>(
|
||||
|
|
|
|||
Loading…
Reference in New Issue