Skip to content

Commit

Permalink
Auto merge of rust-lang#102726 - matthiaskrgr:rollup-2ghn38b, r=matth…
Browse files Browse the repository at this point in the history
…iaskrgr

Rollup of 5 pull requests

Successful merges:

 - rust-lang#102672 (rustdoc: remove unused CSS class `in-band`)
 - rust-lang#102693 (Revert "Use getentropy when possible on all Apple platforms")
 - rust-lang#102694 (Suggest calling method if fn does not exist)
 - rust-lang#102708 (Suggest `==` to wrong assign expr)
 - rust-lang#102710 (Add test for issue 82633)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
  • Loading branch information
bors committed Oct 6, 2022
2 parents 0152393 + b7642fb commit 4bd3078
Show file tree
Hide file tree
Showing 62 changed files with 903 additions and 502 deletions.
1 change: 1 addition & 0 deletions compiler/rustc_errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,7 @@ pub enum StashKey {
ItemNoType,
UnderscoreForArrayLengths,
EarlySyntaxWarning,
CallIntoMethod,
}

fn default_track_diagnostic(_: &Diagnostic) {}
Expand Down
142 changes: 136 additions & 6 deletions compiler/rustc_hir_analysis/src/check/callee.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use super::method::probe::{IsSuggestion, Mode, ProbeScope};
use super::method::MethodCallee;
use super::{DefIdOrName, Expectation, FnCtxt, TupleArgumentsFlag};
use crate::type_error_struct;

use rustc_errors::{struct_span_err, Applicability, Diagnostic};
use rustc_ast::util::parser::PREC_POSTFIX;
use rustc_errors::{struct_span_err, Applicability, Diagnostic, StashKey};
use rustc_hir as hir;
use rustc_hir::def::{self, Namespace, Res};
use rustc_hir::def_id::DefId;
Expand Down Expand Up @@ -60,6 +62,7 @@ pub fn check_legal_trait_for_method_call(
}
}

#[derive(Debug)]
enum CallStep<'tcx> {
Builtin(Ty<'tcx>),
DeferredClosure(LocalDefId, ty::FnSig<'tcx>),
Expand Down Expand Up @@ -188,6 +191,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return None;
}

ty::Error(_) => {
return None;
}

_ => {}
}

Expand Down Expand Up @@ -394,6 +401,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
ty::FnPtr(sig) => (sig, None),
_ => {
if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = &callee_expr.kind
&& let [segment] = path.segments
&& let Some(mut diag) = self
.tcx
.sess
.diagnostic()
.steal_diagnostic(segment.ident.span, StashKey::CallIntoMethod)
{
// Try suggesting `foo(a)` -> `a.foo()` if possible.
if let Some(ty) =
self.suggest_call_as_method(
&mut diag,
segment,
arg_exprs,
call_expr,
expected
)
{
diag.emit();
return ty;
} else {
diag.emit();
}
}

self.report_invalid_callee(call_expr, callee_expr, callee_ty, arg_exprs);

// This is the "default" function signature, used in case of error.
Expand Down Expand Up @@ -441,6 +473,105 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn_sig.output()
}

/// Attempts to reinterpret `method(rcvr, args...)` as `rcvr.method(args...)`
/// and suggesting the fix if the method probe is successful.
fn suggest_call_as_method(
&self,
diag: &mut Diagnostic,
segment: &'tcx hir::PathSegment<'tcx>,
arg_exprs: &'tcx [hir::Expr<'tcx>],
call_expr: &'tcx hir::Expr<'tcx>,
expected: Expectation<'tcx>,
) -> Option<Ty<'tcx>> {
if let [callee_expr, rest @ ..] = arg_exprs {
let callee_ty = self.check_expr(callee_expr);
// First, do a probe with `IsSuggestion(true)` to avoid emitting
// any strange errors. If it's successful, then we'll do a true
// method lookup.
let Ok(pick) = self
.probe_for_name(
call_expr.span,
Mode::MethodCall,
segment.ident,
IsSuggestion(true),
callee_ty,
call_expr.hir_id,
// We didn't record the in scope traits during late resolution
// so we need to probe AllTraits unfortunately
ProbeScope::AllTraits,
) else {
return None;
};

let pick = self.confirm_method(
call_expr.span,
callee_expr,
call_expr,
callee_ty,
pick,
segment,
);
if pick.illegal_sized_bound.is_some() {
return None;
}

let up_to_rcvr_span = segment.ident.span.until(callee_expr.span);
let rest_span = callee_expr.span.shrink_to_hi().to(call_expr.span.shrink_to_hi());
let rest_snippet = if let Some(first) = rest.first() {
self.tcx
.sess
.source_map()
.span_to_snippet(first.span.to(call_expr.span.shrink_to_hi()))
} else {
Ok(")".to_string())
};

if let Ok(rest_snippet) = rest_snippet {
let sugg = if callee_expr.precedence().order() >= PREC_POSTFIX {
vec![
(up_to_rcvr_span, "".to_string()),
(rest_span, format!(".{}({rest_snippet}", segment.ident)),
]
} else {
vec![
(up_to_rcvr_span, "(".to_string()),
(rest_span, format!(").{}({rest_snippet}", segment.ident)),
]
};
let self_ty = self.resolve_vars_if_possible(pick.callee.sig.inputs()[0]);
diag.multipart_suggestion(
format!(
"use the `.` operator to call the method `{}{}` on `{self_ty}`",
self.tcx
.associated_item(pick.callee.def_id)
.trait_container(self.tcx)
.map_or_else(
|| String::new(),
|trait_def_id| self.tcx.def_path_str(trait_def_id) + "::"
),
segment.ident
),
sugg,
Applicability::MaybeIncorrect,
);

// Let's check the method fully now
let return_ty = self.check_method_argument_types(
segment.ident.span,
call_expr,
Ok(pick.callee),
rest,
TupleArgumentsFlag::DontTupleArguments,
expected,
);

return Some(return_ty);
}
}

