diff --git a/README.md b/README.md index 2b7edf2c3c907..ac39435a8c7fb 100644 --- a/README.md +++ b/README.md @@ -20,22 +20,23 @@ Read ["Installation"] from [The Book]. The Rust build system uses a Python script called `x.py` to build the compiler, which manages the bootstrapping process. It lives at the root of the project. -The `x.py` command can be run directly on most systems in the following format: +The `x.py` command can be run directly on most Unix systems in the following format: ```sh ./x.py [flags] ``` -This is how the documentation and examples assume you are running `x.py`. - -Systems such as Ubuntu 20.04 LTS do not create the necessary `python` command by default when Python is installed that allows `x.py` to be run directly. In that case, you can either create a symlink for `python` (Ubuntu provides the `python-is-python3` package for this), or run `x.py` using Python itself: +This is how the documentation and examples assume you are running `x.py`. Some alternative ways are: ```sh -# Python 3 -python3 x.py [flags] +# On a Unix shell if you don't have the necessary `python3` command +./x [flags] + +# On the Windows Command Prompt (if .py files are configured to run Python) +x.py [flags] -# Python 2.7 -python2.7 x.py [flags] +# You can also run Python yourself, e.g.: +python x.py [flags] ``` More information about `x.py` can be found diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 042ff0b46a5af..0335c45a946d5 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -1,5 +1,6 @@ use crate::FnCtxt; use rustc_ast::util::parser::PREC_POSTFIX; +use rustc_errors::MultiSpan; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def::CtorKind; @@ -30,12 +31,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr_ty: Ty<'tcx>, expected: Ty<'tcx>, expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, - _error: Option>, + error: Option>, ) { if expr_ty == expected { return; } + self.annotate_alternative_method_deref(err, expr, error); + // Use `||` to give these suggestions a precedence let _ = self.suggest_missing_parentheses(err, expr) || self.suggest_remove_last_method_call(err, expr, expected) @@ -316,6 +319,162 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + fn annotate_alternative_method_deref( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + error: Option>, + ) { + let parent = self.tcx.hir().get_parent_node(expr.hir_id); + let Some(TypeError::Sorts(ExpectedFound { expected, .. })) = error else {return;}; + let Some(hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Assign(lhs, rhs, _), .. + })) = self.tcx.hir().find(parent) else {return; }; + if rhs.hir_id != expr.hir_id || expected.is_closure() { + return; + } + let hir::ExprKind::Unary(hir::UnOp::Deref, deref) = lhs.kind else { return; }; + let hir::ExprKind::MethodCall(path, base, args, _) = deref.kind else { return; }; + let Some(self_ty) = self.typeck_results.borrow().expr_ty_adjusted_opt(base) else { return; }; + + let Ok(pick) = self + .probe_for_name( + probe::Mode::MethodCall, + path.ident, + probe::IsSuggestion(true), + self_ty, + deref.hir_id, + probe::ProbeScope::TraitsInScope, + ) else { + return; + }; + let in_scope_methods = self.probe_for_name_many( + probe::Mode::MethodCall, + path.ident, + probe::IsSuggestion(true), + self_ty, + deref.hir_id, + probe::ProbeScope::TraitsInScope, + ); + let other_methods_in_scope: Vec<_> = + in_scope_methods.iter().filter(|c| c.item.def_id != pick.item.def_id).collect(); + + let all_methods = self.probe_for_name_many( + probe::Mode::MethodCall, + path.ident, + probe::IsSuggestion(true), + self_ty, + deref.hir_id, + probe::ProbeScope::AllTraits, + ); + let suggestions: Vec<_> = all_methods + .into_iter() + .filter(|c| c.item.def_id != pick.item.def_id) + .map(|c| { + let m = c.item; + let substs = ty::InternalSubsts::for_item(self.tcx, m.def_id, |param, _| { + self.var_for_def(deref.span, param) + }); + vec![ + ( + deref.span.until(base.span), + format!( + "{}({}", + with_no_trimmed_paths!( + self.tcx.def_path_str_with_substs(m.def_id, substs,) + ), + match self.tcx.fn_sig(m.def_id).input(0).skip_binder().kind() { + ty::Ref(_, _, hir::Mutability::Mut) => "&mut ", + ty::Ref(_, _, _) => "&", + _ => "", + }, + ), + ), + match &args[..] { + [] => (base.span.shrink_to_hi().with_hi(deref.span.hi()), ")".to_string()), + [first, ..] => (base.span.between(first.span), ", ".to_string()), + }, + ] + }) + .collect(); + if suggestions.is_empty() { + return; + } + let mut path_span: MultiSpan = path.ident.span.into(); + path_span.push_span_label( + path.ident.span, + with_no_trimmed_paths!(format!( + "refers to `{}`", + self.tcx.def_path_str(pick.item.def_id), + )), + ); + let container_id = pick.item.container_id(self.tcx); + let container = with_no_trimmed_paths!(self.tcx.def_path_str(container_id)); + for def_id in pick.import_ids { + let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); + path_span.push_span_label( + self.tcx.hir().span(hir_id), + format!("`{container}` imported here"), + ); + } + let tail = with_no_trimmed_paths!(match &other_methods_in_scope[..] { + [] => return, + [candidate] => format!( + "the method of the same name on {} `{}`", + match candidate.kind { + probe::CandidateKind::InherentImplCandidate(..) => "the inherent impl for", + _ => "trait", + }, + self.tcx.def_path_str(candidate.item.container_id(self.tcx)) + ), + [.., last] if other_methods_in_scope.len() < 5 => { + format!( + "the methods of the same name on {} and `{}`", + other_methods_in_scope[..other_methods_in_scope.len() - 1] + .iter() + .map(|c| format!( + "`{}`", + self.tcx.def_path_str(c.item.container_id(self.tcx)) + )) + .collect::>() + .join(", "), + self.tcx.def_path_str(last.item.container_id(self.tcx)) + ) + } + _ => format!( + "the methods of the same name on {} other traits", + other_methods_in_scope.len() + ), + }); + err.span_note( + path_span, + &format!( + "the `{}` call is resolved to the method in `{container}`, shadowing {tail}", + path.ident, + ), + ); + if suggestions.len() > other_methods_in_scope.len() { + err.note(&format!( + "additionally, there are {} other available methods that aren't in scope", + suggestions.len() - other_methods_in_scope.len() + )); + } + err.multipart_suggestions( + &format!( + "you might have meant to call {}; you can use the fully-qualified path to call {} \ + explicitly", + if suggestions.len() == 1 { + "the other method" + } else { + "one of the other methods" + }, + if suggestions.len() == 1 { "it" } else { "one of them" }, + ), + suggestions, + Applicability::MaybeIncorrect, + ); + } + /// If the expected type is an enum (Issue #55250) with any variants whose /// sole field is of the found type, suggest such variants. (Issue #42764) fn suggest_compatible_variants( diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index b9e7830bf0792..6b3fa664d9c5a 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -97,7 +97,7 @@ impl<'a, 'tcx> Deref for ProbeContext<'a, 'tcx> { } #[derive(Debug, Clone)] -struct Candidate<'tcx> { +pub(crate) struct Candidate<'tcx> { // Candidates are (I'm not quite sure, but they are mostly) basically // some metadata on top of a `ty::AssocItem` (without substs). // @@ -131,13 +131,13 @@ struct Candidate<'tcx> { // if `T: Sized`. xform_self_ty: Ty<'tcx>, xform_ret_ty: Option>, - item: ty::AssocItem, - kind: CandidateKind<'tcx>, - import_ids: SmallVec<[LocalDefId; 1]>, + pub(crate) item: ty::AssocItem, + pub(crate) kind: CandidateKind<'tcx>, + pub(crate) import_ids: SmallVec<[LocalDefId; 1]>, } #[derive(Debug, Clone)] -enum CandidateKind<'tcx> { +pub(crate) enum CandidateKind<'tcx> { InherentImplCandidate( SubstsRef<'tcx>, // Normalize obligations @@ -322,6 +322,36 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) } + #[instrument(level = "debug", skip(self))] + pub(crate) fn probe_for_name_many( + &self, + mode: Mode, + item_name: Ident, + is_suggestion: IsSuggestion, + self_ty: Ty<'tcx>, + scope_expr_id: hir::HirId, + scope: ProbeScope, + ) -> Vec> { + self.probe_op( + item_name.span, + mode, + Some(item_name), + None, + is_suggestion, + self_ty, + scope_expr_id, + scope, + |probe_cx| { + Ok(probe_cx + .inherent_candidates + .into_iter() + .chain(probe_cx.extension_candidates) + .collect()) + }, + ) + .unwrap() + } + fn probe_op( &'a self, span: Span, diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 3b8df61a0eab7..a7a5234049fe6 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1142,6 +1142,7 @@ impl UnusedDelimLint for UnusedBraces { && !cx.sess().source_map().is_multiline(value.span) && value.attrs.is_empty() && !value.span.from_expansion() + && !inner.span.from_expansion() { self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos) } diff --git a/src/ci/run.sh b/src/ci/run.sh index f05bb81d4a1e3..b64300277508d 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -45,14 +45,6 @@ fi ci_dir=`cd $(dirname $0) && pwd` source "$ci_dir/shared.sh" -if command -v python > /dev/null; then - PYTHON="python" -elif command -v python3 > /dev/null; then - PYTHON="python3" -else - PYTHON="python2" -fi - if ! isCI || isCiBranch auto || isCiBranch beta || isCiBranch try || isCiBranch try-perf; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set build.print-step-timings --enable-verbose-tests" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set build.metrics" @@ -201,7 +193,7 @@ if [ "$RUN_CHECK_WITH_PARALLEL_QUERIES" != "" ]; then mv metrics.json build fi - CARGO_INCREMENTAL=0 $PYTHON ../x.py check + CARGO_INCREMENTAL=0 ../x check fi sccache --show-stats || true diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css index 36f1f955f5f89..7d7bdfb92ce2d 100644 --- a/src/librustdoc/html/static/css/themes/ayu.css +++ b/src/librustdoc/html/static/css/themes/ayu.css @@ -183,11 +183,7 @@ individually rather than as a group) */ /* FIXME: these rules should be at the bottom of the file but currently must be above the `@media (max-width: 700px)` rules due to a bug in the css checker */ /* see https://github.com/rust-lang/rust/pull/71237#issuecomment-618170143 */ -pre.rust .lifetime {} -pre.rust .kw {} #search-tabs > button:hover, #search-tabs > button.selected {} -pre.rust .self, pre.rust .bool-val, pre.rust .prelude-val, pre.rust .attribute {} -pre.rust .kw-2, pre.rust .prelude-ty {} #settings-menu > a img { filter: invert(100); diff --git a/src/test/ui/lint/unused_braces_macro.rs b/src/test/ui/lint/unused_braces_macro.rs new file mode 100644 index 0000000000000..bfee95378bffe --- /dev/null +++ b/src/test/ui/lint/unused_braces_macro.rs @@ -0,0 +1,6 @@ +// build-pass +pub fn foo () {} + +fn main() { + foo::<{cfg!(feature = "foo")}>(); +} diff --git a/src/test/ui/suggestions/shadowed-lplace-method-2.rs b/src/test/ui/suggestions/shadowed-lplace-method-2.rs new file mode 100644 index 0000000000000..dab99fbacd9e6 --- /dev/null +++ b/src/test/ui/suggestions/shadowed-lplace-method-2.rs @@ -0,0 +1,23 @@ +#![allow(unused)] + +struct X { + x: (), +} +pub trait A { + fn foo(&mut self, _: usize) -> &mut (); +} +impl A for X { + fn foo(&mut self, _: usize) -> &mut () { + &mut self.x + } +} +impl X { + fn foo(&mut self, _: usize) -> &mut Self { + self + } +} + +fn main() { + let mut x = X { x: () }; + *x.foo(0) = (); //~ ERROR E0308 +} diff --git a/src/test/ui/suggestions/shadowed-lplace-method-2.stderr b/src/test/ui/suggestions/shadowed-lplace-method-2.stderr new file mode 100644 index 0000000000000..94eef15f3306e --- /dev/null +++ b/src/test/ui/suggestions/shadowed-lplace-method-2.stderr @@ -0,0 +1,25 @@ +error[E0308]: mismatched types + --> $DIR/shadowed-lplace-method-2.rs:22:17 + | +LL | *x.foo(0) = (); + | --------- ^^ expected struct `X`, found `()` + | | + | expected due to the type of this binding + | +note: the `foo` call is resolved to the method in `X`, shadowing the method of the same name on trait `A` + --> $DIR/shadowed-lplace-method-2.rs:22:8 + | +LL | *x.foo(0) = (); + | ^^^ refers to `X::foo` +help: you might have meant to call the other method; you can use the fully-qualified path to call it explicitly + | +LL | *<_ as A>::foo(&mut x, 0) = (); + | ++++++++++++++++++ ~ +help: try wrapping the expression in `X` + | +LL | *x.foo(0) = X { x: () }; + | ++++++ + + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/suggestions/shadowed-lplace-method.fixed b/src/test/ui/suggestions/shadowed-lplace-method.fixed new file mode 100644 index 0000000000000..740ac77ee0c67 --- /dev/null +++ b/src/test/ui/suggestions/shadowed-lplace-method.fixed @@ -0,0 +1,10 @@ +// run-rustfix +#![allow(unused_imports)] +use std::borrow::BorrowMut; +use std::cell::RefCell; +use std::rc::Rc; + +fn main() { + let rc = Rc::new(RefCell::new(true)); + *std::cell::RefCell::<_>::borrow_mut(&rc) = false; //~ ERROR E0308 +} diff --git a/src/test/ui/suggestions/shadowed-lplace-method.rs b/src/test/ui/suggestions/shadowed-lplace-method.rs new file mode 100644 index 0000000000000..6bf12879e6f28 --- /dev/null +++ b/src/test/ui/suggestions/shadowed-lplace-method.rs @@ -0,0 +1,10 @@ +// run-rustfix +#![allow(unused_imports)] +use std::borrow::BorrowMut; +use std::cell::RefCell; +use std::rc::Rc; + +fn main() { + let rc = Rc::new(RefCell::new(true)); + *rc.borrow_mut() = false; //~ ERROR E0308 +} diff --git a/src/test/ui/suggestions/shadowed-lplace-method.stderr b/src/test/ui/suggestions/shadowed-lplace-method.stderr new file mode 100644 index 0000000000000..91d0d1200d436 --- /dev/null +++ b/src/test/ui/suggestions/shadowed-lplace-method.stderr @@ -0,0 +1,26 @@ +error[E0308]: mismatched types + --> $DIR/shadowed-lplace-method.rs:9:24 + | +LL | *rc.borrow_mut() = false; + | ---------------- ^^^^^ expected struct `Rc`, found `bool` + | | + | expected due to the type of this binding + | + = note: expected struct `Rc>` + found type `bool` +note: the `borrow_mut` call is resolved to the method in `std::borrow::BorrowMut`, shadowing the method of the same name on the inherent impl for `std::cell::RefCell` + --> $DIR/shadowed-lplace-method.rs:9:9 + | +LL | use std::borrow::BorrowMut; + | ---------------------- `std::borrow::BorrowMut` imported here +... +LL | *rc.borrow_mut() = false; + | ^^^^^^^^^^ refers to `std::borrow::BorrowMut::borrow_mut` +help: you might have meant to call the other method; you can use the fully-qualified path to call it explicitly + | +LL | *std::cell::RefCell::<_>::borrow_mut(&rc) = false; + | +++++++++++++++++++++++++++++++++++++ ~ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`.