Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ error[call-non-callable]: Object of type `Literal[5]` is not callable
| ^^^^
|
info: Union variant `Literal[5]` is incompatible with this call site
info: Attempted to call union type `(def f1() -> int) | (def f2(name: str) -> int) | (def f3(a: int, b: int) -> int) | (def f4[T](x: T@f4) -> int) | Literal[5] | (Overload[() -> None, (x: str) -> str]) | (Overload[() -> None, (x: str, y: str) -> str]) | PossiblyNotCallable`
info: Attempted to call union type `(def f1() -> int) | (def f2(name: str) -> int) | (def f3(a: int, b: int) -> int) | ... omitted 5 union elements`
info: rule `call-non-callable` is enabled by default

```
Expand All @@ -101,7 +101,7 @@ error[call-non-callable]: Object of type `PossiblyNotCallable` is not callable (
| ^^^^
|
info: Union variant `PossiblyNotCallable` is incompatible with this call site
info: Attempted to call union type `(def f1() -> int) | (def f2(name: str) -> int) | (def f3(a: int, b: int) -> int) | (def f4[T](x: T@f4) -> int) | Literal[5] | (Overload[() -> None, (x: str) -> str]) | (Overload[() -> None, (x: str, y: str) -> str]) | PossiblyNotCallable`
info: Attempted to call union type `(def f1() -> int) | (def f2(name: str) -> int) | (def f3(a: int, b: int) -> int) | ... omitted 5 union elements`
info: rule `call-non-callable` is enabled by default

```
Expand All @@ -116,7 +116,7 @@ error[missing-argument]: No argument provided for required parameter `b` of func
| ^^^^
|
info: Union variant `def f3(a: int, b: int) -> int` is incompatible with this call site
info: Attempted to call union type `(def f1() -> int) | (def f2(name: str) -> int) | (def f3(a: int, b: int) -> int) | (def f4[T](x: T@f4) -> int) | Literal[5] | (Overload[() -> None, (x: str) -> str]) | (Overload[() -> None, (x: str, y: str) -> str]) | PossiblyNotCallable`
info: Attempted to call union type `(def f1() -> int) | (def f2(name: str) -> int) | (def f3(a: int, b: int) -> int) | ... omitted 5 union elements`
info: rule `missing-argument` is enabled by default

```
Expand Down Expand Up @@ -152,7 +152,7 @@ info: Overload implementation defined here
28 | return x + y if x and y else None
|
info: Union variant `Overload[() -> None, (x: str, y: str) -> str]` is incompatible with this call site
info: Attempted to call union type `(def f1() -> int) | (def f2(name: str) -> int) | (def f3(a: int, b: int) -> int) | (def f4[T](x: T@f4) -> int) | Literal[5] | (Overload[() -> None, (x: str) -> str]) | (Overload[() -> None, (x: str, y: str) -> str]) | PossiblyNotCallable`
info: Attempted to call union type `(def f1() -> int) | (def f2(name: str) -> int) | (def f3(a: int, b: int) -> int) | ... omitted 5 union elements`
info: rule `no-matching-overload` is enabled by default

```
Expand All @@ -176,7 +176,7 @@ info: Function defined here
8 | return 0
|
info: Union variant `def f2(name: str) -> int` is incompatible with this call site
info: Attempted to call union type `(def f1() -> int) | (def f2(name: str) -> int) | (def f3(a: int, b: int) -> int) | (def f4[T](x: T@f4) -> int) | Literal[5] | (Overload[() -> None, (x: str) -> str]) | (Overload[() -> None, (x: str, y: str) -> str]) | PossiblyNotCallable`
info: Attempted to call union type `(def f1() -> int) | (def f2(name: str) -> int) | (def f3(a: int, b: int) -> int) | ... omitted 5 union elements`
info: rule `invalid-argument-type` is enabled by default

```
Expand All @@ -200,7 +200,7 @@ info: Type variable defined here
14 | return 0
|
info: Union variant `def f4[T](x: T@f4) -> int` is incompatible with this call site
info: Attempted to call union type `(def f1() -> int) | (def f2(name: str) -> int) | (def f3(a: int, b: int) -> int) | (def f4[T](x: T@f4) -> int) | Literal[5] | (Overload[() -> None, (x: str) -> str]) | (Overload[() -> None, (x: str, y: str) -> str]) | PossiblyNotCallable`
info: Attempted to call union type `(def f1() -> int) | (def f2(name: str) -> int) | (def f3(a: int, b: int) -> int) | ... omitted 5 union elements`
info: rule `invalid-argument-type` is enabled by default

```
Expand All @@ -227,7 +227,7 @@ info: Matching overload defined here
info: Non-matching overloads for function `f5`:
info: () -> None
info: Union variant `Overload[() -> None, (x: str) -> str]` is incompatible with this call site
info: Attempted to call union type `(def f1() -> int) | (def f2(name: str) -> int) | (def f3(a: int, b: int) -> int) | (def f4[T](x: T@f4) -> int) | Literal[5] | (Overload[() -> None, (x: str) -> str]) | (Overload[() -> None, (x: str, y: str) -> str]) | PossiblyNotCallable`
info: Attempted to call union type `(def f1() -> int) | (def f2(name: str) -> int) | (def f3(a: int, b: int) -> int) | ... omitted 5 union elements`
info: rule `invalid-argument-type` is enabled by default

