Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -16,7 +16,7 @@ def _(flag: bool):
else:
x: int

x = 1 # error: [conflicting-declarations] "Conflicting declared types for `x`: str, int"
x = 1 # error: [conflicting-declarations] "Conflicting declared types for `x`: `str` and `int`"
```

## Incompatible declarations for 2 (out of 3) types
Expand All @@ -29,7 +29,7 @@ def _(flag1: bool, flag2: bool):
x: int

# Here, the declared type for `x` is `int | str | Unknown`.
x = 1 # error: [conflicting-declarations] "Conflicting declared types for `x`: str, int"
x = 1 # error: [conflicting-declarations] "Conflicting declared types for `x`: `str` and `int`"
```

## Incompatible declarations with repeated types
Expand All @@ -47,7 +47,7 @@ def _(flag1: bool, flag2: bool, flag3: bool, flag4: bool):
else:
x: bytes

x = "a" # error: [conflicting-declarations] "Conflicting declared types for `x`: str, int, bytes"
x = "a" # error: [conflicting-declarations] "Conflicting declared types for `x`: `str`, `int` and `bytes`"
```

## Incompatible declarations with bad assignment
Expand Down
49 changes: 27 additions & 22 deletions crates/ty_python_semantic/src/types/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use ruff_python_ast::{self as ast, AnyNodeRef};
use ruff_text_size::{Ranged, TextRange};
use rustc_hash::FxHashSet;
use std::fmt::Formatter;
use std::fmt::Write;

/// Registers all known type check lints.
pub(crate) fn register_lints(registry: &mut LintRegistryBuilder) {
Expand Down Expand Up @@ -2012,6 +2013,30 @@ pub(crate) fn report_instance_layout_conflict(
diagnostic.sub(subdiagnostic);
}

/// Format a list of elements as a human-readable enumeration.
///
/// Encloses every element in backticks (`1`, `2` and `3`).
pub(crate) fn format_enumeration<I, IT, D>(elements: I) -> String
where
I: IntoIterator<IntoIter = IT>,
IT: ExactSizeIterator<Item = D> + DoubleEndedIterator,
D: std::fmt::Display,
{
let mut elements = elements.into_iter();
debug_assert!(elements.len() >= 2);

let final_element = elements.next_back().unwrap();
let penultimate_element = elements.next_back().unwrap();

let mut buffer = String::new();
for element in elements {
write!(&mut buffer, "`{element}`, ").ok();
}
write!(&mut buffer, "`{penultimate_element}` and `{final_element}`").ok();

buffer
}

/// Information regarding the conflicting solid bases a class is inferred to have in its MRO.
///
/// For each solid base, we record information about which element in the class's bases list
Expand All @@ -2038,29 +2063,9 @@ impl<'db> IncompatibleBases<'db> {

/// List the problematic class bases in a human-readable format.
fn describe_problematic_class_bases(&self, db: &dyn Db) -> String {
let num_bases = self.len();
debug_assert!(num_bases >= 2);

let mut bad_base_names = self.0.values().map(|info| info.originating_base.name(db));

let final_base = bad_base_names.next_back().unwrap();
let penultimate_base = bad_base_names.next_back().unwrap();

let mut buffer = String::new();

for base_name in bad_base_names {
buffer.push('`');
buffer.push_str(base_name);
buffer.push_str("`, ");
}

buffer.push('`');
buffer.push_str(penultimate_base);
buffer.push_str("` and `");
buffer.push_str(final_base);
buffer.push('`');
let bad_base_names = self.0.values().map(|info| info.originating_base.name(db));

buffer
format_enumeration(bad_base_names)
}

pub(super) fn len(&self) -> usize {
Expand Down
2 changes: 1 addition & 1 deletion crates/ty_python_semantic/src/types/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1643,7 +1643,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
if let Some(builder) = self.context.report_lint(&CONFLICTING_DECLARATIONS, node) {
builder.into_diagnostic(format_args!(
"Conflicting declared types for `{place}`: {}",
conflicting.iter().map(|ty| ty.display(db)).join(", ")
diagnostic::format_enumeration(conflicting.iter().map(|ty| ty.display(db)))
));
}
ty.inner_type()
Expand Down
Loading