Skip to content

Commit b3f75cc

Browse files
committed
Auto merge of #132147 - estebank:long-types-2, r=davidtwco
Tweak E0277 output when a candidate is available *Follow up to #132086.* Go from ``` error[E0277]: the trait bound `Then<Ignored<chumsky::combinator::Filter<chumsky::primitive::Any<&str, chumsky::extra::Full<EmptyErr, (), ()>>, {closure@src/main.rs:9:17: 9:27}>, char>, chumsky::combinator::Map<impl CSTParser<'a, O>, O, {closure@src/main.rs:11:24: 11:27}>, (), (), chumsky::extra::Full<EmptyErr, (), ()>>: CSTParser<'a>` is not satisfied --> src/main.rs:7:50 | 7 | fn leaf<'a, O>(parser: impl CSTParser<'a, O>) -> impl CSTParser<'a, ()> { | ^^^^^^^^^^^^^^^^^^^^^^ the trait `chumsky::private::ParserSealed<'_, &str, (), chumsky::extra::Full<EmptyErr, (), ()>>` is not implemented for `Then<Ignored<Filter<Any<&str, ...>, ...>, ...>, ..., ..., ..., ...>`, which is required by `Then<Ignored<chumsky::combinator::Filter<chumsky::primitive::Any<&str, chumsky::extra::Full<EmptyErr, (), ()>>, {closure@src/main.rs:9:17: 9:27}>, char>, chumsky::combinator::Map<impl CSTParser<'a, O>, O, {closure@src/main.rs:11:24: 11:27}>, (), (), chumsky::extra::Full<EmptyErr, (), ()>>: CSTParser<'a>` | = help: the trait `chumsky::private::ParserSealed<'_, &'a str, ((), ()), chumsky::extra::Full<EmptyErr, (), ()>>` is implemented for `Then<Ignored<chumsky::combinator::Filter<chumsky::primitive::Any<&str, chumsky::extra::Full<EmptyErr, (), ()>>, {closure@src/main.rs:9:17: 9:27}>, char>, chumsky::combinator::Map<impl CSTParser<'a, O>, O, {closure@src/main.rs:11:24: 11:27}>, (), (), chumsky::extra::Full<EmptyErr, (), ()>>` = help: for that trait implementation, expected `((), ())`, found `()` = note: required for `Then<Ignored<Filter<Any<&str, ...>, ...>, ...>, ..., ..., ..., ...>` to implement `Parser<'_, &str, ()>` note: required for `Then<Ignored<Filter<Any<&str, ...>, ...>, ...>, ..., ..., ..., ...>` to implement `CSTParser<'a>` --> src/main.rs:5:16 | 5 | impl<'a, O, T> CSTParser<'a, O> for T where T: Parser<'a, &'a str, O> {} | ^^^^^^^^^^^^^^^^ ^ ---------------------- unsatisfied trait bound introduced here = note: the full name for the type has been written to '/home/gh-estebank/longlong/target/debug/deps/longlong-0008f9a4f2023b08.long-type-13239977239800463552.txt' = note: consider using `--verbose` to print the full type name to the console = note: the full name for the type has been written to '/home/gh-estebank/longlong/target/debug/deps/longlong-0008f9a4f2023b08.long-type-13239977239800463552.txt' = note: consider using `--verbose` to print the full type name to the console ``` to ``` error[E0277]: the trait bound `Then<Ignored<chumsky::combinator::Filter<chumsky::primitive::Any<&str, chumsky::extra::Full<EmptyErr, (), ()>>, {closure@src/main.rs:9:17: 9:27}>, char>, chumsky::combinator::Map<impl CSTParser<'a, O>, O, {closure@src/main.rs:11:24: 11:27}>, (), (), chumsky::extra::Full<EmptyErr, (), ()>>: CSTParser<'a>` is not satisfied --> src/main.rs:7:50 | 7 | fn leaf<'a, O>(parser: impl CSTParser<'a, O>) -> impl CSTParser<'a, ()> { | ^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound ... 11 | ws.then(parser.map(|_| ())) | --------------------------- return type was inferred to be `Then<Ignored<..., ...>, ..., ..., ..., ...>` here | = help: the trait `ParserSealed<'_, &_, (), Full<_, _, _>>` is not implemented for `Then<Ignored<..., ...>, ..., ..., ..., ...>` but trait `ParserSealed<'_, &'a _, ((), ()), Full<_, _, _>>` is implemented for it = help: for that trait implementation, expected `((), ())`, found `()` = note: required for `Then<Ignored<..., ...>, ..., ..., ..., ...>` to implement `Parser<'_, &str, ()>` note: required for `Then<Ignored<..., ...>, ..., ..., ..., ...>` to implement `CSTParser<'a>` --> src/main.rs:5:16 | 5 | impl<'a, O, T> CSTParser<'a, O> for T where T: Parser<'a, &'a str, O> {} | ^^^^^^^^^^^^^^^^ ^ ---------------------- unsatisfied trait bound introduced here = note: the full name for the type has been written to '/home/gh-estebank/longlong/target/debug/deps/longlong-df9d52be87eada65.long-type-1337037744507305372.txt' = note: consider using `--verbose` to print the full type name to the console ``` * Remove redundant wording * Introduce trait diff highlighting logic and use it * Fix incorrect "long type written to path" logic (can be split off) * Point at tail expression in more cases in E0277 * Avoid long primary span labels in E0277 by moving them to a `help` Fix #132013. There are individual commits that can be their own PR. If the review load is too big, happy to split them off.
2 parents 00ed73c + 7e0d3ed commit b3f75cc

