Skip to content

Commit 94e5ec1

Browse files
committed
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. This suggestion applies to both E0233 and E0599 and is only checked when the first ident of a path corresponds to a primitive type.
1 parent d6f513e commit 94e5ec1

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)