Skip to content

Commit 085c16d

Browse files
authored
Rollup merge of #72923 - Patryk27:fix/52468, r=estebank
Improve E0433, so that it suggests missing imports Closes #52468
2 parents adc321a + c55d55e commit 085c16d

File tree

9 files changed

+198
-47
lines changed

9 files changed

+198
-47
lines changed

src/librustc_resolve/diagnostics.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -1475,7 +1475,7 @@ crate fn show_candidates(
14751475
// This is `None` if all placement locations are inside expansions
14761476
use_placement_span: Option<Span>,
14771477
candidates: &[ImportSuggestion],
1478-
better: bool,
1478+
instead: bool,
14791479
found_use: bool,
14801480
) {
14811481
if candidates.is_empty() {
@@ -1486,6 +1486,7 @@ crate fn show_candidates(
14861486
// by iterating through a hash map, so make sure they are ordered:
14871487
let mut path_strings: Vec<_> =
14881488
candidates.iter().map(|c| path_names_to_string(&c.path)).collect();
1489+
14891490
path_strings.sort();
14901491
path_strings.dedup();
14911492

@@ -1494,8 +1495,9 @@ crate fn show_candidates(
14941495
} else {
14951496
("one of these", "items")
14961497
};
1497-
let instead = if better { " instead" } else { "" };
1498-
let msg = format!("consider importing {} {}{}", determiner, kind, instead);
1498+
1499+
let instead = if instead { " instead" } else { "" };
1500+
let mut msg = format!("consider importing {} {}{}", determiner, kind, instead);
14991501

15001502
if let Some(span) = use_placement_span {
15011503
for candidate in &mut path_strings {
@@ -1507,12 +1509,13 @@ crate fn show_candidates(
15071509

15081510
err.span_suggestions(span, &msg, path_strings.into_iter(), Applicability::Unspecified);
15091511
} else {
1510-
let mut msg = msg;
15111512
msg.push(':');
1513+
15121514
for candidate in path_strings {
15131515
msg.push('\n');
15141516
msg.push_str(&candidate);
15151517
}
1518+
15161519
err.note(&msg);
15171520
}
15181521
}

src/librustc_resolve/late.rs

+116-30
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ use rustc_span::Span;
2929
use smallvec::{smallvec, SmallVec};
3030

3131
use log::debug;
32+
use rustc_span::source_map::{respan, Spanned};
3233
use std::collections::BTreeSet;
33-
use std::mem::replace;
34+
use std::mem::{replace, take};
3435

3536
mod diagnostics;
3637
crate mod lifetimes;
@@ -234,6 +235,13 @@ impl<'a> PathSource<'a> {
234235
}
235236
}
236237

238+
fn is_call(self) -> bool {
239+
match self {
240+
PathSource::Expr(Some(&Expr { kind: ExprKind::Call(..), .. })) => true,
241+
_ => false,
242+
}
243+
}
244+
237245
crate fn is_expected(self, res: Res) -> bool {
238246
match self {
239247
PathSource::Type => match res {
@@ -1620,14 +1628,83 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
16201628

16211629
let report_errors = |this: &mut Self, res: Option<Res>| {
16221630
let (err, candidates) = this.smart_resolve_report_errors(path, span, source, res);
1631+
16231632
let def_id = this.parent_scope.module.normal_ancestor_id;
1624-
let better = res.is_some();
1633+
let instead = res.is_some();
16251634
let suggestion =
16261635
if res.is_none() { this.report_missing_type_error(path) } else { None };
1627-
this.r.use_injections.push(UseError { err, candidates, def_id, better, suggestion });
1636+
1637+
this.r.use_injections.push(UseError { err, candidates, def_id, instead, suggestion });
1638+
16281639
PartialRes::new(Res::Err)
16291640
};
16301641

1642+
// For paths originating from calls (like in `HashMap::new()`), tries
1643+
// to enrich the plain `failed to resolve: ...` message with hints
1644+
// about possible missing imports.
1645+
//
1646+
// Similar thing, for types, happens in `report_errors` above.
1647+
let report_errors_for_call = |this: &mut Self, parent_err: Spanned<ResolutionError<'a>>| {
1648+
if !source.is_call() {
1649+
return Some(parent_err);
1650+
}
1651+
1652+
// Before we start looking for candidates, we have to get our hands
1653+
// on the type user is trying to perform invocation on; basically:
1654+
// we're transforming `HashMap::new` into just `HashMap`
1655+
let path = if let Some((_, path)) = path.split_last() {
1656+
path
1657+
} else {
1658+
return Some(parent_err);
1659+
};
1660+
1661+
let (mut err, candidates) =
1662+
this.smart_resolve_report_errors(path, span, PathSource::Type, None);
1663+
1664+
if candidates.is_empty() {
1665+
err.cancel();
1666+
return Some(parent_err);
1667+
}
1668+
1669+
// There are two different error messages user might receive at
1670+
// this point:
1671+
// - E0412 cannot find type `{}` in this scope
1672+
// - E0433 failed to resolve: use of undeclared type or module `{}`
1673+
//
1674+
// The first one is emitted for paths in type-position, and the
1675+
// latter one - for paths in expression-position.
1676+
//
1677+
// Thus (since we're in expression-position at this point), not to
1678+
// confuse the user, we want to keep the *message* from E0432 (so
1679+
// `parent_err`), but we want *hints* from E0412 (so `err`).
1680+
//
1681+
// And that's what happens below - we're just mixing both messages
1682+
// into a single one.
1683+
let mut parent_err = this.r.into_struct_error(parent_err.span, parent_err.node);
1684+
1685+
parent_err.cancel();
1686+
1687+
err.message = take(&mut parent_err.message);
1688+
err.code = take(&mut parent_err.code);
1689+
err.children = take(&mut parent_err.children);
1690+
1691+
drop(parent_err);
1692+
1693+
let def_id = this.parent_scope.module.normal_ancestor_id;
1694+
1695+
this.r.use_injections.push(UseError {
1696+
err,
1697+
candidates,
1698+
def_id,
1699+
instead: false,
1700+
suggestion: None,
1701+
});
1702+
1703+
// We don't return `Some(parent_err)` here, because the error will
1704+
// be already printed as part of the `use` injections
1705+
None
1706+
};
1707+
16311708
let partial_res = match self.resolve_qpath_anywhere(
16321709
id,
16331710
qself,
@@ -1637,14 +1714,15 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
16371714
source.defer_to_typeck(),
16381715
crate_lint,
16391716
) {
1640-
Some(partial_res) if partial_res.unresolved_segments() == 0 => {
1717+
Ok(Some(partial_res)) if partial_res.unresolved_segments() == 0 => {
16411718
if is_expected(partial_res.base_res()) || partial_res.base_res() == Res::Err {
16421719
partial_res
16431720
} else {
16441721
report_errors(self, Some(partial_res.base_res()))
16451722
}
16461723
}
1647-
Some(partial_res) if source.defer_to_typeck() => {
1724+
1725+
Ok(Some(partial_res)) if source.defer_to_typeck() => {
16481726
// Not fully resolved associated item `T::A::B` or `<T as Tr>::A::B`
16491727
// or `<T>::A::B`. If `B` should be resolved in value namespace then
16501728
// it needs to be added to the trait map.
@@ -1655,25 +1733,34 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
16551733
}
16561734

16571735
let mut std_path = vec![Segment::from_ident(Ident::with_dummy_span(sym::std))];
1736+
16581737
std_path.extend(path);
1738+
16591739
if self.r.primitive_type_table.primitive_types.contains_key(&path[0].ident.name) {
1660-
let cl = CrateLint::No;
1661-
let ns = Some(ns);
16621740
if let PathResult::Module(_) | PathResult::NonModule(_) =
1663-
self.resolve_path(&std_path, ns, false, span, cl)
1741+
self.resolve_path(&std_path, Some(ns), false, span, CrateLint::No)
16641742
{
1665-
// check if we wrote `str::from_utf8` instead of `std::str::from_utf8`
1743+
// Check if we wrote `str::from_utf8` instead of `std::str::from_utf8`
16661744
let item_span =
16671745
path.iter().last().map(|segment| segment.ident.span).unwrap_or(span);
1668-
debug!("accessed item from `std` submodule as a bare type {:?}", std_path);
1746+
16691747
let mut hm = self.r.session.confused_type_with_std_module.borrow_mut();
16701748
hm.insert(item_span, span);
1671-
// In some places (E0223) we only have access to the full path
16721749
hm.insert(span, span);
16731750
}
16741751
}
1752+
16751753
partial_res
16761754
}
1755+
1756+
Err(err) => {
1757+
if let Some(err) = report_errors_for_call(self, err) {
1758+
self.r.report_error(err.span, err.node);
1759+
}
1760+
1761+
PartialRes::new(Res::Err)
1762+
}
1763+
16771764
_ => report_errors(self, None),
16781765
};
16791766

@@ -1682,6 +1769,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
16821769
// Avoid recording definition of `A::B` in `<T as A>::B::C`.
16831770
self.r.record_partial_res(id, partial_res);
16841771
}
1772+
16851773
partial_res
16861774
}
16871775

@@ -1711,17 +1799,16 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
17111799
span: Span,
17121800
defer_to_typeck: bool,
17131801
crate_lint: CrateLint,
1714-
) -> Option<PartialRes> {
1802+
) -> Result<Option<PartialRes>, Spanned<ResolutionError<'a>>> {
17151803
let mut fin_res = None;
1804+
17161805
for (i, ns) in [primary_ns, TypeNS, ValueNS].iter().cloned().enumerate() {
17171806
if i == 0 || ns != primary_ns {
1718-
match self.resolve_qpath(id, qself, path, ns, span, crate_lint) {
1719-
// If defer_to_typeck, then resolution > no resolution,
1720-
// otherwise full resolution > partial resolution > no resolution.
1807+
match self.resolve_qpath(id, qself, path, ns, span, crate_lint)? {
17211808
Some(partial_res)
17221809
if partial_res.unresolved_segments() == 0 || defer_to_typeck =>
17231810
{
1724-
return Some(partial_res);
1811+
return Ok(Some(partial_res));
17251812
}
17261813
partial_res => {
17271814
if fin_res.is_none() {
@@ -1732,19 +1819,19 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
17321819
}
17331820
}
17341821

1735-
// `MacroNS`
17361822
assert!(primary_ns != MacroNS);
1823+
17371824
if qself.is_none() {
17381825
let path_seg = |seg: &Segment| PathSegment::from_ident(seg.ident);
17391826
let path = Path { segments: path.iter().map(path_seg).collect(), span };
17401827
if let Ok((_, res)) =
17411828
self.r.resolve_macro_path(&path, None, &self.parent_scope, false, false)
17421829
{
1743-
return Some(PartialRes::new(res));
1830+
return Ok(Some(PartialRes::new(res)));
17441831
}
17451832
}
17461833

1747-
fin_res
1834+
Ok(fin_res)
17481835
}
17491836

17501837
/// Handles paths that may refer to associated items.
@@ -1756,7 +1843,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
17561843
ns: Namespace,
17571844
span: Span,
17581845
crate_lint: CrateLint,
1759-
) -> Option<PartialRes> {
1846+
) -> Result<Option<PartialRes>, Spanned<ResolutionError<'a>>> {
17601847
debug!(
17611848
"resolve_qpath(id={:?}, qself={:?}, path={:?}, ns={:?}, span={:?})",
17621849
id, qself, path, ns, span,
@@ -1767,10 +1854,10 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
17671854
// This is a case like `<T>::B`, where there is no
17681855
// trait to resolve. In that case, we leave the `B`
17691856
// segment to be resolved by type-check.
1770-
return Some(PartialRes::with_unresolved_segments(
1857+
return Ok(Some(PartialRes::with_unresolved_segments(
17711858
Res::Def(DefKind::Mod, DefId::local(CRATE_DEF_INDEX)),
17721859
path.len(),
1773-
));
1860+
)));
17741861
}
17751862

17761863
// Make sure `A::B` in `<T as A::B>::C` is a trait item.
@@ -1800,10 +1887,10 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
18001887
// The remaining segments (the `C` in our example) will
18011888
// have to be resolved by type-check, since that requires doing
18021889
// trait resolution.
1803-
return Some(PartialRes::with_unresolved_segments(
1890+
return Ok(Some(PartialRes::with_unresolved_segments(
18041891
partial_res.base_res(),
18051892
partial_res.unresolved_segments() + path.len() - qself.position - 1,
1806-
));
1893+
)));
18071894
}
18081895

18091896
let result = match self.resolve_path(&path, Some(ns), true, span, crate_lint) {
@@ -1838,11 +1925,10 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
18381925
PartialRes::new(module.res().unwrap())
18391926
}
18401927
PathResult::Failed { is_error_from_last_segment: false, span, label, suggestion } => {
1841-
self.r.report_error(span, ResolutionError::FailedToResolve { label, suggestion });
1842-
PartialRes::new(Res::Err)
1928+
return Err(respan(span, ResolutionError::FailedToResolve { label, suggestion }));
18431929
}
1844-
PathResult::Module(..) | PathResult::Failed { .. } => return None,
1845-
PathResult::Indeterminate => bug!("indetermined path result in resolve_qpath"),
1930+
PathResult::Module(..) | PathResult::Failed { .. } => return Ok(None),
1931+
PathResult::Indeterminate => bug!("indeterminate path result in resolve_qpath"),
18461932
};
18471933

18481934
if path.len() > 1
@@ -1862,7 +1948,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
18621948
PathResult::Module(ModuleOrUniformRoot::Module(module)) => {
18631949
module.res().unwrap()
18641950
}
1865-
_ => return Some(result),
1951+
_ => return Ok(Some(result)),
18661952
}
18671953
};
18681954
if result.base_res() == unqualified_result {
@@ -1871,7 +1957,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
18711957
}
18721958
}
18731959