None
}

fn report_invalid_callee(
&self,
call_expr: &'tcx hir::Expr<'tcx>,
Expand All @@ -459,10 +590,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
def::CtorOf::Struct => "struct",
def::CtorOf::Variant => "enum variant",
};
let removal_span =
callee_expr.span.shrink_to_hi().to(call_expr.span.shrink_to_hi());
unit_variant =
Some((removal_span, descr, rustc_hir_pretty::qpath_to_string(qpath)));
let removal_span = callee_expr.span.shrink_to_hi().to(call_expr.span.shrink_to_hi());
unit_variant = Some((removal_span, descr, rustc_hir_pretty::qpath_to_string(qpath)));
}

let callee_ty = self.resolve_vars_if_possible(callee_ty);
Expand Down Expand Up @@ -525,7 +654,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};

if !self.maybe_suggest_bad_array_definition(&mut err, call_expr, callee_expr) {
if let Some((maybe_def, output_ty, _)) = self.extract_callable_info(callee_expr, callee_ty)
if let Some((maybe_def, output_ty, _)) =
self.extract_callable_info(callee_expr, callee_ty)
&& !self.type_is_sized_modulo_regions(self.param_env, output_ty, callee_expr.span)
{
let descr = match maybe_def {
Expand Down
12 changes: 10 additions & 2 deletions compiler/rustc_hir_analysis/src/check/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1045,6 +1045,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let rhs_ty = self.check_expr(&rhs);
let (applicability, eq) = if self.can_coerce(rhs_ty, lhs_ty) {
(Applicability::MachineApplicable, true)
} else if let ExprKind::Binary(
Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. },
_,
rhs_expr,
) = lhs.kind
{
let actual_lhs_ty = self.check_expr(&rhs_expr);
(Applicability::MaybeIncorrect, self.can_coerce(rhs_ty, actual_lhs_ty))
} else {
(Applicability::MaybeIncorrect, false)
};
Expand All @@ -1067,9 +1075,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
if eq {
err.span_suggestion_verbose(
span,
span.shrink_to_hi(),
"you might have meant to compare for equality",
"==",
'=',
applicability,
);
}
Expand Down
10 changes: 8 additions & 2 deletions compiler/rustc_resolve/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,15 @@ impl<'a> Resolver<'a> {
}

fn report_with_use_injections(&mut self, krate: &Crate) {
for UseError { mut err, candidates, def_id, instead, suggestion, path } in
for UseError { mut err, candidates, def_id, instead, suggestion, path, is_call } in
self.use_injections.drain(..)
{
let (span, found_use) = if let Some(def_id) = def_id.as_local() {
UsePlacementFinder::check(krate, self.def_id_to_node_id[def_id])
} else {
(None, FoundUse::No)
};

if !candidates.is_empty() {
show_candidates(
&self.session,
Expand All @@ -140,10 +141,15 @@ impl<'a> Resolver<'a> {
IsPattern::No,
path,
);
err.emit();
} else if let Some((span, msg, sugg, appl)) = suggestion {
err.span_suggestion(span, msg, sugg, appl);
err.emit();
} else if let [segment] = path.as_slice() && is_call {
err.stash(segment.ident.span, rustc_errors::StashKey::CallIntoMethod);
} else {
err.emit();
}
err.emit();
}
}

Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3263,6 +3263,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
instead,
suggestion,
path: path.into(),
is_call: source.is_call(),
});
}

Expand Down Expand Up @@ -3327,6 +3328,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
instead: false,
suggestion: None,
path: path.into(),
is_call: source.is_call(),
});
} else {
err.cancel();
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_resolve/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,8 @@ struct UseError<'a> {
/// Path `Segment`s at the place of use that failed. Used for accurate suggestion after telling
/// the user to import the item directly.
path: Vec<Segment>,
/// Whether the expected source is a call
is_call: bool,
}

#[derive(Clone, Copy, PartialEq, Debug)]
Expand Down
Loading

0 comments on commit 4bd3078

Please sign in to comment.