mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-19 03:48:29 +00:00
[ty] elide redundant inlay hints for function args (#21365)
This elides the following inlay hints: ```py foo([x=]x) foo([x=]y.x) foo([x=]x[0]) foo([x=]x(...)) # composes to complex situations foo([x=]y.x(..)[0]) ``` Fixes https://github.com/astral-sh/ty/issues/1514
This commit is contained in:
parent
835e31b3ff
commit
4821c050ef
1 changed files with 197 additions and 2 deletions
|
|
@ -4,7 +4,7 @@ use crate::Db;
|
||||||
use ruff_db::files::File;
|
use ruff_db::files::File;
|
||||||
use ruff_db::parsed::parsed_module;
|
use ruff_db::parsed::parsed_module;
|
||||||
use ruff_python_ast::visitor::source_order::{self, SourceOrderVisitor, TraversalSignal};
|
use ruff_python_ast::visitor::source_order::{self, SourceOrderVisitor, TraversalSignal};
|
||||||
use ruff_python_ast::{AnyNodeRef, Expr, Stmt};
|
use ruff_python_ast::{AnyNodeRef, ArgOrKeyword, Expr, Stmt};
|
||||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||||
use ty_python_semantic::types::Type;
|
use ty_python_semantic::types::Type;
|
||||||
use ty_python_semantic::types::ide_support::inlay_hint_function_argument_details;
|
use ty_python_semantic::types::ide_support::inlay_hint_function_argument_details;
|
||||||
|
|
@ -283,7 +283,9 @@ impl SourceOrderVisitor<'_> for InlayHintVisitor<'_, '_> {
|
||||||
self.visit_expr(&call.func);
|
self.visit_expr(&call.func);
|
||||||
|
|
||||||
for (index, arg_or_keyword) in call.arguments.arguments_source_order().enumerate() {
|
for (index, arg_or_keyword) in call.arguments.arguments_source_order().enumerate() {
|
||||||
if let Some(name) = argument_names.get(&index) {
|
if let Some(name) = argument_names.get(&index)
|
||||||
|
&& !arg_matches_name(&arg_or_keyword, name)
|
||||||
|
{
|
||||||
self.add_call_argument_name(arg_or_keyword.range().start(), name);
|
self.add_call_argument_name(arg_or_keyword.range().start(), name);
|
||||||
}
|
}
|
||||||
self.visit_expr(arg_or_keyword.value());
|
self.visit_expr(arg_or_keyword.value());
|
||||||
|
|
@ -296,6 +298,32 @@ impl SourceOrderVisitor<'_> for InlayHintVisitor<'_, '_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Given a positional argument, check if the expression is the "same name"
|
||||||
|
/// as the function argument itself.
|
||||||
|
///
|
||||||
|
/// This allows us to filter out reptitive inlay hints like `x=x`, `x=y.x`, etc.
|
||||||
|
fn arg_matches_name(arg_or_keyword: &ArgOrKeyword, name: &str) -> bool {
|
||||||
|
// Only care about positional args
|
||||||
|
let ArgOrKeyword::Arg(arg) = arg_or_keyword else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut expr = *arg;
|
||||||
|
loop {
|
||||||
|
match expr {
|
||||||
|
// `x=x(1, 2)` counts as a match, recurse for it
|
||||||
|
Expr::Call(expr_call) => expr = &expr_call.func,
|
||||||
|
// `x=x[0]` is a match, recurse for it
|
||||||
|
Expr::Subscript(expr_subscript) => expr = &expr_subscript.value,
|
||||||
|
// `x=x` is a match
|
||||||
|
Expr::Name(expr_name) => return expr_name.id.as_str() == name,
|
||||||
|
// `x=y.x` is a match
|
||||||
|
Expr::Attribute(expr_attribute) => return expr_attribute.attr.as_str() == name,
|
||||||
|
_ => return false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
@ -485,6 +513,173 @@ mod tests {
|
||||||
");
|
");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_function_call_with_positional_or_keyword_parameter_redundant_name() {
|
||||||
|
let test = inlay_hint_test(
|
||||||
|
"
|
||||||
|
def foo(x: int): pass
|
||||||
|
x = 1
|
||||||
|
y = 2
|
||||||
|
foo(x)
|
||||||
|
foo(y)",
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.inlay_hints(), @r"
|
||||||
|
def foo(x: int): pass
|
||||||
|
x[: Literal[1]] = 1
|
||||||
|
y[: Literal[2]] = 2
|
||||||
|
foo(x)
|
||||||
|
foo([x=]y)
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_function_call_with_positional_or_keyword_parameter_redundant_attribute() {
|
||||||
|
let test = inlay_hint_test(
|
||||||
|
"
|
||||||
|
def foo(x: int): pass
|
||||||
|
class MyClass:
|
||||||
|
def __init__():
|
||||||
|
self.x: int = 1
|
||||||
|
self.y: int = 2
|
||||||
|
val = MyClass()
|
||||||
|
|
||||||
|
foo(val.x)
|
||||||
|
foo(val.y)",
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.inlay_hints(), @r"
|
||||||
|
def foo(x: int): pass
|
||||||
|
class MyClass:
|
||||||
|
def __init__():
|
||||||
|
self.x: int = 1
|
||||||
|
self.y: int = 2
|
||||||
|
val[: MyClass] = MyClass()
|
||||||
|
|
||||||
|
foo(val.x)
|
||||||
|
foo([x=]val.y)
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_function_call_with_positional_or_keyword_parameter_redundant_attribute_not() {
|
||||||
|
// This one checks that we don't allow elide `x=` for `x.y`
|
||||||
|
let test = inlay_hint_test(
|
||||||
|
"
|
||||||
|
def foo(x: int): pass
|
||||||
|
class MyClass:
|
||||||
|
def __init__():
|
||||||
|
self.x: int = 1
|
||||||
|
self.y: int = 2
|
||||||
|
x = MyClass()
|
||||||
|
|
||||||
|
foo(x.x)
|
||||||
|
foo(x.y)",
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.inlay_hints(), @r"
|
||||||
|
def foo(x: int): pass
|
||||||
|
class MyClass:
|
||||||
|
def __init__():
|
||||||
|
self.x: int = 1
|
||||||
|
self.y: int = 2
|
||||||
|
x[: MyClass] = MyClass()
|
||||||
|
|
||||||
|
foo(x.x)
|
||||||
|
foo([x=]x.y)
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_function_call_with_positional_or_keyword_parameter_redundant_call() {
|
||||||
|
let test = inlay_hint_test(
|
||||||
|
"
|
||||||
|
def foo(x: int): pass
|
||||||
|
class MyClass:
|
||||||
|
def __init__():
|
||||||
|
def x() -> int:
|
||||||
|
return 1
|
||||||
|
def y() -> int:
|
||||||
|
return 2
|
||||||
|
val = MyClass()
|
||||||
|
|
||||||
|
foo(val.x())
|
||||||
|
foo(val.y())",
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.inlay_hints(), @r"
|
||||||
|
def foo(x: int): pass
|
||||||
|
class MyClass:
|
||||||
|
def __init__():
|
||||||
|
def x() -> int:
|
||||||
|
return 1
|
||||||
|
def y() -> int:
|
||||||
|
return 2
|
||||||
|
val[: MyClass] = MyClass()
|
||||||
|
|
||||||
|
foo(val.x())
|
||||||
|
foo([x=]val.y())
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_function_call_with_positional_or_keyword_parameter_redundant_complex() {
|
||||||
|
let test = inlay_hint_test(
|
||||||
|
"
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
def foo(x: int): pass
|
||||||
|
class MyClass:
|
||||||
|
def __init__():
|
||||||
|
def x() -> List[int]:
|
||||||
|
return 1
|
||||||
|
def y() -> List[int]:
|
||||||
|
return 2
|
||||||
|
val = MyClass()
|
||||||
|
|
||||||
|
foo(val.x()[0])
|
||||||
|
foo(val.y()[1])",
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.inlay_hints(), @r"
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
def foo(x: int): pass
|
||||||
|
class MyClass:
|
||||||
|
def __init__():
|
||||||
|
def x() -> List[int]:
|
||||||
|
return 1
|
||||||
|
def y() -> List[int]:
|
||||||
|
return 2
|
||||||
|
val[: MyClass] = MyClass()
|
||||||
|
|
||||||
|
foo(val.x()[0])
|
||||||
|
foo([x=]val.y()[1])
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_function_call_with_positional_or_keyword_parameter_redundant_subscript() {
|
||||||
|
let test = inlay_hint_test(
|
||||||
|
"
|
||||||
|
def foo(x: int): pass
|
||||||
|
x = [1]
|
||||||
|
y = [2]
|
||||||
|
|
||||||
|
foo(x[0])
|
||||||
|
foo(y[0])",
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.inlay_hints(), @r"
|
||||||
|
def foo(x: int): pass
|
||||||
|
x[: list[Unknown | int]] = [1]
|
||||||
|
y[: list[Unknown | int]] = [2]
|
||||||
|
|
||||||
|
foo(x[0])
|
||||||
|
foo([x=]y[0])
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_function_call_with_positional_only_parameter() {
|
fn test_function_call_with_positional_only_parameter() {
|
||||||
let test = inlay_hint_test(
|
let test = inlay_hint_test(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue