Skip to content

Commit 2a9d97c

Browse files
authored
Rollup merge of rust-lang#60045 - estebank:suggest-std, r=petrochenkov
Suggest appropriate path when calling associated item on bare types When looking at the documentation for `std::f32` or `std::str`, for example, it is easy to get confused and assume `std::f32` and `f32` are the same thing. Because of this, it is not uncommon to attempt writing `f32::consts::PI` instead of the correct `std::f32::consts::PI`. When encountering the former, which results in an access error due to it being an inexistent path, try to access the same path under `std`. If this succeeds, this information is stored for later tweaking of the final E0599 to provide an appropriate suggestion. Fix rust-lang#26760, fix rust-lang#46660.
2 parents 729886d + 94e5ec1 commit 2a9d97c

File tree

7 files changed

+134
-40
lines changed

7 files changed

+134
-40
lines changed

src/librustc/session/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,10 @@ pub struct Session {
165165

166166
/// `Span`s of trait methods that weren't found to avoid emitting object safety errors
167167
pub trait_methods_not_found: Lock<FxHashSet<Span>>,
168+
169+
/// Mapping from ident span to path span for paths that don't exist as written, but that
170+
/// exist under `std`. For example, wrote `str::from_utf8` instead of `std::str::from_utf8`.
171+
pub confused_type_with_std_module: Lock<FxHashMap<Span, Span>>,
168172
}
169173

170174
pub struct PerfStats {
@@ -1248,6 +1252,7 @@ fn build_session_(
12481252
has_panic_handler: Once::new(),
12491253
driver_lint_caps,
12501254
trait_methods_not_found: Lock::new(Default::default()),
1255+
confused_type_with_std_module: Lock::new(Default::default()),
12511256
};
12521257

12531258
validate_commandline_args_with_session_available(&sess);

src/librustc_resolve/lib.rs

+40-19
Original file line numberDiff line numberDiff line change
@@ -3273,6 +3273,25 @@ impl<'a> Resolver<'a> {
32733273
let traits = self.get_traits_containing_item(item_name, ns);
32743274
self.trait_map.insert(id, traits);
32753275
}
3276+
3277+
let mut std_path = vec![Segment::from_ident(Ident::from_str("std"))];
3278+
std_path.extend(path);
3279+
if self.primitive_type_table.primitive_types.contains_key(&path[0].ident.name) {
3280+
let cl = CrateLint::No;
3281+
let ns = Some(ns);
3282+
if let PathResult::Module(_) | PathResult::NonModule(_) =
3283+
self.resolve_path_without_parent_scope(&std_path, ns, false, span, cl)
3284+
{
3285+
// check if we wrote `str::from_utf8` instead of `std::str::from_utf8`
3286+
let item_span = path.iter().last().map(|segment| segment.ident.span)
3287+
.unwrap_or(span);
3288+
debug!("accessed item from `std` submodule as a bare type {:?}", std_path);
3289+
let mut hm = self.session.confused_type_with_std_module.borrow_mut();
3290+
hm.insert(item_span, span);
3291+
// In some places (E0223) we only have access to the full path
3292+
hm.insert(span, span);
3293+
}
3294+
}
32763295
resolution
32773296
}
32783297
_ => report_errors(self, None)
@@ -3387,16 +3406,17 @@ impl<'a> Resolver<'a> {
33873406
}
33883407

