Skip to content

Commit 11339a0

Browse files
committed
Auto merge of #144674 - rperier:add_note_if_a_type_impl_a_trait_with_the_same_name, r=lcnr
Add a diagnostic for similarly named traits cc #133123 This is a first proposal, suggestions are welcome
2 parents 25d319a + c8c0466 commit 11339a0

File tree

15 files changed

+386
-528
lines changed

15 files changed

+386
-528
lines changed

compiler/rustc_hir_typeck/src/method/suggest.rs

Lines changed: 10 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4212,7 +4212,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
42124212
err: &mut Diag<'_>,
42134213
item_def_id: DefId,
42144214
hir_id: hir::HirId,
4215-
rcvr_ty: Option<Ty<'_>>,
4215+
rcvr_ty: Option<Ty<'tcx>>,
42164216
) -> bool {
42174217
let hir_id = self.tcx.parent_hir_id(hir_id);
42184218
let Some(traits) = self.tcx.in_scope_traits(hir_id) else { return false };
@@ -4223,49 +4223,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
42234223
if !self.tcx.is_trait(trait_def_id) {
42244224
return false;
42254225
}
4226-
let krate = self.tcx.crate_name(trait_def_id.krate);
4227-
let name = self.tcx.item_name(trait_def_id);
4228-
let candidates: Vec<_> = traits
4229-
.iter()
4230-
.filter(|c| {
4231-
c.def_id.krate != trait_def_id.krate
4232-
&& self.tcx.crate_name(c.def_id.krate) == krate
4233-
&& self.tcx.item_name(c.def_id) == name
4234-
})
4235-
.map(|c| (c.def_id, c.import_ids.get(0).cloned()))
4236-
.collect();
4237-
if candidates.is_empty() {
4226+
let hir::Node::Expr(rcvr) = self.tcx.hir_node(hir_id) else {
42384227
return false;
4239-
}
4240-
let item_span = self.tcx.def_span(item_def_id);
4241-
let msg = format!(
4242-
"there are multiple different versions of crate `{krate}` in the dependency graph",
4243-
);
4244-
let trait_span = self.tcx.def_span(trait_def_id);
4245-
let mut multi_span: MultiSpan = trait_span.into();
4246-
multi_span.push_span_label(trait_span, "this is the trait that is needed".to_string());
4247-
let descr = self.tcx.associated_item(item_def_id).descr();
4248-
let rcvr_ty =
4249-
rcvr_ty.map(|t| format!("`{t}`")).unwrap_or_else(|| "the receiver".to_string());
4250-
multi_span
4251-
.push_span_label(item_span, format!("the {descr} is available for {rcvr_ty} here"));
4252-
for (def_id, import_def_id) in candidates {
4253-
if let Some(import_def_id) = import_def_id {
4254-
multi_span.push_span_label(
4255-
self.tcx.def_span(import_def_id),
4256-
format!(
4257-
"`{name}` imported here doesn't correspond to the right version of crate \
4258-
`{krate}`",
4259-
),
4260-
);
4261-
}
4262-
multi_span.push_span_label(
4263-
self.tcx.def_span(def_id),
4264-
"this is the trait that was imported".to_string(),
4265-
);
4266-
}
4267-
err.span_note(multi_span, msg);
4268-
true
4228+
};
4229+
let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, rcvr_ty.into_iter());
4230+
let trait_pred = ty::Binder::dummy(ty::TraitPredicate {
4231+
trait_ref,
4232+
polarity: ty::PredicatePolarity::Positive,
4233+
});
4234+
let obligation = Obligation::new(self.tcx, self.misc(rcvr.span), self.param_env, trait_ref);
4235+
self.err_ctxt().note_different_trait_with_same_name(err, &obligation, trait_pred)
42694236
}
42704237

42714238
/// issue #102320, for `unwrap_or` with closure as argument, suggest `unwrap_or_else`

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

Lines changed: 17 additions & 206 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,8 @@ use std::path::PathBuf;
5151
use std::{cmp, fmt, iter};
5252

5353
use rustc_abi::ExternAbi;
54-
use rustc_ast::join_path_syms;
5554
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
56-
use rustc_errors::{
57-
Applicability, Diag, DiagStyledString, IntoDiagArg, MultiSpan, StringPart, pluralize,
58-
};
55+
use rustc_errors::{Applicability, Diag, DiagStyledString, IntoDiagArg, StringPart, pluralize};
5956
use rustc_hir::def::DefKind;
6057
use rustc_hir::def_id::DefId;
6158
use rustc_hir::intravisit::Visitor;
@@ -66,15 +63,12 @@ use rustc_middle::bug;
6663
use rustc_middle::dep_graph::DepContext;
6764
use rustc_middle::traits::PatternOriginExpr;
6865
use rustc_middle::ty::error::{ExpectedFound, TypeError, TypeErrorToStringExt};
69-
use rustc_middle::ty::print::{
70-
PrintError, PrintTraitRefExt as _, WrapBinderMode, with_forced_trimmed_paths,
71-
};
66+
use rustc_middle::ty::print::{PrintTraitRefExt as _, WrapBinderMode, with_forced_trimmed_paths};
7267
use rustc_middle::ty::{
7368
self, List, ParamEnv, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable,
7469
TypeVisitableExt,
7570
};
76-
use rustc_span::def_id::LOCAL_CRATE;
77-
use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Pos, Span, Symbol, sym};
71+
use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Pos, Span, sym};
7872
use tracing::{debug, instrument};
7973