File tree

79 files changed

+487
-227
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+487
-227
lines changed

compiler/rustc_middle/src/ty/error.rs

+18-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use std::borrow::Cow;
2+
use std::fs::File;
23
use std::hash::{DefaultHasher, Hash, Hasher};
4+
use std::io::{Read, Write};
35
use std::path::PathBuf;
46

57
use rustc_errors::pluralize;
@@ -250,8 +252,8 @@ impl<'tcx> TyCtxt<'tcx> {
250252
}
251253

252254
let width = self.sess.diagnostic_width();
253-
let length_limit = width.saturating_sub(30);
254-
if regular.len() <= width {
255+
let length_limit = width / 2;
256+
if regular.len() <= width * 2 / 3 {
255257
return regular;
256258
}
257259
let short = self.ty_string_with_limit(ty, length_limit);
@@ -265,7 +267,20 @@ impl<'tcx> TyCtxt<'tcx> {
265267
*path = Some(path.take().unwrap_or_else(|| {
266268
self.output_filenames(()).temp_path_ext(&format!("long-type-{hash}.txt"), None)
267269
}));
268-
match std::fs::write(path.as_ref().unwrap(), &format!("{regular}\n")) {
270+
let Ok(mut file) =
271+
File::options().create(true).read(true).append(true).open(&path.as_ref().unwrap())
272+
else {
273+
return regular;
274+
};
275+
276+
// Do not write the same type to the file multiple times.
277+
let mut contents = String::new();
278+
let _ = file.read_to_string(&mut contents);
279+
if let Some(_) = contents.lines().find(|line| line == &regular) {
280+
return short;
281+
}
282+
283+
match write!(file, "{regular}\n") {
269284
Ok(_) => short,
270285
Err(_) => regular,
271286
}

compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs

+61
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,67 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
766766
values
767767
}
768768

769+
pub fn cmp_traits(
770+
&self,
771+
def_id1: DefId,
772+
args1: &[ty::GenericArg<'tcx>],
773+
def_id2: DefId,
774+
args2: &[ty::GenericArg<'tcx>],
775+
) -> (DiagStyledString, DiagStyledString) {
776+
let mut values = (DiagStyledString::new(), DiagStyledString::new());
777+
778+
if def_id1 != def_id2 {
779+
values.0.push_highlighted(self.tcx.def_path_str(def_id1).as_str());
780+
values.1.push_highlighted(self.tcx.def_path_str(def_id2).as_str());
781+
} else {
782+
values.0.push_normal(self.tcx.item_name(def_id1).as_str());
783+
values.1.push_normal(self.tcx.item_name(def_id2).as_str());
784+
}
785+
786+
if args1.len() != args2.len() {
787+
let (pre, post) = if args1.len() > 0 { ("<", ">") } else { ("", "") };
788+
values.0.push_normal(format!(
789+
"{pre}{}{post}",
790+
args1.iter().map(|a| a.to_string()).collect::<Vec<_>>().join(", ")
791+
));
792+
let (pre, post) = if args2.len() > 0 { ("<", ">") } else { ("", "") };
793+
values.1.push_normal(format!(
794+
"{pre}{}{post}",
795+
args2.iter().map(|a| a.to_string()).collect::<Vec<_>>().join(", ")
796+
));
797+
return values;
798+
}
799+
800+
if args1.len() > 0 {
801+
values.0.push_normal("<");
802+
values.1.push_normal("<");
803+
}
804+
for (i, (a, b)) in std::iter::zip(args1, args2).enumerate() {
805+
let a_str = a.to_string();
806+
let b_str = b.to_string();
807+
if let (Some(a), Some(b)) = (a.as_type(), b.as_type()) {
808+
let (a, b) = self.cmp(a, b);
809+
values.0.0.extend(a.0);
810+
values.1.0.extend(b.0);
811+
} else if a_str != b_str {
812+
values.0.push_highlighted(a_str);
813+
values.1.push_highlighted(b_str);
814+
} else {
815+
values.0.push_normal(a_str);
816+
values.1.push_normal(b_str);
817+
}
818+
if i + 1 < args1.len() {
819+
values.0.push_normal(", ");
820+
values.1.push_normal(", ");
821+
}
822+
}
823+
if args1.len() > 0 {
824+
values.0.push_normal(">");
825+
values.1.push_normal(">");
826+
}
827+
values
828+
}
829+
769830
/// Compares two given types, eliding parts that are the same between them and highlighting
770831
/// relevant differences, and return two representation of those types for highlighted printing.
771832
pub fn cmp(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> (DiagStyledString, DiagStyledString) {

compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs

+85-16
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use rustc_data_structures::fx::FxHashMap;
66
use rustc_data_structures::unord::UnordSet;
77
use rustc_errors::codes::*;
88
use rustc_errors::{
9-
Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey, StringPart, Suggestions, pluralize,
10-
struct_span_code_err,
9+
Applicability, Diag, ErrorGuaranteed, Level, MultiSpan, StashKey, StringPart, Suggestions,
10+
pluralize, struct_span_code_err,
1111
};
1212
use rustc_hir::def::Namespace;
1313
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
@@ -328,6 +328,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
328328
}
329329
} else if let Some(custom_explanation) = safe_transmute_explanation {
330330
err.span_label(span, custom_explanation);
331+
} else if explanation.len() > self.tcx.sess.diagnostic_width() {
332+
// Really long types don't look good as span labels, instead move it
333+
// to a `help`.
334+
err.span_label(span, "unsatisfied trait bound");
335+
err.help(explanation);
331336
} else {
332337
err.span_label(span, explanation);
333338
}
@@ -1832,21 +1837,81 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
18321837
if impl_trait_ref.references_error() {
18331838
return false;
18341839
}
1835-
let self_ty = impl_trait_ref.self_ty().to_string();
1836-
err.highlighted_help(vec![
1837-
StringPart::normal(format!(
1838-
"the trait `{}` ",
1839-
impl_trait_ref.print_trait_sugared()
1840-
)),
1841-
StringPart::highlighted("is"),
1840+
1841+
if let [child, ..] = &err.children[..]
1842+
&& child.level == Level::Help
1843+
&& let Some(line) = child.messages.get(0)
1844+
&& let Some(line) = line.0.as_str()
1845+
&& line.starts_with("the trait")
1846+
&& line.contains("is not implemented for")
1847+
{
1848+
// HACK(estebank): we remove the pre-existing
1849+
// "the trait `X` is not implemented for" note, which only happens if there
1850+
// was a custom label. We do this because we want that note to always be the
1851+
// first, and making this logic run earlier will get tricky. For now, we
1852+
// instead keep the logic the same and modify the already constructed error
1853+
// to avoid the wording duplication.
1854+
err.children.remove(0);
1855+
}
1856+
1857+
let traits = self.cmp_traits(
1858+
obligation_trait_ref.def_id,
1859+
&obligation_trait_ref.args[1..],
1860+
impl_trait_ref.def_id,
1861+
&impl_trait_ref.args[1..],
1862+
);
1863+
let traits_content = (traits.0.content(), traits.1.content());
1864+
let types = self.cmp(obligation_trait_ref.self_ty(), impl_trait_ref.self_ty());
1865+
let types_content = (types.0.content(), types.1.content());
1866+
let mut msg = vec![StringPart::normal("the trait `")];
1867+
if traits_content.0 == traits_content.1 {
1868+
msg.push(StringPart::normal(
1869+
impl_trait_ref.print_trait_sugared().to_string(),
1870+
));
1871+
} else {
1872+
msg.extend(traits.0.0);
1873+
}
1874+
msg.extend([
1875+
StringPart::normal("` "),
1876+
StringPart::highlighted("is not"),
18421877
StringPart::normal(" implemented for `"),
1843-
if let [TypeError::Sorts(_)] = &terrs[..] {
1844-
StringPart::normal(self_ty)
1845-
} else {
1846-
StringPart::highlighted(self_ty)
1847-
},
1848-
StringPart::normal("`"),
18491878
]);
1879+
if types_content.0 == types_content.1 {
1880+
let ty =
1881+
self.tcx.short_ty_string(obligation_trait_ref.self_ty(), &mut None);
1882+
msg.push(StringPart::normal(ty));
1883+
} else {
1884+
msg.extend(types.0.0);
1885+
}
1886+
msg.push(StringPart::normal("`"));
1887+
if types_content.0 == types_content.1 {
1888+
msg.push(StringPart::normal("\nbut trait `"));
1889+
msg.extend(traits.1.0);
1890+
msg.extend([
1891+
StringPart::normal("` "),
1892+
StringPart::highlighted("is"),
1893+
StringPart::normal(" implemented for it"),
1894+
]);
1895+
} else if traits_content.0 == traits_content.1 {
1896+
msg.extend([
1897+
StringPart::normal("\nbut it "),
1898+
StringPart::highlighted("is"),
1899+
StringPart::normal(" implemented for `"),
1900+
]);
1901+
msg.extend(types.1.0);
1902+
msg.push(StringPart::normal("`"));
1903+
} else {
1904+
msg.push(StringPart::normal("\nbut trait `"));
1905+
msg.extend(traits.1.0);
1906+
msg.extend([
1907+
StringPart::normal("` "),
1908+
StringPart::highlighted("is"),
1909+
StringPart::normal(" implemented for `"),
1910+
]);
1911+
msg.extend(types.1.0);
1912+
msg.push(StringPart::normal("`"));
1913+
}
1914+
err.highlighted_help(msg);
18501915

18511916
if let [TypeError::Sorts(exp_found)] = &terrs[..] {
18521917
let exp_found = self.resolve_vars_if_possible(*exp_found);
@@ -2475,12 +2540,16 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
24752540
&& self.tcx.trait_impls_of(trait_def_id).is_empty()
24762541
&& !self.tcx.trait_is_auto(trait_def_id)
24772542
&& !self.tcx.trait_is_alias(trait_def_id)
2543+
&& trait_predicate.polarity() == ty::PredicatePolarity::Positive
24782544
{
24792545
err.span_help(
24802546
self.tcx.def_span(trait_def_id),
24812547
crate::fluent_generated::trait_selection_trait_has_no_impls,
24822548
);
2483-
} else if !suggested && !unsatisfied_const {
2549+
} else if !suggested
2550+
&& !unsatisfied_const
2551+
&& trait_predicate.polarity() == ty::PredicatePolarity::Positive
2552+
{
24842553
// Can't show anything else useful, try to find similar impls.
24852554
let impl_candidates = self.find_similar_impl_candidates(trait_predicate);
24862555
if !self.report_similar_impl_candidates(

compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs

+31-11
Original file line numberDiff line numberDiff line change
@@ -3563,17 +3563,34 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
35633563
)]);
35643564
}
35653565
ObligationCauseCode::OpaqueReturnType(expr_info) => {
3566-
if let Some((expr_ty, hir_id)) = expr_info {
3567-
let expr_ty = self.tcx.short_ty_string(expr_ty, long_ty_file);
3568-
let expr = self.infcx.tcx.hir().expect_expr(hir_id);
3569-
err.span_label(
3570-
expr.span,
3571-
with_forced_trimmed_paths!(format!(
3572-
"return type was inferred to be `{expr_ty}` here",
3573-
)),
3574-
);
3575-
suggest_remove_deref(err, &expr);
3576-
}
3566+
let (expr_ty, expr) = if let Some((expr_ty, hir_id)) = expr_info {
3567+
let expr_ty = tcx.short_ty_string(expr_ty, long_ty_file);
3568+
let expr = tcx.hir().expect_expr(hir_id);
3569+
(expr_ty, expr)
3570+
} else if let Some(body_id) = tcx.hir_node_by_def_id(body_id).body_id()
3571+
&& let body = tcx.hir().body(body_id)
3572+
&& let hir::ExprKind::Block(block, _) = body.value.kind
3573+
&& let Some(expr) = block.expr
3574+
&& let Some(expr_ty) = self
3575+
.typeck_results
3576+
.as_ref()
3577+
.and_then(|typeck| typeck.node_type_opt(expr.hir_id))
3578+
&& let Some(pred) = predicate.as_clause()
3579+
&& let ty::ClauseKind::Trait(pred) = pred.kind().skip_binder()
3580+
&& self.can_eq(param_env, pred.self_ty(), expr_ty)
3581+
{
3582+
let expr_ty = tcx.short_ty_string(expr_ty, long_ty_file);
3583+
(expr_ty, expr)
3584+
} else {
3585+
return;
3586+
};
3587+
err.span_label(
3588+
expr.span,
3589+
with_forced_trimmed_paths!(format!(
3590+
"return type was inferred to be `{expr_ty}` here",
3591+
)),
3592+
);
3593+
suggest_remove_deref(err, &expr);
35773594
}
35783595
}
35793596
}
@@ -3680,6 +3697,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
36803697
err: &mut Diag<'_>,
36813698
trait_pred: ty::PolyTraitPredicate<'tcx>,
36823699
) {
3700+
if trait_pred.polarity() == ty::PredicatePolarity::Negative {
3701+
return;
3702+
}
36833703
let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) else {
36843704
return;
36853705
};

tests/ui/async-await/async-closures/not-clone-closure.stderr

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ error[E0277]: the trait bound `NotClonableUpvar: Clone` is not satisfied in `{as
22
--> $DIR/not-clone-closure.rs:32:15
33
|
44
LL | not_clone.clone();
5-
| ^^^^^ within `{async closure@$DIR/not-clone-closure.rs:29:21: 29:34}`, the trait `Clone` is not implemented for `NotClonableUpvar`
5+
| ^^^^^ unsatisfied trait bound
66
|
7+
= help: within `{async closure@$DIR/not-clone-closure.rs:29:21: 29:34}`, the trait `Clone` is not implemented for `NotClonableUpvar`
78
note: required because it's used within this closure
89
--> $DIR/not-clone-closure.rs:29:21
910
|

tests/ui/async-await/async-error-span.stderr

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ error[E0277]: `()` is not a future
33
|
44
LL | fn get_future() -> impl Future<Output = ()> {
55
| ^^^^^^^^^^^^^^^^^^^^^^^^ `()` is not a future
6+
LL |
7+
LL | panic!()
8+
| -------- return type was inferred to be `_` here
69
|
710
= help: the trait `Future` is not implemented for `()`
811

tests/ui/async-await/coroutine-not-future.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//@ edition:2018
2+
//@compile-flags: --diagnostic-width=300
23
#![feature(coroutines, coroutine_trait, stmt_expr_attributes)]
34

45
use std::future::Future;

0 commit comments

Comments
 (0)