Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggest appropriate path when calling associated item on bare types #60045

Merged
merged 1 commit into from
Apr 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/librustc/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,10 @@ pub struct Session {

/// `Span`s of trait methods that weren't found to avoid emitting object safety errors
pub trait_methods_not_found: Lock<FxHashSet<Span>>,

/// Mapping from ident span to path span for paths that don't exist as written, but that
/// exist under `std`. For example, wrote `str::from_utf8` instead of `std::str::from_utf8`.
pub confused_type_with_std_module: Lock<FxHashMap<Span, Span>>,
}

pub struct PerfStats {
Expand Down Expand Up @@ -1248,6 +1252,7 @@ fn build_session_(
has_panic_handler: Once::new(),
driver_lint_caps,
trait_methods_not_found: Lock::new(Default::default()),
confused_type_with_std_module: Lock::new(Default::default()),
};

validate_commandline_args_with_session_available(&sess);
Expand Down
59 changes: 40 additions & 19 deletions src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3273,6 +3273,25 @@ impl<'a> Resolver<'a> {
let traits = self.get_traits_containing_item(item_name, ns);
self.trait_map.insert(id, traits);
}

let mut std_path = vec![Segment::from_ident(Ident::from_str("std"))];
std_path.extend(path);
if self.primitive_type_table.primitive_types.contains_key(&path[0].ident.name) {
let cl = CrateLint::No;
let ns = Some(ns);
if let PathResult::Module(_) | PathResult::NonModule(_) =
self.resolve_path_without_parent_scope(&std_path, ns, false, span, cl)
{
// check if we wrote `str::from_utf8` instead of `std::str::from_utf8`
let item_span = path.iter().last().map(|segment| segment.ident.span)
.unwrap_or(span);
debug!("accessed item from `std` submodule as a bare type {:?}", std_path);
let mut hm = self.session.confused_type_with_std_module.borrow_mut();
hm.insert(item_span, span);
// In some places (E0223) we only have access to the full path
hm.insert(span, span);
}
}
resolution
}
_ => report_errors(self, None)
Expand Down Expand Up @@ -3387,16 +3406,17 @@ impl<'a> Resolver<'a> {
}

