+use hir::def_id::DefId;
+use hir::HirId;
+use hir::ItemKind;
use rustc_ast::Mutability;
use rustc_errors::Applicability;
use rustc_hir as hir;
-use rustc_middle::ty::Ty;
+use rustc_middle::ty::{Ref, Ty};
use rustc_session::lint::builtin::FUTURE_PRELUDE_COLLISION;
+use rustc_span::symbol::kw::Underscore;
use rustc_span::symbol::{sym, Ident};
use rustc_span::Span;
call_expr: &'tcx hir::Expr<'tcx>,
self_expr: &'tcx hir::Expr<'tcx>,
pick: &Pick<'tcx>,
+ args: &'tcx [hir::Expr<'tcx>],
) {
debug!(
"lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})",
return;
}
- self.tcx.struct_span_lint_hir(
- FUTURE_PRELUDE_COLLISION,
- call_expr.hir_id,
- call_expr.span,
- |lint| {
- let sp = call_expr.span;
- let trait_name = self.tcx.def_path_str(pick.item.container.id());
+ if matches!(pick.kind, probe::PickKind::InherentImplPick | probe::PickKind::ObjectPick) {
+ // avoid repeatedly adding unneeded `&*`s
+ if pick.autoderefs == 1
+ && matches!(
+ pick.autoref_or_ptr_adjustment,
+ Some(probe::AutorefOrPtrAdjustment::Autoref { .. })
+ )
+ && matches!(self_ty.kind(), Ref(..))
+ {
+ return;
+ }
+
+ // if it's an inherent `self` method (not `&self` or `&mut self`), it will take
+ // precedence over the `TryInto` impl, and thus won't break in 2021 edition
+ if pick.autoderefs == 0 && pick.autoref_or_ptr_adjustment.is_none() {
+ return;
+ }
+
+ // Inherent impls only require not relying on autoref and autoderef in order to
+ // ensure that the trait implementation won't be used
+ self.tcx.struct_span_lint_hir(
+ FUTURE_PRELUDE_COLLISION,
+ self_expr.hir_id,
+ self_expr.span,
+ |lint| {
+ let sp = self_expr.span;
- let mut lint = lint.build(&format!(
- "trait method `{}` will become ambiguous in Rust 2021",
- segment.ident.name
- ));
+ let mut lint = lint.build(&format!(
+ "trait method `{}` will become ambiguous in Rust 2021",
+ segment.ident.name
+ ));
- if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span) {
let derefs = "*".repeat(pick.autoderefs);
let autoref = match pick.autoref_or_ptr_adjustment {
}) => "&",
Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
};
- let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
- pick.autoref_or_ptr_adjustment
+ if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span)
{
- format!("{}{} as *const _", derefs, self_expr)
+ let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
+ pick.autoref_or_ptr_adjustment
+ {
+ format!("{}{} as *const _", derefs, self_expr)
+ } else {
+ format!("{}{}{}", autoref, derefs, self_expr)
+ };
+
+ lint.span_suggestion(
+ sp,
+ "disambiguate the method call",
+ format!("({})", self_adjusted),
+ Applicability::MachineApplicable,
+ );
} else {
- format!("{}{}{}", autoref, derefs, self_expr)
- };
- lint.span_suggestion(
- sp,
- "disambiguate the associated function",
- format!("{}::{}({})", trait_name, segment.ident.name, self_adjusted,),
- Applicability::MachineApplicable,
- );
- } else {
- lint.span_help(
- sp,
- &format!(
- "disambiguate the associated function with `{}::{}(...)`",
- trait_name, segment.ident,
- ),
+ let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
+ pick.autoref_or_ptr_adjustment
+ {
+ format!("{}(...) as *const _", derefs)
+ } else {
+ format!("{}{}...", autoref, derefs)
+ };
+ lint.span_help(
+ sp,
+ &format!("disambiguate the method call with `({})`", self_adjusted,),
+ );
+ }
+
+ lint.emit();
+ },
+ );
+ } else {
+ // trait implementations require full disambiguation to not clash with the new prelude
+ // additions (i.e. convert from dot-call to fully-qualified call)
+ self.tcx.struct_span_lint_hir(
+ FUTURE_PRELUDE_COLLISION,
+ call_expr.hir_id,
+ call_expr.span,
+ |lint| {
+ let sp = call_expr.span;
+ let trait_name = self.trait_path_or_bare_name(
+ span,
+ call_expr.hir_id,
+ pick.item.container.id(),
);
- }
- lint.emit();
- },
- );
+ let mut lint = lint.build(&format!(
+ "trait method `{}` will become ambiguous in Rust 2021",
+ segment.ident.name
+ ));
+
+ let (self_adjusted, precise) = self.adjust_expr(pick, self_expr);
+ if precise {
+ let args = args
+ .iter()
+ .skip(1)
+ .map(|arg| {
+ format!(
+ ", {}",
+ self.sess().source_map().span_to_snippet(arg.span).unwrap()
+ )
+ })
+ .collect::<String>();
+
+ lint.span_suggestion(
+ sp,
+ "disambiguate the associated function",
+ format!(
+ "{}::{}({}{})",
+ trait_name, segment.ident.name, self_adjusted, args
+ ),
+ Applicability::MachineApplicable,
+ );
+ } else {
+ lint.span_help(
+ sp,
+ &format!(
+ "disambiguate the associated function with `{}::{}(...)`",
+ trait_name, segment.ident,
+ ),
+ );
+ }
+
+ lint.emit();
+ },
+ );
+ }
}
pub(super) fn lint_fully_qualified_call_from_2018(
self.tcx.struct_span_lint_hir(FUTURE_PRELUDE_COLLISION, expr_id, span, |lint| {
// "type" refers to either a type or, more likely, a trait from which
// the associated function or method is from.
- let type_name = self.tcx.def_path_str(pick.item.container.id());
- let type_generics = self.tcx.generics_of(pick.item.container.id());
+ let trait_path = self.trait_path_or_bare_name(span, expr_id, pick.item.container.id());
+ let trait_generics = self.tcx.generics_of(pick.item.container.id());
- let parameter_count = type_generics.count() - (type_generics.has_self as usize);
+ let parameter_count = trait_generics.count() - (trait_generics.has_self as usize);
let trait_name = if parameter_count == 0 {
- type_name
+ trait_path
} else {
format!(
"{}<{}>",
- type_name,
+ trait_path,
std::iter::repeat("_").take(parameter_count).collect::<Vec<_>>().join(", ")
)
};
lint.emit();
});
}
+
+ fn trait_path_or_bare_name(
+ &self,
+ span: Span,
+ expr_hir_id: HirId,
+ trait_def_id: DefId,
+ ) -> String {
+ self.trait_path(span, expr_hir_id, trait_def_id).unwrap_or_else(|| {
+ let key = self.tcx.def_key(trait_def_id);
+ format!("{}", key.disambiguated_data.data)
+ })
+ }
+
+ fn trait_path(&self, span: Span, expr_hir_id: HirId, trait_def_id: DefId) -> Option<String> {
+ let applicable_traits = self.tcx.in_scope_traits(expr_hir_id)?;
+ let applicable_trait = applicable_traits.iter().find(|t| t.def_id == trait_def_id)?;
+ if applicable_trait.import_ids.is_empty() {
+ // The trait was declared within the module, we only need to use its name.
+ return None;
+ }
+
+ let import_items: Vec<_> = applicable_trait
+ .import_ids
+ .iter()
+ .map(|&import_id| {
+ let hir_id = self.tcx.hir().local_def_id_to_hir_id(import_id);
+ self.tcx.hir().expect_item(hir_id)
+ })
+ .collect();
+
+ // Find an identifier with which this trait was imported (note that `_` doesn't count).
+ let any_id = import_items
+ .iter()
+ .filter_map(|item| if item.ident.name != Underscore { Some(item.ident) } else { None })
+ .next();
+ if let Some(any_id) = any_id {
+ return Some(format!("{}", any_id));
+ }
+
+ // All that is left is `_`! We need to use the full path. It doesn't matter which one we pick,
+ // so just take the first one.
+ match import_items[0].kind {
+ ItemKind::Use(path, _) => Some(
+ path.segments
+ .iter()
+ .map(|segment| segment.ident.to_string())
+ .collect::<Vec<_>>()
+ .join("::"),
+ ),
+ _ => {
+ span_bug!(span, "unexpected item kind, expected a use: {:?}", import_items[0].kind);
+ }
+ }
+ }
+
+ /// Creates a string version of the `expr` that includes explicit adjustments.
+ /// Returns the string and also a bool indicating whther this is a *precise*
+ /// suggestion.
+ fn adjust_expr(&self, pick: &Pick<'tcx>, expr: &hir::Expr<'tcx>) -> (String, bool) {
+ let derefs = "*".repeat(pick.autoderefs);
+
+ let autoref = match pick.autoref_or_ptr_adjustment {
+ Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Mut, .. }) => "&mut ",
+ Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Not, .. }) => "&",
+ Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
+ };
+
+ let (expr_text, precise) =
+ if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) {
+ (expr_text, true)
+ } else {
+ (format!("(..)"), false)
+ };
+
+ let adjusted_text = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
+ pick.autoref_or_ptr_adjustment
+ {
+ format!("{}{} as *const _", derefs, expr_text)
+ } else {
+ format!("{}{}{}", autoref, derefs, expr_text)
+ };
+
+ (adjusted_text, precise)
+ }
}