8074
use crate::error_reporting::TypeErrCtxt;
@@ -216,213 +210,30 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
216210

217211
/// Adds a note if the types come from similarly named crates
218212
fn check_and_note_conflicting_crates(&self, err: &mut Diag<'_>, terr: TypeError<'tcx>) -> bool {
219-
// FIXME(estebank): unify with `report_similar_impl_candidates`. The message is similar,
220-
// even if the logic needed to detect the case is very different.
221-
use hir::def_id::CrateNum;
222-
use rustc_hir::definitions::DisambiguatedDefPathData;
223-
use ty::GenericArg;
224-
use ty::print::Printer;
225-
226-
struct ConflictingPathPrinter<'tcx> {
227-
tcx: TyCtxt<'tcx>,
228-
segments: Vec<Symbol>,
229-
}
230-
231-
impl<'tcx> Printer<'tcx> for ConflictingPathPrinter<'tcx> {
232-
fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
233-
self.tcx
234-
}
235-
236-
fn print_region(&mut self, _region: ty::Region<'_>) -> Result<(), PrintError> {
237-
unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs`
238-
}
239-
240-
fn print_type(&mut self, _ty: Ty<'tcx>) -> Result<(), PrintError> {
241-
unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs`
242-
}
243-
244-
fn print_dyn_existential(
245-
&mut self,
246-
_predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
247-
) -> Result<(), PrintError> {
248-
unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs`
249-
}
250-
251-
fn print_const(&mut self, _ct: ty::Const<'tcx>) -> Result<(), PrintError> {
252-
unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs`
253-
}
254-
255-
fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
256-
self.segments = vec![self.tcx.crate_name(cnum)];
257-
Ok(())
258-
}
259-
260-
fn print_path_with_qualified(
261-
&mut self,
262-
_self_ty: Ty<'tcx>,
263-
_trait_ref: Option<ty::TraitRef<'tcx>>,
264-
) -> Result<(), PrintError> {
265-
Err(fmt::Error)
266-
}
267-
268-
fn print_path_with_impl(
269-
&mut self,
270-
_print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
271-
_self_ty: Ty<'tcx>,
272-
_trait_ref: Option<ty::TraitRef<'tcx>>,
273-
) -> Result<(), PrintError> {
274-
Err(fmt::Error)
275-
}
276-
277-
fn print_path_with_simple(
278-
&mut self,
279-
print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
280-
disambiguated_data: &DisambiguatedDefPathData,
281-
) -> Result<(), PrintError> {
282-
print_prefix(self)?;
283-
self.segments.push(disambiguated_data.as_sym(true));
284-
Ok(())
285-
}
286-
287-
fn print_path_with_generic_args(
288-
&mut self,
289-
print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
290-
_args: &[GenericArg<'tcx>],
291-
) -> Result<(), PrintError> {
292-
print_prefix(self)
293-
}
294-
}
295-
296-
let report_path_match = |err: &mut Diag<'_>, did1: DefId, did2: DefId, ty: &str| -> bool {
297-
// Only report definitions from different crates. If both definitions
298-
// are from a local module we could have false positives, e.g.
299-
// let _ = [{struct Foo; Foo}, {struct Foo; Foo}];
300-
if did1.krate != did2.krate {
301-
let abs_path = |def_id| {
302-
let mut p = ConflictingPathPrinter { tcx: self.tcx, segments: vec![] };
303-
p.print_def_path(def_id, &[]).map(|_| p.segments)
304-
};
305-
306-
// We compare strings because DefPath can be different for imported and
307-
// non-imported crates.
308-
let expected_str = self.tcx.def_path_str(did1);
309-
let found_str = self.tcx.def_path_str(did2);
310-
let Ok(expected_abs) = abs_path(did1) else { return false };
311-
let Ok(found_abs) = abs_path(did2) else { return false };
312-
let same_path = expected_str == found_str || expected_abs == found_abs;
313-
if same_path {
314-
// We want to use as unique a type path as possible. If both types are "locally
315-
// known" by the same name, we use the "absolute path" which uses the original
316-
// crate name instead.
317-
let (expected, found) = if expected_str == found_str {
318-
(join_path_syms(&expected_abs), join_path_syms(&found_abs))
319-
} else {
320-
(expected_str, found_str)
321-
};
322-
323-
// We've displayed "expected `a::b`, found `a::b`". We add context to
324-
// differentiate the different cases where that might happen.
325-
let expected_crate_name = self.tcx.crate_name(did1.krate);
326-
let found_crate_name = self.tcx.crate_name(did2.krate);
327-
let same_crate = expected_crate_name == found_crate_name;
328-
let expected_sp = self.tcx.def_span(did1);
329-
let found_sp = self.tcx.def_span(did2);
330-
331-
let both_direct_dependencies = if !did1.is_local()
332-
&& !did2.is_local()
333-
&& let Some(data1) = self.tcx.extern_crate(did1.krate)
334-
&& let Some(data2) = self.tcx.extern_crate(did2.krate)
335-
&& data1.dependency_of == LOCAL_CRATE
336-
&& data2.dependency_of == LOCAL_CRATE
337-
{
338-
// If both crates are directly depended on, we don't want to mention that
339-
// in the final message, as it is redundant wording.
340-
// We skip the case of semver trick, where one version of the local crate
341-
// depends on another version of itself by checking that both crates at play
342-
// are not the current one.
343-
true
344-
} else {
345-
false
346-
};
347-
348-
let mut span: MultiSpan = vec![expected_sp, found_sp].into();
349-
span.push_span_label(
350-
self.tcx.def_span(did1),
351-
format!("this is the expected {ty} `{expected}`"),
352-
);
353-
span.push_span_label(
354-
self.tcx.def_span(did2),
355-
format!("this is the found {ty} `{found}`"),
356-
);
357-
for def_id in [did1, did2] {
358-
let crate_name = self.tcx.crate_name(def_id.krate);
359-
if !def_id.is_local()
360-
&& let Some(data) = self.tcx.extern_crate(def_id.krate)
361-
{
362-
let descr = if same_crate {
363-
"one version of".to_string()
364-
} else {
365-
format!("one {ty} comes from")
366-
};
367-
let dependency = if both_direct_dependencies {
368-
if let rustc_session::cstore::ExternCrateSource::Extern(def_id) =
369-
data.src
370-
&& let Some(name) = self.tcx.opt_item_name(def_id)
371-
{
372-
format!(", which is renamed locally to `{name}`")
373-
} else {
374-
String::new()
375-
}
376-
} else if data.dependency_of == LOCAL_CRATE {
377-
", as a direct dependency of the current crate".to_string()
378-
} else {
379-
let dep = self.tcx.crate_name(data.dependency_of);
380-
format!(", as a dependency of crate `{dep}`")
381-
};
382-
span.push_span_label(
383-
data.span,
384-
format!("{descr} crate `{crate_name}` used here{dependency}"),
385-
);
386-
}
387-
}
388-
let msg = if (did1.is_local() || did2.is_local()) && same_crate {
389-
format!(
390-
"the crate `{expected_crate_name}` is compiled multiple times, \
391-
possibly with different configurations",
392-
)
393-
} else if same_crate {
394-
format!(
395-
"two different versions of crate `{expected_crate_name}` are being \
396-
used; two types coming from two different versions of the same crate \
397-
are different types even if they look the same",
398-
)
399-
} else {
400-
format!(
401-
"two types coming from two different crates are different types even \
402-
if they look the same",
403-
)
404-
};
405-
err.span_note(span, msg);
406-
if same_crate {
407-
err.help("you can use `cargo tree` to explore your dependency tree");
408-
}
409-
return true;
410-
}
411-
}
412-
false
413-
};
414213
match terr {
415214
TypeError::Sorts(ref exp_found) => {
416215
// if they are both "path types", there's a chance of ambiguity
417216
// due to different versions of the same crate
418217
if let (&ty::Adt(exp_adt, _), &ty::Adt(found_adt, _)) =
419218
(exp_found.expected.kind(), exp_found.found.kind())
420219
{
421-
return report_path_match(err, exp_adt.did(), found_adt.did(), "type");
220+
return self.check_same_definition_different_crate(
221+
err,
222+
exp_adt.did(),
223+
[found_adt.did()].into_iter(),
224+
|did| vec![self.tcx.def_span(did)],
225+
"type",
226+
);
422227
}
423228
}
424229
TypeError::Traits(ref exp_found) => {
425-
return report_path_match(err, exp_found.expected, exp_found.found, "trait");
230+
return self.check_same_definition_different_crate(
231+
err,
232+
exp_found.expected,
233+
[exp_found.found].into_iter(),
234+
|did| vec![self.tcx.def_span(did)],
235+
"trait",
236+
);
426237
}
427238
_ => (), // FIXME(#22750) handle traits and stuff
428239
}

0 commit comments

Comments
 (0)