33893408
// Resolve in alternative namespaces if resolution in the primary namespace fails.
3390-
fn resolve_qpath_anywhere(&mut self,
3391-
id: NodeId,
3392-
qself: Option<&QSelf>,
3393-
path: &[Segment],
3394-
primary_ns: Namespace,
3395-
span: Span,
3396-
defer_to_typeck: bool,
3397-
global_by_default: bool,
3398-
crate_lint: CrateLint)
3399-
-> Option<PathResolution> {
3409+
fn resolve_qpath_anywhere(
3410+
&mut self,
3411+
id: NodeId,
3412+
qself: Option<&QSelf>,
3413+
path: &[Segment],
3414+
primary_ns: Namespace,
3415+
span: Span,
3416+
defer_to_typeck: bool,
3417+
global_by_default: bool,
3418+
crate_lint: CrateLint,
3419+
) -> Option<PathResolution> {
34003420
let mut fin_res = None;
34013421
// FIXME: can't resolve paths in macro namespace yet, macros are
34023422
// processed by the little special hack below.
@@ -3426,15 +3446,16 @@ impl<'a> Resolver<'a> {
34263446
}
34273447

34283448
/// Handles paths that may refer to associated items.
3429-
fn resolve_qpath(&mut self,
3430-
id: NodeId,
3431-
qself: Option<&QSelf>,
3432-
path: &[Segment],
3433-
ns: Namespace,
3434-
span: Span,
3435-
global_by_default: bool,
3436-
crate_lint: CrateLint)
3437-
-> Option<PathResolution> {
3449+
fn resolve_qpath(
3450+
&mut self,
3451+
id: NodeId,
3452+
qself: Option<&QSelf>,
3453+
path: &[Segment],
3454+
ns: Namespace,
3455+
span: Span,
3456+
global_by_default: bool,
3457+
crate_lint: CrateLint,
3458+
) -> Option<PathResolution> {
34383459
debug!(
34393460
"resolve_qpath(id={:?}, qself={:?}, path={:?}, \
34403461
ns={:?}, span={:?}, global_by_default={:?})",

src/librustc_typeck/astconv.rs

+38-19
Original file line numberDiff line numberDiff line change
@@ -1187,18 +1187,33 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
11871187
ty
11881188
}
11891189

1190-
fn report_ambiguous_associated_type(&self,
1191-
span: Span,
1192-
type_str: &str,
1193-
trait_str: &str,
1194-
name: &str) {
1195-
struct_span_err!(self.tcx().sess, span, E0223, "ambiguous associated type")
1196-
.span_suggestion(
1190+
fn report_ambiguous_associated_type(
1191+
&self,
1192+
span: Span,
1193+
type_str: &str,
1194+
trait_str: &str,
1195+
name: &str,
1196+
) {
1197+
let mut err = struct_span_err!(self.tcx().sess, span, E0223, "ambiguous associated type");
1198+
if let (Some(_), Ok(snippet)) = (
1199+
self.tcx().sess.confused_type_with_std_module.borrow().get(&span),
1200+
self.tcx().sess.source_map().span_to_snippet(span),
1201+
) {
1202+
err.span_suggestion(
11971203
span,
1198-
"use fully-qualified syntax",
1199-
format!("<{} as {}>::{}", type_str, trait_str, name),
1200-
Applicability::HasPlaceholders
1201-
).emit();
1204+
"you are looking for the module in `std`, not the primitive type",
1205+
format!("std::{}", snippet),
1206+
Applicability::MachineApplicable,
1207+
);
1208+
} else {
1209+
err.span_suggestion(
1210+
span,
1211+
"use fully-qualified syntax",
1212+
format!("<{} as {}>::{}", type_str, trait_str, name),
1213+
Applicability::HasPlaceholders
1214+
);
1215+
}
1216+
err.emit();
12021217
}
12031218

12041219
// Search for a bound on a type parameter which includes the associated item
@@ -1391,10 +1406,12 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
13911406
err.emit();
13921407
} else if !qself_ty.references_error() {
13931408
// Don't print `TyErr` to the user.
1394-
self.report_ambiguous_associated_type(span,
1395-
&qself_ty.to_string(),
1396-
"Trait",
1397-
&assoc_ident.as_str());
1409+
self.report_ambiguous_associated_type(
1410+
span,
1411+
&qself_ty.to_string(),
1412+
"Trait",
1413+
&assoc_ident.as_str(),
1414+
);
13981415
}
13991416
return (tcx.types.err, Def::Err);
14001417
}
@@ -1461,10 +1478,12 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
14611478
ty
14621479
} else {
14631480
let path_str = tcx.def_path_str(trait_def_id);
1464-
self.report_ambiguous_associated_type(span,
1465-
"Type",
1466-
&path_str,
1467-
&item_segment.ident.as_str());
1481+
self.report_ambiguous_associated_type(
1482+
span,
1483+
"Type",
1484+
&path_str,
1485+
&item_segment.ident.as_str(),
1486+
);
14681487
return tcx.types.err;
14691488
};
14701489

src/librustc_typeck/check/method/suggest.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -292,15 +292,29 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
292292
return;
293293
} else {
294294
span = item_name.span;
295-
struct_span_err!(
295+
let mut err = struct_span_err!(
296296
tcx.sess,
297297
span,
298298
E0599,
299299
"no {} named `{}` found for type `{}` in the current scope",
300300
item_kind,
301301
item_name,
302302
ty_str
303-
)
303+
);
304+
if let Some(span) = tcx.sess.confused_type_with_std_module.borrow()
305+
.get(&span)
306+
{
307+
if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*span) {
308+
err.span_suggestion(
309+
*span,
310+
"you are looking for the module in `std`, \
311+
not the primitive type",
312+
format!("std::{}", snippet),
313+
Applicability::MachineApplicable,
314+
);
315+
}
316+
}
317+
err
304318
}
305319
} else {
306320
tcx.sess.diagnostic().struct_dummy()

src/test/ui/issues/issue-22933-3.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ error[E0599]: no associated item named `MIN` found for type `u8` in the current
33
|
44
LL | const FOO: [u32; u8::MIN as usize] = [];
55
| ^^^ associated item not found in `u8`
6+
help: you are looking for the module in `std`, not the primitive type
7+
|
8+
LL | const FOO: [u32; std::u8::MIN as usize] = [];
9+
| ^^^^^^^^^^^^
610

711
error: aborting due to previous error
812

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
fn main() {
2+
let pi = f32::consts::PI; //~ ERROR ambiguous associated type
3+
let bytes = "hello world".as_bytes();
4+
let string = unsafe {
5+
str::from_utf8(bytes) //~ ERROR no function or associated item named `from_utf8` found
6+
};
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
error[E0223]: ambiguous associated type
2+
--> $DIR/suggest-std-when-using-type.rs:2:14
3+
|
4+
LL | let pi = f32::consts::PI;
5+
| ^^^^^^^^^^^^^^^
6+
help: you are looking for the module in `std`, not the primitive type
7+
|
8+
LL | let pi = std::f32::consts::PI;
9+
| ^^^^^^^^^^^^^^^^^^^^
10+
11+
error[E0599]: no function or associated item named `from_utf8` found for type `str` in the current scope
12+
--> $DIR/suggest-std-when-using-type.rs:5:14
13+
|
14+
LL | str::from_utf8(bytes)
15+
| ^^^^^^^^^ function or associated item not found in `str`
16+
help: you are looking for the module in `std`, not the primitive type
17+
|
18+
LL | std::str::from_utf8(bytes)
19+
| ^^^^^^^^^^^^^^^^^^^
20+
21+
error: aborting due to 2 previous errors
22+
23+
Some errors occurred: E0223, E0599.
24+
For more information about an error, try `rustc --explain E0223`.

0 commit comments

Comments
 (0)