```
Expand All @@ -242,7 +242,7 @@ error[too-many-positional-arguments]: Too many positional arguments to function
| ^
|
info: Union variant `def f1() -> int` is incompatible with this call site
info: Attempted to call union type `(def f1() -> int) | (def f2(name: str) -> int) | (def f3(a: int, b: int) -> int) | (def f4[T](x: T@f4) -> int) | Literal[5] | (Overload[() -> None, (x: str) -> str]) | (Overload[() -> None, (x: str, y: str) -> str]) | PossiblyNotCallable`
info: Attempted to call union type `(def f1() -> int) | (def f2(name: str) -> int) | (def f3(a: int, b: int) -> int) | ... omitted 5 union elements`
info: rule `too-many-positional-arguments` is enabled by default

```
69 changes: 69 additions & 0 deletions crates/ty_python_semantic/src/types/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ pub struct DisplaySettings<'db> {
/// Class names that should be displayed fully qualified
/// (e.g., `module.ClassName` instead of just `ClassName`)
pub qualified: Rc<FxHashSet<&'db str>>,
/// Whether long unions are displayed in full
pub preserve_full_unions: bool,
}

impl<'db> DisplaySettings<'db> {
Expand All @@ -54,6 +56,22 @@ impl<'db> DisplaySettings<'db> {
}
}

#[must_use]
pub fn truncate_long_unions(self) -> Self {
Self {
preserve_full_unions: false,
..self
}
}

#[must_use]
pub fn preserve_long_unions(self) -> Self {
Self {
preserve_full_unions: true,
..self
}
}

#[must_use]
pub fn from_possibly_ambiguous_type_pair(
db: &'db dyn Db,
Expand Down Expand Up @@ -1265,6 +1283,9 @@ struct DisplayUnionType<'db> {
settings: DisplaySettings<'db>,
}

const MAX_DISPLAYED_UNION_ITEMS: usize = 5;
const MAX_DISPLAYED_UNION_ITEMS_WHEN_ELIDED: usize = 3;

impl Display for DisplayUnionType<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
fn is_condensable(ty: Type<'_>) -> bool {
Expand All @@ -1286,19 +1307,43 @@ impl Display for DisplayUnionType<'_> {
.filter(|element| is_condensable(*element))
.collect::<Vec<_>>();

let total_entries =
usize::from(!condensed_types.is_empty()) + elements.len() - condensed_types.len();

assert_ne!(total_entries, 0);

let mut join = f.join(" | ");

let display_limit = if self.settings.preserve_full_unions {
total_entries
} else {
let limit = if total_entries > MAX_DISPLAYED_UNION_ITEMS {
MAX_DISPLAYED_UNION_ITEMS_WHEN_ELIDED
} else {
MAX_DISPLAYED_UNION_ITEMS
};
limit.min(total_entries)
};

let mut condensed_types = Some(condensed_types);
let mut displayed_entries = 0usize;

for element in elements {
if displayed_entries >= display_limit {
break;
}

if is_condensable(*element) {
if let Some(condensed_types) = condensed_types.take() {
displayed_entries += 1;
join.entry(&DisplayLiteralGroup {
literals: condensed_types,
db: self.db,
settings: self.settings.singleline(),
});
}
} else {
displayed_entries += 1;
join.entry(&DisplayMaybeParenthesizedType {
ty: *element,
db: self.db,
Expand All @@ -1307,6 +1352,15 @@ impl Display for DisplayUnionType<'_> {
}
}

if !self.settings.preserve_full_unions {
let omitted_entries = total_entries.saturating_sub(displayed_entries);
if omitted_entries > 0 {
join.entry(&DisplayUnionOmitted {
count: omitted_entries,
});
}
}

join.finish()?;

Ok(())
Expand All @@ -1319,6 +1373,21 @@ impl fmt::Debug for DisplayUnionType<'_> {
}
}

struct DisplayUnionOmitted {
count: usize,
}

impl Display for DisplayUnionOmitted {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let plural = if self.count == 1 {
"element"
} else {
"elements"
};
write!(f, "... omitted {} union {}", self.count, plural)
}
}

struct DisplayLiteralGroup<'db> {
literals: Vec<Type<'db>>,
db: &'db dyn Db,
Expand Down
10 changes: 6 additions & 4 deletions crates/ty_python_semantic/src/types/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ use crate::types::diagnostic::{
report_bad_argument_to_get_protocol_members, report_bad_argument_to_protocol_interface,
report_runtime_check_against_non_runtime_checkable_protocol,
};
use crate::types::display::DisplaySettings;
use crate::types::generics::GenericContext;
use crate::types::narrow::ClassInfoConstraintFunction;
use crate::types::signatures::{CallableSignature, Signature};
Expand Down Expand Up @@ -1386,10 +1387,11 @@ impl KnownFunction {
{
let mut diag = builder.into_diagnostic("Revealed type");
let span = context.span(&call_expression.arguments.args[0]);
diag.annotate(
Annotation::primary(span)
.message(format_args!("`{}`", revealed_type.display(db))),
);
diag.annotate(Annotation::primary(span).message(format_args!(
"`{}`",
revealed_type
.display_with(db, DisplaySettings::default().preserve_long_unions())
)));
}
}

Expand Down
Loading