From 94e5ec19439f180522e739cc430207623d446f69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 17 Apr 2019 10:24:50 -0700 Subject: [PATCH] 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. --- src/librustc/session/mod.rs | 5 ++ src/librustc_resolve/lib.rs | 59 +++++++++++++------ src/librustc_typeck/astconv.rs | 57 ++++++++++++------ src/librustc_typeck/check/method/suggest.rs | 18 +++++- src/test/ui/issues/issue-22933-3.stderr | 4 ++ .../suggest-std-when-using-type.rs | 7 +++ .../suggest-std-when-using-type.stderr | 24 ++++++++ 7 files changed, 134 insertions(+), 40 deletions(-) create mode 100644 src/test/ui/suggestions/suggest-std-when-using-type.rs create mode 100644 src/test/ui/suggestions/suggest-std-when-using-type.stderr diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index eed516a438175..d9da995037325 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -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>, + + /// 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>, } pub struct PerfStats { @@ -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); diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 08b2f1a0f16fc..8df83120738c1 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -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) @@ -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 { + 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 { let mut fin_res = None; // FIXME: can't resolve paths in macro namespace yet, macros are // processed by the little special hack below. @@ -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 { + fn resolve_qpath( + &mut self, + id: NodeId, + qself: Option<&QSelf>, + path: &[Segment], + ns: Namespace, + span: Span, + global_by_default: bool, + crate_lint: CrateLint, + ) -> Option { debug!( "resolve_qpath(id={:?}, qself={:?}, path={:?}, \ ns={:?}, span={:?}, global_by_default={:?})", diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 0c206b27f8058..be8e5dae1d9f9 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -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 @@ -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); } @@ -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; }; diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index ff889c89770af..7121b06e27a0f 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -292,7 +292,7 @@ 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, @@ -300,7 +300,21 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { 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() diff --git a/src/test/ui/issues/issue-22933-3.stderr b/src/test/ui/issues/issue-22933-3.stderr index b1afda6d15114..e0e8b5e18a42d 100644 --- a/src/test/ui/issues/issue-22933-3.stderr +++ b/src/test/ui/issues/issue-22933-3.stderr @@ -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 diff --git a/src/test/ui/suggestions/suggest-std-when-using-type.rs b/src/test/ui/suggestions/suggest-std-when-using-type.rs new file mode 100644 index 0000000000000..9ca68a635da96 --- /dev/null +++ b/src/test/ui/suggestions/suggest-std-when-using-type.rs @@ -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 + }; +} diff --git a/src/test/ui/suggestions/suggest-std-when-using-type.stderr b/src/test/ui/suggestions/suggest-std-when-using-type.stderr new file mode 100644 index 0000000000000..7c19586bbd3c1 --- /dev/null +++ b/src/test/ui/suggestions/suggest-std-when-using-type.stderr @@ -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 occurred: E0223, E0599. +For more information about an error, try `rustc --explain E0223`.