// Resolve in alternative namespaces if resolution in the primary namespace fails.
fn resolve_qpath_anywhere(&mut self,
id: NodeId,
qself: Option<&QSelf>,
path: &[Segment],
primary_ns: Namespace,
span: Span,
defer_to_typeck: bool,
global_by_default: bool,
crate_lint: CrateLint)
-> Option<PathResolution> {
fn resolve_qpath_anywhere(
&mut self,
id: NodeId,
qself: Option<&QSelf>,
path: &[Segment],
primary_ns: Namespace,
span: Span,
defer_to_typeck: bool,
global_by_default: bool,
crate_lint: CrateLint,
) -> Option<PathResolution> {
let mut fin_res = None;
// FIXME: can't resolve paths in macro namespace yet, macros are
// processed by the little special hack below.
Expand Down Expand Up @@ -3426,15 +3446,16 @@ impl<'a> Resolver<'a> {
}

/// Handles paths that may refer to associated items.
fn resolve_qpath(&mut self,
id: NodeId,
qself: Option<&QSelf>,
path: &[Segment],
ns: Namespace,
span: Span,
global_by_default: bool,
crate_lint: CrateLint)
-> Option<PathResolution> {
fn resolve_qpath(
&mut self,
id: NodeId,
qself: Option<&QSelf>,
path: &[Segment],
ns: Namespace,
span: Span,
global_by_default: bool,
crate_lint: CrateLint,
) -> Option<PathResolution> {
debug!(
"resolve_qpath(id={:?}, qself={:?}, path={:?}, \
ns={:?}, span={:?}, global_by_default={:?})",
Expand Down
57 changes: 38 additions & 19 deletions src/librustc_typeck/astconv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1187,18 +1187,33 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
ty
}

fn report_ambiguous_associated_type(&self,
span: Span,
type_str: &str,
trait_str: &str,
name: &str) {
struct_span_err!(self.tcx().sess, span, E0223, "ambiguous associated type")
.span_suggestion(
fn report_ambiguous_associated_type(
&self,
span: Span,
type_str: &str,
trait_str: &str,
name: &str,
) {
let mut err = struct_span_err!(self.tcx().sess, span, E0223, "ambiguous associated type");
if let (Some(_), Ok(snippet)) = (
self.tcx().sess.confused_type_with_std_module.borrow().get(&span),
self.tcx().sess.source_map().span_to_snippet(span),
) {
err.span_suggestion(
span,
"use fully-qualified syntax",
format!("<{} as {}>::{}", type_str, trait_str, name),
Applicability::HasPlaceholders
).emit();
"you are looking for the module in `std`, not the primitive type",
format!("std::{}", snippet),
Applicability::MachineApplicable,
);
} else {
err.span_suggestion(
span,
"use fully-qualified syntax",
format!("<{} as {}>::{}", type_str, trait_str, name),
Applicability::HasPlaceholders
);
}
err.emit();
}

// Search for a bound on a type parameter which includes the associated item
Expand Down Expand Up @@ -1391,10 +1406,12 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
err.emit();
} else if !qself_ty.references_error() {
// Don't print `TyErr` to the user.
self.report_ambiguous_associated_type(span,
&qself_ty.to_string(),
"Trait",
&assoc_ident.as_str());
self.report_ambiguous_associated_type(
span,
&qself_ty.to_string(),
"Trait",
&assoc_ident.as_str(),
);
}
return (tcx.types.err, Def::Err);
}
Expand Down Expand Up @@ -1461,10 +1478,12 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
ty
} else {
let path_str = tcx.def_path_str(trait_def_id);
self.report_ambiguous_associated_type(span,
"Type",
&path_str,
&item_segment.ident.as_str());
self.report_ambiguous_associated_type(
span,
"Type",
&path_str,
&item_segment.ident.as_str(),
);
return tcx.types.err;
};

Expand Down
18 changes: 16 additions & 2 deletions src/librustc_typeck/check/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,15 +292,29 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
return;
} else {
span = item_name.span;
struct_span_err!(
let mut err = struct_span_err!(
tcx.sess,
span,
E0599,
"no {} named `{}` found for type `{}` in the current scope",
item_kind,
item_name,
ty_str
)
);
if let Some(span) = tcx.sess.confused_type_with_std_module.borrow()
.get(&span)
{
if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*span) {
err.span_suggestion(
*span,
"you are looking for the module in `std`, \
not the primitive type",
format!("std::{}", snippet),
Applicability::MachineApplicable,
);
}
}
err
}
} else {
tcx.sess.diagnostic().struct_dummy()
Expand Down
4 changes: 4 additions & 0 deletions src/test/ui/issues/issue-22933-3.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ error[E0599]: no associated item named `MIN` found for type `u8` in the current
|
LL | const FOO: [u32; u8::MIN as usize] = [];
| ^^^ associated item not found in `u8`
help: you are looking for the module in `std`, not the primitive type
|
LL | const FOO: [u32; std::u8::MIN as usize] = [];
| ^^^^^^^^^^^^

error: aborting due to previous error

Expand Down
7 changes: 7 additions & 0 deletions src/test/ui/suggestions/suggest-std-when-using-type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
fn main() {
let pi = f32::consts::PI; //~ ERROR ambiguous associated type
let bytes = "hello world".as_bytes();
let string = unsafe {
str::from_utf8(bytes) //~ ERROR no function or associated item named `from_utf8` found
};
}
24 changes: 24 additions & 0 deletions src/test/ui/suggestions/suggest-std-when-using-type.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
error[E0223]: ambiguous associated type
--> $DIR/suggest-std-when-using-type.rs:2:14
|
LL | let pi = f32::consts::PI;
| ^^^^^^^^^^^^^^^
help: you are looking for the module in `std`, not the primitive type
|
LL | let pi = std::f32::consts::PI;
| ^^^^^^^^^^^^^^^^^^^^

error[E0599]: no function or associated item named `from_utf8` found for type `str` in the current scope
--> $DIR/suggest-std-when-using-type.rs:5:14
|
LL | str::from_utf8(bytes)
| ^^^^^^^^^ function or associated item not found in `str`
help: you are looking for the module in `std`, not the primitive type
|
LL | std::str::from_utf8(bytes)
| ^^^^^^^^^^^^^^^^^^^

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0223, E0599.
For more information about an error, try `rustc --explain E0223`.