1874-
Some(result)
1960+
Ok(Some(result))
18751961
}
18761962

18771963
fn with_resolved_label(&mut self, label: Option<Label>, id: NodeId, f: impl FnOnce(&mut Self)) {

src/librustc_resolve/lib.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -618,13 +618,13 @@ struct PrivacyError<'a> {
618618

619619
struct UseError<'a> {
620620
err: DiagnosticBuilder<'a>,
621-
/// Attach `use` statements for these candidates.
621+
/// Candidates which user could `use` to access the missing type.
622622
candidates: Vec<ImportSuggestion>,
623-
/// The `NodeId` of the module to place the use-statements in.
623+
/// The `DefId` of the module to place the use-statements in.
624624
def_id: DefId,
625-
/// Whether the diagnostic should state that it's "better".
626-
better: bool,
627-
/// Extra free form suggestion. Currently used to suggest new type parameter.
625+
/// Whether the diagnostic should say "instead" (as in `consider importing ... instead`).
626+
instead: bool,
627+
/// Extra free-form suggestion.
628628
suggestion: Option<(Span, &'static str, String, Applicability)>,
629629
}
630630

@@ -2577,12 +2577,12 @@ impl<'a> Resolver<'a> {
25772577
}
25782578

25792579
fn report_with_use_injections(&mut self, krate: &Crate) {
2580-
for UseError { mut err, candidates, def_id, better, suggestion } in
2580+
for UseError { mut err, candidates, def_id, instead, suggestion } in
25812581
self.use_injections.drain(..)
25822582
{
25832583
let (span, found_use) = UsePlacementFinder::check(&self.definitions, krate, def_id);
25842584
if !candidates.is_empty() {
2585-
diagnostics::show_candidates(&mut err, span, &candidates, better, found_use);
2585+
diagnostics::show_candidates(&mut err, span, &candidates, instead, found_use);
25862586
} else if let Some((span, msg, sugg, appl)) = suggestion {
25872587
err.span_suggestion(span, msg, sugg, appl);
25882588
}

src/test/ui/derived-errors/issue-31997-1.stderr

+8-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,14 @@ error[E0433]: failed to resolve: use of undeclared type or module `HashMap`
22
--> $DIR/issue-31997-1.rs:20:19
33
|
44
LL | let mut map = HashMap::new();
5-
| ^^^^^^^ use of undeclared type or module `HashMap`
5+
| ^^^^^^^ not found in this scope
6+
|
7+
help: consider importing one of these items
8+
|
9+
LL | use std::collections::HashMap;
10+
|
11+
LL | use std::collections::hash_map::HashMap;
12+
|
613

714
error: aborting due to previous error
815

src/test/ui/error-codes/E0433.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
fn main () {
2-
let map = HashMap::new(); //~ ERROR E0433
2+
let map = NonExistingMap::new(); //~ ERROR E0433
33
}

0 commit comments

Comments
 (0)