diff --git a/.gitattributes b/.gitattributes index 51a670b5fbe..d29c15fe712 100644 --- a/.gitattributes +++ b/.gitattributes @@ -9,7 +9,6 @@ src/etc/installer/gfx/* binary src/vendor/** -text Cargo.lock linguist-generated=false -config.toml.example linguist-language=TOML # Older git versions try to fix line endings on images and fonts, this prevents it. *.png binary diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 44f789c592b..3873a020b75 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -443,7 +443,7 @@ jobs: os: windows-latest-xl - name: dist-x86_64-msvc env: - RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --host=x86_64-pc-windows-msvc --target=x86_64-pc-windows-msvc --enable-full-tools --enable-profiler --set rust.lto=thin" + RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --host=x86_64-pc-windows-msvc --target=x86_64-pc-windows-msvc --enable-full-tools --enable-profiler" SCRIPT: PGO_HOST=x86_64-pc-windows-msvc python src/ci/stage-build.py python x.py dist bootstrap --include-default-paths DIST_REQUIRE_ALL_TOOLS: 1 os: windows-latest-xl diff --git a/.reuse/dep5 b/.reuse/dep5 index 5135f92a9d8..9a59f455fe9 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -1,13 +1,79 @@ +# WARNING: this metadata is currently incomplete, do not rely on it yet. + Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Files-Excluded: src/llvm-project -Files: * +# Note that we're explicitly listing the individual files at the root of the +# repository rather than just having `Files: *`. This is explicitly done to +# help downstream forks of the Rust compiler: this way, the files they add +# won't be automatically marked as authored by the Rust project. +Files: compiler/* + library/* + tests/* + src/* + .github/* + Cargo.lock + Cargo.toml + CODE_OF_CONDUCT.md + config.example.toml + configure + CONTRIBUTING.md + COPYRIGHT + LICENSE-APACHE + LICENSE-MIT + README.md + RELEASES.md + rustfmt.toml + triagebot.toml + x + x.ps1 + x.py + .editorconfig + .git-blame-ignore-revs + .gitattributes + .gitignore + .gitmodules + .mailmap Copyright: The Rust Project Developers (see https://thanks.rust-lang.org) License: MIT or Apache-2.0 +Files: compiler/rustc_apfloat/* +Copyright: LLVM APFloat authors + The Rust Project Developers (see https://thanks.rust-lang.org) +License: NCSA AND (MIT OR Apache-2.0) + +Files: compiler/rustc_codegen_cranelift/src/cranelift_native.rs +Copyright: The Cranelift Project Developers + The Rust Project Developers (see https://thanks.rust-lang.org) +License: Apache-2.0 WITH LLVM-exception AND (Apache-2.0 OR MIT) + +Files: compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp +Copyright: LLVM authors + The Rust Project Developers (see https://thanks.rust-lang.org) +License: Apache-2.0 WITH LLVM-exception AND (Apache-2.0 OR MIT) + +Files: library/core/src/unicode/unicode_data.rs +Copyright: 1991-2022 Unicode, Inc. All rights reserved. +License: Unicode-DFS-2016 + +Files: library/std/src/sync/mpmc/* +Copyright: 2019 The Crossbeam Project Developers + The Rust Project Developers (see https://thanks.rust-lang.org) +License: MIT OR Apache-2.0 + +Files: library/std/src/sys/unix/locks/fuchsia_mutex.rs +Copyright: 2016 The Fuchsia Authors + The Rust Project Developers (see https://thanks.rust-lang.org) +License: BSD-2-Clause AND (MIT OR Apache-2.0) + +Files: src/test/rustdoc/auxiliary/enum-primitive.rs +Copyright: 2015 Anders Kaseorg +License: MIT + Files: src/librustdoc/html/static/fonts/FiraSans* -Copyright: 2014, Mozilla Foundation, 2014, Telefonica S.A. +Copyright: 2014, Mozilla Foundation + 2014, Telefonica S.A. License: OFL-1.1 Files: src/librustdoc/html/static/fonts/NanumBarun* @@ -15,9 +81,19 @@ Copyright: 2010 NAVER Corporation License: OFL-1.1 Files: src/librustdoc/html/static/fonts/SourceCodePro* -Copyright: 2010, 2012 Adobe Systems Incorporated + src/librustdoc/html/static/fonts/SourceSerif4* +Copyright: 2010, 2012, 2014-2023, Adobe Systems Incorporated License: OFL-1.1 -Files: src/librustdoc/html/static/fonts/SourceSerif4* -Copyright: 2014-2021 Adobe Systems Incorporated -License: OFL-1.1 +Files: src/librustdoc/html/static/css/normalize.css +Copyright: Nicolas Gallagher and Jonathan Neal +License: MIT + +Files: src/librustdoc/html/static/css/themes/ayu.css +Copyright: Ike Ku, Jessica Stokes, Leon Guan + The Rust Project Developers (see https://thanks.rust-lang.org) +License: MIT OR Apache-2.0 + +Files: src/doc/rustc-dev-guide/mermaid.min.js +Copyright: Knut Sveidqvist +License: MIT diff --git a/Cargo.lock b/Cargo.lock index 7776964adf9..51332919fe7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -738,7 +738,7 @@ dependencies = [ [[package]] name = "clippy" -version = "0.1.69" +version = "0.1.70" dependencies = [ "clap 4.1.4", "clippy_lints", @@ -781,7 +781,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.69" +version = "0.1.70" dependencies = [ "cargo_metadata 0.15.3", "clippy_utils", @@ -804,7 +804,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.69" +version = "0.1.70" dependencies = [ "arrayvec 0.7.0", "if_chain", @@ -1150,7 +1150,7 @@ checksum = "a0afaad2b26fa326569eb264b1363e8ae3357618c43982b3f285f0774ce76b69" [[package]] name = "declare_clippy_lint" -version = "0.1.69" +version = "0.1.70" dependencies = [ "itertools", "quote", diff --git a/LICENSES/BSD-2-Clause.txt b/LICENSES/BSD-2-Clause.txt new file mode 100644 index 00000000000..5f662b354cd --- /dev/null +++ b/LICENSES/BSD-2-Clause.txt @@ -0,0 +1,9 @@ +Copyright (c) + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/LICENSES/NCSA.txt b/LICENSES/NCSA.txt new file mode 100644 index 00000000000..cf5413effa2 --- /dev/null +++ b/LICENSES/NCSA.txt @@ -0,0 +1,15 @@ +University of Illinois/NCSA Open Source License + +Copyright (c) . All rights reserved. + +Developed by: + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal with the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimers in the documentation and/or other materials provided with the distribution. + + * Neither the names of , nor the names of its contributors may be used to endorse or promote products derived from this Software without specific prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. diff --git a/LICENSES/Unicode-DFS-2016.txt b/LICENSES/Unicode-DFS-2016.txt new file mode 100644 index 00000000000..71fd6ac5e12 --- /dev/null +++ b/LICENSES/Unicode-DFS-2016.txt @@ -0,0 +1,22 @@ +UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE + +Unicode Data Files include all data files under the directories http://www.unicode.org/Public/, http://www.unicode.org/reports/, http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and http://www.unicode.org/utility/trac/browser/. + +Unicode Data Files do not include PDF online code charts under the directory http://www.unicode.org/Public/. + +Software includes any source code published in the Unicode Standard or under the directories http://www.unicode.org/Public/, http://www.unicode.org/reports/, http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and http://www.unicode.org/utility/trac/browser/. + +NOTICE TO USER: Carefully read the following legal agreement. BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 1991-2016 Unicode, Inc. All rights reserved. Distributed under the Terms of Use in http://www.unicode.org/copyright.html. + +Permission is hereby granted, free of charge, to any person obtaining a copy of the Unicode data files and any associated documentation (the "Data Files") or Unicode software and any associated documentation (the "Software") to deal in the Data Files or Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the Data Files or Software, and to permit persons to whom the Data Files or Software are furnished to do so, provided that either + + (a) this copyright and permission notice appear with all copies of the Data Files or Software, or + (b) this copyright and permission notice appear in associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in these Data Files or Software without prior written authorization of the copyright holder. diff --git a/README.md b/README.md index c424bd12ffd..c19e129a920 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ See [the rustc-dev-guide for more info][sysllvm]. The Rust build system uses a file named `config.toml` in the root of the source tree to determine various configuration settings for the build. Set up the defaults intended for distros to get started. You can see a full - list of options in `config.toml.example`. + list of options in `config.example.toml`. ```sh printf 'profile = "user" \nchangelog-seen = 2 \n' > config.toml diff --git a/compiler/rustc/src/main.rs b/compiler/rustc/src/main.rs index e21c9b66044..434b978ae31 100644 --- a/compiler/rustc/src/main.rs +++ b/compiler/rustc/src/main.rs @@ -24,6 +24,15 @@ // The two crates we link to here, `std` and `rustc_driver`, are both dynamic // libraries. So we must reference jemalloc symbols one way or another, because // this file is the only object code in the rustc executable. +// +// NOTE: if you are reading this comment because you want to set a custom `global_allocator` for +// benchmarking, consider using the benchmarks in the `rustc-perf` collector suite instead: +// https://github.com/rust-lang/rustc-perf/blob/master/collector/README.md#profiling +// +// NOTE: if you are reading this comment because you want to replace jemalloc with another allocator +// to compare their performance, see +// https://github.com/rust-lang/rust/commit/b90cfc887c31c3e7a9e6d462e2464db1fe506175#diff-43914724af6e464c1da2171e4a9b6c7e607d5bc1203fa95c0ab85be4122605ef +// for an example of how to do so. #[unix_sigpipe = "sig_dfl"] fn main() { diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 03c375c4666..6503bf2bab7 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1230,7 +1230,6 @@ impl Expr { pub fn precedence(&self) -> ExprPrecedence { match self.kind { - ExprKind::Box(_) => ExprPrecedence::Box, ExprKind::Array(_) => ExprPrecedence::Array, ExprKind::ConstBlock(_) => ExprPrecedence::ConstBlock, ExprKind::Call(..) => ExprPrecedence::Call, @@ -1291,8 +1290,7 @@ impl Expr { /// To a first-order approximation, is this a pattern? pub fn is_approximately_pattern(&self) -> bool { match &self.peel_parens().kind { - ExprKind::Box(_) - | ExprKind::Array(_) + ExprKind::Array(_) | ExprKind::Call(_, _) | ExprKind::Tup(_) | ExprKind::Lit(_) @@ -1363,8 +1361,6 @@ pub struct StructExpr { #[derive(Clone, Encodable, Decodable, Debug)] pub enum ExprKind { - /// A `box x` expression. - Box(P), /// An array (`[a, b, c, d]`) Array(ThinVec>), /// Allow anonymous constants from an inline `const` block diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 6fed0b660e8..45a5a3ecb53 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1316,7 +1316,6 @@ pub fn noop_visit_expr( vis: &mut T, ) { match kind { - ExprKind::Box(expr) => vis.visit_expr(expr), ExprKind::Array(exprs) => visit_thin_exprs(exprs, vis), ExprKind::ConstBlock(anon_const) => { vis.visit_anon_const(anon_const); diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index cdc244c1218..607b77705cf 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -35,7 +35,6 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> { | Assign(_, e, _) | AssignOp(_, _, e) | Binary(_, _, e) - | Box(e) | Break(_, Some(e)) | Let(_, e, _) | Range(_, Some(e), _) diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index bdb1879ec20..9a4da6d4396 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -772,7 +772,6 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { walk_list!(visitor, visit_attribute, expression.attrs.iter()); match &expression.kind { - ExprKind::Box(subexpression) => visitor.visit_expr(subexpression), ExprKind::Array(subexpressions) => { walk_list!(visitor, visit_expr, subexpressions); } diff --git a/compiler/rustc_ast_lowering/locales/en-US.ftl b/compiler/rustc_ast_lowering/messages.ftl similarity index 100% rename from compiler/rustc_ast_lowering/locales/en-US.ftl rename to compiler/rustc_ast_lowering/messages.ftl diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index ffb30b1b391..181f94ab74f 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -32,7 +32,7 @@ impl<'hir> LoweringContext<'_, 'hir> { pub(super) fn lower_expr_mut(&mut self, e: &Expr) -> hir::Expr<'hir> { ensure_sufficient_stack(|| { match &e.kind { - // Paranthesis expression does not have a HirId and is handled specially. + // Parenthesis expression does not have a HirId and is handled specially. ExprKind::Paren(ex) => { let mut ex = self.lower_expr_mut(ex); // Include parens in span, but only if it is a super-span. @@ -63,6 +63,20 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::ForLoop(pat, head, body, opt_label) => { return self.lower_expr_for(e, pat, head, body, *opt_label); } + // Similarly, async blocks do not use `e.id` but rather `closure_node_id`. + ExprKind::Async(capture_clause, closure_node_id, block) => { + let hir_id = self.lower_node_id(*closure_node_id); + self.lower_attrs(hir_id, &e.attrs); + return self.make_async_expr( + *capture_clause, + hir_id, + *closure_node_id, + None, + e.span, + hir::AsyncGeneratorKind::Block, + |this| this.with_new_scopes(|this| this.lower_block_expr(block)), + ); + } _ => (), } @@ -70,7 +84,6 @@ impl<'hir> LoweringContext<'_, 'hir> { self.lower_attrs(hir_id, &e.attrs); let kind = match &e.kind { - ExprKind::Box(inner) => hir::ExprKind::Box(self.lower_expr(inner)), ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), ExprKind::ConstBlock(anon_const) => { let anon_const = self.lower_anon_const(anon_const); @@ -174,15 +187,6 @@ impl<'hir> LoweringContext<'_, 'hir> { self.arena.alloc_from_iter(arms.iter().map(|x| self.lower_arm(x))), hir::MatchSource::Normal, ), - ExprKind::Async(capture_clause, closure_node_id, block) => self.make_async_expr( - *capture_clause, - hir_id, - *closure_node_id, - None, - e.span, - hir::AsyncGeneratorKind::Block, - |this| this.with_new_scopes(|this| this.lower_block_expr(block)), - ), ExprKind::Await(expr) => { let dot_await_span = if expr.span.hi() < e.span.hi() { let span_with_whitespace = self @@ -316,7 +320,9 @@ impl<'hir> LoweringContext<'_, 'hir> { ), ExprKind::Try(sub_expr) => self.lower_expr_try(e.span, sub_expr), - ExprKind::Paren(_) | ExprKind::ForLoop(..) => unreachable!("already handled"), + ExprKind::Paren(_) | ExprKind::ForLoop(..) | ExprKind::Async(..) => { + unreachable!("already handled") + } ExprKind::MacCall(_) => panic!("{:?} shouldn't exist here", e.span), }; @@ -578,9 +584,9 @@ impl<'hir> LoweringContext<'_, 'hir> { /// This results in: /// /// ```text - /// std::future::identity_future(static move? |_task_context| -> { + /// static move? |_task_context| -> { /// - /// }) + /// } /// ``` pub(super) fn make_async_expr( &mut self, @@ -591,7 +597,7 @@ impl<'hir> LoweringContext<'_, 'hir> { span: Span, async_gen_kind: hir::AsyncGeneratorKind, body: impl FnOnce(&mut Self) -> hir::Expr<'hir>, - ) -> hir::ExprKind<'hir> { + ) -> hir::Expr<'hir> { let output = ret_ty.unwrap_or_else(|| hir::FnRetTy::DefaultReturn(self.lower_span(span))); // Resume argument type: `ResumeTy` @@ -656,13 +662,12 @@ impl<'hir> LoweringContext<'_, 'hir> { }; let hir_id = self.lower_node_id(closure_node_id); - let unstable_span = - self.mark_span_with_reason(DesugaringKind::Async, span, self.allow_gen_future.clone()); - if self.tcx.features().closure_track_caller && let Some(attrs) = self.attrs.get(&outer_hir_id.local_id) && attrs.into_iter().any(|attr| attr.has_name(sym::track_caller)) { + let unstable_span = + self.mark_span_with_reason(DesugaringKind::Async, span, self.allow_gen_future.clone()); self.lower_attrs( hir_id, &[Attribute { @@ -681,22 +686,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ); } - let generator = hir::Expr { hir_id, kind: generator_kind, span: self.lower_span(span) }; - - // FIXME(swatinem): - // For some reason, the async block needs to flow through *any* - // call (like the identity function), as otherwise type and lifetime - // inference have a hard time figuring things out. - // Without this, we would get: - // E0720 in tests/ui/impl-trait/in-trait/default-body-with-rpit.rs - // E0700 in tests/ui/self/self_lifetime-async.rs - - // `future::identity_future`: - let identity_future = - self.expr_lang_item_path(unstable_span, hir::LangItem::IdentityFuture, None); - - // `future::identity_future(generator)`: - hir::ExprKind::Call(self.arena.alloc(identity_future), arena_vec![self; generator]) + hir::Expr { hir_id, kind: generator_kind, span: self.lower_span(span) } } /// Desugar `.await` into: @@ -1002,7 +992,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } // Transform `async |x: u8| -> X { ... }` into - // `|x: u8| identity_future(|| -> X { ... })`. + // `|x: u8| || -> X { ... }`. let body_id = this.lower_fn_body(&outer_decl, |this| { let async_ret_ty = if let FnRetTy::Ty(ty) = &decl.output { let itctx = ImplTraitContext::Disallowed(ImplTraitPosition::AsyncBlock); @@ -1011,7 +1001,7 @@ impl<'hir> LoweringContext<'_, 'hir> { None }; - let async_body = this.make_async_expr( + this.make_async_expr( capture_clause, closure_hir_id, inner_closure_id, @@ -1019,8 +1009,7 @@ impl<'hir> LoweringContext<'_, 'hir> { body.span, hir::AsyncGeneratorKind::Closure, |this| this.with_new_scopes(|this| this.lower_expr_mut(body)), - ); - this.expr(fn_decl_span, async_body) + ) }); body_id }); diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index debe0acb04e..9a117ac9a3c 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1180,7 +1180,7 @@ impl<'hir> LoweringContext<'_, 'hir> { }, ); - (this.arena.alloc_from_iter(parameters), this.expr(body.span, async_expr)) + (this.arena.alloc_from_iter(parameters), async_expr) }) } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index b20157f2c7c..ea7fa02521e 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -92,7 +92,7 @@ mod lifetime_collector; mod pat; mod path; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } struct LoweringContext<'a, 'hir> { tcx: TyCtxt<'hir>, @@ -435,7 +435,9 @@ fn compute_hir_hash( pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> { let sess = tcx.sess; - tcx.ensure().output_filenames(()); + // Queries that borrow `resolver_for_lowering`. + tcx.ensure_with_value().output_filenames(()); + tcx.ensure_with_value().early_lint_checks(()); let (mut resolver, krate) = tcx.resolver_for_lowering(()).steal(); let ast_index = index_crate(&resolver.node_id_to_def_id, &krate); @@ -463,8 +465,10 @@ pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> { rustc_span::hygiene::clear_syntax_context_map(); } - let hir_hash = compute_hir_hash(tcx, &owners); - hir::Crate { owners, hir_hash } + // Don't hash unless necessary, because it's expensive. + let opt_hir_hash = + if tcx.sess.needs_crate_hash() { Some(compute_hir_hash(tcx, &owners)) } else { None }; + hir::Crate { owners, opt_hir_hash } } #[derive(Copy, Clone, PartialEq, Debug)] @@ -657,42 +661,33 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { bodies.sort_by_key(|(k, _)| *k); let bodies = SortedMap::from_presorted_elements(bodies); - let (hash_including_bodies, hash_without_bodies) = self.hash_owner(node, &bodies); - let (nodes, parenting) = - index::index_hir(self.tcx.sess, &*self.tcx.definitions_untracked(), node, &bodies); - let nodes = hir::OwnerNodes { hash_including_bodies, hash_without_bodies, nodes, bodies }; - let attrs = { - let hash = self.tcx.with_stable_hashing_context(|mut hcx| { + + // Don't hash unless necessary, because it's expensive. + let (opt_hash_including_bodies, attrs_hash) = if self.tcx.sess.needs_crate_hash() { + self.tcx.with_stable_hashing_context(|mut hcx| { + let mut stable_hasher = StableHasher::new(); + hcx.with_hir_bodies(node.def_id(), &bodies, |hcx| { + node.hash_stable(hcx, &mut stable_hasher) + }); + let h1 = stable_hasher.finish(); + let mut stable_hasher = StableHasher::new(); attrs.hash_stable(&mut hcx, &mut stable_hasher); - stable_hasher.finish() - }); - hir::AttributeMap { map: attrs, hash } + let h2 = stable_hasher.finish(); + + (Some(h1), Some(h2)) + }) + } else { + (None, None) }; + let (nodes, parenting) = + index::index_hir(self.tcx.sess, &*self.tcx.definitions_untracked(), node, &bodies); + let nodes = hir::OwnerNodes { opt_hash_including_bodies, nodes, bodies }; + let attrs = hir::AttributeMap { map: attrs, opt_hash: attrs_hash }; self.arena.alloc(hir::OwnerInfo { nodes, parenting, attrs, trait_map }) } - /// Hash the HIR node twice, one deep and one shallow hash. This allows to differentiate - /// queries which depend on the full HIR tree and those which only depend on the item signature. - fn hash_owner( - &mut self, - node: hir::OwnerNode<'hir>, - bodies: &SortedMap>, - ) -> (Fingerprint, Fingerprint) { - self.tcx.with_stable_hashing_context(|mut hcx| { - let mut stable_hasher = StableHasher::new(); - hcx.with_hir_bodies(node.def_id(), bodies, |hcx| { - node.hash_stable(hcx, &mut stable_hasher) - }); - let hash_including_bodies = stable_hasher.finish(); - let mut stable_hasher = StableHasher::new(); - hcx.without_hir_bodies(|hcx| node.hash_stable(hcx, &mut stable_hasher)); - let hash_without_bodies = stable_hasher.finish(); - (hash_including_bodies, hash_without_bodies) - }) - } - /// This method allocates a new `HirId` for the given `NodeId` and stores it in /// the `LoweringContext`'s `NodeId => HirId` map. /// Take care not to call this method if the resulting `HirId` is then not diff --git a/compiler/rustc_ast_passes/locales/en-US.ftl b/compiler/rustc_ast_passes/messages.ftl similarity index 100% rename from compiler/rustc_ast_passes/locales/en-US.ftl rename to compiler/rustc_ast_passes/messages.ftl diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 96042ea3078..6a5d5614b1c 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -337,9 +337,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { ast::TyKind::Never => { gate_feature_post!(&self, never_type, ty.span, "the `!` type is experimental"); } - ast::TyKind::TraitObject(_, ast::TraitObjectSyntax::DynStar, ..) => { - gate_feature_post!(&self, dyn_star, ty.span, "dyn* trait objects are unstable"); - } _ => {} } visit::walk_ty(self, ty) @@ -395,14 +392,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { fn visit_expr(&mut self, e: &'a ast::Expr) { match e.kind { - ast::ExprKind::Box(_) => { - gate_feature_post!( - &self, - box_syntax, - e.span, - "box expression syntax is experimental; you can call `Box::new` instead" - ); - } ast::ExprKind::Type(..) => { if self.sess.parse_sess.span_diagnostic.err_count() == 0 { // To avoid noise about type ascription in common syntax errors, @@ -425,14 +414,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { ast::ExprKind::TryBlock(_) => { gate_feature_post!(&self, try_blocks, e.span, "`try` expression is experimental"); } - ast::ExprKind::Closure(box ast::Closure { constness: ast::Const::Yes(_), .. }) => { - gate_feature_post!( - &self, - const_closures, - e.span, - "const closures are experimental" - ); - } _ => {} } visit::walk_expr(self, e) @@ -594,6 +575,8 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { gate_all!(inline_const_pat, "inline-const in pattern position is experimental"); gate_all!(associated_const_equality, "associated const equality is incomplete"); gate_all!(yeet_expr, "`do yeet` expression is experimental"); + gate_all!(dyn_star, "`dyn*` trait objects are experimental"); + gate_all!(const_closures, "const closures are experimental"); // All uses of `gate_all!` below this point were added in #65742, // and subsequently disabled (with the non-early gating readded). @@ -613,7 +596,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { gate_all!(box_patterns, "box pattern syntax is experimental"); gate_all!(exclusive_range_pattern, "exclusive range pattern syntax is experimental"); gate_all!(try_blocks, "`try` blocks are unstable"); - gate_all!(box_syntax, "box expression syntax is experimental; you can call `Box::new` instead"); gate_all!(type_ascription, "type ascription is experimental"); visit::walk_crate(&mut visitor, krate); diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs index 6207a32b28d..e2c666604b3 100644 --- a/compiler/rustc_ast_passes/src/lib.rs +++ b/compiler/rustc_ast_passes/src/lib.rs @@ -21,4 +21,4 @@ pub mod feature_gate; pub mod node_count; pub mod show_span; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index cacfe9eb2f1..e2f63641ffa 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -244,6 +244,10 @@ impl<'a> State<'a> { (&ast::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(prec) => { parser::PREC_FORCE_PAREN } + // For a binary expression like `(match () { _ => a }) OP b`, the parens are required + // otherwise the parser would interpret `match () { _ => a }` as a statement, + // with the remaining `OP b` not making sense. So we force parens. + (&ast::ExprKind::Match(..), _) => parser::PREC_FORCE_PAREN, _ => left_prec, }; @@ -292,10 +296,6 @@ impl<'a> State<'a> { self.ibox(INDENT_UNIT); self.ann.pre(self, AnnNode::Expr(expr)); match &expr.kind { - ast::ExprKind::Box(expr) => { - self.word_space("box"); - self.print_expr_maybe_paren(expr, parser::PREC_PREFIX); - } ast::ExprKind::Array(exprs) => { self.print_expr_vec(exprs); } diff --git a/compiler/rustc_attr/locales/en-US.ftl b/compiler/rustc_attr/messages.ftl similarity index 100% rename from compiler/rustc_attr/locales/en-US.ftl rename to compiler/rustc_attr/messages.ftl diff --git a/compiler/rustc_attr/src/lib.rs b/compiler/rustc_attr/src/lib.rs index 5fede0a58ac..49818c14f27 100644 --- a/compiler/rustc_attr/src/lib.rs +++ b/compiler/rustc_attr/src/lib.rs @@ -26,4 +26,4 @@ pub use rustc_ast::attr::*; pub(crate) use rustc_ast::HashStableContext; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_borrowck/locales/en-US.ftl b/compiler/rustc_borrowck/messages.ftl similarity index 100% rename from compiler/rustc_borrowck/locales/en-US.ftl rename to compiler/rustc_borrowck/messages.ftl diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index f43b611f54e..7c88205da3b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1985,16 +1985,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let (place_desc, note) = if let Some(place_desc) = opt_place_desc { let local_kind = if let Some(local) = borrow.borrowed_place.as_local() { match self.body.local_kind(local) { - LocalKind::ReturnPointer | LocalKind::Temp => { - bug!("temporary or return pointer with a name") + LocalKind::Temp if self.body.local_decls[local].is_user_variable() => { + "local variable " } - LocalKind::Var => "local variable ", LocalKind::Arg if !self.upvars.is_empty() && local == ty::CAPTURE_STRUCT_LOCAL => { "variable captured by `move` " } LocalKind::Arg => "function parameter ", + LocalKind::ReturnPointer | LocalKind::Temp => { + bug!("temporary or return pointer with a name") + } } } else { "local data " @@ -2008,16 +2010,16 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap(); let local = root_place.local; match self.body.local_kind(local) { - LocalKind::ReturnPointer | LocalKind::Temp => { - ("temporary value".to_string(), "temporary value created here".to_string()) - } LocalKind::Arg => ( "function parameter".to_string(), "function parameter borrowed here".to_string(), ), - LocalKind::Var => { + LocalKind::Temp if self.body.local_decls[local].is_user_variable() => { ("local binding".to_string(), "local binding introduced here".to_string()) } + LocalKind::ReturnPointer | LocalKind::Temp => { + ("temporary value".to_string(), "temporary value created here".to_string()) + } } }; @@ -2482,15 +2484,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let (place_description, assigned_span) = match local_decl { Some(LocalDecl { local_info: - Some(box LocalInfo::User( - ClearCrossCrate::Clear - | ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { + ClearCrossCrate::Set( + box LocalInfo::User(BindingForm::Var(VarBindingForm { opt_match_place: None, .. - })), - )) - | Some(box LocalInfo::StaticRef { .. }) - | None, + })) + | box LocalInfo::StaticRef { .. } + | box LocalInfo::Boring, + ), .. }) | None => (self.describe_any_place(place.as_ref()), assigned_span), diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 19855075ced..62b3f3ecfc3 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -6,8 +6,8 @@ use rustc_hir::intravisit::Visitor; use rustc_index::vec::IndexVec; use rustc_infer::infer::NllRegionVariableOrigin; use rustc_middle::mir::{ - Body, CastKind, ConstraintCategory, FakeReadCause, Local, Location, Operand, Place, Rvalue, - Statement, StatementKind, TerminatorKind, + Body, CastKind, ConstraintCategory, FakeReadCause, Local, LocalInfo, Location, Operand, Place, + Rvalue, Statement, StatementKind, TerminatorKind, }; use rustc_middle::ty::adjustment::PointerCast; use rustc_middle::ty::{self, RegionVid, TyCtxt}; @@ -220,7 +220,7 @@ impl<'tcx> BorrowExplanation<'tcx> { ); err.span_label(body.source_info(drop_loc).span, message); - if let Some(info) = &local_decl.is_block_tail { + if let LocalInfo::BlockTailTemp(info) = local_decl.local_info() { if info.tail_result_is_ignored { // #85581: If the first mutable borrow's scope contains // the second borrow, this suggestion isn't helpful. diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index af705e6a80f..611abb01238 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -196,10 +196,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if self.body.local_decls[local].is_ref_for_guard() { continue; } - if let Some(box LocalInfo::StaticRef { def_id, .. }) = - &self.body.local_decls[local].local_info + if let LocalInfo::StaticRef { def_id, .. } = + *self.body.local_decls[local].local_info() { - buf.push_str(self.infcx.tcx.item_name(*def_id).as_str()); + buf.push_str(self.infcx.tcx.item_name(def_id).as_str()); ok = Ok(()); continue; } diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 5e4c7292e59..3662bec0c76 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -102,14 +102,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // // opt_match_place is None for let [mut] x = ... statements, // whether or not the right-hand side is a place expression - if let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( - VarBindingForm { - opt_match_place: Some((opt_match_place, match_span)), - binding_mode: _, - opt_ty_info: _, - pat_span: _, - }, - )))) = local_decl.local_info + if let LocalInfo::User(BindingForm::Var(VarBindingForm { + opt_match_place: Some((opt_match_place, match_span)), + binding_mode: _, + opt_ty_info: _, + pat_span: _, + })) = *local_decl.local_info() { let stmt_source_info = self.body.source_info(location); self.append_binding_error( @@ -478,9 +476,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let mut suggestions: Vec<(Span, String, String)> = Vec::new(); for local in binds_to { let bind_to = &self.body.local_decls[*local]; - if let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( - VarBindingForm { pat_span, .. }, - )))) = bind_to.local_info + if let LocalInfo::User(BindingForm::Var(VarBindingForm { pat_span, .. })) = + *bind_to.local_info() { let Ok(pat_snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(pat_span) else { continue; }; diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index eded913ae96..bad08451adf 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -7,7 +7,7 @@ use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{ hir::place::PlaceBase, - mir::{self, BindingForm, ClearCrossCrate, Local, LocalDecl, LocalInfo, LocalKind, Location}, + mir::{self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location}, }; use rustc_span::source_map::DesugaringKind; use rustc_span::symbol::{kw, Symbol}; @@ -105,8 +105,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { reason = String::new(); } else { item_msg = access_place_desc; - let local_info = &self.body.local_decls[local].local_info; - if let Some(box LocalInfo::StaticRef { def_id, .. }) = *local_info { + let local_info = self.body.local_decls[local].local_info(); + if let LocalInfo::StaticRef { def_id, .. } = *local_info { let static_name = &self.infcx.tcx.item_name(def_id); reason = format!(", as `{static_name}` is an immutable static item"); } else { @@ -305,15 +305,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .. }) = &self.body[location.block].statements.get(location.statement_index) { - match decl.local_info { - Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( - mir::VarBindingForm { - binding_mode: ty::BindingMode::BindByValue(Mutability::Not), - opt_ty_info: Some(sp), - opt_match_place: _, - pat_span: _, - }, - )))) => { + match *decl.local_info() { + LocalInfo::User(BindingForm::Var(mir::VarBindingForm { + binding_mode: ty::BindingMode::BindByValue(Mutability::Not), + opt_ty_info: Some(sp), + opt_match_place: _, + pat_span: _, + })) => { if suggest { err.span_note(sp, "the binding is already a mutable borrow"); } @@ -346,10 +344,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } } else if decl.mutability.is_not() { if matches!( - decl.local_info, - Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf( - hir::ImplicitSelfKind::MutRef - ),))) + decl.local_info(), + LocalInfo::User(BindingForm::ImplicitSelf(hir::ImplicitSelfKind::MutRef)) ) { err.note( "as `Self` may be unsized, this call attempts to take `&mut &mut self`", @@ -482,22 +478,18 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { match self.local_names[local] { Some(name) if !local_decl.from_compiler_desugaring() => { - let label = match local_decl.local_info.as_deref().unwrap() { - LocalInfo::User(ClearCrossCrate::Set( - mir::BindingForm::ImplicitSelf(_), - )) => { + let label = match *local_decl.local_info() { + LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => { let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, local_decl); Some((true, span, suggestion)) } - LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var( - mir::VarBindingForm { - binding_mode: ty::BindingMode::BindByValue(_), - opt_ty_info, - .. - }, - ))) => { + LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm { + binding_mode: ty::BindingMode::BindByValue(_), + opt_ty_info, + .. + })) => { // check if the RHS is from desugaring let opt_assignment_rhs_span = self.body.find_assignments(local).first().map(|&location| { @@ -534,16 +526,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { self.infcx.tcx, local_decl, opt_assignment_rhs_span, - *opt_ty_info, + opt_ty_info, ) } else { - match local_decl.local_info.as_deref() { - Some(LocalInfo::User(ClearCrossCrate::Set( - mir::BindingForm::Var(mir::VarBindingForm { - opt_ty_info: None, - .. - }), - ))) => { + match local_decl.local_info() { + LocalInfo::User(mir::BindingForm::Var( + mir::VarBindingForm { + opt_ty_info: None, .. + }, + )) => { let (span, sugg) = suggest_ampmut_self( self.infcx.tcx, local_decl, @@ -555,7 +546,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { self.infcx.tcx, local_decl, opt_assignment_rhs_span, - *opt_ty_info, + opt_ty_info, ), } }; @@ -564,21 +555,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } } - LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var( - mir::VarBindingForm { - binding_mode: ty::BindingMode::BindByReference(_), - .. - }, - ))) => { + LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm { + binding_mode: ty::BindingMode::BindByReference(_), + .. + })) => { let pattern_span = local_decl.source_info.span; suggest_ref_mut(self.infcx.tcx, pattern_span) .map(|replacement| (true, pattern_span, replacement)) } - LocalInfo::User(ClearCrossCrate::Clear) => { - bug!("saw cleared local state") - } - _ => unreachable!(), }; @@ -1151,20 +1136,19 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option) -> bool { debug!("local_info: {:?}, ty.kind(): {:?}", local_decl.local_info, local_decl.ty.kind()); - match local_decl.local_info.as_deref() { + match *local_decl.local_info() { // Check if mutably borrowing a mutable reference. - Some(LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var( - mir::VarBindingForm { - binding_mode: ty::BindingMode::BindByValue(Mutability::Not), .. - }, - )))) => matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut)), - Some(LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::ImplicitSelf(kind)))) => { + LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm { + binding_mode: ty::BindingMode::BindByValue(Mutability::Not), + .. + })) => matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut)), + LocalInfo::User(mir::BindingForm::ImplicitSelf(kind)) => { // Check if the user variable is a `&mut self` and we can therefore // suggest removing the `&mut`. // // Deliberately fall into this case for all implicit self types, // so that we don't fall in to the next case with them. - *kind == hir::ImplicitSelfKind::MutRef + kind == hir::ImplicitSelfKind::MutRef } _ if Some(kw::SelfLower) == local_name => { // Otherwise, check if the name is the `self` keyword - in which case diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index f84a4691d32..5e77f6b190a 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -100,7 +100,7 @@ use places_conflict::{places_conflict, PlaceConflictBias}; use region_infer::RegionInferenceContext; use renumber::RegionCtxt; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } // FIXME(eddyb) perhaps move this somewhere more centrally. #[derive(Debug)] diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index ec4b2e9d3e4..748c8b9e442 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -325,7 +325,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { if errors.is_empty() { definition_ty } else { - let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None); + let reported = infcx.err_ctxt().report_fulfillment_errors(&errors); self.tcx.ty_error(reported) } } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 3919c4793a0..53fef4d75bf 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1180,10 +1180,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } Some(l) - if matches!( - body.local_decls[l].local_info, - Some(box LocalInfo::AggregateTemp) - ) => + if matches!(body.local_decls[l].local_info(), LocalInfo::AggregateTemp) => { ConstraintCategory::Usage } @@ -1684,7 +1681,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // - maybe we should make that a warning. return; } - LocalKind::Var | LocalKind::Temp => {} + LocalKind::Temp => {} } // When `unsized_fn_params` or `unsized_locals` is enabled, only function calls diff --git a/compiler/rustc_builtin_macros/locales/en-US.ftl b/compiler/rustc_builtin_macros/messages.ftl similarity index 100% rename from compiler/rustc_builtin_macros/locales/en-US.ftl rename to compiler/rustc_builtin_macros/messages.ftl diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 3fdbc971527..8c1579baacb 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -203,17 +203,6 @@ pub fn parse_asm_args<'a>( // Validate the order of named, positional & explicit register operands and // clobber_abi/options. We do this at the end once we have the full span // of the argument available. - if !args.options_spans.is_empty() { - diag.struct_span_err(span, "arguments are not allowed after options") - .span_labels(args.options_spans.clone(), "previous options") - .span_label(span, "argument") - .emit(); - } else if let Some((_, abi_span)) = args.clobber_abis.last() { - diag.struct_span_err(span, "arguments are not allowed after clobber_abi") - .span_label(*abi_span, "clobber_abi") - .span_label(span, "argument") - .emit(); - } if explicit_reg { if name.is_some() { diag.struct_span_err(span, "explicit register arguments cannot have names").emit(); @@ -227,17 +216,6 @@ pub fn parse_asm_args<'a>( .emit(); continue; } - if !args.reg_args.is_empty() { - let mut err = diag.struct_span_err( - span, - "named arguments cannot follow explicit register arguments", - ); - err.span_label(span, "named argument"); - for pos in &args.reg_args { - err.span_label(args.operands[*pos].1, "explicit register argument"); - } - err.emit(); - } args.named_args.insert(name, slot); } else { if !args.named_args.is_empty() || !args.reg_args.is_empty() { @@ -478,15 +456,6 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, let full_span = span_start.to(p.prev_token.span); - if !args.options_spans.is_empty() { - let mut err = p - .sess - .span_diagnostic - .struct_span_err(full_span, "clobber_abi is not allowed after options"); - err.span_labels(args.options_spans.clone(), "options"); - return Err(err); - } - match &new_abis[..] { // should have errored above during parsing [] => unreachable!(), @@ -699,6 +668,10 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option Context<'cx, 'a> { | ExprKind::Async(_, _, _) | ExprKind::Await(_) | ExprKind::Block(_, _) - | ExprKind::Box(_) | ExprKind::Break(_, _) | ExprKind::Closure(_) | ExprKind::ConstBlock(_) diff --git a/compiler/rustc_builtin_macros/src/concat.rs b/compiler/rustc_builtin_macros/src/concat.rs index 7da9bdc38a2..36682bbe070 100644 --- a/compiler/rustc_builtin_macros/src/concat.rs +++ b/compiler/rustc_builtin_macros/src/concat.rs @@ -42,6 +42,18 @@ pub fn expand_concat( has_errors = true; } }, + // We also want to allow negative numeric literals. + ast::ExprKind::Unary(ast::UnOp::Neg, ref expr) if let ast::ExprKind::Lit(token_lit) = expr.kind => { + match ast::LitKind::from_token_lit(token_lit) { + Ok(ast::LitKind::Int(i, _)) => accumulator.push_str(&format!("-{i}")), + Ok(ast::LitKind::Float(f, _)) => accumulator.push_str(&format!("-{f}")), + Err(err) => { + report_lit_error(&cx.sess.parse_sess, err, token_lit, e.span); + has_errors = true; + } + _ => missing_literal.push(e.span), + } + } ast::ExprKind::IncludedBytes(..) => { cx.span_err(e.span, "cannot concatenate a byte string literal") } @@ -53,9 +65,10 @@ pub fn expand_concat( } } } + if !missing_literal.is_empty() { let mut err = cx.struct_span_err(missing_literal, "expected a literal"); - err.note("only literals (like `\"foo\"`, `42` and `3.14`) can be passed to `concat!()`"); + err.note("only literals (like `\"foo\"`, `-42` and `3.14`) can be passed to `concat!()`"); err.emit(); return DummyResult::any(sp); } else if has_errors { diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 1f819beeb5d..6b3053fdfac 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -1052,6 +1052,7 @@ impl<'a> MethodDef<'a> { /// ::core::hash::Hash::hash(&{ self.y }, state) /// } /// } + /// ``` fn expand_struct_method_body<'b>( &self, cx: &mut ExtCtxt<'_>, diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index e93a23394c0..db2ef7fba4b 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -36,6 +36,21 @@ enum PositionUsedAs { } use PositionUsedAs::*; +struct MacroInput { + fmtstr: P, + args: FormatArguments, + /// Whether the first argument was a string literal or a result from eager macro expansion. + /// If it's not a string literal, we disallow implicit arugment capturing. + /// + /// This does not correspond to whether we can treat spans to the literal normally, as the whole + /// invocation might be the result of another macro expansion, in which case this flag may still be true. + /// + /// See [RFC 2795] for more information. + /// + /// [RFC 2795]: https://rust-lang.github.io/rfcs/2795-format-args-implicit-identifiers.html#macro-hygiene + is_direct_literal: bool, +} + /// Parses the arguments from the given list of tokens, returning the diagnostic /// if there's a parse error so we can continue parsing other format! /// expressions. @@ -45,11 +60,7 @@ use PositionUsedAs::*; /// ```text /// Ok((fmtstr, parsed arguments)) /// ``` -fn parse_args<'a>( - ecx: &mut ExtCtxt<'a>, - sp: Span, - tts: TokenStream, -) -> PResult<'a, (P, FormatArguments)> { +fn parse_args<'a>(ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a, MacroInput> { let mut args = FormatArguments::new(); let mut p = ecx.new_parser_from_tts(tts); @@ -59,25 +70,21 @@ fn parse_args<'a>( } let first_token = &p.token; - let fmtstr = match first_token.kind { - token::TokenKind::Literal(token::Lit { - kind: token::LitKind::Str | token::LitKind::StrRaw(_), - .. - }) => { - // If the first token is a string literal, then a format expression - // is constructed from it. - // - // This allows us to properly handle cases when the first comma - // after the format string is mistakenly replaced with any operator, - // which cause the expression parser to eat too much tokens. - p.parse_literal_maybe_minus()? - } - _ => { - // Otherwise, we fall back to the expression parser. - p.parse_expr()? - } + + let fmtstr = if let token::Literal(lit) = first_token.kind && matches!(lit.kind, token::Str | token::StrRaw(_)) { + // This allows us to properly handle cases when the first comma + // after the format string is mistakenly replaced with any operator, + // which cause the expression parser to eat too much tokens. + p.parse_literal_maybe_minus()? + } else { + // Otherwise, we fall back to the expression parser. + p.parse_expr()? }; + // Only allow implicit captures to be used when the argument is a direct literal + // instead of a macro expanding to one. + let is_direct_literal = matches!(fmtstr.kind, ExprKind::Lit(_)); + let mut first = true; while p.token != token::Eof { @@ -147,17 +154,19 @@ fn parse_args<'a>( } } } - Ok((fmtstr, args)) + Ok(MacroInput { fmtstr, args, is_direct_literal }) } -pub fn make_format_args( +fn make_format_args( ecx: &mut ExtCtxt<'_>, - efmt: P, - mut args: FormatArguments, + input: MacroInput, append_newline: bool, ) -> Result { let msg = "format argument must be a string literal"; - let unexpanded_fmt_span = efmt.span; + let unexpanded_fmt_span = input.fmtstr.span; + + let MacroInput { fmtstr: efmt, mut args, is_direct_literal } = input; + let (fmt_str, fmt_style, fmt_span) = match expr_to_spanned_string(ecx, efmt, msg) { Ok(mut fmt) if append_newline => { fmt.0 = Symbol::intern(&format!("{}\n", fmt.0)); @@ -208,11 +217,11 @@ pub fn make_format_args( } } - let is_literal = parser.is_literal; + let is_source_literal = parser.is_source_literal; if !parser.errors.is_empty() { let err = parser.errors.remove(0); - let sp = if is_literal { + let sp = if is_source_literal { fmt_span.from_inner(InnerSpan::new(err.span.start, err.span.end)) } else { // The format string could be another macro invocation, e.g.: @@ -230,7 +239,7 @@ pub fn make_format_args( if let Some(note) = err.note { e.note(¬e); } - if let Some((label, span)) = err.secondary_label && is_literal { + if let Some((label, span)) = err.secondary_label && is_source_literal { e.span_label(fmt_span.from_inner(InnerSpan::new(span.start, span.end)), label); } if err.should_be_replaced_with_positional_argument { @@ -256,7 +265,7 @@ pub fn make_format_args( } let to_span = |inner_span: rustc_parse_format::InnerSpan| { - is_literal.then(|| { + is_source_literal.then(|| { fmt_span.from_inner(InnerSpan { start: inner_span.start, end: inner_span.end }) }) }; @@ -304,7 +313,7 @@ pub fn make_format_args( // Name not found in `args`, so we add it as an implicitly captured argument. let span = span.unwrap_or(fmt_span); let ident = Ident::new(name, span); - let expr = if is_literal { + let expr = if is_direct_literal { ecx.expr_ident(span, ident) } else { // For the moment capturing variables from format strings expanded from macros is @@ -814,7 +823,7 @@ fn report_invalid_references( // for `println!("{7:7$}", 1);` indexes.sort(); indexes.dedup(); - let span: MultiSpan = if !parser.is_literal || parser.arg_places.is_empty() { + let span: MultiSpan = if !parser.is_source_literal || parser.arg_places.is_empty() { MultiSpan::from_span(fmt_span) } else { MultiSpan::from_spans(invalid_refs.iter().filter_map(|&(_, span, _, _)| span).collect()) @@ -855,8 +864,8 @@ fn expand_format_args_impl<'cx>( ) -> Box { sp = ecx.with_def_site_ctxt(sp); match parse_args(ecx, sp, tts) { - Ok((efmt, args)) => { - if let Ok(format_args) = make_format_args(ecx, efmt, args, nl) { + Ok(input) => { + if let Ok(format_args) = make_format_args(ecx, input, nl) { MacEager::expr(ecx.expr(sp, ExprKind::FormatArgs(P(format_args)))) } else { MacEager::expr(DummyResult::raw_expr(sp, true)) diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 8afb6e56069..71177b8789b 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -56,7 +56,7 @@ pub mod proc_macro_harness; pub mod standard_library_imports; pub mod test_harness; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { let mut register = |name, kind| resolver.register_builtin_macro(name, kind); diff --git a/compiler/rustc_codegen_cranelift/example/alloc_example.rs b/compiler/rustc_codegen_cranelift/example/alloc_example.rs index bc1594d82ec..4ede2fe4efe 100644 --- a/compiler/rustc_codegen_cranelift/example/alloc_example.rs +++ b/compiler/rustc_codegen_cranelift/example/alloc_example.rs @@ -1,4 +1,4 @@ -#![feature(start, core_intrinsics, alloc_error_handler, box_syntax)] +#![feature(start, core_intrinsics, alloc_error_handler)] #![no_std] extern crate alloc; @@ -29,7 +29,7 @@ fn alloc_error_handler(_: alloc::alloc::Layout) -> ! { #[start] fn main(_argc: isize, _argv: *const *const u8) -> isize { - let world: Box<&str> = box "Hello World!\0"; + let world: Box<&str> = Box::new("Hello World!\0"); unsafe { puts(*world as *const str as *const u8); } diff --git a/compiler/rustc_codegen_cranelift/example/alloc_system.rs b/compiler/rustc_codegen_cranelift/example/alloc_system.rs index 50261c19397..e64daf96b01 100644 --- a/compiler/rustc_codegen_cranelift/example/alloc_system.rs +++ b/compiler/rustc_codegen_cranelift/example/alloc_system.rs @@ -1,12 +1,6 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. +// SPDX-License-Identifier: MIT OR Apache-2.0 +// SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org) + #![no_std] pub struct System; diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs index c00f8a2e0cd..04e7795bbfa 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs @@ -1,4 +1,4 @@ -#![feature(no_core, lang_items, never_type, linkage, extern_types, thread_local, box_syntax)] +#![feature(no_core, lang_items, never_type, linkage, extern_types, thread_local)] #![no_core] #![allow(dead_code, non_camel_case_types)] @@ -178,7 +178,7 @@ fn main() { let ptr: *const i8 = hello as *const [u8] as *const i8; puts(ptr); - let world: Box<&str> = box "World!\0"; + let world: Box<&str> = Box::new("World!\0"); puts(*world as *const str as *const i8); world as Box; @@ -238,10 +238,10 @@ fn main() { } } - let _ = box NoisyDrop { + let _ = Box::new(NoisyDrop { text: "Boxed outer got dropped!\0", inner: NoisyDropInner, - } as Box; + }) as Box; const FUNC_REF: Option = Some(main); match FUNC_REF { diff --git a/compiler/rustc_codegen_cranelift/src/allocator.rs b/compiler/rustc_codegen_cranelift/src/allocator.rs index 1c73957ca57..2c246ceb37d 100644 --- a/compiler/rustc_codegen_cranelift/src/allocator.rs +++ b/compiler/rustc_codegen_cranelift/src/allocator.rs @@ -4,6 +4,7 @@ use crate::prelude::*; use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS}; +use rustc_codegen_ssa::base::allocator_kind_for_codegen; use rustc_session::config::OomStrategy; use rustc_span::symbol::sym; @@ -13,24 +14,15 @@ pub(crate) fn codegen( module: &mut impl Module, unwind_context: &mut UnwindContext, ) -> bool { - let any_dynamic_crate = tcx.dependency_formats(()).iter().any(|(_, list)| { - use rustc_middle::middle::dependency_format::Linkage; - list.iter().any(|&linkage| linkage == Linkage::Dynamic) - }); - if any_dynamic_crate { - false - } else if let Some(kind) = tcx.allocator_kind(()) { - codegen_inner( - module, - unwind_context, - kind, - tcx.alloc_error_handler_kind(()).unwrap(), - tcx.sess.opts.unstable_opts.oom, - ); - true - } else { - false - } + let Some(kind) = allocator_kind_for_codegen(tcx) else { return false }; + codegen_inner( + module, + unwind_context, + kind, + tcx.alloc_error_handler_kind(()).unwrap(), + tcx.sess.opts.unstable_opts.oom, + ); + true } fn codegen_inner( diff --git a/compiler/rustc_codegen_gcc/example/alloc_example.rs b/compiler/rustc_codegen_gcc/example/alloc_example.rs index c80348ca549..754e7931412 100644 --- a/compiler/rustc_codegen_gcc/example/alloc_example.rs +++ b/compiler/rustc_codegen_gcc/example/alloc_example.rs @@ -1,4 +1,4 @@ -#![feature(start, box_syntax, core_intrinsics, alloc_error_handler, lang_items)] +#![feature(start, core_intrinsics, alloc_error_handler, lang_items)] #![no_std] extern crate alloc; @@ -38,7 +38,7 @@ unsafe extern "C" fn _Unwind_Resume() { #[start] fn main(_argc: isize, _argv: *const *const u8) -> isize { - let world: Box<&str> = box "Hello World!\0"; + let world: Box<&str> = Box::new("Hello World!\0"); unsafe { puts(*world as *const str as *const u8); } diff --git a/compiler/rustc_codegen_gcc/example/alloc_system.rs b/compiler/rustc_codegen_gcc/example/alloc_system.rs index fd01fcf1fc8..9ec18da90d8 100644 --- a/compiler/rustc_codegen_gcc/example/alloc_system.rs +++ b/compiler/rustc_codegen_gcc/example/alloc_system.rs @@ -1,12 +1,6 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. +// SPDX-License-Identifier: MIT OR Apache-2.0 +// SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org) + #![no_std] #![feature(allocator_api, rustc_private)] #![cfg_attr(any(unix, target_os = "redox"), feature(libc))] diff --git a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs index 993a31e68ea..cff26077740 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs @@ -1,7 +1,7 @@ // Adapted from https://github.com/sunfishcode/mir2cranelift/blob/master/rust-examples/nocore-hello-world.rs #![feature( - no_core, unboxed_closures, start, lang_items, box_syntax, never_type, linkage, + no_core, unboxed_closures, start, lang_items, never_type, linkage, extern_types, thread_local )] #![no_core] @@ -163,7 +163,7 @@ fn main() { let ptr: *const u8 = hello as *const [u8] as *const u8; puts(ptr); - let world: Box<&str> = box "World!\0"; + let world: Box<&str> = Box::new("World!\0"); puts(*world as *const str as *const u8); world as Box; @@ -223,10 +223,10 @@ fn main() { } } - let _ = box NoisyDrop { + let _ = Box::new(NoisyDrop { text: "Boxed outer got dropped!\0", inner: NoisyDropInner, - } as Box; + }) as Box; const FUNC_REF: Option = Some(main); #[allow(unreachable_code)] diff --git a/compiler/rustc_codegen_gcc/example/mod_bench.rs b/compiler/rustc_codegen_gcc/example/mod_bench.rs index 95bcad2cd17..5e2e7f25a2c 100644 --- a/compiler/rustc_codegen_gcc/example/mod_bench.rs +++ b/compiler/rustc_codegen_gcc/example/mod_bench.rs @@ -1,4 +1,4 @@ -#![feature(start, box_syntax, core_intrinsics, lang_items)] +#![feature(start, core_intrinsics, lang_items)] #![no_std] #[link(name = "c")] diff --git a/compiler/rustc_codegen_gcc/locales/en-US.ftl b/compiler/rustc_codegen_gcc/messages.ftl similarity index 100% rename from compiler/rustc_codegen_gcc/locales/en-US.ftl rename to compiler/rustc_codegen_gcc/messages.ftl diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 1b7feb5f8a1..0b661505acc 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -87,7 +87,7 @@ use rustc_span::Symbol; use rustc_span::fatal_error::FatalError; use tempfile::TempDir; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } pub struct PrintOnPanic String>(pub F); diff --git a/compiler/rustc_codegen_llvm/locales/en-US.ftl b/compiler/rustc_codegen_llvm/messages.ftl similarity index 100% rename from compiler/rustc_codegen_llvm/locales/en-US.ftl rename to compiler/rustc_codegen_llvm/messages.ftl diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs index 93419d27a62..978141917c6 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs @@ -438,6 +438,7 @@ fn build_enum_variant_member_di_node<'ll, 'tcx>( /// DW_TAG_structure_type (type of variant 1) /// DW_TAG_structure_type (type of variant 2) /// DW_TAG_structure_type (type of variant 3) +/// ``` struct VariantMemberInfo<'a, 'll> { variant_index: VariantIdx, variant_name: Cow<'a, str>, diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index c41e74c51a0..8dafe1b750b 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -84,7 +84,7 @@ mod type_of; mod va_arg; mod value; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } #[derive(Clone)] pub struct LlvmCodegenBackend(()); diff --git a/compiler/rustc_codegen_ssa/locales/en-US.ftl b/compiler/rustc_codegen_ssa/messages.ftl similarity index 100% rename from compiler/rustc_codegen_ssa/locales/en-US.ftl rename to compiler/rustc_codegen_ssa/messages.ftl diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 52c01b423a7..23e2b272410 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -720,6 +720,7 @@ impl<'a> Linker for GccLinker<'a> { let mut arg = OsString::from("--version-script="); arg.push(path); self.linker_arg(arg); + self.linker_arg("--no-undefined-version"); } } } diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 1a94d4ab8b1..7b58e55dbe8 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -1,3 +1,5 @@ +use crate::base::allocator_kind_for_codegen; + use std::collections::hash_map::Entry::*; use rustc_ast::expand::allocator::ALLOCATOR_METHODS; @@ -200,7 +202,8 @@ fn exported_symbols_provider_local( )); } - if tcx.allocator_kind(()).is_some() { + // Mark allocator shim symbols as exported only if they were generated. + if allocator_kind_for_codegen(tcx).is_some() { for symbol_name in ALLOCATOR_METHODS .iter() .map(|method| format!("__rust_{}", method.name)) diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 73179249bc4..abc510e360d 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -13,6 +13,7 @@ use crate::mir::place::PlaceRef; use crate::traits::*; use crate::{CachedModuleCodegen, CompiledModule, CrateInfo, MemFlags, ModuleCodegen, ModuleKind}; +use rustc_ast::expand::allocator::AllocatorKind; use rustc_attr as attr; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; @@ -545,6 +546,23 @@ pub fn collect_debugger_visualizers_transitive( .collect::>() } +/// Decide allocator kind to codegen. If `Some(_)` this will be the same as +/// `tcx.allocator_kind`, but it may be `None` in more cases (e.g. if using +/// allocator definitions from a dylib dependency). +pub fn allocator_kind_for_codegen(tcx: TyCtxt<'_>) -> Option { + // If the crate doesn't have an `allocator_kind` set then there's definitely + // no shim to generate. Otherwise we also check our dependency graph for all + // our output crate types. If anything there looks like its a `Dynamic` + // linkage, then it's already got an allocator shim and we'll be using that + // one instead. If nothing exists then it's our job to generate the + // allocator! + let any_dynamic_crate = tcx.dependency_formats(()).iter().any(|(_, list)| { + use rustc_middle::middle::dependency_format::Linkage; + list.iter().any(|&linkage| linkage == Linkage::Dynamic) + }); + if any_dynamic_crate { None } else { tcx.allocator_kind(()) } +} + pub fn codegen_crate( backend: B, tcx: TyCtxt<'_>, @@ -615,20 +633,7 @@ pub fn codegen_crate( ); // Codegen an allocator shim, if necessary. - // - // If the crate doesn't have an `allocator_kind` set then there's definitely - // no shim to generate. Otherwise we also check our dependency graph for all - // our output crate types. If anything there looks like its a `Dynamic` - // linkage, then it's already got an allocator shim and we'll be using that - // one instead. If nothing exists then it's our job to generate the - // allocator! - let any_dynamic_crate = tcx.dependency_formats(()).iter().any(|(_, list)| { - use rustc_middle::middle::dependency_format::Linkage; - list.iter().any(|&linkage| linkage == Linkage::Dynamic) - }); - let allocator_module = if any_dynamic_crate { - None - } else if let Some(kind) = tcx.allocator_kind(()) { + if let Some(kind) = allocator_kind_for_codegen(tcx) { let llmod_id = cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("allocator")).to_string(); let module_llvm = tcx.sess.time("write_allocator_module", || { @@ -642,13 +647,10 @@ pub fn codegen_crate( ) }); - Some(ModuleCodegen { name: llmod_id, module_llvm, kind: ModuleKind::Allocator }) - } else { - None - }; - - if let Some(allocator_module) = allocator_module { - ongoing_codegen.submit_pre_codegened_module_to_llvm(tcx, allocator_module); + ongoing_codegen.submit_pre_codegened_module_to_llvm( + tcx, + ModuleCodegen { name: llmod_id, module_llvm, kind: ModuleKind::Allocator }, + ); } // For better throughput during parallel processing by LLVM, we used to sort diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index b75ced4a6dd..f9bb8359208 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -247,6 +247,9 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs { // Note that this is also allowed if `actually_rustdoc` so // if a target is documenting some wasm-specific code then // it's not spuriously denied. + // + // This exception needs to be kept in sync with allowing + // `#[target_feature]` on `main` and `start`. } else if !tcx.features().target_feature_11 { let mut err = feature_err( &tcx.sess.parse_sess, diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index ebe9e50ffe6..8e721a84eaf 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -56,7 +56,7 @@ pub mod mono_item; pub mod target_features; pub mod traits; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } pub struct ModuleCodegen { /// The name of the module. When the crate may be saved between diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 708f3bc0c78..6e32c28a42c 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -241,12 +241,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) { let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full; - // FIXME(eddyb) maybe name the return place as `_0` or `return`? - if local == mir::RETURN_PLACE && !self.mir.local_decls[mir::RETURN_PLACE].is_user_variable() - { - return; - } - let vars = match &self.per_local_var_debug_info { Some(per_local) => &per_local[local], None => return, @@ -303,7 +297,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let local_ref = &self.locals[local]; - let name = if bx.sess().fewer_names() { + // FIXME Should the return place be named? + let name = if bx.sess().fewer_names() || local == mir::RETURN_PLACE { None } else { Some(match whole_local_var.or(fallback_var.clone()) { diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 2ec9fdbf44f..5cffca5230a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -258,6 +258,10 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // Apply debuginfo to the newly allocated locals. fx.debug_introduce_locals(&mut start_bx); + // The builders will be created separately for each basic block at `codegen_block`. + // So drop the builder of `start_llbb` to avoid having two at the same time. + drop(start_bx); + // Codegen the body of each block using reverse postorder for (bb, _) in traversal::reverse_postorder(&mir) { fx.codegen_block(bb); diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 576e90ae66b..754b085f1a8 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -192,7 +192,7 @@ const X86_ALLOWED_FEATURES: &[(&str, Option)] = &[ ("fxsr", None), ("gfni", Some(sym::avx512_target_feature)), ("lzcnt", None), - ("movbe", Some(sym::movbe_target_feature)), + ("movbe", None), ("pclmulqdq", None), ("popcnt", None), ("rdrand", None), @@ -394,7 +394,6 @@ pub fn from_target_feature( Some(sym::sse4a_target_feature) => rust_features.sse4a_target_feature, Some(sym::tbm_target_feature) => rust_features.tbm_target_feature, Some(sym::wasm_target_feature) => rust_features.wasm_target_feature, - Some(sym::movbe_target_feature) => rust_features.movbe_target_feature, Some(sym::rtm_target_feature) => rust_features.rtm_target_feature, Some(sym::ermsb_target_feature) => rust_features.ermsb_target_feature, Some(sym::bpf_target_feature) => rust_features.bpf_target_feature, @@ -442,7 +441,7 @@ fn asm_target_features(tcx: TyCtxt<'_>, did: DefId) -> &FxIndexSet { pub fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: Span) { if let DefKind::AssocFn = tcx.def_kind(id) { let parent_id = tcx.local_parent(id); - if let DefKind::Impl { of_trait: true } = tcx.def_kind(parent_id) { + if let DefKind::Trait | DefKind::Impl { of_trait: true } = tcx.def_kind(parent_id) { tcx.sess .struct_span_err( attr_span, diff --git a/compiler/rustc_const_eval/locales/en-US.ftl b/compiler/rustc_const_eval/messages.ftl similarity index 100% rename from compiler/rustc_const_eval/locales/en-US.ftl rename to compiler/rustc_const_eval/messages.ftl diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 39c74191258..0918ffcd982 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -536,24 +536,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { local: mir::Local, layout: Option>, ) -> InterpResult<'tcx, TyAndLayout<'tcx>> { - // `const_prop` runs into this with an invalid (empty) frame, so we - // have to support that case (mostly by skipping all caching). - match frame.locals.get(local).and_then(|state| state.layout.get()) { - None => { - let layout = from_known_layout(self.tcx, self.param_env, layout, || { - let local_ty = frame.body.local_decls[local].ty; - let local_ty = - self.subst_from_frame_and_normalize_erasing_regions(frame, local_ty)?; - self.layout_of(local_ty) - })?; - if let Some(state) = frame.locals.get(local) { - // Layouts of locals are requested a lot, so we cache them. - state.layout.set(Some(layout)); - } - Ok(layout) - } - Some(layout) => Ok(layout), + let state = &frame.locals[local]; + if let Some(layout) = state.layout.get() { + return Ok(layout); } + + let layout = from_known_layout(self.tcx, self.param_env, layout, || { + let local_ty = frame.body.local_decls[local].ty; + let local_ty = self.subst_from_frame_and_normalize_erasing_regions(frame, local_ty)?; + self.layout_of(local_ty) + })?; + + // Layouts of locals are requested a lot, so we cache them. + state.layout.set(Some(layout)); + Ok(layout) } /// Returns the actual dynamic size and alignment of the place at the given type. diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index ed9efe568fb..16b83af91ac 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -39,7 +39,7 @@ use rustc_macros::fluent_messages; use rustc_middle::ty; use rustc_middle::ty::query::Providers; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } pub fn provide(providers: &mut Providers) { const_eval::provide(providers); diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 081d9dc8700..f775b479667 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -643,7 +643,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { if base_ty.is_unsafe_ptr() { if proj_base.is_empty() { let decl = &self.body.local_decls[place_local]; - if let Some(box LocalInfo::StaticRef { def_id, .. }) = decl.local_info { + if let LocalInfo::StaticRef { def_id, .. } = *decl.local_info() { let span = decl.source_info.span; self.check_static(def_id, span); return; @@ -770,7 +770,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { let errors = ocx.select_all_or_error(); if !errors.is_empty() { - infcx.err_ctxt().report_fulfillment_errors(&errors, None); + infcx.err_ctxt().report_fulfillment_errors(&errors); } } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index e586720a0d0..c0f5b3725b3 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -704,7 +704,7 @@ pub mod ty { fn importance(&self) -> DiagnosticImportance { match self.0 { - mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary, + mir::LocalKind::Temp => DiagnosticImportance::Secondary, mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => { DiagnosticImportance::Primary } diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs index 3f3b66b0645..648a86d32fc 100644 --- a/compiler/rustc_const_eval/src/transform/promote_consts.rs +++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs @@ -106,8 +106,9 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> { debug!("visit_local: index={:?} context={:?} location={:?}", index, context, location); // We're only interested in temporaries and the return place match self.ccx.body.local_kind(index) { - LocalKind::Temp | LocalKind::ReturnPointer => {} - LocalKind::Arg | LocalKind::Var => return, + LocalKind::Arg => return, + LocalKind::Temp if self.ccx.body.local_decls[index].is_user_variable() => return, + LocalKind::ReturnPointer | LocalKind::Temp => {} } // Ignore drops, if the temp gets promoted, diff --git a/compiler/rustc_driver_impl/locales/en-US.ftl b/compiler/rustc_driver_impl/messages.ftl similarity index 100% rename from compiler/rustc_driver_impl/locales/en-US.ftl rename to compiler/rustc_driver_impl/messages.ftl diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 464ddae476a..555917c8b5e 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -64,7 +64,7 @@ use crate::session_diagnostics::{ RLinkWrongFileType, RlinkNotAFile, RlinkUnableToRead, }; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ // tidy-alphabetical-start @@ -331,6 +331,7 @@ fn run_compiler( if let Some(ppm) = &sess.opts.pretty { if ppm.needs_ast_map() { queries.global_ctxt()?.enter(|tcx| { + tcx.ensure().early_lint_checks(()); pretty::print_after_hir_lowering(tcx, *ppm); Ok(()) })?; diff --git a/compiler/rustc_error_codes/src/error_codes/E0010.md b/compiler/rustc_error_codes/src/error_codes/E0010.md index 71c790e102f..f03f8a6605f 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0010.md +++ b/compiler/rustc_error_codes/src/error_codes/E0010.md @@ -5,7 +5,5 @@ the heap at runtime, and therefore cannot be done at compile time. Erroneous code example: ```compile_fail,E0010 -#![feature(box_syntax)] - -const CON : Box = box 0; +const CON : Vec = vec![1, 2, 3]; ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0368.md b/compiler/rustc_error_codes/src/error_codes/E0368.md index 7b9d9334821..b18e8758d71 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0368.md +++ b/compiler/rustc_error_codes/src/error_codes/E0368.md @@ -41,7 +41,7 @@ impl Add for Foo { fn main() { let mut x: Foo = Foo(5); - x += Foo(7); // error, `+= cannot be applied to the type `Foo` + x += Foo(7); // error, `+=` cannot be applied to the type `Foo` } ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0710.md b/compiler/rustc_error_codes/src/error_codes/E0710.md index b7037ea611b..84d55d52426 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0710.md +++ b/compiler/rustc_error_codes/src/error_codes/E0710.md @@ -3,14 +3,14 @@ An unknown tool name was found in a scoped lint. Erroneous code examples: ```compile_fail,E0710 -#[allow(clipp::filter_map)] // error!` +#[allow(clipp::filter_map)] // error! fn main() { // business logic } ``` ```compile_fail,E0710 -#[warn(clipp::filter_map)] // error!` +#[warn(clipp::filter_map)] // error! fn main() { // business logic } diff --git a/compiler/rustc_error_messages/locales/en-US.ftl b/compiler/rustc_error_messages/messages.ftl similarity index 100% rename from compiler/rustc_error_messages/locales/en-US.ftl rename to compiler/rustc_error_messages/messages.ftl diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 010e5f060bf..301dacc2824 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -34,7 +34,7 @@ use intl_memoizer::IntlLangMemoizer; pub use fluent_bundle::{self, types::FluentType, FluentArgs, FluentError, FluentValue}; pub use unic_langid::{langid, LanguageIdentifier}; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } pub type FluentBundle = fluent_bundle::bundle::FluentBundle; diff --git a/compiler/rustc_errors/locales/en-US.ftl b/compiler/rustc_errors/messages.ftl similarity index 100% rename from compiler/rustc_errors/locales/en-US.ftl rename to compiler/rustc_errors/messages.ftl diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 99af872f07f..bab4f31e777 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -76,7 +76,7 @@ pub use snippet::Style; pub type PErr<'a> = DiagnosticBuilder<'a, ErrorGuaranteed>; pub type PResult<'a, T> = Result>; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } // `PResult` is used a lot. Make sure it doesn't unintentionally get bigger. // (See also the comment on `DiagnosticBuilderInner`'s `diagnostic` field.) diff --git a/compiler/rustc_expand/locales/en-US.ftl b/compiler/rustc_expand/messages.ftl similarity index 100% rename from compiler/rustc_expand/locales/en-US.ftl rename to compiler/rustc_expand/messages.ftl diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 22bc90f5cac..713e4fbbdce 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -12,13 +12,13 @@ use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{AssocCtxt, Visitor}; use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind}; use rustc_attr::{self as attr, Deprecation, Stability}; -use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; +use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::{self, Lrc}; use rustc_errors::{ Applicability, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, MultiSpan, PResult, }; use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT; -use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics}; +use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics, RegisteredTools}; use rustc_parse::{self, parser, MACRO_ARGUMENTS}; use rustc_session::errors::report_lit_error; use rustc_session::{parse::ParseSess, Limit, Session}; @@ -947,14 +947,14 @@ pub trait ResolverExpand { fn declare_proc_macro(&mut self, id: NodeId); /// Tools registered with `#![register_tool]` and used by tool attributes and lints. - fn registered_tools(&self) -> &FxHashSet; + fn registered_tools(&self) -> &RegisteredTools; } pub trait LintStoreExpand { fn pre_expansion_lint( &self, sess: &Session, - registered_tools: &FxHashSet, + registered_tools: &RegisteredTools, node_id: NodeId, attrs: &[Attribute], items: &[P], diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index 634e206e58a..ced7531c3fe 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -64,4 +64,4 @@ mod mut_visit { mod tests; } -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index f469b2daef5..b1d9cea2773 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -1,12 +1,10 @@ -use std::borrow::Cow; - use crate::base::{DummyResult, ExtCtxt, MacResult}; use crate::expand::{parse_ast_fragment, AstFragmentKind}; use crate::mbe::{ macro_parser::{MatcherLoc, NamedParseResult, ParseResult::*, TtParser}, macro_rules::{try_match_macro, Tracker}, }; -use rustc_ast::token::{self, Token}; +use rustc_ast::token::{self, Token, TokenKind}; use rustc_ast::tokenstream::TokenStream; use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, DiagnosticMessage}; @@ -14,6 +12,7 @@ use rustc_parse::parser::{Parser, Recovery}; use rustc_span::source_map::SourceMap; use rustc_span::symbol::Ident; use rustc_span::Span; +use std::borrow::Cow; use super::macro_rules::{parser_from_cx, NoopTracker}; @@ -63,6 +62,13 @@ pub(super) fn failed_to_match_macro<'cx>( err.note(format!("while trying to match {remaining_matcher}")); } + if let MatcherLoc::Token { token: expected_token } = &remaining_matcher + && (matches!(expected_token.kind, TokenKind::Interpolated(_)) + || matches!(token.kind, TokenKind::Interpolated(_))) + { + err.note("captured metavariables except for `$tt`, `$ident` and `$lifetime` cannot be compared to other tokens"); + } + // Check whether there's a missing comma in this macro call, like `println!("{}" a);` if let Some((arg, comma_span)) = arg.add_comma() { for lhs in lhses { diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index e7b2df34ccc..ac93dd555b7 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -90,7 +90,7 @@ declare_features! ( (accepted, clone_closures, "1.26.0", Some(44490), None), /// Allows coercing non capturing closures to function pointers. (accepted, closure_to_fn_coercion, "1.19.0", Some(39817), None), - /// Allows using `cmpxchg16b` from `core::arch::x86_64`. + /// Allows using the CMPXCHG16B target feature. (accepted, cmpxchg16b_target_feature, "CURRENT_RUSTC_VERSION", Some(44839), None), /// Allows usage of the `compile_error!` macro. (accepted, compile_error, "1.20.0", Some(40872), None), @@ -238,6 +238,8 @@ declare_features! ( (accepted, min_const_unsafe_fn, "1.33.0", Some(55607), None), /// Allows using `Self` and associated types in struct expressions and patterns. (accepted, more_struct_aliases, "1.16.0", Some(37544), None), + /// Allows using the MOVBE target feature. + (accepted, movbe_target_feature, "CURRENT_RUSTC_VERSION", Some(44839), None), /// Allows patterns with concurrent by-move and by-ref bindings. /// For example, you can write `Foo(a, ref b)` where `a` is by-move and `b` is by-ref. (accepted, move_ref_pattern, "1.49.0", Some(68354), None), diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index a01914f969e..c893b34b4ac 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -200,8 +200,6 @@ declare_features! ( (active, auto_traits, "1.50.0", Some(13231), None), /// Allows using `box` in patterns (RFC 469). (active, box_patterns, "1.0.0", Some(29641), None), - /// Allows using the `box $expr` syntax. - (active, box_syntax, "1.0.0", Some(49733), None), /// Allows `#[doc(notable_trait)]`. /// Renamed from `doc_spotlight`. (active, doc_notable_trait, "1.52.0", Some(45040), None), @@ -259,7 +257,6 @@ declare_features! ( (active, ermsb_target_feature, "1.49.0", Some(44839), None), (active, hexagon_target_feature, "1.27.0", Some(44839), None), (active, mips_target_feature, "1.27.0", Some(44839), None), - (active, movbe_target_feature, "1.34.0", Some(44839), None), (active, powerpc_target_feature, "1.27.0", Some(44839), None), (active, riscv_target_feature, "1.45.0", Some(44839), None), (active, rtm_target_feature, "1.35.0", Some(44839), None), diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 04d4f6cb14e..48d9fbfa6d2 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -52,6 +52,8 @@ declare_features! ( (removed, allow_fail, "1.19.0", Some(46488), None, Some("removed due to no clear use cases")), (removed, await_macro, "1.38.0", Some(50547), None, Some("subsumed by `.await` syntax")), + /// Allows using the `box $expr` syntax. + (removed, box_syntax, "CURRENT_RUSTC_VERSION", Some(49733), None, Some("replaced with `#[rustc_box]`")), /// Allows capturing disjoint fields in a closure/generator (RFC 2229). (removed, capture_disjoint_fields, "1.49.0", Some(53488), None, Some("stabilized in Rust 2021")), /// Allows comparing raw pointers during const eval. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 19d3d41c984..f00a5f24e45 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -815,12 +815,13 @@ pub struct ParentedNode<'tcx> { #[derive(Debug)] pub struct AttributeMap<'tcx> { pub map: SortedMap, - pub hash: Fingerprint, + // Only present when the crate hash is needed. + pub opt_hash: Option, } impl<'tcx> AttributeMap<'tcx> { pub const EMPTY: &'static AttributeMap<'static> = - &AttributeMap { map: SortedMap::new(), hash: Fingerprint::ZERO }; + &AttributeMap { map: SortedMap::new(), opt_hash: Some(Fingerprint::ZERO) }; #[inline] pub fn get(&self, id: ItemLocalId) -> &'tcx [Attribute] { @@ -832,10 +833,9 @@ impl<'tcx> AttributeMap<'tcx> { /// These nodes are mapped by `ItemLocalId` alongside the index of their parent node. /// The HIR tree, including bodies, is pre-hashed. pub struct OwnerNodes<'tcx> { - /// Pre-computed hash of the full HIR. - pub hash_including_bodies: Fingerprint, - /// Pre-computed hash of the item signature, without recursing into the body. - pub hash_without_bodies: Fingerprint, + /// Pre-computed hash of the full HIR. Used in the crate hash. Only present + /// when incr. comp. is enabled. + pub opt_hash_including_bodies: Option, /// Full HIR for the current owner. // The zeroth node's parent should never be accessed: the owner's parent is computed by the // hir_owner_parent query. It is set to `ItemLocalId::INVALID` to force an ICE if accidentally @@ -872,8 +872,7 @@ impl fmt::Debug for OwnerNodes<'_> { .collect::>(), ) .field("bodies", &self.bodies) - .field("hash_without_bodies", &self.hash_without_bodies) - .field("hash_including_bodies", &self.hash_including_bodies) + .field("opt_hash_including_bodies", &self.opt_hash_including_bodies) .finish() } } @@ -940,7 +939,8 @@ impl MaybeOwner { #[derive(Debug)] pub struct Crate<'hir> { pub owners: IndexVec>>, - pub hir_hash: Fingerprint, + // Only present when incr. comp. is enabled. + pub opt_hir_hash: Option, } #[derive(Debug, HashStable_Generic)] diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 60fa5a99e10..72ff317d45d 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -296,7 +296,6 @@ language_item_table! { // FIXME(swatinem): the following lang items are used for async lowering and // should become obsolete eventually. ResumeTy, sym::ResumeTy, resume_ty, Target::Struct, GenericRequirement::None; - IdentityFuture, sym::identity_future, identity_future_fn, Target::Fn, GenericRequirement::None; GetContext, sym::get_context, get_context_fn, Target::Fn, GenericRequirement::None; Context, sym::Context, context, Target::Struct, GenericRequirement::None; diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs index 85d0e02d0b6..97fa710b354 100644 --- a/compiler/rustc_hir/src/stable_hash_impls.rs +++ b/compiler/rustc_hir/src/stable_hash_impls.rs @@ -100,24 +100,23 @@ impl<'tcx, HirCtx: crate::HashStableContext> HashStable for OwnerNodes<' // `local_id_to_def_id` is also ignored because is dependent on the body, then just hashing // the body satisfies the condition of two nodes being different have different // `hash_stable` results. - let OwnerNodes { hash_including_bodies, hash_without_bodies: _, nodes: _, bodies: _ } = - *self; - hash_including_bodies.hash_stable(hcx, hasher); + let OwnerNodes { opt_hash_including_bodies, nodes: _, bodies: _ } = *self; + opt_hash_including_bodies.unwrap().hash_stable(hcx, hasher); } } impl<'tcx, HirCtx: crate::HashStableContext> HashStable for AttributeMap<'tcx> { fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { - // We ignore the `map` since it refers to information included in `hash` which is hashed in - // the collector and used for the crate hash. - let AttributeMap { hash, map: _ } = *self; - hash.hash_stable(hcx, hasher); + // We ignore the `map` since it refers to information included in `opt_hash` which is + // hashed in the collector and used for the crate hash. + let AttributeMap { opt_hash, map: _ } = *self; + opt_hash.unwrap().hash_stable(hcx, hasher); } } impl HashStable for Crate<'_> { fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { - let Crate { owners: _, hir_hash } = self; - hir_hash.hash_stable(hcx, hasher) + let Crate { owners: _, opt_hir_hash } = self; + opt_hir_hash.unwrap().hash_stable(hcx, hasher) } } diff --git a/compiler/rustc_hir_analysis/locales/en-US.ftl b/compiler/rustc_hir_analysis/messages.ftl similarity index 95% rename from compiler/rustc_hir_analysis/locales/en-US.ftl rename to compiler/rustc_hir_analysis/messages.ftl index df428816444..0105cbf36de 100644 --- a/compiler/rustc_hir_analysis/locales/en-US.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -42,6 +42,9 @@ hir_analysis_assoc_type_binding_not_allowed = associated type bindings are not allowed here .label = associated type not allowed here +hir_analysis_parenthesized_fn_trait_expansion = + parenthesized trait syntax expands to `{$expanded_type}` + hir_analysis_typeof_reserved_keyword_used = `typeof` is a reserved keyword but unimplemented .suggestion = consider replacing `typeof(...)` with an actual type @@ -125,9 +128,14 @@ hir_analysis_where_clause_on_main = `main` function is not allowed to have a `wh hir_analysis_track_caller_on_main = `main` function is not allowed to be `#[track_caller]` .suggestion = remove this annotation +hir_analysis_target_feature_on_main = `main` function is not allowed to have `#[target_feature]` + hir_analysis_start_not_track_caller = `start` is not allowed to be `#[track_caller]` .label = `start` is not allowed to be `#[track_caller]` +hir_analysis_start_not_target_feature = `start` is not allowed to have `#[target_feature]` + .label = `start` is not allowed to have `#[target_feature]` + hir_analysis_start_not_async = `start` is not allowed to be `async` .label = `start` is not allowed to be `async` diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index c49e4d9d581..156334fe785 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -1,10 +1,14 @@ use crate::astconv::AstConv; -use crate::errors::{ManualImplementation, MissingTypeParams}; +use crate::errors::{ + AssocTypeBindingNotAllowed, ManualImplementation, MissingTypeParams, + ParenthesizedFnTraitExpansion, +}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_infer::traits::FulfillmentError; +use rustc_middle::ty::TyCtxt; use rustc_middle::ty::{self, Ty}; use rustc_session::parse::feature_err; use rustc_span::edit_distance::find_best_match_for_name; @@ -78,43 +82,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // Do not suggest the other syntax if we are in trait impl: // the desugaring would contain an associated type constraint. if !is_impl { - let args = trait_segment - .args - .as_ref() - .and_then(|args| args.args.get(0)) - .and_then(|arg| match arg { - hir::GenericArg::Type(ty) => match ty.kind { - hir::TyKind::Tup(t) => t - .iter() - .map(|e| sess.source_map().span_to_snippet(e.span)) - .collect::, _>>() - .map(|a| a.join(", ")), - _ => sess.source_map().span_to_snippet(ty.span), - } - .map(|s| format!("({})", s)) - .ok(), - _ => None, - }) - .unwrap_or_else(|| "()".to_string()); - let ret = trait_segment - .args() - .bindings - .iter() - .find_map(|b| match (b.ident.name == sym::Output, &b.kind) { - (true, hir::TypeBindingKind::Equality { term }) => { - let span = match term { - hir::Term::Ty(ty) => ty.span, - hir::Term::Const(c) => self.tcx().hir().span(c.hir_id), - }; - sess.source_map().span_to_snippet(span).ok() - } - _ => None, - }) - .unwrap_or_else(|| "()".to_string()); err.span_suggestion( span, "use parenthetical notation instead", - format!("{}{} -> {}", trait_segment.ident, args, ret), + fn_trait_to_string(self.tcx(), trait_segment, true), Applicability::MaybeIncorrect, ); } @@ -629,3 +600,69 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { err.emit(); } } + +/// Emits an error regarding forbidden type binding associations +pub fn prohibit_assoc_ty_binding( + tcx: TyCtxt<'_>, + span: Span, + segment: Option<(&hir::PathSegment<'_>, Span)>, +) { + tcx.sess.emit_err(AssocTypeBindingNotAllowed { span, fn_trait_expansion: if let Some((segment, span)) = segment && segment.args().parenthesized { + Some(ParenthesizedFnTraitExpansion { span, expanded_type: fn_trait_to_string(tcx, segment, false) }) + } else { + None + }}); +} + +pub(crate) fn fn_trait_to_string( + tcx: TyCtxt<'_>, + trait_segment: &hir::PathSegment<'_>, + parenthesized: bool, +) -> String { + let args = trait_segment + .args + .as_ref() + .and_then(|args| args.args.get(0)) + .and_then(|arg| match arg { + hir::GenericArg::Type(ty) => match ty.kind { + hir::TyKind::Tup(t) => t + .iter() + .map(|e| tcx.sess.source_map().span_to_snippet(e.span)) + .collect::, _>>() + .map(|a| a.join(", ")), + _ => tcx.sess.source_map().span_to_snippet(ty.span), + } + .map(|s| { + // `s.empty()` checks to see if the type is the unit tuple, if so we don't want a comma + if parenthesized || s.is_empty() { format!("({})", s) } else { format!("({},)", s) } + }) + .ok(), + _ => None, + }) + .unwrap_or_else(|| "()".to_string()); + + let ret = trait_segment + .args() + .bindings + .iter() + .find_map(|b| match (b.ident.name == sym::Output, &b.kind) { + (true, hir::TypeBindingKind::Equality { term }) => { + let span = match term { + hir::Term::Ty(ty) => ty.span, + hir::Term::Const(c) => tcx.hir().span(c.hir_id), + }; + + (span != tcx.hir().span(trait_segment.hir_id)) + .then_some(tcx.sess.source_map().span_to_snippet(span).ok()) + .flatten() + } + _ => None, + }) + .unwrap_or_else(|| "()".to_string()); + + if parenthesized { + format!("{}{} -> {}", trait_segment.ident, args, ret) + } else { + format!("{}<{}, Output={}>", trait_segment.ident, args, ret) + } +} diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs index 7f6518ffd71..2f4963f6bc3 100644 --- a/compiler/rustc_hir_analysis/src/astconv/generics.rs +++ b/compiler/rustc_hir_analysis/src/astconv/generics.rs @@ -1,9 +1,8 @@ use super::IsMethodCall; use crate::astconv::{ - CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch, - GenericArgCountResult, GenericArgPosition, + errors::prohibit_assoc_ty_binding, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, + GenericArgCountMismatch, GenericArgCountResult, GenericArgPosition, }; -use crate::errors::AssocTypeBindingNotAllowed; use crate::structured_errors::{GenericArgsInfo, StructuredDiagnostic, WrongNumberOfGenericArgs}; use rustc_ast::ast::ParamKindOrd; use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan}; @@ -433,7 +432,7 @@ pub(crate) fn check_generic_arg_count( (gen_pos != GenericArgPosition::Type || infer_args) && !gen_args.has_lifetime_params(); if gen_pos != GenericArgPosition::Type && let Some(b) = gen_args.bindings.first() { - prohibit_assoc_ty_binding(tcx, b.span); + prohibit_assoc_ty_binding(tcx, b.span, None); } let explicit_late_bound = @@ -589,11 +588,6 @@ pub(crate) fn check_generic_arg_count( } } -/// Emits an error regarding forbidden type binding associations -pub fn prohibit_assoc_ty_binding(tcx: TyCtxt<'_>, span: Span) { - tcx.sess.emit_err(AssocTypeBindingNotAllowed { span }); -} - /// Prohibits explicit lifetime arguments if late-bound lifetime parameters /// are present. This is used both for datatypes and function calls. pub(crate) fn prohibit_explicit_late_bound_lifetimes( diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 899029d98e0..3cd4c4afe86 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -5,9 +5,8 @@ mod errors; pub mod generics; -use crate::astconv::generics::{ - check_generic_arg_count, create_substs_for_generic_args, prohibit_assoc_ty_binding, -}; +use crate::astconv::errors::prohibit_assoc_ty_binding; +use crate::astconv::generics::{check_generic_arg_count, create_substs_for_generic_args}; use crate::bounds::Bounds; use crate::collect::HirPlaceholderCollector; use crate::errors::{ @@ -295,7 +294,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ty::BoundConstness::NotConst, ); if let Some(b) = item_segment.args().bindings.first() { - prohibit_assoc_ty_binding(self.tcx(), b.span); + prohibit_assoc_ty_binding(self.tcx(), b.span, Some((item_segment, span))); } substs @@ -631,7 +630,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ); if let Some(b) = item_segment.args().bindings.first() { - prohibit_assoc_ty_binding(self.tcx(), b.span); + prohibit_assoc_ty_binding(self.tcx(), b.span, Some((item_segment, span))); } args @@ -825,7 +824,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { constness, ); if let Some(b) = trait_segment.args().bindings.first() { - prohibit_assoc_ty_binding(self.tcx(), b.span); + prohibit_assoc_ty_binding(self.tcx(), b.span, Some((trait_segment, span))); } self.tcx().mk_trait_ref(trait_def_id, substs) } @@ -2596,7 +2595,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { for segment in segments { // Only emit the first error to avoid overloading the user with error messages. if let Some(b) = segment.args().bindings.first() { - prohibit_assoc_ty_binding(self.tcx(), b.span); + prohibit_assoc_ty_binding(self.tcx(), b.span, None); return true; } } @@ -3049,10 +3048,18 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } &hir::TyKind::OpaqueDef(item_id, lifetimes, in_trait) => { let opaque_ty = tcx.hir().item(item_id); - let def_id = item_id.owner_id.to_def_id(); match opaque_ty.kind { hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => { + let local_def_id = item_id.owner_id.def_id; + // If this is an RPITIT and we are using the new RPITIT lowering scheme, we + // generate the def_id of an associated type for the trait and return as + // type a projection. + let def_id = if in_trait && tcx.lower_impl_trait_in_trait_to_assoc_ty() { + tcx.associated_item_for_impl_trait_in_trait(local_def_id).to_def_id() + } else { + local_def_id.to_def_id() + }; self.impl_trait_ty_to_ty(def_id, lifetimes, origin, in_trait) } ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i), diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index be0ae4ce2ef..14dc9d89180 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -444,7 +444,7 @@ fn check_opaque_meets_bounds<'tcx>( // version. let errors = ocx.select_all_or_error(); if !errors.is_empty() { - infcx.err_ctxt().report_fulfillment_errors(&errors, None); + infcx.err_ctxt().report_fulfillment_errors(&errors); } match origin { // Checked when type checking the function containing them. @@ -1545,6 +1545,6 @@ pub(super) fn check_generator_obligations(tcx: TyCtxt<'_>, def_id: LocalDefId) { let errors = fulfillment_cx.select_all_or_error(&infcx); debug!(?errors); if !errors.is_empty() { - infcx.err_ctxt().report_fulfillment_errors(&errors, None); + infcx.err_ctxt().report_fulfillment_errors(&errors); } } diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 5adc7a87323..6e6f8c1533b 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -320,7 +320,7 @@ fn compare_method_predicate_entailment<'tcx>( }); } CheckImpliedWfMode::Skip => { - let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None); + let reported = infcx.err_ctxt().report_fulfillment_errors(&errors); return Err(reported); } } @@ -720,7 +720,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( // RPITs. let errors = ocx.select_all_or_error(); if !errors.is_empty() { - let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None); + let reported = infcx.err_ctxt().report_fulfillment_errors(&errors); return Err(reported); } @@ -830,7 +830,7 @@ impl<'tcx> TypeFolder> for ImplTraitInTraitCollector<'_, 'tcx> { fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { if let ty::Alias(ty::Projection, proj) = ty.kind() - && self.interner().def_kind(proj.def_id) == DefKind::ImplTraitPlaceholder + && self.interner().is_impl_trait_in_trait(proj.def_id) { if let Some((ty, _)) = self.types.get(&proj.def_id) { return *ty; @@ -1731,7 +1731,7 @@ pub(super) fn compare_impl_const_raw( // version. let errors = ocx.select_all_or_error(); if !errors.is_empty() { - return Err(infcx.err_ctxt().report_fulfillment_errors(&errors, None)); + return Err(infcx.err_ctxt().report_fulfillment_errors(&errors)); } let outlives_environment = OutlivesEnvironment::new(param_env); @@ -1831,7 +1831,7 @@ fn compare_type_predicate_entailment<'tcx>( // version. let errors = ocx.select_all_or_error(); if !errors.is_empty() { - let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None); + let reported = infcx.err_ctxt().report_fulfillment_errors(&errors); return Err(reported); } @@ -1995,13 +1995,20 @@ pub(super) fn check_type_bounds<'tcx>( let infcx = tcx.infer_ctxt().build(); let ocx = ObligationCtxt::new(&infcx); - let impl_ty_span = match tcx.hir().get_by_def_id(impl_ty_def_id) { - hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Type(_, Some(ty)), - .. - }) => ty.span, - hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Type(ty), .. }) => ty.span, - _ => bug!(), + // A synthetic impl Trait for RPITIT desugaring has no HIR, which we currently use to get the + // span for an impl's associated type. Instead, for these, use the def_span for the synthesized + // associated type. + let impl_ty_span = if tcx.opt_rpitit_info(impl_ty.def_id).is_some() { + tcx.def_span(impl_ty_def_id) + } else { + match tcx.hir().get_by_def_id(impl_ty_def_id) { + hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Type(_, Some(ty)), + .. + }) => ty.span, + hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Type(ty), .. }) => ty.span, + _ => bug!(), + } }; let assumed_wf_types = ocx.assumed_wf_types(param_env, impl_ty_span, impl_ty_def_id); @@ -2044,7 +2051,7 @@ pub(super) fn check_type_bounds<'tcx>( // version. let errors = ocx.select_all_or_error(); if !errors.is_empty() { - let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None); + let reported = infcx.err_ctxt().report_fulfillment_errors(&errors); return Err(reported); } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 4cccdf30c5f..71050864ce0 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -111,7 +111,7 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>( let errors = wfcx.select_all_or_error(); if !errors.is_empty() { - infcx.err_ctxt().report_fulfillment_errors(&errors, None); + infcx.err_ctxt().report_fulfillment_errors(&errors); return; } diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 8294d92c936..5e8f69677cf 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -345,7 +345,7 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef }), ); if !errors.is_empty() { - infcx.err_ctxt().report_fulfillment_errors(&errors, None); + infcx.err_ctxt().report_fulfillment_errors(&errors); } // Finally, resolve all regions. @@ -585,7 +585,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn predicate_for_trait_def(tcx, param_env, cause, trait_def_id, 0, [source, target]); let errors = traits::fully_solve_obligation(&infcx, predicate); if !errors.is_empty() { - infcx.err_ctxt().report_fulfillment_errors(&errors, None); + infcx.err_ctxt().report_fulfillment_errors(&errors); } // Finally, resolve all regions. diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index 02f3eeee0e7..07a33bcbb50 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -11,7 +11,7 @@ use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; -use rustc_middle::ty::fast_reject::{simplify_type, SimplifiedType, TreatParams}; +use rustc_middle::ty::fast_reject::{simplify_type, SimplifiedType, TreatParams, TreatProjections}; use rustc_middle::ty::{self, CrateInherentImpls, Ty, TyCtxt}; use rustc_span::symbol::sym; @@ -99,7 +99,12 @@ impl<'tcx> InherentCollect<'tcx> { } } - if let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsInfer) { + if let Some(simp) = simplify_type( + self.tcx, + self_ty, + TreatParams::AsCandidateKey, + TreatProjections::AsCandidateKey, + ) { self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id); } else { bug!("unexpected self type: {:?}", self_ty); @@ -159,7 +164,12 @@ impl<'tcx> InherentCollect<'tcx> { } } - if let Some(simp) = simplify_type(self.tcx, ty, TreatParams::AsInfer) { + if let Some(simp) = simplify_type( + self.tcx, + ty, + TreatParams::AsCandidateKey, + TreatProjections::AsCandidateKey, + ) { self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id); } else { bug!("unexpected primitive type: {:?}", ty); diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 6f6f993f727..7dce29cc0bb 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -3,7 +3,7 @@ use crate::astconv::AstConv; use rustc_hir as hir; use rustc_infer::traits::util; use rustc_middle::ty::subst::InternalSubsts; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, ImplTraitInTraitData, Ty, TyCtxt}; use rustc_span::def_id::DefId; use rustc_span::Span; @@ -58,17 +58,10 @@ fn opaque_type_bounds<'tcx>( tcx: TyCtxt<'tcx>, opaque_def_id: DefId, ast_bounds: &'tcx [hir::GenericBound<'tcx>], + item_ty: Ty<'tcx>, span: Span, - in_trait: bool, ) -> &'tcx [(ty::Predicate<'tcx>, Span)] { ty::print::with_no_queries!({ - let substs = InternalSubsts::identity_for_item(tcx, opaque_def_id); - let item_ty = if in_trait { - tcx.mk_projection(opaque_def_id, substs) - } else { - tcx.mk_opaque(opaque_def_id, substs) - }; - let icx = ItemCtxt::new(tcx, opaque_def_id); let mut bounds = icx.astconv().compute_bounds(item_ty, ast_bounds); // Opaque types are implicitly sized unless a `?Sized` bound is found @@ -83,7 +76,18 @@ pub(super) fn explicit_item_bounds( tcx: TyCtxt<'_>, def_id: DefId, ) -> &'_ [(ty::Predicate<'_>, Span)] { - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + // If the def_id is about an RPITIT, delegate explicit_item_bounds to the opaque_def_id that + // generated the synthesized associate type. + let rpitit_info = if let Some(ImplTraitInTraitData::Trait { opaque_def_id, .. }) = + tcx.opt_rpitit_info(def_id) + { + Some(opaque_def_id) + } else { + None + }; + + let bounds_def_id = rpitit_info.unwrap_or(def_id); + let hir_id = tcx.hir().local_def_id_to_hir_id(bounds_def_id.expect_local()); match tcx.hir().get(hir_id) { hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Type(bounds, _), @@ -94,7 +98,15 @@ pub(super) fn explicit_item_bounds( kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, in_trait, .. }), span, .. - }) => opaque_type_bounds(tcx, def_id, bounds, *span, *in_trait), + }) => { + let substs = InternalSubsts::identity_for_item(tcx, def_id); + let item_ty = if *in_trait || rpitit_info.is_some() { + tcx.mk_projection(def_id, substs) + } else { + tcx.mk_opaque(def_id, substs) + }; + opaque_type_bounds(tcx, bounds_def_id, bounds, item_ty, *span) + } _ => bug!("item_bounds called on {:?}", def_id), } } diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 7fc0711a155..fe44fabf57d 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -9,7 +9,8 @@ use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{ - self, IsSuggestable, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitableExt, + self, ImplTraitInTraitData, IsSuggestable, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, }; use rustc_span::symbol::Ident; use rustc_span::{Span, DUMMY_SP}; @@ -244,6 +245,24 @@ fn get_path_containing_arg_in_pat<'hir>( } pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::EarlyBinder> { + // If we are computing `type_of` the synthesized associated type for an RPITIT in the impl + // side, use `collect_return_position_impl_trait_in_trait_tys` to infer the value of the + // associated type in the impl. + if let Some(ImplTraitInTraitData::Impl { fn_def_id, .. }) = tcx.opt_rpitit_info(def_id) { + match tcx.collect_return_position_impl_trait_in_trait_tys(fn_def_id) { + Ok(map) => { + let assoc_item = tcx.associated_item(def_id); + return ty::EarlyBinder(map[&assoc_item.trait_item_def_id.unwrap()]); + } + Err(_) => { + return ty::EarlyBinder(tcx.ty_error_with_message( + DUMMY_SP, + "Could not collect return position impl trait in trait tys", + )); + } + } + } + let def_id = def_id.expect_local(); use rustc_hir::*; diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index dd40706f1d3..f57197edeb7 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -129,6 +129,18 @@ pub struct AssocTypeBindingNotAllowed { #[primary_span] #[label] pub span: Span, + + #[subdiagnostic] + pub fn_trait_expansion: Option, +} + +#[derive(Subdiagnostic)] +#[help(hir_analysis_parenthesized_fn_trait_expansion)] +pub struct ParenthesizedFnTraitExpansion { + #[primary_span] + pub span: Span, + + pub expanded_type: String, } #[derive(Diagnostic)] @@ -315,6 +327,14 @@ pub(crate) struct TrackCallerOnMain { pub annotated: Span, } +#[derive(Diagnostic)] +#[diag(hir_analysis_target_feature_on_main)] +pub(crate) struct TargetFeatureOnMain { + #[primary_span] + #[label(hir_analysis_target_feature_on_main)] + pub main: Span, +} + #[derive(Diagnostic)] #[diag(hir_analysis_start_not_track_caller)] pub(crate) struct StartTrackCaller { @@ -324,6 +344,15 @@ pub(crate) struct StartTrackCaller { pub start: Span, } +#[derive(Diagnostic)] +#[diag(hir_analysis_start_not_target_feature)] +pub(crate) struct StartTargetFeature { + #[primary_span] + pub span: Span, + #[label] + pub start: Span, +} + #[derive(Diagnostic)] #[diag(hir_analysis_start_not_async, code = "E0752")] pub(crate) struct StartAsync { diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index daa5d15704d..58dd03811f7 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -174,7 +174,7 @@ fn get_impl_substs( let errors = ocx.select_all_or_error(); if !errors.is_empty() { - ocx.infcx.err_ctxt().report_fulfillment_errors(&errors, None); + ocx.infcx.err_ctxt().report_fulfillment_errors(&errors); return None; } diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 33c132fd534..62abcbbdc9f 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -120,7 +120,7 @@ use std::ops::Not; use astconv::AstConv; use bounds::Bounds; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi, span: Span) { const CONVENTIONS_UNSTABLE: &str = "`C`, `cdecl`, `win64`, `sysv64` or `efiapi`"; @@ -176,7 +176,7 @@ fn require_same_types<'tcx>( match &errors[..] { [] => true, errors => { - infcx.err_ctxt().report_fulfillment_errors(errors, None); + infcx.err_ctxt().report_fulfillment_errors(errors); false } } @@ -283,6 +283,15 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { error = true; } + if !tcx.codegen_fn_attrs(main_def_id).target_features.is_empty() + // Calling functions with `#[target_feature]` is not unsafe on WASM, see #84988 + && !tcx.sess.target.is_like_wasm + && !tcx.sess.opts.actually_rustdoc + { + tcx.sess.emit_err(errors::TargetFeatureOnMain { main: main_span }); + error = true; + } + if error { return; } @@ -309,7 +318,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { ocx.register_bound(cause, param_env, norm_return_ty, term_did); let errors = ocx.select_all_or_error(); if !errors.is_empty() { - infcx.err_ctxt().report_fulfillment_errors(&errors, None); + infcx.err_ctxt().report_fulfillment_errors(&errors); error = true; } // now we can take the return type of the given main function @@ -373,6 +382,18 @@ fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) { }); error = true; } + if attr.has_name(sym::target_feature) + // Calling functions with `#[target_feature]` is + // not unsafe on WASM, see #84988 + && !tcx.sess.target.is_like_wasm + && !tcx.sess.opts.actually_rustdoc + { + tcx.sess.emit_err(errors::StartTargetFeature { + span: attr.span, + start: start_span, + }); + error = true; + } } if error { diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index c021fca7133..d715b03e40e 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1367,8 +1367,8 @@ impl<'a> State<'a> { self.ann.pre(self, AnnNode::Expr(expr)); match expr.kind { hir::ExprKind::Box(expr) => { - self.word_space("box"); - self.print_expr_maybe_paren(expr, parser::PREC_PREFIX); + self.word_space("Box::new"); + self.print_call_post(std::slice::from_ref(expr)); } hir::ExprKind::Array(exprs) => { self.print_expr_vec(exprs); diff --git a/compiler/rustc_hir_typeck/locales/en-US.ftl b/compiler/rustc_hir_typeck/messages.ftl similarity index 100% rename from compiler/rustc_hir_typeck/locales/en-US.ftl rename to compiler/rustc_hir_typeck/messages.ftl diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 6a0d5c01109..ecbe4a97dd9 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -311,9 +311,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let fn_decl_span = if hir.body(body).generator_kind == Some(hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure)) { - // Actually need to unwrap a few more layers of HIR to get to + // Actually need to unwrap one more layer of HIR to get to // the _real_ closure... - let async_closure = hir.parent_id(hir.parent_id(parent_hir_id)); + let async_closure = hir.parent_id(parent_hir_id); if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }), .. diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index d84fabb7834..773ac0e40c5 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -398,7 +398,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// Here: /// - E would be `fn(&u32) -> &u32`. - /// - S would be `fn(&u32) -> + /// - S would be `fn(&u32) -> ?T` /// - E' is `&'!0 u32 -> &'!0 u32` /// - S' is `&'?0 u32 -> ?T` /// diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index ac7d984c161..4720306e159 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -581,7 +581,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !errors.is_empty() { self.adjust_fulfillment_errors_for_expr_obligation(&mut errors); - self.err_ctxt().report_fulfillment_errors(&errors, self.inh.body_id); + self.err_ctxt().report_fulfillment_errors(&errors); } } @@ -594,7 +594,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !result.is_empty() { mutate_fulfillment_errors(&mut result); self.adjust_fulfillment_errors_for_expr_obligation(&mut result); - self.err_ctxt().report_fulfillment_errors(&result, self.inh.body_id); + self.err_ctxt().report_fulfillment_errors(&result); } } @@ -924,12 +924,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir_id, kind: hir::ExprKind::Closure(..), .. - }) if let Some(Node::Expr(&hir::Expr { - hir_id, - kind: hir::ExprKind::Call(..), - .. - })) = self.tcx.hir().find_parent(hir_id) && - let Some(Node::Item(&hir::Item { + }) if let Some(Node::Item(&hir::Item { ident, kind: hir::ItemKind::Fn(ref sig, ..), .. @@ -1411,7 +1406,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { let e = self.tainted_by_errors().unwrap_or_else(|| { self.err_ctxt() - .emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282, true) + .emit_inference_failure_err(self.body_id, sp, ty.into(), E0282, true) .emit() }); let err = self.tcx.ty_error(e); diff --git a/compiler/rustc_hir_typeck/src/inherited.rs b/compiler/rustc_hir_typeck/src/inherited.rs index 26020382d81..4110b176b41 100644 --- a/compiler/rustc_hir_typeck/src/inherited.rs +++ b/compiler/rustc_hir_typeck/src/inherited.rs @@ -4,7 +4,6 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::HirIdMap; -use rustc_infer::infer; use rustc_infer::infer::{DefiningAnchor, InferCtxt, InferOk, TyCtxtInferExt}; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -58,8 +57,6 @@ pub struct Inherited<'tcx> { pub(super) deferred_generator_interiors: RefCell, hir::GeneratorKind)>>, - pub(super) body_id: Option, - /// Whenever we introduce an adjustment from `!` into a type variable, /// we record that type variable here. This is later used to inform /// fallback. See the `fallback` module for details. @@ -75,48 +72,16 @@ impl<'tcx> Deref for Inherited<'tcx> { } } -/// A temporary returned by `Inherited::build(...)`. This is necessary -/// for multiple `InferCtxt` to share the same `typeck_results` -/// without using `Rc` or something similar. -pub struct InheritedBuilder<'tcx> { - infcx: infer::InferCtxtBuilder<'tcx>, - def_id: LocalDefId, - typeck_results: RefCell>, -} - impl<'tcx> Inherited<'tcx> { - pub fn build(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> InheritedBuilder<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { let hir_owner = tcx.hir().local_def_id_to_hir_id(def_id).owner; - InheritedBuilder { - infcx: tcx - .infer_ctxt() - .ignoring_regions() - .with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id)), - def_id, - typeck_results: RefCell::new(ty::TypeckResults::new(hir_owner)), - } - } -} - -impl<'tcx> InheritedBuilder<'tcx> { - pub fn enter(mut self, f: F) -> R - where - F: FnOnce(&Inherited<'tcx>) -> R, - { - let def_id = self.def_id; - f(&Inherited::new(self.infcx.build(), def_id, self.typeck_results)) - } -} - -impl<'tcx> Inherited<'tcx> { - fn new( - infcx: InferCtxt<'tcx>, - def_id: LocalDefId, - typeck_results: RefCell>, - ) -> Self { - let tcx = infcx.tcx; - let body_id = tcx.hir().maybe_body_owned_by(def_id); + let infcx = tcx + .infer_ctxt() + .ignoring_regions() + .with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id)) + .build(); + let typeck_results = RefCell::new(ty::TypeckResults::new(hir_owner)); Inherited { typeck_results, @@ -130,7 +95,6 @@ impl<'tcx> Inherited<'tcx> { deferred_asm_checks: RefCell::new(Vec::new()), deferred_generator_interiors: RefCell::new(Vec::new()), diverging_type_vars: RefCell::new(Default::default()), - body_id, infer_var_info: RefCell::new(Default::default()), } } diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index e397dfd4570..d9c56134b66 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -45,13 +45,14 @@ mod rvalue_scopes; mod upvar; mod writeback; -pub use diverges::Diverges; -pub use expectation::Expectation; -pub use fn_ctxt::*; -pub use inherited::{Inherited, InheritedBuilder}; +pub use fn_ctxt::FnCtxt; +pub use inherited::Inherited; use crate::check::check_fn; use crate::coercion::DynamicCoerceMany; +use crate::diverges::Diverges; +use crate::expectation::Expectation; +use crate::fn_ctxt::RawTy; use crate::gather_locals::GatherLocalsVisitor; use rustc_data_structures::unord::UnordSet; use rustc_errors::{ @@ -74,7 +75,7 @@ use rustc_session::Session; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::{sym, Span}; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } #[macro_export] macro_rules! type_error_struct { @@ -105,10 +106,9 @@ pub struct LocalTy<'tcx> { /// (notably closures), `typeck_results(def_id)` would wind up /// redirecting to the owning function. fn primary_body_of( - tcx: TyCtxt<'_>, - id: hir::HirId, + node: Node<'_>, ) -> Option<(hir::BodyId, Option<&hir::Ty<'_>>, Option<&hir::FnSig<'_>>)> { - match tcx.hir().get(id) { + match node { Node::Item(item) => match item.kind { hir::ItemKind::Const(ty, body) | hir::ItemKind::Static(ty, _, body) => { Some((body, Some(ty), None)) @@ -142,8 +142,7 @@ fn has_typeck_results(tcx: TyCtxt<'_>, def_id: DefId) -> bool { } if let Some(def_id) = def_id.as_local() { - let id = tcx.hir().local_def_id_to_hir_id(def_id); - primary_body_of(tcx, id).is_some() + primary_body_of(tcx.hir().get_by_def_id(def_id)).is_some() } else { false } @@ -198,143 +197,140 @@ fn typeck_with_fallback<'tcx>( } let id = tcx.hir().local_def_id_to_hir_id(def_id); + let node = tcx.hir().get(id); let span = tcx.hir().span(id); // Figure out what primary body this item has. - let (body_id, body_ty, fn_sig) = primary_body_of(tcx, id).unwrap_or_else(|| { + let (body_id, body_ty, fn_sig) = primary_body_of(node).unwrap_or_else(|| { span_bug!(span, "can't type-check body of {:?}", def_id); }); let body = tcx.hir().body(body_id); - let typeck_results = Inherited::build(tcx, def_id).enter(|inh| { - let param_env = tcx.param_env(def_id); - let param_env = if tcx.has_attr(def_id.to_def_id(), sym::rustc_do_not_const_check) { - param_env.without_const() + let param_env = tcx.param_env(def_id); + let param_env = if tcx.has_attr(def_id.to_def_id(), sym::rustc_do_not_const_check) { + param_env.without_const() + } else { + param_env + }; + let inh = Inherited::new(tcx, def_id); + let mut fcx = FnCtxt::new(&inh, param_env, def_id); + + if let Some(hir::FnSig { header, decl, .. }) = fn_sig { + let fn_sig = if rustc_hir_analysis::collect::get_infer_ret_ty(&decl.output).is_some() { + fcx.astconv().ty_of_fn(id, header.unsafety, header.abi, decl, None, None) } else { - param_env + tcx.fn_sig(def_id).subst_identity() }; - let mut fcx = FnCtxt::new(&inh, param_env, def_id); - if let Some(hir::FnSig { header, decl, .. }) = fn_sig { - let fn_sig = if rustc_hir_analysis::collect::get_infer_ret_ty(&decl.output).is_some() { - fcx.astconv().ty_of_fn(id, header.unsafety, header.abi, decl, None, None) - } else { - tcx.fn_sig(def_id).subst_identity() - }; + check_abi(tcx, id, span, fn_sig.abi()); - check_abi(tcx, id, span, fn_sig.abi()); + // Compute the function signature from point of view of inside the fn. + let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig); + let fn_sig = fcx.normalize(body.value.span, fn_sig); - // Compute the function signature from point of view of inside the fn. - let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig); - let fn_sig = fcx.normalize(body.value.span, fn_sig); - - check_fn(&mut fcx, fn_sig, decl, def_id, body, None); - } else { - let expected_type = body_ty - .and_then(|ty| match ty.kind { - hir::TyKind::Infer => Some(fcx.astconv().ast_ty_to_ty(ty)), - _ => None, - }) - .unwrap_or_else(|| match tcx.hir().get(id) { - Node::AnonConst(_) => match tcx.hir().get(tcx.hir().parent_id(id)) { - Node::Expr(&hir::Expr { - kind: hir::ExprKind::ConstBlock(ref anon_const), - .. - }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }), - Node::Ty(&hir::Ty { - kind: hir::TyKind::Typeof(ref anon_const), .. - }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }), - Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(asm), .. }) - | Node::Item(&hir::Item { kind: hir::ItemKind::GlobalAsm(asm), .. }) => { - let operand_ty = - asm.operands.iter().find_map(|(op, _op_sp)| match op { - hir::InlineAsmOperand::Const { anon_const } - if anon_const.hir_id == id => - { - // Inline assembly constants must be integers. - Some(fcx.next_int_var()) - } - hir::InlineAsmOperand::SymFn { anon_const } - if anon_const.hir_id == id => - { - Some(fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span, - })) - } - _ => None, - }); - operand_ty.unwrap_or_else(fallback) + check_fn(&mut fcx, fn_sig, decl, def_id, body, None); + } else { + let expected_type = if let Some(&hir::Ty { kind: hir::TyKind::Infer, span, .. }) = body_ty { + Some(fcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span, + })) + } else if let Node::AnonConst(_) = node { + match tcx.hir().get(tcx.hir().parent_id(id)) { + Node::Expr(&hir::Expr { + kind: hir::ExprKind::ConstBlock(ref anon_const), .. + }) if anon_const.hir_id == id => Some(fcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span, + })), + Node::Ty(&hir::Ty { kind: hir::TyKind::Typeof(ref anon_const), .. }) + if anon_const.hir_id == id => + { + Some(fcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span, + })) + } + Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(asm), .. }) + | Node::Item(&hir::Item { kind: hir::ItemKind::GlobalAsm(asm), .. }) => { + asm.operands.iter().find_map(|(op, _op_sp)| match op { + hir::InlineAsmOperand::Const { anon_const } if anon_const.hir_id == id => { + // Inline assembly constants must be integers. + Some(fcx.next_int_var()) } - _ => fallback(), - }, - _ => fallback(), - }); - - let expected_type = fcx.normalize(body.value.span, expected_type); - fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized); - - // Gather locals in statics (because of block expressions). - GatherLocalsVisitor::new(&fcx).visit_body(body); - - fcx.check_expr_coercable_to_type(&body.value, expected_type, None); - - fcx.write_ty(id, expected_type); + hir::InlineAsmOperand::SymFn { anon_const } if anon_const.hir_id == id => { + Some(fcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span, + })) + } + _ => None, + }) + } + _ => None, + } + } else { + None }; + let expected_type = expected_type.unwrap_or_else(fallback); - fcx.type_inference_fallback(); + let expected_type = fcx.normalize(body.value.span, expected_type); + fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized); - // Even though coercion casts provide type hints, we check casts after fallback for - // backwards compatibility. This makes fallback a stronger type hint than a cast coercion. - fcx.check_casts(); - fcx.select_obligations_where_possible(|_| {}); + // Gather locals in statics (because of block expressions). + GatherLocalsVisitor::new(&fcx).visit_body(body); - // Closure and generator analysis may run after fallback - // because they don't constrain other type variables. - // Closure analysis only runs on closures. Therefore they only need to fulfill non-const predicates (as of now) - let prev_constness = fcx.param_env.constness(); - fcx.param_env = fcx.param_env.without_const(); - fcx.closure_analyze(body); - fcx.param_env = fcx.param_env.with_constness(prev_constness); - assert!(fcx.deferred_call_resolutions.borrow().is_empty()); - // Before the generator analysis, temporary scopes shall be marked to provide more - // precise information on types to be captured. - fcx.resolve_rvalue_scopes(def_id.to_def_id()); + fcx.check_expr_coercable_to_type(&body.value, expected_type, None); - for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) { - let ty = fcx.normalize(span, ty); - fcx.require_type_is_sized(ty, span, code); - } + fcx.write_ty(id, expected_type); + }; - fcx.select_obligations_where_possible(|_| {}); + fcx.type_inference_fallback(); - debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations()); + // Even though coercion casts provide type hints, we check casts after fallback for + // backwards compatibility. This makes fallback a stronger type hint than a cast coercion. + fcx.check_casts(); + fcx.select_obligations_where_possible(|_| {}); - // This must be the last thing before `report_ambiguity_errors`. - fcx.resolve_generator_interiors(def_id.to_def_id()); + // Closure and generator analysis may run after fallback + // because they don't constrain other type variables. + // Closure analysis only runs on closures. Therefore they only need to fulfill non-const predicates (as of now) + let prev_constness = fcx.param_env.constness(); + fcx.param_env = fcx.param_env.without_const(); + fcx.closure_analyze(body); + fcx.param_env = fcx.param_env.with_constness(prev_constness); + assert!(fcx.deferred_call_resolutions.borrow().is_empty()); + // Before the generator analysis, temporary scopes shall be marked to provide more + // precise information on types to be captured. + fcx.resolve_rvalue_scopes(def_id.to_def_id()); - debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations()); + for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) { + let ty = fcx.normalize(span, ty); + fcx.require_type_is_sized(ty, span, code); + } - if let None = fcx.infcx.tainted_by_errors() { - fcx.report_ambiguity_errors(); - } + fcx.select_obligations_where_possible(|_| {}); - if let None = fcx.infcx.tainted_by_errors() { - fcx.check_transmutes(); - } + debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations()); - fcx.check_asms(); + // This must be the last thing before `report_ambiguity_errors`. + fcx.resolve_generator_interiors(def_id.to_def_id()); - fcx.infcx.skip_region_resolution(); + debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations()); - fcx.resolve_type_vars_in_body(body) - }); + if let None = fcx.infcx.tainted_by_errors() { + fcx.report_ambiguity_errors(); + } + + if let None = fcx.infcx.tainted_by_errors() { + fcx.check_transmutes(); + } + + fcx.check_asms(); + + fcx.infcx.skip_region_resolution(); + + let typeck_results = fcx.resolve_type_vars_in_body(body); // Consistency check our TypeckResults instance can hold all ItemLocalIds // it will need to hold. diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 57805f7c800..3254a930342 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -15,6 +15,7 @@ use rustc_infer::infer::canonical::OriginalQueryValues; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; use rustc_middle::middle::stability; +use rustc_middle::ty::fast_reject::TreatProjections; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; use rustc_middle::ty::AssocItem; use rustc_middle::ty::GenericParamDefKind; @@ -699,7 +700,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } fn assemble_inherent_candidates_for_incoherent_ty(&mut self, self_ty: Ty<'tcx>) { - let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsInfer) else { + let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsCandidateKey, TreatProjections::AsCandidateKey) else { bug!("unexpected incoherent type: {:?}", self_ty) }; for &impl_def_id in self.tcx.incoherent_impls(simp) { @@ -1338,6 +1339,7 @@ impl<'tcx> Pick<'tcx> { container: _, trait_item_def_id: _, fn_has_self_parameter: _, + opt_rpitit_info: _, }, kind: _, import_ids: _, diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 4b15e48bd27..7055d9257ec 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -25,6 +25,7 @@ use rustc_infer::infer::{ use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::traits::util::supertraits; use rustc_middle::ty::fast_reject::DeepRejectCtxt; +use rustc_middle::ty::fast_reject::TreatProjections; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; use rustc_middle::ty::print::{with_crate_prefix, with_forced_trimmed_paths}; use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeVisitableExt}; @@ -1257,7 +1258,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let target_ty = self .autoderef(sugg_span, rcvr_ty) .find(|(rcvr_ty, _)| { - DeepRejectCtxt { treat_obligation_params: TreatParams::AsInfer } + DeepRejectCtxt { treat_obligation_params: TreatParams::AsCandidateKey } .types_may_unify(*rcvr_ty, impl_ty) }) .map_or(impl_ty, |(ty, _)| ty) @@ -1516,7 +1517,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .into_iter() .any(|info| self.associated_value(info.def_id, item_name).is_some()); let found_assoc = |ty: Ty<'tcx>| { - simplify_type(tcx, ty, TreatParams::AsInfer) + simplify_type(tcx, ty, TreatParams::AsCandidateKey, TreatProjections::AsCandidateKey) .and_then(|simp| { tcx.incoherent_impls(simp) .iter() @@ -2645,9 +2646,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // FIXME: Even though negative bounds are not implemented, we could maybe handle // cases where a positive bound implies a negative impl. (candidates, Vec::new()) - } else if let Some(simp_rcvr_ty) = - simplify_type(self.tcx, rcvr_ty, TreatParams::AsPlaceholder) - { + } else if let Some(simp_rcvr_ty) = simplify_type( + self.tcx, + rcvr_ty, + TreatParams::ForLookup, + TreatProjections::ForLookup, + ) { let mut potential_candidates = Vec::new(); let mut explicitly_negative = Vec::new(); for candidate in candidates { @@ -2660,8 +2664,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) .any(|imp_did| { let imp = self.tcx.impl_trait_ref(imp_did).unwrap().subst_identity(); - let imp_simp = - simplify_type(self.tcx, imp.self_ty(), TreatParams::AsPlaceholder); + let imp_simp = simplify_type( + self.tcx, + imp.self_ty(), + TreatParams::ForLookup, + TreatProjections::ForLookup, + ); imp_simp.map_or(false, |s| s == simp_rcvr_ty) }) { diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index eecded557a5..37783bc91bb 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -148,10 +148,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_ty, op, ); - self.demand_suptype(expr.span, builtin_return_ty, return_ty); + self.demand_eqtype(expr.span, builtin_return_ty, return_ty); + builtin_return_ty + } else { + return_ty } - - return_ty } } } diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 00348f3afdc..af588b16d59 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -748,7 +748,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { .infcx .err_ctxt() .emit_inference_failure_err( - Some(self.body.id()), + self.tcx.hir().body_owner_def_id(self.body.id()), self.span.to_span(self.tcx), p.into(), E0282, diff --git a/compiler/rustc_incremental/locales/en-US.ftl b/compiler/rustc_incremental/messages.ftl similarity index 100% rename from compiler/rustc_incremental/locales/en-US.ftl rename to compiler/rustc_incremental/messages.ftl diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index 511e466c2ae..df958e4a61f 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs @@ -35,4 +35,4 @@ pub use persist::{build_dep_graph, load_dep_graph, DepGraphFuture}; use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage}; use rustc_macros::fluent_messages; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 73d7e3becab..4deae9f41c7 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -297,10 +297,12 @@ pub fn prepare_session_directory( /// renaming it to `s-{timestamp}-{svh}` and releasing the file lock. /// If there have been compilation errors, however, this function will just /// delete the presumably invalid session directory. -pub fn finalize_session_directory(sess: &Session, svh: Svh) { +pub fn finalize_session_directory(sess: &Session, svh: Option) { if sess.opts.incremental.is_none() { return; } + // The svh is always produced when incr. comp. is enabled. + let svh = svh.unwrap(); let _timer = sess.timer("incr_comp_finalize_session_directory"); diff --git a/compiler/rustc_infer/locales/en-US.ftl b/compiler/rustc_infer/messages.ftl similarity index 100% rename from compiler/rustc_infer/locales/en-US.ftl rename to compiler/rustc_infer/messages.ftl diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index a43330e2d4e..8ac82653c0e 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -411,15 +411,28 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { } } - ty::Infer(ty::IntVar(_)) => self.canonicalize_ty_var( - CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Int) }, - t, - ), - - ty::Infer(ty::FloatVar(_)) => self.canonicalize_ty_var( - CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Float) }, - t, - ), + ty::Infer(ty::IntVar(vid)) => { + let nt = self.infcx.opportunistic_resolve_int_var(vid); + if nt != t { + return self.fold_ty(nt); + } else { + self.canonicalize_ty_var( + CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Int) }, + t, + ) + } + } + ty::Infer(ty::FloatVar(vid)) => { + let nt = self.infcx.opportunistic_resolve_float_var(vid); + if nt != t { + return self.fold_ty(nt); + } else { + self.canonicalize_ty_var( + CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Float) }, + t, + ) + } + } ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { bug!("encountered a fresh type during canonicalization") diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index 33292e871b1..a2332797e86 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -34,7 +34,6 @@ use rustc_hir::def_id::DefId; use rustc_middle::infer::canonical::OriginalQueryValues; use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; -use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; @@ -119,20 +118,30 @@ impl<'tcx> InferCtxt<'tcx> { self.unify_float_variable(!a_is_expected, v_id, v) } + // We don't expect `TyVar` or `Fresh*` vars at this point with lazy norm. + ( + ty::Alias(AliasKind::Projection, _), + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)), + ) + | ( + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)), + ty::Alias(AliasKind::Projection, _), + ) if self.tcx.trait_solver_next() => { + bug!() + } + + (_, ty::Alias(AliasKind::Projection, _)) | (ty::Alias(AliasKind::Projection, _), _) + if self.tcx.trait_solver_next() => + { + relation.register_type_equate_obligation(a, b); + Ok(a) + } + // All other cases of inference are errors (&ty::Infer(_), _) | (_, &ty::Infer(_)) => { Err(TypeError::Sorts(ty::relate::expected_found(relation, a, b))) } - (ty::Alias(AliasKind::Projection, _), _) if self.tcx.trait_solver_next() => { - relation.register_type_equate_obligation(a, b); - Ok(b) - } - (_, ty::Alias(AliasKind::Projection, _)) if self.tcx.trait_solver_next() => { - relation.register_type_equate_obligation(b, a); - Ok(a) - } - _ => ty::relate::super_relate_tys(relation, a, b), } } @@ -161,9 +170,9 @@ impl<'tcx> InferCtxt<'tcx> { // // This probe is probably not strictly necessary but it seems better to be safe and not accidentally find // ourselves with a check to find bugs being required for code to compile because it made inference progress. - self.probe(|_| { + let compatible_types = self.probe(|_| { if a.ty() == b.ty() { - return; + return Ok(()); } // We don't have access to trait solving machinery in `rustc_infer` so the logic for determining if the @@ -173,15 +182,24 @@ impl<'tcx> InferCtxt<'tcx> { (relation.param_env(), a.ty(), b.ty()), &mut OriginalQueryValues::default(), ); - - if let Err(NoSolution) = self.tcx.check_tys_might_be_eq(canonical) { + self.tcx.check_tys_might_be_eq(canonical).map_err(|_| { self.tcx.sess.delay_span_bug( DUMMY_SP, &format!("cannot relate consts of different types (a={:?}, b={:?})", a, b,), - ); - } + ) + }) }); + // If the consts have differing types, just bail with a const error with + // the expected const's type. Specifically, we don't want const infer vars + // to do any type shapeshifting before and after resolution. + if let Err(guar) = compatible_types { + return Ok(self.tcx.const_error_with_guaranteed( + if relation.a_is_expected() { a.ty() } else { b.ty() }, + guar, + )); + } + match (a.kind(), b.kind()) { ( ty::ConstKind::Infer(InferConst::Var(a_vid)), diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index a3151d2d365..bde16fad821 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -10,7 +10,7 @@ use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, IntoDiagnosticArg}; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def::{CtorOf, DefKind, Namespace}; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, Local, LocalSource}; use rustc_middle::hir::nested_filter; @@ -386,7 +386,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { #[instrument(level = "debug", skip(self, error_code))] pub fn emit_inference_failure_err( &self, - body_id: Option, + body_def_id: LocalDefId, failure_span: Span, arg: GenericArg<'tcx>, error_code: TypeAnnotationNeeded, @@ -403,8 +403,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }; let mut local_visitor = FindInferSourceVisitor::new(&self, typeck_results, arg); - if let Some(body_id) = body_id { - let expr = self.tcx.hir().expect_expr(body_id.hir_id); + if let Some(body_id) = self.tcx.hir().maybe_body_owned_by( + self.tcx.typeck_root_def_id(body_def_id.to_def_id()).expect_local(), + ) { + let expr = self.tcx.hir().body(body_id).value; local_visitor.visit_expr(expr); } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs index b06ff10d86e..22c1e387117 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -104,7 +104,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let (mention_influencer, influencer_point) = if sup_origin.span().overlaps(param.param_ty_span) { // Account for `async fn` like in `async-await/issues/issue-62097.rs`. - // The desugaring of `async `fn`s causes `sup_origin` and `param` to point at the same + // The desugaring of `async fn`s causes `sup_origin` and `param` to point at the same // place (but with different `ctxt`, hence `overlaps` instead of `==` above). // // This avoids the following: diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 4a2a5557313..4a834957959 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1363,6 +1363,28 @@ impl<'tcx> InferCtxt<'tcx> { self.inner.borrow_mut().const_unification_table().find(var) } + /// Resolves an int var to a rigid int type, if it was constrained to one, + /// or else the root int var in the unification table. + pub fn opportunistic_resolve_int_var(&self, vid: ty::IntVid) -> Ty<'tcx> { + let mut inner = self.inner.borrow_mut(); + if let Some(value) = inner.int_unification_table().probe_value(vid) { + value.to_type(self.tcx) + } else { + self.tcx.mk_int_var(inner.int_unification_table().find(vid)) + } + } + + /// Resolves a float var to a rigid int type, if it was constrained to one, + /// or else the root float var in the unification table. + pub fn opportunistic_resolve_float_var(&self, vid: ty::FloatVid) -> Ty<'tcx> { + let mut inner = self.inner.borrow_mut(); + if let Some(value) = inner.float_unification_table().probe_value(vid) { + value.to_type(self.tcx) + } else { + self.tcx.mk_float_var(inner.float_unification_table().find(vid)) + } + } + /// Where possible, replaces type/const variables in /// `value` with their final value. Note that region variables /// are unaffected. If a type/const variable has not been unified, it diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index bdc313c2141..738a1237651 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -41,4 +41,4 @@ mod errors; pub mod infer; pub mod traits; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index 3a82899660b..77c67c14ecc 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -53,6 +53,12 @@ pub struct Obligation<'tcx, T> { pub recursion_depth: usize, } +impl<'tcx, P> From> for solve::Goal<'tcx, P> { + fn from(value: Obligation<'tcx, P>) -> Self { + solve::Goal { param_env: value.param_env, predicate: value.predicate } + } +} + pub type PredicateObligation<'tcx> = Obligation<'tcx, ty::Predicate<'tcx>>; pub type TraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitPredicate<'tcx>>; diff --git a/compiler/rustc_interface/locales/en-US.ftl b/compiler/rustc_interface/messages.ftl similarity index 100% rename from compiler/rustc_interface/locales/en-US.ftl rename to compiler/rustc_interface/messages.ftl diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index 1abbe8d4fab..15e02671075 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -31,4 +31,4 @@ pub use queries::Queries; #[cfg(test)] mod tests; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 81c1d665ef0..71bdd4df95b 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -11,7 +11,7 @@ use rustc_data_structures::parallel; use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal}; use rustc_errors::PResult; -use rustc_expand::base::{ExtCtxt, LintStoreExpand, ResolverExpand}; +use rustc_expand::base::{ExtCtxt, LintStoreExpand}; use rustc_hir::def_id::{StableCrateId, LOCAL_CRATE}; use rustc_lint::{unerased_lint_store, BufferedEarlyLint, EarlyCheckNode, LintStore}; use rustc_metadata::creader::CStore; @@ -26,7 +26,7 @@ use rustc_plugin_impl as plugin; use rustc_query_impl::{OnDiskCache, Queries as TcxQueries}; use rustc_resolve::Resolver; use rustc_session::config::{CrateType, Input, OutputFilenames, OutputType}; -use rustc_session::cstore::{CrateStoreDyn, MetadataLoader, Untracked}; +use rustc_session::cstore::{MetadataLoader, Untracked}; use rustc_session::output::filename_for_input; use rustc_session::search_paths::PathKind; use rustc_session::{Limit, Session}; @@ -178,7 +178,7 @@ fn configure_and_expand(mut krate: ast::Crate, resolver: &mut Resolver<'_, '_>) let sess = tcx.sess; let lint_store = unerased_lint_store(tcx); let crate_name = tcx.crate_name(LOCAL_CRATE); - pre_expansion_lint(sess, lint_store, resolver.registered_tools(), &krate, crate_name); + pre_expansion_lint(sess, lint_store, tcx.registered_tools(()), &krate, crate_name); rustc_builtin_macros::register_builtin_macros(resolver); krate = sess.time("crate_injection", || { @@ -302,6 +302,16 @@ fn configure_and_expand(mut krate: ast::Crate, resolver: &mut Resolver<'_, '_>) // Done with macro expansion! + resolver.resolve_crate(&krate); + + krate +} + +fn early_lint_checks(tcx: TyCtxt<'_>, (): ()) { + let sess = tcx.sess; + let (resolver, krate) = &*tcx.resolver_for_lowering(()).borrow(); + let mut lint_buffer = resolver.lint_buffer.steal(); + if sess.opts.unstable_opts.input_stats { eprintln!("Post-expansion node count: {}", count_nodes(&krate)); } @@ -310,8 +320,6 @@ fn configure_and_expand(mut krate: ast::Crate, resolver: &mut Resolver<'_, '_>) hir_stats::print_ast_stats(&krate, "POST EXPANSION AST STATS", "ast-stats-2"); } - resolver.resolve_crate(&krate); - // Needs to go *after* expansion to be able to check the results of macro expansion. sess.time("complete_gated_feature_checking", || { rustc_ast_passes::feature_gate::check_crate(&krate, sess); @@ -321,7 +329,7 @@ fn configure_and_expand(mut krate: ast::Crate, resolver: &mut Resolver<'_, '_>) sess.parse_sess.buffered_lints.with_lock(|buffered_lints| { info!("{} parse sess buffered_lints", buffered_lints.len()); for early_lint in buffered_lints.drain(..) { - resolver.lint_buffer().add_early_lint(early_lint); + lint_buffer.add_early_lint(early_lint); } }); @@ -340,20 +348,16 @@ fn configure_and_expand(mut krate: ast::Crate, resolver: &mut Resolver<'_, '_>) } }); - sess.time("early_lint_checks", || { - let lint_buffer = Some(std::mem::take(resolver.lint_buffer())); - rustc_lint::check_ast_node( - sess, - false, - lint_store, - resolver.registered_tools(), - lint_buffer, - rustc_lint::BuiltinCombinedEarlyLintPass::new(), - &krate, - ) - }); - - krate + let lint_store = unerased_lint_store(tcx); + rustc_lint::check_ast_node( + sess, + false, + lint_store, + tcx.registered_tools(()), + Some(lint_buffer), + rustc_lint::BuiltinCombinedEarlyLintPass::new(), + &**krate, + ) } // Returns all the paths that correspond to generated files. @@ -438,13 +442,9 @@ fn escape_dep_env(symbol: Symbol) -> String { escaped } -fn write_out_deps( - sess: &Session, - cstore: &CrateStoreDyn, - outputs: &OutputFilenames, - out_filenames: &[PathBuf], -) { +fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[PathBuf]) { // Write out dependency rules to the dep-info file if requested + let sess = tcx.sess; if !sess.opts.output_types.contains_key(&OutputType::DepInfo) { return; } @@ -492,9 +492,8 @@ fn write_out_deps( } } - let cstore = cstore.as_any().downcast_ref::().unwrap(); - for cnum in cstore.crates_untracked() { - let source = cstore.crate_source_untracked(cnum); + for &cnum in tcx.crates(()) { + let source = tcx.used_crate_source(cnum); if let Some((path, _)) = &source.dylib { files.push(escape_dep_filename(&path.display().to_string())); } @@ -557,6 +556,7 @@ fn resolver_for_lowering<'tcx>( (): (), ) -> &'tcx Steal<(ty::ResolverAstLowering, Lrc)> { let arenas = Resolver::arenas(); + let _ = tcx.registered_tools(()); // Uses `crate_for_resolver`. let krate = tcx.crate_for_resolver(()).steal(); let mut resolver = Resolver::new(tcx, &krate, &arenas); let krate = configure_and_expand(krate, &mut resolver); @@ -607,7 +607,7 @@ fn output_filenames(tcx: TyCtxt<'_>, (): ()) -> Arc { } } - write_out_deps(sess, &*tcx.cstore_untracked(), &outputs, &output_paths); + write_out_deps(tcx, &outputs, &output_paths); let only_dep_info = sess.opts.output_types.contains_key(&OutputType::DepInfo) && sess.opts.output_types.len() == 1; @@ -629,6 +629,7 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock = LazyLock::new(|| { providers.hir_crate = rustc_ast_lowering::lower_to_hir; providers.output_filenames = output_filenames; providers.resolver_for_lowering = resolver_for_lowering; + providers.early_lint_checks = early_lint_checks; proc_macro_decls::provide(providers); rustc_const_eval::provide(providers); rustc_middle::hir::provide(providers); @@ -637,6 +638,7 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock = LazyLock::new(|| { rustc_mir_transform::provide(providers); rustc_monomorphize::provide(providers); rustc_privacy::provide(providers); + rustc_resolve::provide(providers); rustc_hir_analysis::provide(providers); rustc_hir_typeck::provide(providers); ty::provide(providers); diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index a96cc95a384..58ad044b399 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -284,7 +284,11 @@ impl<'tcx> Queries<'tcx> { let codegen_backend = self.codegen_backend().clone(); let (crate_hash, prepare_outputs, dep_graph) = self.global_ctxt()?.enter(|tcx| { - (tcx.crate_hash(LOCAL_CRATE), tcx.output_filenames(()).clone(), tcx.dep_graph.clone()) + ( + if tcx.sess.needs_crate_hash() { Some(tcx.crate_hash(LOCAL_CRATE)) } else { None }, + tcx.output_filenames(()).clone(), + tcx.dep_graph.clone(), + ) }); let ongoing_codegen = self.ongoing_codegen()?.steal(); @@ -308,7 +312,8 @@ pub struct Linker { // compilation outputs dep_graph: DepGraph, prepare_outputs: Arc, - crate_hash: Svh, + // Only present when incr. comp. is enabled. + crate_hash: Option, ongoing_codegen: Box, } diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index e5d2fb2ea28..043892410ce 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -110,7 +110,7 @@ pub fn create_session( add_configuration(&mut cfg, &mut sess, &*codegen_backend); let mut check_cfg = config::to_crate_check_config(check_cfg); - check_cfg.fill_well_known(); + check_cfg.fill_well_known(&sess.target); sess.parse_sess.config = cfg; sess.parse_sess.check_config = check_cfg; diff --git a/compiler/rustc_lexer/src/unescape.rs b/compiler/rustc_lexer/src/unescape.rs index 8507ca9d89e..bb4d91247b8 100644 --- a/compiler/rustc_lexer/src/unescape.rs +++ b/compiler/rustc_lexer/src/unescape.rs @@ -298,10 +298,10 @@ where } let tail = &tail[first_non_space..]; if let Some(c) = tail.chars().nth(0) { - // For error reporting, we would like the span to contain the character that was not - // skipped. The +1 is necessary to account for the leading \ that started the escape. - let end = start + first_non_space + c.len_utf8() + 1; if c.is_whitespace() { + // For error reporting, we would like the span to contain the character that was not + // skipped. The +1 is necessary to account for the leading \ that started the escape. + let end = start + first_non_space + c.len_utf8() + 1; callback(start..end, Err(EscapeError::UnskippedWhitespaceWarning)); } } diff --git a/compiler/rustc_lint/locales/en-US.ftl b/compiler/rustc_lint/messages.ftl similarity index 100% rename from compiler/rustc_lint/locales/en-US.ftl rename to compiler/rustc_lint/messages.ftl diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 2fd0ef3cda8..a14dc20fca3 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -17,6 +17,11 @@ use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; declare_tool_lint! { + /// The `default_hash_type` lint detects use of [`std::collections::HashMap`]/[`std::collections::HashSet`], + /// suggesting the use of `FxHashMap`/`FxHashSet`. + /// + /// This can help as `FxHasher` can perform better than the default hasher. DOS protection is not + /// required as input is assumed to be trusted. pub rustc::DEFAULT_HASH_TYPES, Allow, "forbid HashMap and HashSet and suggest the FxHash* variants", @@ -67,6 +72,12 @@ fn typeck_results_of_method_fn<'tcx>( } declare_tool_lint! { + /// The `potential_query_instability` lint detects use of methods which can lead to + /// potential query instability, such as iterating over a `HashMap`. + /// + /// Due to the [incremental compilation](https://rustc-dev-guide.rust-lang.org/queries/incremental-compilation.html) model, + /// queries must return deterministic, stable results. `HashMap` iteration order can change between compilations, + /// and will introduce instability if query results expose the order. pub rustc::POTENTIAL_QUERY_INSTABILITY, Allow, "require explicit opt-in when using potentially unstable methods or functions", @@ -92,6 +103,8 @@ impl LateLintPass<'_> for QueryStability { } declare_tool_lint! { + /// The `usage_of_ty_tykind` lint detects usages of `ty::TyKind::`, + /// where `ty::` would suffice. pub rustc::USAGE_OF_TY_TYKIND, Allow, "usage of `ty::TyKind` outside of the `ty::sty` module", @@ -99,6 +112,8 @@ declare_tool_lint! { } declare_tool_lint! { + /// The `usage_of_qualified_ty` lint detects usages of `ty::TyKind`, + /// where `Ty` should be used instead. pub rustc::USAGE_OF_QUALIFIED_TY, Allow, "using `ty::{Ty,TyCtxt}` instead of importing it", @@ -254,6 +269,8 @@ fn gen_args(segment: &PathSegment<'_>) -> String { } declare_tool_lint! { + /// The `lint_pass_impl_without_macro` detects manual implementations of a lint + /// pass, without using [`declare_lint_pass`] or [`impl_lint_pass`]. pub rustc::LINT_PASS_IMPL_WITHOUT_MACRO, Allow, "`impl LintPass` without the `declare_lint_pass!` or `impl_lint_pass!` macros" @@ -285,6 +302,8 @@ impl EarlyLintPass for LintPassImpl { } declare_tool_lint! { + /// The `existing_doc_keyword` lint detects use `#[doc()]` keywords + /// that don't exist, e.g. `#[doc(keyword = "..")]`. pub rustc::EXISTING_DOC_KEYWORD, Allow, "Check that documented keywords in std and core actually exist", @@ -325,6 +344,10 @@ impl<'tcx> LateLintPass<'tcx> for ExistingDocKeyword { } declare_tool_lint! { + /// The `untranslatable_diagnostic` lint detects diagnostics created + /// without using translatable Fluent strings. + /// + /// More details on translatable diagnostics can be found [here](https://rustc-dev-guide.rust-lang.org/diagnostics/translation.html). pub rustc::UNTRANSLATABLE_DIAGNOSTIC, Allow, "prevent creation of diagnostics which cannot be translated", @@ -332,6 +355,11 @@ declare_tool_lint! { } declare_tool_lint! { + /// The `diagnostic_outside_of_impl` lint detects diagnostics created manually, + /// and inside an `IntoDiagnostic`/`AddToDiagnostic` implementation, + /// or a `#[derive(Diagnostic)]`/`#[derive(Subdiagnostic)]` expansion. + /// + /// More details on diagnostics implementations can be found [here](https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-structs.html). pub rustc::DIAGNOSTIC_OUTSIDE_OF_IMPL, Allow, "prevent creation of diagnostics outside of `IntoDiagnostic`/`AddToDiagnostic` impls", @@ -396,6 +424,8 @@ impl LateLintPass<'_> for Diagnostics { } declare_tool_lint! { + /// The `bad_opt_access` lint detects accessing options by field instad of + /// the wrapper function. pub rustc::BAD_OPT_ACCESS, Deny, "prevent using options by field access when there is a wrapper function", diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index bc7488fab4a..a76229dd352 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -128,7 +128,7 @@ fn lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(LintExpectationId, LintExp }, warn_about_weird_lints: false, store, - registered_tools: &tcx.resolutions(()).registered_tools, + registered_tools: &tcx.registered_tools(()), }; builder.add_command_line(); @@ -156,7 +156,7 @@ fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLe }, warn_about_weird_lints: false, store, - registered_tools: &tcx.resolutions(()).registered_tools, + registered_tools: &tcx.registered_tools(()), }; if owner == hir::CRATE_OWNER_ID { diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 35dc533e56c..b3578540516 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -126,7 +126,7 @@ pub use rustc_session::lint::Level::{self, *}; pub use rustc_session::lint::{BufferedEarlyLint, FutureIncompatibleInfo, Lint, LintId}; pub use rustc_session::lint::{LintArray, LintPass}; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } pub fn provide(providers: &mut Providers) { levels::provide(providers); diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index c43162f6325..2fab82d55cd 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1349,9 +1349,8 @@ declare_lint! { /// ### Example /// /// ```rust - /// #![feature(box_syntax)] /// fn main() { - /// let a = (box [1, 2, 3]).len(); + /// let a = Box::new([1, 2, 3]).len(); /// } /// ``` /// @@ -1372,7 +1371,11 @@ declare_lint_pass!(UnusedAllocation => [UNUSED_ALLOCATION]); impl<'tcx> LateLintPass<'tcx> for UnusedAllocation { fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) { match e.kind { - hir::ExprKind::Box(_) => {} + hir::ExprKind::Call(path_expr, [_]) + if let hir::ExprKind::Path(qpath) = &path_expr.kind + && let Some(did) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id() + && cx.tcx.is_diagnostic_item(sym::box_new, did) + => {} _ => return, } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 534aff7fb62..6f22bdabff4 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -8,7 +8,7 @@ extern crate rustc_macros; pub use self::Level::*; use rustc_ast::node_id::NodeId; use rustc_ast::{AttrId, Attribute}; -use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; use rustc_error_messages::{DiagnosticMessage, MultiSpan}; use rustc_hir::HashStableContext; @@ -533,6 +533,7 @@ pub enum BuiltinLintDiagnostics { /// Lints that are buffered up early on in the `Session` before the /// `LintLevels` is calculated. +#[derive(Debug)] pub struct BufferedEarlyLint { /// The span of code that we are linting on. pub span: MultiSpan, @@ -551,7 +552,7 @@ pub struct BufferedEarlyLint { pub diagnostic: BuiltinLintDiagnostics, } -#[derive(Default)] +#[derive(Default, Debug)] pub struct LintBuffer { pub map: FxIndexMap>, } @@ -601,6 +602,8 @@ impl LintBuffer { } } +pub type RegisteredTools = FxIndexSet; + /// Declares a static item of type `&'static Lint`. /// /// See for diff --git a/compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp index 974207e918c..0493d6b05d0 100644 --- a/compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp @@ -9,7 +9,6 @@ #include "llvm/IR/LLVMContext.h" #include "llvm/Object/ObjectFile.h" -#include "llvm/ADT/Optional.h" using namespace llvm; using namespace llvm::sys; diff --git a/compiler/rustc_metadata/locales/en-US.ftl b/compiler/rustc_metadata/messages.ftl similarity index 100% rename from compiler/rustc_metadata/locales/en-US.ftl rename to compiler/rustc_metadata/messages.ftl diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index b05626311e8..f870a1db82d 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -8,7 +8,7 @@ use rustc_ast::expand::allocator::AllocatorKind; use rustc_ast::{self as ast, *}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::svh::Svh; -use rustc_data_structures::sync::MappedReadGuard; +use rustc_data_structures::sync::{MappedReadGuard, MappedWriteGuard, ReadGuard, WriteGuard}; use rustc_expand::base::SyntaxExtension; use rustc_hir::def_id::{CrateNum, LocalDefId, StableCrateId, LOCAL_CRATE}; use rustc_hir::definitions::Definitions; @@ -133,8 +133,14 @@ impl<'a> std::fmt::Debug for CrateDump<'a> { impl CStore { pub fn from_tcx(tcx: TyCtxt<'_>) -> MappedReadGuard<'_, CStore> { - MappedReadGuard::map(tcx.cstore_untracked(), |c| { - c.as_any().downcast_ref::().expect("`tcx.cstore` is not a `CStore`") + ReadGuard::map(tcx.untracked().cstore.read(), |cstore| { + cstore.as_any().downcast_ref::().expect("`tcx.cstore` is not a `CStore`") + }) + } + + pub fn from_tcx_mut(tcx: TyCtxt<'_>) -> MappedWriteGuard<'_, CStore> { + WriteGuard::map(tcx.untracked().cstore.write(), |cstore| { + cstore.untracked_as_any().downcast_mut().expect("`tcx.cstore` is not a `CStore`") }) } @@ -268,9 +274,6 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { ) -> Self { CrateLoader { tcx, cstore, used_extern_options } } - pub fn cstore(&self) -> &CStore { - &self.cstore - } fn existing_match(&self, name: Symbol, hash: Option, kind: PathKind) -> Option { for (cnum, data) in self.cstore.iter_crate_data() { diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs index f6431899731..08de828fbdb 100644 --- a/compiler/rustc_metadata/src/fs.rs +++ b/compiler/rustc_metadata/src/fs.rs @@ -6,9 +6,9 @@ use crate::{encode_metadata, EncodedMetadata}; use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::ty::TyCtxt; -use rustc_session::config::{CrateType, OutputType}; +use rustc_session::config::OutputType; use rustc_session::output::filename_for_metadata; -use rustc_session::Session; +use rustc_session::{MetadataKind, Session}; use tempfile::Builder as TempFileBuilder; use std::fs; @@ -39,27 +39,6 @@ pub fn emit_wrapper_file( } pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) { - #[derive(PartialEq, Eq, PartialOrd, Ord)] - enum MetadataKind { - None, - Uncompressed, - Compressed, - } - - let metadata_kind = tcx - .sess - .crate_types() - .iter() - .map(|ty| match *ty { - CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => MetadataKind::None, - - CrateType::Rlib => MetadataKind::Uncompressed, - - CrateType::Dylib | CrateType::ProcMacro => MetadataKind::Compressed, - }) - .max() - .unwrap_or(MetadataKind::None); - let crate_name = tcx.crate_name(LOCAL_CRATE); let out_filename = filename_for_metadata(tcx.sess, crate_name, tcx.output_filenames(())); // To avoid races with another rustc process scanning the output directory, @@ -76,6 +55,7 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) { // Always create a file at `metadata_filename`, even if we have nothing to write to it. // This simplifies the creation of the output `out_filename` when requested. + let metadata_kind = tcx.sess.metadata_kind(); match metadata_kind { MetadataKind::None => { std::fs::File::create(&metadata_filename).unwrap_or_else(|err| { diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index 6f6d3731cea..98d9ad31fe0 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -47,4 +47,4 @@ pub use fs::{emit_wrapper_file, METADATA_FILENAME}; pub use native_libs::find_native_static_library; pub use rmeta::{encode_metadata, EncodedMetadata, METADATA_HEADER}; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index b1e59b0a470..ca26e1497aa 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -30,7 +30,6 @@ use rustc_session::cstore::{ }; use rustc_session::Session; use rustc_span::hygiene::ExpnIndex; -use rustc_span::source_map::{respan, Spanned}; use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::{self, BytePos, ExpnId, Pos, Span, SyntaxContext, DUMMY_SP}; @@ -311,8 +310,11 @@ impl LazyArray { impl<'a, 'tcx> DecodeContext<'a, 'tcx> { #[inline] fn tcx(&self) -> TyCtxt<'tcx> { - debug_assert!(self.tcx.is_some(), "missing TyCtxt in DecodeContext"); - self.tcx.unwrap() + let Some(tcx) = self.tcx else { + bug!("No TyCtxt found for decoding. \ + You need to explicitly pass `(crate_metadata_ref, tcx)` to `decode` instead of just `crate_metadata_ref`."); + }; + tcx } #[inline] @@ -454,7 +456,12 @@ impl<'a, 'tcx> Decodable> for ast::AttrId { impl<'a, 'tcx> Decodable> for SyntaxContext { fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> SyntaxContext { let cdata = decoder.cdata(); - let sess = decoder.sess.unwrap(); + + let Some(sess) = decoder.sess else { + bug!("Cannot decode SyntaxContext without Session.\ + You need to explicitly pass `(crate_metadata_ref, tcx)` to `decode` instead of just `crate_metadata_ref`."); + }; + let cname = cdata.root.name; rustc_span::hygiene::decode_syntax_context(decoder, &cdata.hygiene_context, |_, id| { debug!("SpecializedDecoder: decoding {}", id); @@ -471,7 +478,11 @@ impl<'a, 'tcx> Decodable> for SyntaxContext { impl<'a, 'tcx> Decodable> for ExpnId { fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> ExpnId { let local_cdata = decoder.cdata(); - let sess = decoder.sess.unwrap(); + + let Some(sess) = decoder.sess else { + bug!("Cannot decode ExpnId without Session. \ + You need to explicitly pass `(crate_metadata_ref, tcx)` to `decode` instead of just `crate_metadata_ref`."); + }; let cnum = CrateNum::decode(decoder); let index = u32::decode(decoder); @@ -520,7 +531,8 @@ impl<'a, 'tcx> Decodable> for Span { let hi = lo + len; let Some(sess) = decoder.sess else { - bug!("Cannot decode Span without Session.") + bug!("Cannot decode Span without Session. \ + You need to explicitly pass `(crate_metadata_ref, tcx)` to `decode` instead of just `crate_metadata_ref`.") }; // Index of the file in the corresponding crate's list of encoded files. @@ -917,7 +929,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { self.root.tables.generics_of.get(self, item_id).unwrap().decode((self, sess)) } - fn get_visibility(self, id: DefIndex) -> ty::Visibility { + fn get_visibility(self, id: DefIndex) -> Visibility { self.root .tables .visibility @@ -1083,6 +1095,8 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { trait_item_def_id: self.get_trait_item_def_id(id), container, fn_has_self_parameter: has_self, + // FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): We need to encode this + opt_rpitit_info: None, } } @@ -1121,33 +1135,6 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .decode((self, sess)) } - fn get_struct_field_names( - self, - id: DefIndex, - sess: &'a Session, - ) -> impl Iterator> + 'a { - self.root - .tables - .children - .get(self, id) - .expect("fields not encoded for a struct") - .decode(self) - .map(move |index| respan(self.get_span(index, sess), self.item_name(index))) - } - - fn get_struct_field_visibilities( - self, - id: DefIndex, - ) -> impl Iterator> + 'a { - self.root - .tables - .children - .get(self, id) - .expect("fields not encoded for a struct") - .decode(self) - .map(move |field_index| self.get_visibility(field_index)) - } - fn get_inherent_implementations_for_type( self, tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 83a0e833edc..6c5e8863010 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -15,12 +15,12 @@ use rustc_middle::middle::exported_symbols::ExportedSymbol; use rustc_middle::middle::stability::DeprecationEntry; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::query::{ExternProviders, Providers}; -use rustc_middle::ty::{self, TyCtxt, Visibility}; -use rustc_session::cstore::{CrateSource, CrateStore}; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_session::cstore::CrateStore; use rustc_session::{Session, StableCrateId}; use rustc_span::hygiene::{ExpnHash, ExpnId}; -use rustc_span::source_map::{Span, Spanned}; use rustc_span::symbol::{kw, Symbol}; +use rustc_span::Span; use rustc_data_structures::sync::Lrc; use std::any::Any; @@ -501,35 +501,18 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { tcx.arena .alloc_slice(&CStore::from_tcx(tcx).crate_dependencies_in_postorder(LOCAL_CRATE)) }, - crates: |tcx, ()| tcx.arena.alloc_from_iter(CStore::from_tcx(tcx).crates_untracked()), + crates: |tcx, ()| { + tcx.arena.alloc_from_iter(CStore::from_tcx(tcx).iter_crate_data().map(|(cnum, _)| cnum)) + }, ..*providers }; } impl CStore { - pub fn struct_field_names_untracked<'a>( - &'a self, - def: DefId, - sess: &'a Session, - ) -> impl Iterator> + 'a { - self.get_crate_data(def.krate).get_struct_field_names(def.index, sess) - } - - pub fn struct_field_visibilities_untracked( - &self, - def: DefId, - ) -> impl Iterator> + '_ { - self.get_crate_data(def.krate).get_struct_field_visibilities(def.index) - } - pub fn ctor_untracked(&self, def: DefId) -> Option<(CtorKind, DefId)> { self.get_crate_data(def.krate).get_ctor(def.index) } - pub fn visibility_untracked(&self, def: DefId) -> Visibility { - self.get_crate_data(def.krate).get_visibility(def.index) - } - pub fn module_children_untracked<'a>( &'a self, def_id: DefId, @@ -566,14 +549,6 @@ impl CStore { ) } - pub fn fn_has_self_parameter_untracked(&self, def: DefId, sess: &Session) -> bool { - self.get_crate_data(def.krate).get_fn_has_self_parameter(def.index, sess) - } - - pub fn crate_source_untracked(&self, cnum: CrateNum) -> Lrc { - self.get_crate_data(cnum).source.clone() - } - pub fn get_span_untracked(&self, def_id: DefId, sess: &Session) -> Span { self.get_crate_data(def_id.krate).get_span(def_id.index, sess) } @@ -582,10 +557,6 @@ impl CStore { self.get_crate_data(def.krate).def_kind(def.index) } - pub fn crates_untracked(&self) -> impl Iterator + '_ { - self.iter_crate_data().map(|(cnum, _)| cnum) - } - pub fn item_generics_num_lifetimes(&self, def_id: DefId, sess: &Session) -> usize { self.get_crate_data(def_id.krate).get_generics(def_id.index, sess).own_counts().lifetimes } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 3ab01f7809b..bbab8a62a2b 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -26,7 +26,7 @@ use rustc_middle::middle::exported_symbols::{ use rustc_middle::mir::interpret; use rustc_middle::traits::specialization_graph; use rustc_middle::ty::codec::TyEncoder; -use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams}; +use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams, TreatProjections}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, SymbolName, Ty, TyCtxt}; use rustc_middle::util::common::to_readable_str; @@ -1104,7 +1104,7 @@ fn should_encode_const(def_kind: DefKind) -> bool { // We only encode impl trait in trait when using `lower-impl-trait-in-trait-to-assoc-ty` unstable // option. fn should_encode_fn_impl_trait_in_trait<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { - if tcx.sess.opts.unstable_opts.lower_impl_trait_in_trait_to_assoc_ty + if tcx.lower_impl_trait_in_trait_to_assoc_ty() && let Some(assoc_item) = tcx.opt_associated_item(def_id) && assoc_item.container == ty::AssocItemContainer::TraitContainer && assoc_item.kind == ty::AssocKind::Fn @@ -1858,7 +1858,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let simplified_self_ty = fast_reject::simplify_type( self.tcx, trait_ref.self_ty(), - TreatParams::AsInfer, + TreatParams::AsCandidateKey, + TreatProjections::AsCandidateKey, ); fx_hash_map @@ -2050,13 +2051,13 @@ fn prefetch_mir(tcx: TyCtxt<'_>) { let (encode_const, encode_opt) = should_encode_mir(tcx, def_id); if encode_const { - tcx.ensure().mir_for_ctfe(def_id); + tcx.ensure_with_value().mir_for_ctfe(def_id); } if encode_opt { - tcx.ensure().optimized_mir(def_id); + tcx.ensure_with_value().optimized_mir(def_id); } if encode_opt || encode_const { - tcx.ensure().promoted_mir(def_id); + tcx.ensure_with_value().promoted_mir(def_id); } }) } diff --git a/compiler/rustc_middle/locales/en-US.ftl b/compiler/rustc_middle/messages.ftl similarity index 100% rename from compiler/rustc_middle/locales/en-US.ftl rename to compiler/rustc_middle/messages.ftl diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 55ea78c0474..72907fba5e6 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -108,6 +108,7 @@ macro_rules! arena_types { // (during lowering) and the `librustc_middle` arena (for decoding MIR) [decode] asm_template: rustc_ast::InlineAsmTemplatePiece, [decode] used_trait_imports: rustc_data_structures::unord::UnordSet, + [decode] registered_tools: rustc_middle::ty::RegisteredTools, [decode] is_late_bound_map: rustc_data_structures::fx::FxIndexSet, [decode] impl_source: rustc_middle::traits::ImplSource<'tcx, ()>, diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 43eef1c770c..746cf488589 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -1134,7 +1134,7 @@ impl<'hir> intravisit::Map<'hir> for Map<'hir> { pub(super) fn crate_hash(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Svh { debug_assert_eq!(crate_num, LOCAL_CRATE); let krate = tcx.hir_crate(()); - let hir_body_hash = krate.hir_hash; + let hir_body_hash = krate.opt_hir_hash.expect("HIR hash missing while computing crate hash"); let upstream_crates = upstream_crates(tcx); diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 6706b9db3f5..403b2b65088 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -8,7 +8,6 @@ pub mod place; use crate::ty::query::Providers; use crate::ty::{ImplSubject, TyCtxt}; -use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{par_for_each_in, Send, Sync}; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -24,14 +23,15 @@ use rustc_span::{ExpnId, DUMMY_SP}; #[derive(Copy, Clone, Debug)] pub struct Owner<'tcx> { node: OwnerNode<'tcx>, - hash_without_bodies: Fingerprint, } impl<'a, 'tcx> HashStable> for Owner<'tcx> { #[inline] fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - let Owner { node: _, hash_without_bodies } = self; - hash_without_bodies.hash_stable(hcx, hasher) + // Perform a shallow hash instead using the deep hash saved in `OwnerNodes`. This lets us + // differentiate queries that depend on the full HIR tree from those that only depend on + // the item signature. + hcx.without_hir_bodies(|hcx| self.node.hash_stable(hcx, hasher)); } } @@ -123,7 +123,7 @@ pub fn provide(providers: &mut Providers) { providers.hir_owner = |tcx, id| { let owner = tcx.hir_crate(()).owners.get(id.def_id)?.as_owner()?; let node = owner.node(); - Some(Owner { node, hash_without_bodies: owner.nodes.hash_without_bodies }) + Some(Owner { node }) }; providers.opt_local_def_id_to_hir_id = |tcx, id| { let owner = tcx.hir_crate(()).owners[id].map(|_| ()); @@ -177,7 +177,6 @@ pub fn provide(providers: &mut Providers) { } }; providers.opt_def_kind = |tcx, def_id| tcx.hir().opt_def_kind(def_id.expect_local()); - providers.opt_rpitit_info = |_, _| None; providers.all_local_trait_impls = |tcx, ()| &tcx.resolutions(()).trait_impls; providers.expn_that_defined = |tcx, id| { let id = id.expect_local(); diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index c33b9d84eb0..45c4a1057d2 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -109,4 +109,4 @@ pub mod util { // Allows macros to refer to this crate as `::rustc_middle` extern crate self as rustc_middle; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 5215e3db798..7d515bb0f5a 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -401,8 +401,6 @@ impl<'tcx> Body<'tcx> { LocalKind::ReturnPointer } else if index < self.arg_count + 1 { LocalKind::Arg - } else if self.local_decls[local].is_user_variable() { - LocalKind::Var } else { LocalKind::Temp } @@ -572,6 +570,13 @@ impl ClearCrossCrate { } } + pub fn as_mut(&mut self) -> ClearCrossCrate<&mut T> { + match self { + ClearCrossCrate::Clear => ClearCrossCrate::Clear, + ClearCrossCrate::Set(v) => ClearCrossCrate::Set(v), + } + } + pub fn assert_crate_local(self) -> T { match self { ClearCrossCrate::Clear => bug!("unwrapping cross-crate data"), @@ -661,9 +666,7 @@ impl Atom for Local { /// Classifies locals into categories. See `Body::local_kind`. #[derive(Clone, Copy, PartialEq, Eq, Debug, HashStable)] pub enum LocalKind { - /// User-declared variable binding. - Var, - /// Compiler-introduced temporary. + /// User-declared variable binding or compiler-introduced temporary. Temp, /// Function argument. Arg, @@ -760,7 +763,7 @@ pub struct LocalDecl<'tcx> { pub mutability: Mutability, // FIXME(matthewjasper) Don't store in this in `Body` - pub local_info: Option>>, + pub local_info: ClearCrossCrate>>, /// `true` if this is an internal local. /// @@ -778,13 +781,6 @@ pub struct LocalDecl<'tcx> { /// generator. pub internal: bool, - /// If this local is a temporary and `is_block_tail` is `Some`, - /// then it is a temporary created for evaluation of some - /// subexpression of some block's tail expression (with no - /// intervening statement context). - // FIXME(matthewjasper) Don't store in this in `Body` - pub is_block_tail: Option, - /// The type of this local. pub ty: Ty<'tcx>, @@ -890,7 +886,7 @@ pub enum LocalInfo<'tcx> { /// The `BindingForm` is solely used for local diagnostics when generating /// warnings/errors when compiling the current crate, and therefore it need /// not be visible across crates. - User(ClearCrossCrate>), + User(BindingForm<'tcx>), /// A temporary created that references the static with the given `DefId`. StaticRef { def_id: DefId, is_thread_local: bool }, /// A temporary created that references the const with the given `DefId` @@ -898,13 +894,23 @@ pub enum LocalInfo<'tcx> { /// A temporary created during the creation of an aggregate /// (e.g. a temporary for `foo` in `MyStruct { my_field: foo }`) AggregateTemp, + /// A temporary created for evaluation of some subexpression of some block's tail expression + /// (with no intervening statement context). + // FIXME(matthewjasper) Don't store in this in `Body` + BlockTailTemp(BlockTailInfo), /// A temporary created during the pass `Derefer` to avoid it's retagging DerefTemp, /// A temporary created for borrow checking. FakeBorrow, + /// A local without anything interesting about it. + Boring, } impl<'tcx> LocalDecl<'tcx> { + pub fn local_info(&self) -> &LocalInfo<'tcx> { + &**self.local_info.as_ref().assert_crate_local() + } + /// Returns `true` only if local is a binding that can itself be /// made mutable via the addition of the `mut` keyword, namely /// something like the occurrences of `x` in: @@ -913,15 +919,15 @@ impl<'tcx> LocalDecl<'tcx> { /// - or `match ... { C(x) => ... }` pub fn can_be_made_mutable(&self) -> bool { matches!( - self.local_info, - Some(box LocalInfo::User(ClearCrossCrate::Set( + self.local_info(), + LocalInfo::User( BindingForm::Var(VarBindingForm { binding_mode: ty::BindingMode::BindByValue(_), opt_ty_info: _, opt_match_place: _, pat_span: _, }) | BindingForm::ImplicitSelf(ImplicitSelfKind::Imm), - ))) + ) ) } @@ -930,15 +936,15 @@ impl<'tcx> LocalDecl<'tcx> { /// mutable bindings, but the inverse does not necessarily hold). pub fn is_nonref_binding(&self) -> bool { matches!( - self.local_info, - Some(box LocalInfo::User(ClearCrossCrate::Set( + self.local_info(), + LocalInfo::User( BindingForm::Var(VarBindingForm { binding_mode: ty::BindingMode::BindByValue(_), opt_ty_info: _, opt_match_place: _, pat_span: _, }) | BindingForm::ImplicitSelf(_), - ))) + ) ) } @@ -946,38 +952,35 @@ impl<'tcx> LocalDecl<'tcx> { /// parameter declared by the user. #[inline] pub fn is_user_variable(&self) -> bool { - matches!(self.local_info, Some(box LocalInfo::User(_))) + matches!(self.local_info(), LocalInfo::User(_)) } /// Returns `true` if this is a reference to a variable bound in a `match` /// expression that is used to access said variable for the guard of the /// match arm. pub fn is_ref_for_guard(&self) -> bool { - matches!( - self.local_info, - Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::RefForGuard))) - ) + matches!(self.local_info(), LocalInfo::User(BindingForm::RefForGuard)) } /// Returns `Some` if this is a reference to a static item that is used to /// access that static. pub fn is_ref_to_static(&self) -> bool { - matches!(self.local_info, Some(box LocalInfo::StaticRef { .. })) + matches!(self.local_info(), LocalInfo::StaticRef { .. }) } /// Returns `Some` if this is a reference to a thread-local static item that is used to /// access that static. pub fn is_ref_to_thread_local(&self) -> bool { - match self.local_info { - Some(box LocalInfo::StaticRef { is_thread_local, .. }) => is_thread_local, + match self.local_info() { + LocalInfo::StaticRef { is_thread_local, .. } => *is_thread_local, _ => false, } } /// Returns `true` if this is a DerefTemp pub fn is_deref_temp(&self) -> bool { - match self.local_info { - Some(box LocalInfo::DerefTemp) => return true, + match self.local_info() { + LocalInfo::DerefTemp => return true, _ => (), } return false; @@ -1001,9 +1004,8 @@ impl<'tcx> LocalDecl<'tcx> { pub fn with_source_info(ty: Ty<'tcx>, source_info: SourceInfo) -> Self { LocalDecl { mutability: Mutability::Mut, - local_info: None, + local_info: ClearCrossCrate::Set(Box::new(LocalInfo::Boring)), internal: false, - is_block_tail: None, ty, user_ty: None, source_info, @@ -1023,14 +1025,6 @@ impl<'tcx> LocalDecl<'tcx> { self.mutability = Mutability::Not; self } - - /// Converts `self` into same `LocalDecl` except tagged as internal temporary. - #[inline] - pub fn block_tail(mut self, info: BlockTailInfo) -> Self { - assert!(self.is_block_tail.is_none()); - self.is_block_tail = Some(info); - self - } } #[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] @@ -1999,6 +1993,9 @@ impl BorrowKind { } impl BinOp { + /// The checkable operators are those whose overflow checking behavior is controlled by + /// -Coverflow-checks option. The remaining operators have either no overflow conditions (e.g., + /// BitAnd, BitOr, BitXor) or are always checked for overflow (e.g., Div, Rem). pub fn is_checkable(self) -> bool { use self::BinOp::*; matches!(self, Add | Sub | Mul | Shl | Shr) @@ -3088,7 +3085,7 @@ mod size_asserts { use rustc_data_structures::static_assert_size; // tidy-alphabetical-start static_assert_size!(BasicBlockData<'_>, 144); - static_assert_size!(LocalDecl<'_>, 56); + static_assert_size!(LocalDecl<'_>, 40); static_assert_size!(Statement<'_>, 32); static_assert_size!(StatementKind<'_>, 16); static_assert_size!(Terminator<'_>, 112); diff --git a/compiler/rustc_middle/src/mir/patch.rs b/compiler/rustc_middle/src/mir/patch.rs index 24fe3b47256..eb860c04de2 100644 --- a/compiler/rustc_middle/src/mir/patch.rs +++ b/compiler/rustc_middle/src/mir/patch.rs @@ -72,12 +72,12 @@ impl<'tcx> MirPatch<'tcx> { &mut self, ty: Ty<'tcx>, span: Span, - local_info: Option>>, + local_info: LocalInfo<'tcx>, ) -> Local { let index = self.next_local; self.next_local += 1; let mut new_decl = LocalDecl::new(ty, span).internal(); - new_decl.local_info = local_info; + **new_decl.local_info.as_mut().assert_crate_local() = local_info; self.new_locals.push(new_decl); Local::new(index as usize) } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index d8829e3e782..7e51953599d 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -123,6 +123,7 @@ fn dump_matched_mir_node<'tcx, F>( // see notes on #41697 above let def_path = ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id())); + // ignore-tidy-odd-backticks the literal below is fine write!(file, "// MIR for `{}", def_path)?; match body.source.promoted { None => write!(file, "`")?, diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index cbeacf21c19..cffdd7ff37f 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -804,7 +804,6 @@ macro_rules! make_mir_visitor { source_info, internal: _, local_info: _, - is_block_tail: _, } = local_decl; self.visit_ty($(& $mutability)? *ty, TyContext::LocalDecl { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index f15d71ba794..75f05c4af23 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -26,6 +26,15 @@ rustc_queries! { desc { "triggering a delay span bug" } } + query registered_tools(_: ()) -> &'tcx ty::RegisteredTools { + arena_cache + desc { "compute registered tools for crate" } + } + + query early_lint_checks(_: ()) -> () { + desc { "perform lints prior to macro expansion" } + } + query resolutions(_: ()) -> &'tcx ty::ResolverGlobalCtxt { feedable no_hash @@ -1152,14 +1161,6 @@ rustc_queries! { feedable } - /// The `opt_rpitit_info` query returns the pair of the def id of the function where the RPIT - /// is defined and the opaque def id if any. - query opt_rpitit_info(def_id: DefId) -> Option { - desc { |tcx| "opt_rpitit_info `{}`", tcx.def_path_str(def_id) } - cache_on_disk_if { def_id.is_local() } - feedable - } - /// Gets the span for the definition. query def_span(def_id: DefId) -> Span { desc { |tcx| "looking up span for `{}`", tcx.def_path_str(def_id) } @@ -1324,6 +1325,7 @@ rustc_queries! { /// might want to use `reveal_all()` method to change modes. query param_env(def_id: DefId) -> ty::ParamEnv<'tcx> { desc { |tcx| "computing normalized predicates of `{}`", tcx.def_path_str(def_id) } + feedable } /// Like `param_env`, but returns the `ParamEnv` in `Reveal::All` mode. diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index bd43867a3da..92d3e73e683 100644 --- a/compiler/rustc_middle/src/traits/solve.rs +++ b/compiler/rustc_middle/src/traits/solve.rs @@ -1,12 +1,104 @@ use std::ops::ControlFlow; use rustc_data_structures::intern::Interned; +use rustc_query_system::cache::Cache; -use crate::infer::canonical::QueryRegionConstraints; +use crate::infer::canonical::{CanonicalVarValues, QueryRegionConstraints}; +use crate::traits::query::NoSolution; +use crate::traits::Canonical; use crate::ty::{ - FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, + self, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable, + TypeVisitor, }; +pub type EvaluationCache<'tcx> = Cache, QueryResult<'tcx>>; + +/// A goal is a statement, i.e. `predicate`, we want to prove +/// given some assumptions, i.e. `param_env`. +/// +/// Most of the time the `param_env` contains the `where`-bounds of the function +/// we're currently typechecking while the `predicate` is some trait bound. +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)] +pub struct Goal<'tcx, P> { + pub param_env: ty::ParamEnv<'tcx>, + pub predicate: P, +} + +impl<'tcx, P> Goal<'tcx, P> { + pub fn new( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + predicate: impl ToPredicate<'tcx, P>, + ) -> Goal<'tcx, P> { + Goal { param_env, predicate: predicate.to_predicate(tcx) } + } + + /// Updates the goal to one with a different `predicate` but the same `param_env`. + pub fn with(self, tcx: TyCtxt<'tcx>, predicate: impl ToPredicate<'tcx, Q>) -> Goal<'tcx, Q> { + Goal { param_env: self.param_env, predicate: predicate.to_predicate(tcx) } + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)] +pub struct Response<'tcx> { + pub var_values: CanonicalVarValues<'tcx>, + /// Additional constraints returned by this query. + pub external_constraints: ExternalConstraints<'tcx>, + pub certainty: Certainty, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)] +pub enum Certainty { + Yes, + Maybe(MaybeCause), +} + +impl Certainty { + pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity); + + /// When proving multiple goals using **AND**, e.g. nested obligations for an impl, + /// use this function to unify the certainty of these goals + pub fn unify_and(self, other: Certainty) -> Certainty { + match (self, other) { + (Certainty::Yes, Certainty::Yes) => Certainty::Yes, + (Certainty::Yes, Certainty::Maybe(_)) => other, + (Certainty::Maybe(_), Certainty::Yes) => self, + (Certainty::Maybe(MaybeCause::Overflow), Certainty::Maybe(MaybeCause::Overflow)) => { + Certainty::Maybe(MaybeCause::Overflow) + } + // If at least one of the goals is ambiguous, hide the overflow as the ambiguous goal + // may still result in failure. + (Certainty::Maybe(MaybeCause::Ambiguity), Certainty::Maybe(_)) + | (Certainty::Maybe(_), Certainty::Maybe(MaybeCause::Ambiguity)) => { + Certainty::Maybe(MaybeCause::Ambiguity) + } + } + } +} + +/// Why we failed to evaluate a goal. +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)] +pub enum MaybeCause { + /// We failed due to ambiguity. This ambiguity can either + /// be a true ambiguity, i.e. there are multiple different answers, + /// or we hit a case where we just don't bother, e.g. `?x: Trait` goals. + Ambiguity, + /// We gave up due to an overflow, most often by hitting the recursion limit. + Overflow, +} + +pub type CanonicalGoal<'tcx, T = ty::Predicate<'tcx>> = Canonical<'tcx, Goal<'tcx, T>>; + +pub type CanonicalResponse<'tcx> = Canonical<'tcx, Response<'tcx>>; + +/// The result of evaluating a canonical query. +/// +/// FIXME: We use a different type than the existing canonical queries. This is because +/// we need to add a `Certainty` for `overflow` and may want to restructure this code without +/// having to worry about changes to currently used code. Once we've made progress on this +/// solver, merge the two responses again. +pub type QueryResult<'tcx> = Result, NoSolution>; + #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] pub struct ExternalConstraints<'tcx>(pub(crate) Interned<'tcx, ExternalConstraintsData<'tcx>>); diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index dfe23cf991f..090b769323a 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -30,6 +30,11 @@ pub struct AssocItem { /// Whether this is a method with an explicit self /// as its first parameter, allowing method calls. pub fn_has_self_parameter: bool, + + /// `Some` if the associated item (an associated type) comes from the + /// return-position `impl Trait` in trait desugaring. The `ImplTraitInTraitData` + /// provides additional information about its source. + pub opt_rpitit_info: Option, } impl AssocItem { diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 5d0cf23280b..42101f6b931 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -135,6 +135,9 @@ impl<'tcx> Const<'tcx> { _, &hir::Path { res: Res::Def(DefKind::ConstParam, def_id), .. }, )) => { + // Use the type from the param's definition, since we can resolve it, + // not the expected parameter type from WithOptConstParam. + let param_ty = tcx.type_of(def_id).subst_identity(); match tcx.named_bound_var(expr.hir_id) { Some(rbv::ResolvedArg::EarlyBound(_)) => { // Find the name and index of the const parameter by indexing the generics of @@ -143,14 +146,14 @@ impl<'tcx> Const<'tcx> { let generics = tcx.generics_of(item_def_id); let index = generics.param_def_id_to_index[&def_id]; let name = tcx.item_name(def_id); - Some(tcx.mk_const(ty::ParamConst::new(index, name), ty)) + Some(tcx.mk_const(ty::ParamConst::new(index, name), param_ty)) } Some(rbv::ResolvedArg::LateBound(debruijn, index, _)) => Some(tcx.mk_const( ty::ConstKind::Bound(debruijn, ty::BoundVar::from_u32(index)), - ty, + param_ty, )), Some(rbv::ResolvedArg::Error(guar)) => { - Some(tcx.const_error_with_guaranteed(ty, guar)) + Some(tcx.const_error_with_guaranteed(param_ty, guar)) } arg => bug!("unexpected bound var resolution for {:?}: {arg:?}", expr.hir_id), } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 82da846ea68..d5ba0785fa6 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -17,6 +17,7 @@ use crate::mir::{ }; use crate::thir::Thir; use crate::traits; +use crate::traits::solve; use crate::traits::solve::{ExternalConstraints, ExternalConstraintsData}; use crate::ty::query::{self, TyCtxtAt}; use crate::ty::{ @@ -537,6 +538,9 @@ pub struct GlobalCtxt<'tcx> { /// Merge this with `selection_cache`? pub evaluation_cache: traits::EvaluationCache<'tcx>, + /// Caches the results of goal evaluation in the new solver. + pub new_solver_evaluation_cache: solve::EvaluationCache<'tcx>, + /// Data layout specification for the current target. pub data_layout: TargetDataLayout, @@ -712,6 +716,7 @@ impl<'tcx> TyCtxt<'tcx> { pred_rcache: Default::default(), selection_cache: Default::default(), evaluation_cache: Default::default(), + new_solver_evaluation_cache: Default::default(), data_layout, alloc_map: Lock::new(interpret::AllocMap::new()), } @@ -2440,6 +2445,18 @@ impl<'tcx> TyCtxt<'tcx> { pub fn trait_solver_next(self) -> bool { self.sess.opts.unstable_opts.trait_solver == rustc_session::config::TraitSolver::Next } + + pub fn lower_impl_trait_in_trait_to_assoc_ty(self) -> bool { + self.sess.opts.unstable_opts.lower_impl_trait_in_trait_to_assoc_ty + } + + pub fn is_impl_trait_in_trait(self, def_id: DefId) -> bool { + if self.lower_impl_trait_in_trait_to_assoc_ty() { + self.opt_rpitit_info(def_id).is_some() + } else { + self.def_kind(def_id) == DefKind::ImplTraitPlaceholder + } + } } impl<'tcx> TyCtxtAt<'tcx> { diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 9c171a69d06..aff6c77e039 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -151,12 +151,8 @@ impl<'tcx> TypeError<'tcx> { .into(), RegionsPlaceholderMismatch => "one type is more general than the other".into(), ArgumentSorts(values, _) | Sorts(values) => { - let mut expected = values.expected.sort_string(tcx); - let mut found = values.found.sort_string(tcx); - if expected == found { - expected = values.expected.sort_string(tcx); - found = values.found.sort_string(tcx); - } + let expected = values.expected.sort_string(tcx); + let found = values.found.sort_string(tcx); report_maybe_different(&expected, &found).into() } Traits(values) => { diff --git a/compiler/rustc_middle/src/ty/fast_reject.rs b/compiler/rustc_middle/src/ty/fast_reject.rs index 59deade0a07..ee505742be9 100644 --- a/compiler/rustc_middle/src/ty/fast_reject.rs +++ b/compiler/rustc_middle/src/ty/fast_reject.rs @@ -51,15 +51,35 @@ pub enum SimplifiedType { /// generic parameters as if they were inference variables in that case. #[derive(PartialEq, Eq, Debug, Clone, Copy)] pub enum TreatParams { - /// Treat parameters as placeholders in the given environment. + /// Treat parameters as infer vars. This is the correct mode for caching + /// an impl's type for lookup. + AsCandidateKey, + /// Treat parameters as placeholders in the given environment. This is the + /// correct mode for *lookup*, as during candidate selection. + ForLookup, +} + +/// During fast-rejection, we have the choice of treating projection types +/// as either simplifyable or not, depending on whether we expect the projection +/// to be normalized/rigid. +#[derive(PartialEq, Eq, Debug, Clone, Copy)] +pub enum TreatProjections { + /// In candidates, we may be able to normalize the projection + /// after instantiating the candidate and equating it with a goal. /// - /// Note that this also causes us to treat projections as if they were - /// placeholders. This is only correct if the given projection cannot - /// be normalized in the current context. Even if normalization fails, - /// it may still succeed later if the projection contains any inference - /// variables. - AsPlaceholder, - AsInfer, + /// We must assume that the `impl Trait for ::This` + /// can apply to all self types so we don't return a simplified type + /// for `::This`. + AsCandidateKey, + /// In the old solver we don't try to normalize projections + /// when looking up impls and only access them by using the + /// current self type. This means that if the self type is + /// a projection which could later be normalized, we must not + /// treat it as rigid. + ForLookup, + /// We can treat projections in the self type as opaque as + /// we separately look up impls for the normalized self type. + NextSolverLookup, } /// Tries to simplify a type by only returning the outermost injective¹ layer, if one exists. @@ -87,6 +107,7 @@ pub fn simplify_type<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, treat_params: TreatParams, + treat_projections: TreatProjections, ) -> Option { match *ty.kind() { ty::Bool => Some(BoolSimplifiedType), @@ -115,19 +136,13 @@ pub fn simplify_type<'tcx>( ty::FnPtr(f) => Some(FunctionSimplifiedType(f.skip_binder().inputs().len())), ty::Placeholder(..) => Some(PlaceholderSimplifiedType), ty::Param(_) => match treat_params { - TreatParams::AsPlaceholder => Some(PlaceholderSimplifiedType), - TreatParams::AsInfer => None, + TreatParams::ForLookup => Some(PlaceholderSimplifiedType), + TreatParams::AsCandidateKey => None, }, - ty::Alias(..) => match treat_params { - // When treating `ty::Param` as a placeholder, projections also - // don't unify with anything else as long as they are fully normalized. - // - // We will have to be careful with lazy normalization here. - TreatParams::AsPlaceholder if !ty.has_non_region_infer() => { - debug!("treating `{}` as a placeholder", ty); - Some(PlaceholderSimplifiedType) - } - TreatParams::AsPlaceholder | TreatParams::AsInfer => None, + ty::Alias(..) => match treat_projections { + TreatProjections::ForLookup if !ty.needs_infer() => Some(PlaceholderSimplifiedType), + TreatProjections::NextSolverLookup => Some(PlaceholderSimplifiedType), + TreatProjections::AsCandidateKey | TreatProjections::ForLookup => None, }, ty::Foreign(def_id) => Some(ForeignSimplifiedType(def_id)), ty::Bound(..) | ty::Infer(_) | ty::Error(_) => None, @@ -295,8 +310,8 @@ impl DeepRejectCtxt { // Depending on the value of `treat_obligation_params`, we either // treat generic parameters like placeholders or like inference variables. ty::Param(_) => match self.treat_obligation_params { - TreatParams::AsPlaceholder => false, - TreatParams::AsInfer => true, + TreatParams::ForLookup => false, + TreatParams::AsCandidateKey => true, }, ty::Infer(_) => true, @@ -333,8 +348,8 @@ impl DeepRejectCtxt { let k = impl_ct.kind(); match obligation_ct.kind() { ty::ConstKind::Param(_) => match self.treat_obligation_params { - TreatParams::AsPlaceholder => false, - TreatParams::AsInfer => true, + TreatParams::ForLookup => false, + TreatParams::AsCandidateKey => true, }, // As we don't necessarily eagerly evaluate constants, diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 8cc8a0573bb..04d7de531c2 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -34,6 +34,7 @@ use rustc_attr as attr; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::intern::Interned; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::steal::Steal; use rustc_data_structures::tagged_ptr::CopyTaggedPtr; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; @@ -44,6 +45,8 @@ use rustc_index::vec::IndexVec; use rustc_macros::HashStable; use rustc_query_system::ich::StableHashingContext; use rustc_serialize::{Decodable, Encodable}; +use rustc_session::lint::LintBuffer; +pub use rustc_session::lint::RegisteredTools; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{ExpnId, ExpnKind, Span}; @@ -148,8 +151,6 @@ mod typeck_results; // Data types -pub type RegisteredTools = FxHashSet; - pub struct ResolverOutputs { pub global_ctxt: ResolverGlobalCtxt, pub ast_lowering: ResolverAstLowering, @@ -175,7 +176,6 @@ pub struct ResolverGlobalCtxt { /// 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: FxHashMap, - pub registered_tools: RegisteredTools, pub doc_link_resolutions: FxHashMap, pub doc_link_traits_in_scope: FxHashMap>, pub all_macro_rules: FxHashMap>, @@ -209,6 +209,9 @@ pub struct ResolverAstLowering { pub builtin_macro_kinds: FxHashMap, /// List functions and methods for which lifetime elision was successful. pub lifetime_elision_allowed: FxHashSet, + + /// Lints that were emitted by the resolver and early lints. + pub lint_buffer: Steal, } #[derive(Clone, Copy, Debug)] @@ -995,7 +998,7 @@ impl<'tcx> Term<'tcx> { pub fn is_infer(&self) -> bool { match self.unpack() { - TermKind::Ty(ty) => ty.is_ty_or_numeric_infer(), + TermKind::Ty(ty) => ty.is_ty_var(), TermKind::Const(ct) => ct.is_ct_infer(), } } @@ -2067,7 +2070,9 @@ pub enum ImplOverlapKind { Issue33140, } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +/// Useful source information about where a desugared associated type for an +/// RPITIT originated from. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Encodable, Decodable, HashStable)] pub enum ImplTraitInTraitData { Trait { fn_def_id: DefId, opaque_def_id: DefId }, Impl { fn_def_id: DefId }, @@ -2210,6 +2215,17 @@ impl<'tcx> TyCtxt<'tcx> { } } + /// If the def-id is an associated type that was desugared from a + /// return-position `impl Trait` from a trait, then provide the source info + /// about where that RPITIT came from. + pub fn opt_rpitit_info(self, def_id: DefId) -> Option { + if let DefKind::AssocTy = self.def_kind(def_id) { + self.associated_item(def_id).opt_rpitit_info + } else { + None + } + } + pub fn find_field_index(self, ident: Ident, variant: &VariantDef) -> Option { variant .fields diff --git a/compiler/rustc_middle/src/ty/query.rs b/compiler/rustc_middle/src/ty/query.rs index 9548be4c138..05219efe5f5 100644 --- a/compiler/rustc_middle/src/ty/query.rs +++ b/compiler/rustc_middle/src/ty/query.rs @@ -99,6 +99,11 @@ pub struct TyCtxtEnsure<'tcx> { pub tcx: TyCtxt<'tcx>, } +#[derive(Copy, Clone)] +pub struct TyCtxtEnsureWithValue<'tcx> { + pub tcx: TyCtxt<'tcx>, +} + impl<'tcx> TyCtxt<'tcx> { /// Returns a transparent wrapper for `TyCtxt`, which ensures queries /// are executed instead of just returning their results. @@ -107,6 +112,15 @@ impl<'tcx> TyCtxt<'tcx> { TyCtxtEnsure { tcx: self } } + /// Returns a transparent wrapper for `TyCtxt`, which ensures queries + /// are executed instead of just returning their results. + /// + /// This version verifies that the computed result exists in the cache before returning. + #[inline(always)] + pub fn ensure_with_value(self) -> TyCtxtEnsureWithValue<'tcx> { + TyCtxtEnsureWithValue { tcx: self } + } + /// Returns a transparent wrapper for `TyCtxt` which uses /// `span` as the location of queries performed through it. #[inline(always)] @@ -252,6 +266,36 @@ macro_rules! define_callbacks { )* } + $( + // Ensure that keys grow no larger than 64 bytes + #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] + const _: () = { + if mem::size_of::>() > 64 { + panic!("{}", concat!( + "the query `", + stringify!($name), + "` has a key type `", + stringify!($($K)*), + "` that is too large" + )); + } + }; + + // Ensure that values grow no larger than 64 bytes + #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] + const _: () = { + if mem::size_of::>() > 64 { + panic!("{}", concat!( + "the query `", + stringify!($name), + "` has a value type `", + stringify!($V), + "` that is too large" + )); + } + }; + )* + pub struct QueryArenas<'tcx> { $($(#[$attr])* pub $name: query_if_arena!([$($modifiers)*] (WorkerLocal::Target>>) @@ -284,7 +328,31 @@ macro_rules! define_callbacks { match try_get_cached(self.tcx, &self.tcx.query_system.caches.$name, &key) { Some(_) => return, - None => self.tcx.queries.$name(self.tcx, DUMMY_SP, key, QueryMode::Ensure), + None => self.tcx.queries.$name( + self.tcx, + DUMMY_SP, + key, + QueryMode::Ensure { check_cache: false }, + ), + }; + })* + } + + impl<'tcx> TyCtxtEnsureWithValue<'tcx> { + $($(#[$attr])* + #[inline(always)] + pub fn $name(self, key: query_helper_param_ty!($($K)*)) { + let key = key.into_query_param(); + opt_remap_env_constness!([$($modifiers)*][key]); + + match try_get_cached(self.tcx, &self.tcx.query_system.caches.$name, &key) { + Some(_) => return, + None => self.tcx.queries.$name( + self.tcx, + DUMMY_SP, + key, + QueryMode::Ensure { check_cache: true }, + ), }; })* } diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index 649a58c9170..bf2b121f704 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -1,5 +1,5 @@ use crate::traits::specialization_graph; -use crate::ty::fast_reject::{self, SimplifiedType, TreatParams}; +use crate::ty::fast_reject::{self, SimplifiedType, TreatParams, TreatProjections}; use crate::ty::visit::TypeVisitableExt; use crate::ty::{Ident, Ty, TyCtxt}; use hir::def_id::LOCAL_CRATE; @@ -118,16 +118,32 @@ impl<'tcx> TyCtxt<'tcx> { /// Iterate over every impl that could possibly match the self type `self_ty`. /// /// `trait_def_id` MUST BE the `DefId` of a trait. - pub fn for_each_relevant_impl( + pub fn for_each_relevant_impl( self, trait_def_id: DefId, self_ty: Ty<'tcx>, - mut f: F, + f: impl FnMut(DefId), ) { - let _: Option<()> = self.find_map_relevant_impl(trait_def_id, self_ty, |did| { - f(did); - None - }); + self.for_each_relevant_impl_treating_projections( + trait_def_id, + self_ty, + TreatProjections::ForLookup, + f, + ) + } + + pub fn for_each_relevant_impl_treating_projections( + self, + trait_def_id: DefId, + self_ty: Ty<'tcx>, + treat_projections: TreatProjections, + mut f: impl FnMut(DefId), + ) { + let _: Option<()> = + self.find_map_relevant_impl(trait_def_id, self_ty, treat_projections, |did| { + f(did); + None + }); } /// `trait_def_id` MUST BE the `DefId` of a trait. @@ -137,7 +153,12 @@ impl<'tcx> TyCtxt<'tcx> { self_ty: Ty<'tcx>, ) -> impl Iterator + 'tcx { let impls = self.trait_impls_of(trait_def_id); - if let Some(simp) = fast_reject::simplify_type(self, self_ty, TreatParams::AsInfer) { + if let Some(simp) = fast_reject::simplify_type( + self, + self_ty, + TreatParams::AsCandidateKey, + TreatProjections::AsCandidateKey, + ) { if let Some(impls) = impls.non_blanket_impls.get(&simp) { return impls.iter().copied(); } @@ -150,11 +171,12 @@ impl<'tcx> TyCtxt<'tcx> { /// the first non-none value. /// /// `trait_def_id` MUST BE the `DefId` of a trait. - pub fn find_map_relevant_impl Option>( + pub fn find_map_relevant_impl( self, trait_def_id: DefId, self_ty: Ty<'tcx>, - mut f: F, + treat_projections: TreatProjections, + mut f: impl FnMut(DefId) -> Option, ) -> Option { // FIXME: This depends on the set of all impls for the trait. That is // unfortunate wrt. incremental compilation. @@ -169,14 +191,13 @@ impl<'tcx> TyCtxt<'tcx> { } } - // Note that we're using `TreatParams::AsPlaceholder` to query `non_blanket_impls` while using - // `TreatParams::AsInfer` while actually adding them. - // // This way, when searching for some impl for `T: Trait`, we do not look at any impls // whose outer level is not a parameter or projection. Especially for things like // `T: Clone` this is incredibly useful as we would otherwise look at all the impls // of `Clone` for `Option`, `Vec`, `ConcreteType` and so on. - if let Some(simp) = fast_reject::simplify_type(self, self_ty, TreatParams::AsPlaceholder) { + if let Some(simp) = + fast_reject::simplify_type(self, self_ty, TreatParams::ForLookup, treat_projections) + { if let Some(impls) = impls.non_blanket_impls.get(&simp) { for &impl_def_id in impls { if let result @ Some(_) = f(impl_def_id) { @@ -237,9 +258,12 @@ pub(super) fn trait_impls_of_provider(tcx: TyCtxt<'_>, trait_id: DefId) -> Trait continue; } - if let Some(simplified_self_ty) = - fast_reject::simplify_type(tcx, impl_self_ty, TreatParams::AsInfer) - { + if let Some(simplified_self_ty) = fast_reject::simplify_type( + tcx, + impl_self_ty, + TreatParams::AsCandidateKey, + TreatProjections::AsCandidateKey, + ) { impls.non_blanket_impls.entry(simplified_self_ty).or_default().push(impl_def_id); } else { impls.blanket_impls.push(impl_def_id); diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 8b5469743da..b0f6127baa5 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -2,6 +2,7 @@ use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::mir; +use crate::ty::fast_reject::TreatProjections; use crate::ty::layout::IntegerExt; use crate::ty::{ self, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, @@ -363,14 +364,20 @@ impl<'tcx> TyCtxt<'tcx> { self.ensure().coherent_trait(drop_trait); let ty = self.type_of(adt_did).subst_identity(); - let (did, constness) = self.find_map_relevant_impl(drop_trait, ty, |impl_did| { - if let Some(item_id) = self.associated_item_def_ids(impl_did).first() { - if validate(self, impl_did).is_ok() { - return Some((*item_id, self.constness(impl_did))); + let (did, constness) = self.find_map_relevant_impl( + drop_trait, + ty, + // FIXME: This could also be some other mode, like "unexpected" + TreatProjections::ForLookup, + |impl_did| { + if let Some(item_id) = self.associated_item_def_ids(impl_did).first() { + if validate(self, impl_did).is_ok() { + return Some((*item_id, self.constness(impl_did))); + } } - } - None - })?; + None + }, + )?; Some(ty::Destructor { did, constness }) } diff --git a/compiler/rustc_mir_build/locales/en-US.ftl b/compiler/rustc_mir_build/messages.ftl similarity index 100% rename from compiler/rustc_mir_build/locales/en-US.ftl rename to compiler/rustc_mir_build/messages.ftl diff --git a/compiler/rustc_mir_build/src/build/expr/as_operand.rs b/compiler/rustc_mir_build/src/build/expr/as_operand.rs index ff3198847df..6941da331fc 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_operand.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_operand.rs @@ -20,7 +20,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { expr: &Expr<'tcx>, ) -> BlockAnd> { let local_scope = self.local_scope(); - self.as_operand(block, Some(local_scope), expr, None, NeedsTemporary::Maybe) + self.as_operand(block, Some(local_scope), expr, LocalInfo::Boring, NeedsTemporary::Maybe) } /// Returns an operand suitable for use until the end of the current scope expression and @@ -102,7 +102,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { mut block: BasicBlock, scope: Option, expr: &Expr<'tcx>, - local_info: Option>>, + local_info: LocalInfo<'tcx>, needs_temporary: NeedsTemporary, ) -> BlockAnd> { let this = self; @@ -124,8 +124,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } Category::Constant | Category::Place | Category::Rvalue(..) => { let operand = unpack!(block = this.as_temp(block, scope, expr, Mutability::Mut)); - if this.local_decls[operand].local_info.is_none() { - this.local_decls[operand].local_info = local_info; + // Overwrite temp local info if we have something more interesting to record. + if !matches!(local_info, LocalInfo::Boring) { + let decl_info = this.local_decls[operand].local_info.as_mut().assert_crate_local(); + if let LocalInfo::Boring | LocalInfo::BlockTailTemp(_) = **decl_info { + **decl_info = local_info; + } } block.and(Operand::Move(Place::from(operand))) } @@ -178,6 +182,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - this.as_operand(block, scope, expr, None, NeedsTemporary::Maybe) + this.as_operand(block, scope, expr, LocalInfo::Boring, NeedsTemporary::Maybe) } } diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index a4e48c1545d..b8115f5aa18 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -63,7 +63,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block, scope, &this.thir[value], - None, + LocalInfo::Boring, NeedsTemporary::No ) ); @@ -73,18 +73,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ExprKind::Binary { op, lhs, rhs } => { let lhs = unpack!( block = - this.as_operand(block, scope, &this.thir[lhs], None, NeedsTemporary::Maybe) + this.as_operand(block, scope, &this.thir[lhs], LocalInfo::Boring, NeedsTemporary::Maybe) ); let rhs = unpack!( block = - this.as_operand(block, scope, &this.thir[rhs], None, NeedsTemporary::No) + this.as_operand(block, scope, &this.thir[rhs], LocalInfo::Boring, NeedsTemporary::No) ); this.build_binary_op(block, op, expr_span, expr.ty, lhs, rhs) } ExprKind::Unary { op, arg } => { let arg = unpack!( block = - this.as_operand(block, scope, &this.thir[arg], None, NeedsTemporary::No) + this.as_operand(block, scope, &this.thir[arg], LocalInfo::Boring, NeedsTemporary::No) ); // Check for -MIN on signed integers if this.check_overflow && op == UnOp::Neg && expr.ty.is_signed() { @@ -259,7 +259,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } else { let ty = source.ty; let source = unpack!( - block = this.as_operand(block, scope, source, None, NeedsTemporary::No) + block = this.as_operand(block, scope, source, LocalInfo::Boring, NeedsTemporary::No) ); (source, ty) }; @@ -272,7 +272,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ExprKind::Pointer { cast, source } => { let source = unpack!( block = - this.as_operand(block, scope, &this.thir[source], None, NeedsTemporary::No) + this.as_operand(block, scope, &this.thir[source], LocalInfo::Boring, NeedsTemporary::No) ); block.and(Rvalue::Cast(CastKind::Pointer(cast), source, expr.ty)) } @@ -314,7 +314,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block, scope, &this.thir[f], - None, + LocalInfo::Boring, NeedsTemporary::Maybe ) ) @@ -335,7 +335,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block, scope, &this.thir[f], - None, + LocalInfo::Boring, NeedsTemporary::Maybe ) ) @@ -423,7 +423,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block, scope, upvar, - None, + LocalInfo::Boring, NeedsTemporary::Maybe ) ) @@ -502,7 +502,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Some(Category::Rvalue(RvalueFunc::AsRvalue) | Category::Constant) )); let operand = - unpack!(block = this.as_operand(block, scope, expr, None, NeedsTemporary::No)); + unpack!(block = this.as_operand(block, scope, expr, LocalInfo::Boring, NeedsTemporary::No)); block.and(Rvalue::Use(operand)) } } @@ -622,7 +622,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } else { // For a non-const, we may need to generate an appropriate `Drop` let value_operand = - unpack!(block = this.as_operand(block, scope, value, None, NeedsTemporary::No)); + unpack!(block = this.as_operand(block, scope, value, LocalInfo::Boring, NeedsTemporary::No)); if let Operand::Move(to_drop) = value_operand { let success = this.cfg.start_new_block(); this.cfg.terminate( diff --git a/compiler/rustc_mir_build/src/build/expr/as_temp.rs b/compiler/rustc_mir_build/src/build/expr/as_temp.rs index 3d3cf75559e..c8910c272b1 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_temp.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_temp.rs @@ -49,29 +49,28 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } debug!("creating temp {:?} with block_context: {:?}", local_decl, this.block_context); - // Find out whether this temp is being created within the - // tail expression of a block whose result is ignored. - if let Some(tail_info) = this.block_context.currently_in_block_tail() { - local_decl = local_decl.block_tail(tail_info); - } - match expr.kind { + let local_info = match expr.kind { ExprKind::StaticRef { def_id, .. } => { assert!(!this.tcx.is_thread_local_static(def_id)); local_decl.internal = true; - local_decl.local_info = - Some(Box::new(LocalInfo::StaticRef { def_id, is_thread_local: false })); + LocalInfo::StaticRef { def_id, is_thread_local: false } } ExprKind::ThreadLocalRef(def_id) => { assert!(this.tcx.is_thread_local_static(def_id)); local_decl.internal = true; - local_decl.local_info = - Some(Box::new(LocalInfo::StaticRef { def_id, is_thread_local: true })); + LocalInfo::StaticRef { def_id, is_thread_local: true } } ExprKind::NamedConst { def_id, .. } | ExprKind::ConstParam { def_id, .. } => { - local_decl.local_info = Some(Box::new(LocalInfo::ConstRef { def_id })); + LocalInfo::ConstRef { def_id } } - _ => {} - } + // Find out whether this temp is being created within the + // tail expression of a block whose result is ignored. + _ if let Some(tail_info) = this.block_context.currently_in_block_tail() => { + LocalInfo::BlockTailTemp(tail_info) + } + _ => LocalInfo::Boring, + }; + **local_decl.local_info.as_mut().assert_crate_local() = local_info; this.local_decls.push(local_decl) }; let temp_place = Place::from(temp); diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index dac9bf0a883..ebe8ea25ad3 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -328,7 +328,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let fields_map: FxHashMap<_, _> = fields .into_iter() .map(|f| { - let local_info = Box::new(LocalInfo::AggregateTemp); ( f.name, unpack!( @@ -336,7 +335,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block, Some(scope), &this.thir[f.expr], - Some(local_info), + LocalInfo::AggregateTemp, NeedsTemporary::Maybe, ) ), @@ -526,7 +525,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block, Some(scope), &this.thir[value], - None, + LocalInfo::Boring, NeedsTemporary::No ) ); diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 32892e0ae11..22785dfd2ce 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -588,8 +588,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // let PATTERN = ... might not even exist until we do the assignment. // so we set it here instead. if set_match_place { - let mut candidate_ref = &candidate; - while let Some(next) = { + let mut next = Some(&candidate); + while let Some(candidate_ref) = next.take() { for binding in &candidate_ref.bindings { let local = self.var_local_id(binding.var_id, OutsideGuard); // `try_to_place` may fail if it is unable to resolve the given @@ -607,9 +607,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // }; // ``` if let Some(place) = initializer.try_to_place(self) { - let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( + let LocalInfo::User(BindingForm::Var( VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. }, - )))) = self.local_decls[local].local_info else { + )) = **self.local_decls[local].local_info.as_mut().assert_crate_local() else { bug!("Let binding to non-user variable.") }; *match_place = Some(place); @@ -617,9 +617,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } // All of the subcandidates should bind the same locals, so we // only visit the first one. - candidate_ref.subcandidates.get(0) - } { - candidate_ref = next; + next = candidate_ref.subcandidates.get(0) } } @@ -1756,7 +1754,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let fake_borrow_ty = tcx.mk_imm_ref(tcx.lifetimes.re_erased, fake_borrow_deref_ty); let mut fake_borrow_temp = LocalDecl::new(fake_borrow_ty, temp_span); fake_borrow_temp.internal = self.local_decls[matched_place.local].internal; - fake_borrow_temp.local_info = Some(Box::new(LocalInfo::FakeBorrow)); + fake_borrow_temp.local_info = ClearCrossCrate::Set(Box::new(LocalInfo::FakeBorrow)); let fake_borrow_temp = self.local_decls.push(fake_borrow_temp); (matched_place, fake_borrow_temp) @@ -1888,6 +1886,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // let place = Foo::new(); // match place { Foo { .. } if { let tmp1 = &place; inspect(*tmp1) } // => { let tmp2 = place; feed(tmp2) }, ... } + // ``` // // And an input like: // @@ -2225,8 +2224,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { user_ty: if user_ty.is_empty() { None } else { Some(Box::new(user_ty)) }, source_info, internal: false, - is_block_tail: None, - local_info: Some(Box::new(LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( + local_info: ClearCrossCrate::Set(Box::new(LocalInfo::User(BindingForm::Var( VarBindingForm { binding_mode, // hypothetically, `visit_primary_bindings` could try to unzip @@ -2237,7 +2235,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { opt_match_place, pat_span, }, - ))))), + )))), }; let for_arm_body = self.local_decls.push(local); self.var_debug_info.push(VarDebugInfo { @@ -2254,10 +2252,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { user_ty: None, source_info, internal: false, - is_block_tail: None, - local_info: Some(Box::new(LocalInfo::User(ClearCrossCrate::Set( - BindingForm::RefForGuard, - )))), + local_info: ClearCrossCrate::Set(Box::new(LocalInfo::User(BindingForm::RefForGuard))), }); self.var_debug_info.push(VarDebugInfo { name, diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index b3f9d82829f..6814fd4cb35 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -48,17 +48,14 @@ pub(crate) fn mir_built( /// Construct the MIR for a given `DefId`. fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) -> Body<'_> { // Ensure unsafeck and abstract const building is ran before we steal the THIR. - // We can't use `ensure()` for `thir_abstract_const` as it doesn't compute the query - // if inputs are green. This can cause ICEs when calling `thir_abstract_const` after - // THIR has been stolen if we haven't computed this query yet. match def { ty::WithOptConstParam { did, const_param_did: Some(const_param_did) } => { - tcx.ensure().thir_check_unsafety_for_const_arg((did, const_param_did)); - drop(tcx.thir_abstract_const_of_const_arg((did, const_param_did))); + tcx.ensure_with_value().thir_check_unsafety_for_const_arg((did, const_param_did)); + tcx.ensure_with_value().thir_abstract_const_of_const_arg((did, const_param_did)); } ty::WithOptConstParam { did, const_param_did: None } => { - tcx.ensure().thir_check_unsafety(did); - drop(tcx.thir_abstract_const(did)); + tcx.ensure_with_value().thir_check_unsafety(did); + tcx.ensure_with_value().thir_abstract_const(did); } } @@ -879,20 +876,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } => { self.local_decls[local].mutability = mutability; self.local_decls[local].source_info.scope = self.source_scope; - self.local_decls[local].local_info = if let Some(kind) = param.self_kind { - Some(Box::new(LocalInfo::User(ClearCrossCrate::Set( + **self.local_decls[local].local_info.as_mut().assert_crate_local() = if let Some(kind) = param.self_kind { + LocalInfo::User( BindingForm::ImplicitSelf(kind), - )))) + ) } else { let binding_mode = ty::BindingMode::BindByValue(mutability); - Some(Box::new(LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( + LocalInfo::User(BindingForm::Var( VarBindingForm { binding_mode, opt_ty_info: param.ty_span, opt_match_place: Some((None, span)), pat_span: span, }, - ))))) + )) }; self.var_indices.insert(var, LocalsForNode::One(local)); } diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index e10a264d385..04709197578 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -28,7 +28,7 @@ use rustc_middle::ty::query::Providers; use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage}; use rustc_macros::fluent_messages; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } pub fn provide(providers: &mut Providers) { providers.check_match = thir::pattern::check_match; diff --git a/compiler/rustc_mir_dataflow/locales/en-US.ftl b/compiler/rustc_mir_dataflow/messages.ftl similarity index 100% rename from compiler/rustc_mir_dataflow/locales/en-US.ftl rename to compiler/rustc_mir_dataflow/messages.ftl diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index b1e03faff05..4ed6f7e90ff 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -46,7 +46,7 @@ pub mod storage; pub mod un_derefer; pub mod value_analysis; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } pub(crate) mod indexes { pub(crate) use super::move_paths::MovePathIndex; diff --git a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs index 536745d2cfe..3d32c586554 100644 --- a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs +++ b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs @@ -24,7 +24,7 @@ struct ConstMutationChecker<'a, 'tcx> { impl<'tcx> ConstMutationChecker<'_, 'tcx> { fn is_const_item(&self, local: Local) -> Option { - if let Some(box LocalInfo::ConstRef { def_id }) = self.body.local_decls[local].local_info { + if let LocalInfo::ConstRef { def_id } = *self.body.local_decls[local].local_info() { Some(def_id) } else { None diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs index a8ec568eb0d..c4d058e8ecb 100644 --- a/compiler/rustc_mir_transform/src/check_unsafety.rs +++ b/compiler/rustc_mir_transform/src/check_unsafety.rs @@ -182,7 +182,7 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> { // If the projection root is an artificial local that we introduced when // desugaring `static`, give a more specific error message // (avoid the general "raw pointer" clause below, that would only be confusing). - if let Some(box LocalInfo::StaticRef { def_id, .. }) = decl.local_info { + if let LocalInfo::StaticRef { def_id, .. } = *decl.local_info() { if self.tcx.is_mutable_static(def_id) { self.require_unsafe( UnsafetyViolationKind::General, diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index 9bc4b26db92..de7b8c63fc8 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -1,12 +1,9 @@ //! Propagates constants for early reporting of statically known //! assertion failures -use std::cell::Cell; - use either::Right; use rustc_const_eval::const_eval::CheckAlignment; -use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::DefKind; use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; @@ -17,7 +14,7 @@ use rustc_middle::mir::*; use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::InternalSubsts; use rustc_middle::ty::{self, ConstKind, Instance, ParamEnv, Ty, TyCtxt, TypeVisitableExt}; -use rustc_span::{def_id::DefId, Span}; +use rustc_span::{def_id::DefId, Span, DUMMY_SP}; use rustc_target::abi::{self, Align, HasDataLayout, Size, TargetDataLayout}; use rustc_target::spec::abi::Abi as CallAbi; use rustc_trait_selection::traits; @@ -25,8 +22,8 @@ use rustc_trait_selection::traits; use crate::MirPass; use rustc_const_eval::interpret::{ self, compile_time_machine, AllocId, ConstAllocation, ConstValue, CtfeValidationMode, Frame, - ImmTy, Immediate, InterpCx, InterpResult, LocalState, LocalValue, MemoryKind, OpTy, PlaceTy, - Pointer, Scalar, StackPopCleanup, StackPopUnwind, + ImmTy, Immediate, InterpCx, InterpResult, LocalValue, MemoryKind, OpTy, PlaceTy, Pointer, + Scalar, StackPopCleanup, StackPopUnwind, }; /// The maximum number of bytes that we'll allocate space for a local or the return value. @@ -154,24 +151,12 @@ impl<'tcx> MirPass<'tcx> for ConstProp { pub struct ConstPropMachine<'mir, 'tcx> { /// The virtual call stack. stack: Vec>, - /// `OnlyInsideOwnBlock` locals that were written in the current block get erased at the end. - pub written_only_inside_own_block_locals: FxHashSet, - /// Locals that need to be cleared after every block terminates. - pub only_propagate_inside_block_locals: BitSet, pub can_const_prop: IndexVec, } impl ConstPropMachine<'_, '_> { - pub fn new( - only_propagate_inside_block_locals: BitSet, - can_const_prop: IndexVec, - ) -> Self { - Self { - stack: Vec::new(), - written_only_inside_own_block_locals: Default::default(), - only_propagate_inside_block_locals, - can_const_prop, - } + pub fn new(can_const_prop: IndexVec) -> Self { + Self { stack: Vec::new(), can_const_prop } } } @@ -257,16 +242,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> frame: usize, local: Local, ) -> InterpResult<'tcx, &'a mut interpret::Operand> { - if ecx.machine.can_const_prop[local] == ConstPropMode::NoPropagation { - throw_machine_stop_str!("tried to write to a local that is marked as not propagatable") - } - if frame == 0 && ecx.machine.only_propagate_inside_block_locals.contains(local) { - trace!( - "mutating local {:?} which is restricted to its block. \ - Will remove it from const-prop after block is finished.", - local - ); - ecx.machine.written_only_inside_own_block_locals.insert(local); + assert_eq!(frame, 0); + match ecx.machine.can_const_prop[local] { + ConstPropMode::NoPropagation => { + throw_machine_stop_str!( + "tried to write to a local that is marked as not propagatable" + ) + } + ConstPropMode::OnlyInsideOwnBlock | ConstPropMode::FullConstProp => {} } ecx.machine.stack[frame].locals[local].access_mut() } @@ -328,9 +311,6 @@ struct ConstPropagator<'mir, 'tcx> { tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, local_decls: &'mir IndexVec>, - // Because we have `MutVisitor` we can't obtain the `SourceInfo` from a `Location`. So we store - // the last known `SourceInfo` here and just keep revisiting it. - source_info: Option, } impl<'tcx> LayoutOfHelpers<'tcx> for ConstPropagator<'_, 'tcx> { @@ -374,17 +354,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let param_env = tcx.param_env_reveal_all_normalized(def_id); let can_const_prop = CanConstProp::check(tcx, param_env, body); - let mut only_propagate_inside_block_locals = BitSet::new_empty(can_const_prop.len()); - for (l, mode) in can_const_prop.iter_enumerated() { - if *mode == ConstPropMode::OnlyInsideOwnBlock { - only_propagate_inside_block_locals.insert(l); - } - } let mut ecx = InterpCx::new( tcx, tcx.def_span(def_id), param_env, - ConstPropMachine::new(only_propagate_inside_block_locals, can_const_prop), + ConstPropMachine::new(can_const_prop), ); let ret_layout = ecx @@ -411,13 +385,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { ) .expect("failed to push initial stack frame"); - ConstPropagator { - ecx, - tcx, - param_env, - local_decls: &dummy_body.local_decls, - source_info: None, - } + ConstPropagator { ecx, tcx, param_env, local_decls: &dummy_body.local_decls } } fn get_const(&self, place: Place<'tcx>) -> Option> { @@ -446,10 +414,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { /// Remove `local` from the pool of `Locals`. Allows writing to them, /// but not reading from them anymore. fn remove_const(ecx: &mut InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>, local: Local) { - ecx.frame_mut().locals[local] = LocalState { - value: LocalValue::Live(interpret::Operand::Immediate(interpret::Immediate::Uninit)), - layout: Cell::new(None), - }; + ecx.frame_mut().locals[local].value = + LocalValue::Live(interpret::Operand::Immediate(interpret::Immediate::Uninit)); } /// Returns the value, if any, of evaluating `c`. @@ -492,11 +458,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { scalar, )) = *value { - *operand = self.operand_from_scalar( - scalar, - value.layout.ty, - self.source_info.unwrap().span, - ); + *operand = self.operand_from_scalar(scalar, value.layout.ty); } } } @@ -504,7 +466,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } } - fn const_prop(&mut self, rvalue: &Rvalue<'tcx>, place: Place<'tcx>) -> Option<()> { + fn check_rvalue(&mut self, rvalue: &Rvalue<'tcx>) -> Option<()> { // Perform any special handling for specific Rvalue types. // Generally, checks here fall into one of two categories: // 1. Additional checking to provide useful lints to the user @@ -561,7 +523,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { return None; } - self.eval_rvalue_with_identities(rvalue, place) + Some(()) } // Attempt to use algebraic identities to eliminate constant expressions @@ -621,20 +583,24 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } /// Creates a new `Operand::Constant` from a `Scalar` value - fn operand_from_scalar(&self, scalar: Scalar, ty: Ty<'tcx>, span: Span) -> Operand<'tcx> { + fn operand_from_scalar(&self, scalar: Scalar, ty: Ty<'tcx>) -> Operand<'tcx> { Operand::Constant(Box::new(Constant { - span, + span: DUMMY_SP, user_ty: None, literal: ConstantKind::from_scalar(self.tcx, scalar, ty), })) } - fn replace_with_const( - &mut self, - rval: &mut Rvalue<'tcx>, - value: &OpTy<'tcx>, - source_info: SourceInfo, - ) { + fn replace_with_const(&mut self, place: Place<'tcx>, rval: &mut Rvalue<'tcx>) { + // This will return None if the above `const_prop` invocation only "wrote" a + // type whose creation requires no write. E.g. a generator whose initial state + // consists solely of uninitialized memory (so it doesn't capture any locals). + let Some(ref value) = self.get_const(place) else { return }; + if !self.should_const_prop(value) { + return; + } + trace!("replacing {:?}={:?} with {:?}", place, rval, value); + if let Rvalue::Use(Operand::Constant(c)) = rval { match c.literal { ConstantKind::Ty(c) if matches!(c.kind(), ConstKind::Unevaluated(..)) => {} @@ -664,11 +630,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { if let Some(Right(imm)) = imm { match *imm { interpret::Immediate::Scalar(scalar) => { - *rval = Rvalue::Use(self.operand_from_scalar( - scalar, - value.layout.ty, - source_info.span, - )); + *rval = Rvalue::Use(self.operand_from_scalar(scalar, value.layout.ty)); } Immediate::ScalarPair(..) => { // Found a value represented as a pair. For now only do const-prop if the type @@ -701,7 +663,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let const_val = ConstValue::ByRef { alloc, offset: Size::ZERO }; let literal = ConstantKind::Val(const_val, ty); *rval = Rvalue::Use(Operand::Constant(Box::new(Constant { - span: source_info.span, + span: DUMMY_SP, user_ty: None, literal, }))); @@ -730,6 +692,19 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { _ => false, } } + + fn ensure_not_propagated(&mut self, local: Local) { + if cfg!(debug_assertions) { + assert!( + self.get_const(local.into()).is_none() + || self + .layout_of(self.local_decls[local].ty) + .map_or(true, |layout| layout.is_zst()), + "failed to remove values for `{local:?}`, value={:?}", + self.get_const(local.into()), + ) + } + } } /// The mode that `ConstProp` is allowed to run in for a given `Local`. @@ -739,8 +714,6 @@ pub enum ConstPropMode { FullConstProp, /// The `Local` can only be propagated into and from its own block. OnlyInsideOwnBlock, - /// The `Local` can be propagated into but reads cannot be propagated. - OnlyPropagateInto, /// The `Local` cannot be part of propagation at all. Any statement /// referencing it either for reading or writing will not get propagated. NoPropagation, @@ -750,8 +723,6 @@ pub struct CanConstProp { can_const_prop: IndexVec, // False at the beginning. Once set, no more assignments are allowed to that local. found_assignment: BitSet, - // Cache of locals' information - local_kinds: IndexVec, } impl CanConstProp { @@ -764,10 +735,6 @@ impl CanConstProp { let mut cpv = CanConstProp { can_const_prop: IndexVec::from_elem(ConstPropMode::FullConstProp, &body.local_decls), found_assignment: BitSet::new_empty(body.local_decls.len()), - local_kinds: IndexVec::from_fn_n( - |local| body.local_kind(local), - body.local_decls.len(), - ), }; for (local, val) in cpv.can_const_prop.iter_enumerated_mut() { let ty = body.local_decls[local].ty; @@ -780,24 +747,10 @@ impl CanConstProp { continue; } } - // Cannot use args at all - // Cannot use locals because if x < y { y - x } else { x - y } would - // lint for x != y - // FIXME(oli-obk): lint variables until they are used in a condition - // FIXME(oli-obk): lint if return value is constant - if cpv.local_kinds[local] == LocalKind::Arg { - *val = ConstPropMode::OnlyPropagateInto; - trace!( - "local {:?} can't be const propagated because it's a function argument", - local - ); - } else if cpv.local_kinds[local] == LocalKind::Var { - *val = ConstPropMode::OnlyInsideOwnBlock; - trace!( - "local {:?} will only be propagated inside its block, because it's a user variable", - local - ); - } + } + // Consider that arguments are assigned on entry. + for arg in body.args_iter() { + cpv.found_assignment.insert(arg); } cpv.visit_body(&body); cpv.can_const_prop @@ -827,7 +780,6 @@ impl Visitor<'_> for CanConstProp { // states as applicable. ConstPropMode::OnlyInsideOwnBlock => {} ConstPropMode::NoPropagation => {} - ConstPropMode::OnlyPropagateInto => {} other @ ConstPropMode::FullConstProp => { trace!( "local {:?} can't be propagated because of multiple assignments. Previous state: {:?}", @@ -892,42 +844,23 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> { self.eval_constant(constant); } - fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) { - trace!("visit_statement: {:?}", statement); - let source_info = statement.source_info; - self.source_info = Some(source_info); - match statement.kind { - StatementKind::Assign(box (place, ref mut rval)) => { - let can_const_prop = self.ecx.machine.can_const_prop[place.local]; - if let Some(()) = self.const_prop(rval, place) { - // This will return None if the above `const_prop` invocation only "wrote" a - // type whose creation requires no write. E.g. a generator whose initial state - // consists solely of uninitialized memory (so it doesn't capture any locals). - if let Some(ref value) = self.get_const(place) && self.should_const_prop(value) { - trace!("replacing {:?} with {:?}", rval, value); - self.replace_with_const(rval, value, source_info); - if can_const_prop == ConstPropMode::FullConstProp - || can_const_prop == ConstPropMode::OnlyInsideOwnBlock - { - trace!("propagated into {:?}", place); - } - } - match can_const_prop { - ConstPropMode::OnlyInsideOwnBlock => { - trace!( - "found local restricted to its block. \ - Will remove it from const-prop after block is finished. Local: {:?}", - place.local - ); - } - ConstPropMode::OnlyPropagateInto | ConstPropMode::NoPropagation => { - trace!("can't propagate into {:?}", place); - if place.local != RETURN_PLACE { - Self::remove_const(&mut self.ecx, place.local); - } - } - ConstPropMode::FullConstProp => {} - } + fn visit_assign( + &mut self, + place: &mut Place<'tcx>, + rvalue: &mut Rvalue<'tcx>, + location: Location, + ) { + self.super_assign(place, rvalue, location); + + let Some(()) = self.check_rvalue(rvalue) else { return }; + + match self.ecx.machine.can_const_prop[place.local] { + // Do nothing if the place is indirect. + _ if place.is_indirect() => {} + ConstPropMode::NoPropagation => self.ensure_not_propagated(place.local), + ConstPropMode::OnlyInsideOwnBlock | ConstPropMode::FullConstProp => { + if let Some(()) = self.eval_rvalue_with_identities(rvalue, *place) { + self.replace_with_const(*place, rvalue); } else { // Const prop failed, so erase the destination, ensuring that whatever happens // from here on, does not know about the previous value. @@ -947,8 +880,22 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> { Self::remove_const(&mut self.ecx, place.local); } } + } + } + + fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) { + trace!("visit_statement: {:?}", statement); + + // We want to evaluate operands before any change to the assigned-to value, + // so we recurse first. + self.super_statement(statement, location); + + match statement.kind { StatementKind::SetDiscriminant { ref place, .. } => { match self.ecx.machine.can_const_prop[place.local] { + // Do nothing if the place is indirect. + _ if place.is_indirect() => {} + ConstPropMode::NoPropagation => self.ensure_not_propagated(place.local), ConstPropMode::FullConstProp | ConstPropMode::OnlyInsideOwnBlock => { if self.ecx.statement(statement).is_ok() { trace!("propped discriminant into {:?}", place); @@ -956,28 +903,22 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> { Self::remove_const(&mut self.ecx, place.local); } } - ConstPropMode::OnlyPropagateInto | ConstPropMode::NoPropagation => { - Self::remove_const(&mut self.ecx, place.local); - } } } - StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => { + StatementKind::StorageLive(local) => { let frame = self.ecx.frame_mut(); - frame.locals[local].value = if let StatementKind::StorageLive(_) = statement.kind { - LocalValue::Live(interpret::Operand::Immediate(interpret::Immediate::Uninit)) - } else { - LocalValue::Dead - }; + frame.locals[local].value = + LocalValue::Live(interpret::Operand::Immediate(interpret::Immediate::Uninit)); + } + StatementKind::StorageDead(local) => { + let frame = self.ecx.frame_mut(); + frame.locals[local].value = LocalValue::Dead; } _ => {} } - - self.super_statement(statement, location); } fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) { - let source_info = terminator.source_info; - self.source_info = Some(source_info); self.super_terminator(terminator, location); match &mut terminator.kind { @@ -987,11 +928,7 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> { && self.should_const_prop(value) { trace!("assertion on {:?} should be {:?}", value, expected); - *cond = self.operand_from_scalar( - value_const, - self.tcx.types.bool, - source_info.span, - ); + *cond = self.operand_from_scalar(value_const, self.tcx.types.bool); } } TerminatorKind::SwitchInt { ref mut discr, .. } => { @@ -1026,23 +963,17 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> { // We remove all Locals which are restricted in propagation to their containing blocks and // which were modified in the current block. // Take it out of the ecx so we can get a mutable reference to the ecx for `remove_const`. - let mut locals = std::mem::take(&mut self.ecx.machine.written_only_inside_own_block_locals); - for &local in locals.iter() { - Self::remove_const(&mut self.ecx, local); - } - locals.clear(); - // Put it back so we reuse the heap of the storage - self.ecx.machine.written_only_inside_own_block_locals = locals; - if cfg!(debug_assertions) { - // Ensure we are correctly erasing locals with the non-debug-assert logic. - for local in self.ecx.machine.only_propagate_inside_block_locals.iter() { - assert!( - self.get_const(local.into()).is_none() - || self - .layout_of(self.local_decls[local].ty) - .map_or(true, |layout| layout.is_zst()) - ) + let can_const_prop = std::mem::take(&mut self.ecx.machine.can_const_prop); + for (local, &mode) in can_const_prop.iter_enumerated() { + match mode { + ConstPropMode::FullConstProp => {} + ConstPropMode::NoPropagation => self.ensure_not_propagated(local), + ConstPropMode::OnlyInsideOwnBlock => { + Self::remove_const(&mut self.ecx, local); + self.ensure_not_propagated(local); + } } } + self.ecx.machine.can_const_prop = can_const_prop; } } diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs index 77402b8737e..68e50070e56 100644 --- a/compiler/rustc_mir_transform/src/const_prop_lint.rs +++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs @@ -1,24 +1,17 @@ //! Propagates constants for early reporting of statically known //! assertion failures -use std::cell::Cell; - use either::{Left, Right}; use rustc_const_eval::interpret::Immediate; use rustc_const_eval::interpret::{ - self, InterpCx, InterpResult, LocalState, LocalValue, MemoryKind, OpTy, Scalar, StackPopCleanup, + self, InterpCx, InterpResult, LocalValue, MemoryKind, OpTy, Scalar, StackPopCleanup, }; use rustc_hir::def::DefKind; use rustc_hir::HirId; -use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::{ - AssertKind, BinOp, Body, Constant, Local, LocalDecl, Location, Operand, Place, Rvalue, - SourceInfo, SourceScope, SourceScopeData, Statement, StatementKind, Terminator, TerminatorKind, - UnOp, RETURN_PLACE, -}; +use rustc_middle::mir::*; use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::InternalSubsts; use rustc_middle::ty::{ @@ -185,17 +178,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let param_env = tcx.param_env_reveal_all_normalized(def_id); let can_const_prop = CanConstProp::check(tcx, param_env, body); - let mut only_propagate_inside_block_locals = BitSet::new_empty(can_const_prop.len()); - for (l, mode) in can_const_prop.iter_enumerated() { - if *mode == ConstPropMode::OnlyInsideOwnBlock { - only_propagate_inside_block_locals.insert(l); - } - } let mut ecx = InterpCx::new( tcx, tcx.def_span(def_id), param_env, - ConstPropMachine::new(only_propagate_inside_block_locals, can_const_prop), + ConstPropMachine::new(can_const_prop), ); let ret_layout = ecx @@ -258,10 +245,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { /// Remove `local` from the pool of `Locals`. Allows writing to them, /// but not reading from them anymore. fn remove_const(ecx: &mut InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>, local: Local) { - ecx.frame_mut().locals[local] = LocalState { - value: LocalValue::Live(interpret::Operand::Immediate(interpret::Immediate::Uninit)), - layout: Cell::new(None), - }; + ecx.frame_mut().locals[local].value = + LocalValue::Live(interpret::Operand::Immediate(interpret::Immediate::Uninit)); } fn lint_root(&self, source_info: SourceInfo) -> Option { @@ -420,12 +405,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { Some(()) } - fn const_prop( - &mut self, - rvalue: &Rvalue<'tcx>, - source_info: SourceInfo, - place: Place<'tcx>, - ) -> Option<()> { + fn check_rvalue(&mut self, rvalue: &Rvalue<'tcx>, source_info: SourceInfo) -> Option<()> { // Perform any special handling for specific Rvalue types. // Generally, checks here fall into one of two categories: // 1. Additional checking to provide useful lints to the user @@ -501,7 +481,20 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { return None; } - self.use_ecx(source_info, |this| this.ecx.eval_rvalue_into_place(rvalue, place)) + Some(()) + } + + fn ensure_not_propagated(&mut self, local: Local) { + if cfg!(debug_assertions) { + assert!( + self.get_const(local.into()).is_none() + || self + .layout_of(self.local_decls[local].ty) + .map_or(true, |layout| layout.is_zst()), + "failed to remove values for `{local:?}`, value={:?}", + self.get_const(local.into()), + ) + } } } @@ -522,82 +515,78 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { self.eval_constant(constant, self.source_info.unwrap()); } + fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) { + self.super_assign(place, rvalue, location); + + let source_info = self.source_info.unwrap(); + let Some(()) = self.check_rvalue(rvalue, source_info) else { return }; + + match self.ecx.machine.can_const_prop[place.local] { + // Do nothing if the place is indirect. + _ if place.is_indirect() => {} + ConstPropMode::NoPropagation => self.ensure_not_propagated(place.local), + ConstPropMode::OnlyInsideOwnBlock | ConstPropMode::FullConstProp => { + if self + .use_ecx(source_info, |this| this.ecx.eval_rvalue_into_place(rvalue, *place)) + .is_none() + { + // Const prop failed, so erase the destination, ensuring that whatever happens + // from here on, does not know about the previous value. + // This is important in case we have + // ```rust + // let mut x = 42; + // x = SOME_MUTABLE_STATIC; + // // x must now be uninit + // ``` + // FIXME: we overzealously erase the entire local, because that's easier to + // implement. + trace!( + "propagation into {:?} failed. + Nuking the entire site from orbit, it's the only way to be sure", + place, + ); + Self::remove_const(&mut self.ecx, place.local); + } + } + } + } + fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { trace!("visit_statement: {:?}", statement); let source_info = statement.source_info; self.source_info = Some(source_info); - if let StatementKind::Assign(box (place, ref rval)) = statement.kind { - let can_const_prop = self.ecx.machine.can_const_prop[place.local]; - if let Some(()) = self.const_prop(rval, source_info, place) { - match can_const_prop { - ConstPropMode::OnlyInsideOwnBlock => { - trace!( - "found local restricted to its block. \ - Will remove it from const-prop after block is finished. Local: {:?}", - place.local - ); - } - ConstPropMode::OnlyPropagateInto | ConstPropMode::NoPropagation => { - trace!("can't propagate into {:?}", place); - if place.local != RETURN_PLACE { - Self::remove_const(&mut self.ecx, place.local); - } - } - ConstPropMode::FullConstProp => {} - } - } else { - // Const prop failed, so erase the destination, ensuring that whatever happens - // from here on, does not know about the previous value. - // This is important in case we have - // ```rust - // let mut x = 42; - // x = SOME_MUTABLE_STATIC; - // // x must now be uninit - // ``` - // FIXME: we overzealously erase the entire local, because that's easier to - // implement. - trace!( - "propagation into {:?} failed. - Nuking the entire site from orbit, it's the only way to be sure", - place, - ); - Self::remove_const(&mut self.ecx, place.local); - } - } else { - match statement.kind { - StatementKind::SetDiscriminant { ref place, .. } => { - match self.ecx.machine.can_const_prop[place.local] { - ConstPropMode::FullConstProp | ConstPropMode::OnlyInsideOwnBlock => { - if self - .use_ecx(source_info, |this| this.ecx.statement(statement)) - .is_some() - { - trace!("propped discriminant into {:?}", place); - } else { - Self::remove_const(&mut self.ecx, place.local); - } - } - ConstPropMode::OnlyPropagateInto | ConstPropMode::NoPropagation => { - Self::remove_const(&mut self.ecx, place.local); - } - } - } - StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => { - let frame = self.ecx.frame_mut(); - frame.locals[local].value = - if let StatementKind::StorageLive(_) = statement.kind { - LocalValue::Live(interpret::Operand::Immediate( - interpret::Immediate::Uninit, - )) - } else { - LocalValue::Dead - }; - } - _ => {} - } - } + // We want to evaluate operands before any change to the assigned-to value, + // so we recurse first. self.super_statement(statement, location); + + match statement.kind { + StatementKind::SetDiscriminant { ref place, .. } => { + match self.ecx.machine.can_const_prop[place.local] { + // Do nothing if the place is indirect. + _ if place.is_indirect() => {} + ConstPropMode::NoPropagation => self.ensure_not_propagated(place.local), + ConstPropMode::FullConstProp | ConstPropMode::OnlyInsideOwnBlock => { + if self.use_ecx(source_info, |this| this.ecx.statement(statement)).is_some() + { + trace!("propped discriminant into {:?}", place); + } else { + Self::remove_const(&mut self.ecx, place.local); + } + } + } + } + StatementKind::StorageLive(local) => { + let frame = self.ecx.frame_mut(); + frame.locals[local].value = + LocalValue::Live(interpret::Operand::Immediate(interpret::Immediate::Uninit)); + } + StatementKind::StorageDead(local) => { + let frame = self.ecx.frame_mut(); + frame.locals[local].value = LocalValue::Dead; + } + _ => {} + } } fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { @@ -694,27 +683,25 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { | TerminatorKind::Call { .. } | TerminatorKind::InlineAsm { .. } => {} } + } + + fn visit_basic_block_data(&mut self, block: BasicBlock, data: &BasicBlockData<'tcx>) { + self.super_basic_block_data(block, data); // We remove all Locals which are restricted in propagation to their containing blocks and // which were modified in the current block. // Take it out of the ecx so we can get a mutable reference to the ecx for `remove_const`. - let mut locals = std::mem::take(&mut self.ecx.machine.written_only_inside_own_block_locals); - for &local in locals.iter() { - Self::remove_const(&mut self.ecx, local); - } - locals.clear(); - // Put it back so we reuse the heap of the storage - self.ecx.machine.written_only_inside_own_block_locals = locals; - if cfg!(debug_assertions) { - // Ensure we are correctly erasing locals with the non-debug-assert logic. - for local in self.ecx.machine.only_propagate_inside_block_locals.iter() { - assert!( - self.get_const(local.into()).is_none() - || self - .layout_of(self.local_decls[local].ty) - .map_or(true, |layout| layout.is_zst()) - ) + let can_const_prop = std::mem::take(&mut self.ecx.machine.can_const_prop); + for (local, &mode) in can_const_prop.iter_enumerated() { + match mode { + ConstPropMode::FullConstProp => {} + ConstPropMode::NoPropagation => self.ensure_not_propagated(local), + ConstPropMode::OnlyInsideOwnBlock => { + Self::remove_const(&mut self.ecx, local); + self.ensure_not_propagated(local); + } } } + self.ecx.machine.can_const_prop = can_const_prop; } } diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index c03cacd8615..49028ca4e5e 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -537,29 +537,29 @@ impl TraverseCoverageGraphWithLoops { "TraverseCoverageGraphWithLoops::next - context_stack: {:?}", self.context_stack.iter().rev().collect::>() ); - while let Some(next_bcb) = { - // Strip contexts with empty worklists from the top of the stack - while self.context_stack.last().map_or(false, |context| context.worklist.is_empty()) { + + while let Some(context) = self.context_stack.last_mut() { + if let Some(next_bcb) = context.worklist.pop() { + if !self.visited.insert(next_bcb) { + debug!("Already visited: {:?}", next_bcb); + continue; + } + debug!("Visiting {:?}", next_bcb); + if self.backedges[next_bcb].len() > 0 { + debug!("{:?} is a loop header! Start a new TraversalContext...", next_bcb); + self.context_stack.push(TraversalContext { + loop_backedges: Some((self.backedges[next_bcb].clone(), next_bcb)), + worklist: Vec::new(), + }); + } + self.extend_worklist(basic_coverage_blocks, next_bcb); + return Some(next_bcb); + } else { + // Strip contexts with empty worklists from the top of the stack self.context_stack.pop(); } - // Pop the next bcb off of the current context_stack. If none, all BCBs were visited. - self.context_stack.last_mut().map_or(None, |context| context.worklist.pop()) - } { - if !self.visited.insert(next_bcb) { - debug!("Already visited: {:?}", next_bcb); - continue; - } - debug!("Visiting {:?}", next_bcb); - if self.backedges[next_bcb].len() > 0 { - debug!("{:?} is a loop header! Start a new TraversalContext...", next_bcb); - self.context_stack.push(TraversalContext { - loop_backedges: Some((self.backedges[next_bcb].clone(), next_bcb)), - worklist: Vec::new(), - }); - } - self.extend_worklist(basic_coverage_blocks, next_bcb); - return Some(next_bcb); } + None } diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 9a617159813..5ecb2d6a631 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -577,5 +577,5 @@ fn get_body_span<'tcx>( fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx rustc_hir::Body<'tcx>) -> u64 { // FIXME(cjgillot) Stop hashing HIR manually here. let owner = hir_body.id().hir_id.owner; - tcx.hir_owner_nodes(owner).unwrap().hash_including_bodies.to_smaller_hash() + tcx.hir_owner_nodes(owner).unwrap().opt_hash_including_bodies.unwrap().to_smaller_hash() } diff --git a/compiler/rustc_mir_transform/src/deref_separator.rs b/compiler/rustc_mir_transform/src/deref_separator.rs index 7508df92df1..b8a5b92be4a 100644 --- a/compiler/rustc_mir_transform/src/deref_separator.rs +++ b/compiler/rustc_mir_transform/src/deref_separator.rs @@ -40,7 +40,7 @@ impl<'tcx> MutVisitor<'tcx> for DerefChecker<'tcx> { let temp = self.patcher.new_internal_with_info( ty, self.local_decls[p_ref.local].source_info.span, - Some(Box::new(LocalInfo::DerefTemp)), + LocalInfo::DerefTemp, ); // We are adding current p_ref's projections to our diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 7344ec793ea..35e7efed87a 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -788,7 +788,7 @@ impl<'tcx> Visitor<'tcx> for FindAssignments<'_, '_, 'tcx> { fn is_local_required(local: Local, body: &Body<'_>) -> bool { match body.local_kind(local) { LocalKind::Arg | LocalKind::ReturnPointer => true, - LocalKind::Var | LocalKind::Temp => false, + LocalKind::Temp => false, } } diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs index b7f1cdfc7f2..e6875fad306 100644 --- a/compiler/rustc_mir_transform/src/generator.rs +++ b/compiler/rustc_mir_transform/src/generator.rs @@ -924,13 +924,19 @@ fn compute_layout<'tcx>( debug!(?decl); let ignore_for_traits = if tcx.sess.opts.unstable_opts.drop_tracking_mir { + // Do not `assert_crate_local` here, as post-borrowck cleanup may have already cleared + // the information. This is alright, since `ignore_for_traits` is only relevant when + // this code runs on pre-cleanup MIR, and `ignore_for_traits = false` is the safer + // default. match decl.local_info { // Do not include raw pointers created from accessing `static` items, as those could // well be re-created by another access to the same static. - Some(box LocalInfo::StaticRef { is_thread_local, .. }) => !is_thread_local, + ClearCrossCrate::Set(box LocalInfo::StaticRef { is_thread_local, .. }) => { + !is_thread_local + } // Fake borrows are only read by fake reads, so do not have any reality in // post-analysis MIR. - Some(box LocalInfo::FakeBorrow) => true, + ClearCrossCrate::Set(box LocalInfo::FakeBorrow) => true, _ => false, } } else { diff --git a/compiler/rustc_mir_transform/src/large_enums.rs b/compiler/rustc_mir_transform/src/large_enums.rs index 89e0a007dac..9447a2ff040 100644 --- a/compiler/rustc_mir_transform/src/large_enums.rs +++ b/compiler/rustc_mir_transform/src/large_enums.rs @@ -158,10 +158,12 @@ impl EnumSizeOpt { tmp_ty, ), }; - let rval = Rvalue::Use(Operand::Constant(box (constant_vals))); + let rval = Rvalue::Use(Operand::Constant(Box::new(constant_vals))); - let const_assign = - Statement { source_info, kind: StatementKind::Assign(box (place, rval)) }; + let const_assign = Statement { + source_info, + kind: StatementKind::Assign(Box::new((place, rval))), + }; let discr_place = Place::from( local_decls @@ -170,7 +172,10 @@ impl EnumSizeOpt { let store_discr = Statement { source_info, - kind: StatementKind::Assign(box (discr_place, Rvalue::Discriminant(*rhs))), + kind: StatementKind::Assign(Box::new(( + discr_place, + Rvalue::Discriminant(*rhs), + ))), }; let discr_cast_place = @@ -178,14 +183,14 @@ impl EnumSizeOpt { let cast_discr = Statement { source_info, - kind: StatementKind::Assign(box ( + kind: StatementKind::Assign(Box::new(( discr_cast_place, Rvalue::Cast( CastKind::IntToInt, Operand::Copy(discr_place), tcx.types.usize, ), - )), + ))), }; let size_place = @@ -193,14 +198,14 @@ impl EnumSizeOpt { let store_size = Statement { source_info, - kind: StatementKind::Assign(box ( + kind: StatementKind::Assign(Box::new(( size_place, Rvalue::Use(Operand::Copy(Place { local: size_array_local, projection: tcx .mk_place_elems(&[PlaceElem::Index(discr_cast_place.local)]), })), - )), + ))), }; let dst = @@ -208,10 +213,10 @@ impl EnumSizeOpt { let dst_ptr = Statement { source_info, - kind: StatementKind::Assign(box ( + kind: StatementKind::Assign(Box::new(( dst, Rvalue::AddressOf(Mutability::Mut, *lhs), - )), + ))), }; let dst_cast_ty = tcx.mk_mut_ptr(tcx.types.u8); @@ -220,10 +225,10 @@ impl EnumSizeOpt { let dst_cast = Statement { source_info, - kind: StatementKind::Assign(box ( + kind: StatementKind::Assign(Box::new(( dst_cast_place, Rvalue::Cast(CastKind::PtrToPtr, Operand::Copy(dst), dst_cast_ty), - )), + ))), }; let src = @@ -231,10 +236,10 @@ impl EnumSizeOpt { let src_ptr = Statement { source_info, - kind: StatementKind::Assign(box ( + kind: StatementKind::Assign(Box::new(( src, Rvalue::AddressOf(Mutability::Not, *rhs), - )), + ))), }; let src_cast_ty = tcx.mk_imm_ptr(tcx.types.u8); @@ -243,24 +248,24 @@ impl EnumSizeOpt { let src_cast = Statement { source_info, - kind: StatementKind::Assign(box ( + kind: StatementKind::Assign(Box::new(( src_cast_place, Rvalue::Cast(CastKind::PtrToPtr, Operand::Copy(src), src_cast_ty), - )), + ))), }; let deinit_old = - Statement { source_info, kind: StatementKind::Deinit(box dst) }; + Statement { source_info, kind: StatementKind::Deinit(Box::new(dst)) }; let copy_bytes = Statement { source_info, - kind: StatementKind::Intrinsic( - box NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { + kind: StatementKind::Intrinsic(Box::new( + NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { src: Operand::Copy(src_cast_place), dst: Operand::Copy(dst_cast_place), count: Operand::Copy(size_place), }), - ), + )), }; let store_dead = Statement { diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 5fd923190ef..50c3023b02b 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -1,7 +1,6 @@ #![allow(rustc::potential_query_instability)] #![feature(box_patterns)] #![feature(drain_filter)] -#![feature(box_syntax)] #![feature(let_chains)] #![feature(map_try_insert)] #![feature(min_specialization)] @@ -30,9 +29,9 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_index::vec::IndexVec; use rustc_middle::mir::visit::Visitor as _; use rustc_middle::mir::{ - traversal, AnalysisPhase, Body, ConstQualifs, Constant, LocalDecl, MirPass, MirPhase, Operand, - Place, ProjectionElem, Promoted, RuntimePhase, Rvalue, SourceInfo, Statement, StatementKind, - TerminatorKind, + traversal, AnalysisPhase, Body, ClearCrossCrate, ConstQualifs, Constant, LocalDecl, MirPass, + MirPhase, Operand, Place, ProjectionElem, Promoted, RuntimePhase, Rvalue, SourceInfo, + Statement, StatementKind, TerminatorKind, }; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; @@ -278,14 +277,14 @@ fn mir_const(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) -> &Steal< // Unsafety check uses the raw mir, so make sure it is run. if !tcx.sess.opts.unstable_opts.thir_unsafeck { if let Some(param_did) = def.const_param_did { - tcx.ensure().unsafety_check_result_for_const_arg((def.did, param_did)); + tcx.ensure_with_value().unsafety_check_result_for_const_arg((def.did, param_did)); } else { - tcx.ensure().unsafety_check_result(def.did); + tcx.ensure_with_value().unsafety_check_result(def.did); } } // has_ffi_unwind_calls query uses the raw mir, so make sure it is run. - tcx.ensure().has_ffi_unwind_calls(def.did); + tcx.ensure_with_value().has_ffi_unwind_calls(def.did); let mut body = tcx.mir_built(def).steal(); @@ -433,7 +432,7 @@ fn mir_drops_elaborated_and_const_checked( if tcx.sess.opts.unstable_opts.drop_tracking_mir && let DefKind::Generator = tcx.def_kind(def.did) { - tcx.ensure().mir_generator_witnesses(def.did); + tcx.ensure_with_value().mir_generator_witnesses(def.did); } let mir_borrowck = tcx.mir_borrowck_opt_const_arg(def); @@ -444,7 +443,7 @@ fn mir_drops_elaborated_and_const_checked( // Do not compute the mir call graph without said call graph actually being used. if inline::Inline.is_enabled(&tcx.sess) { - let _ = tcx.mir_inliner_callees(ty::InstanceDef::Item(def)); + tcx.ensure_with_value().mir_inliner_callees(ty::InstanceDef::Item(def)); } } @@ -533,6 +532,12 @@ fn run_runtime_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &[&lower_intrinsics::LowerIntrinsics, &simplify::SimplifyCfg::new("elaborate-drops")]; pm::run_passes(tcx, body, passes, Some(MirPhase::Runtime(RuntimePhase::PostCleanup))); + + // Clear this by anticipation. Optimizations and runtime MIR have no reason to look + // into this information, which is meant for borrowck diagnostics. + for decl in &mut body.local_decls { + decl.local_info = ClearCrossCrate::Clear; + } } fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { @@ -613,7 +618,7 @@ fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> { // Run the `mir_for_ctfe` query, which depends on `mir_drops_elaborated_and_const_checked` // which we are going to steal below. Thus we need to run `mir_for_ctfe` first, so it // computes and caches its result. - Some(hir::ConstContext::ConstFn) => tcx.ensure().mir_for_ctfe(did), + Some(hir::ConstContext::ConstFn) => tcx.ensure_with_value().mir_for_ctfe(did), None => {} Some(other) => panic!("do not use `optimized_mir` for constants: {:?}", other), } diff --git a/compiler/rustc_mir_transform/src/nrvo.rs b/compiler/rustc_mir_transform/src/nrvo.rs index 4291e81c78c..b6e73eaad50 100644 --- a/compiler/rustc_mir_transform/src/nrvo.rs +++ b/compiler/rustc_mir_transform/src/nrvo.rs @@ -102,7 +102,7 @@ fn local_eligible_for_nrvo(body: &mut mir::Body<'_>) -> Option { mir::LocalKind::Arg => return None, mir::LocalKind::ReturnPointer => bug!("Return place was assigned to itself?"), - mir::LocalKind::Var | mir::LocalKind::Temp => {} + mir::LocalKind::Temp => {} } // If multiple different locals are copied to the return place. We can't pick a diff --git a/compiler/rustc_monomorphize/locales/en-US.ftl b/compiler/rustc_monomorphize/messages.ftl similarity index 100% rename from compiler/rustc_monomorphize/locales/en-US.ftl rename to compiler/rustc_monomorphize/messages.ftl diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index f6b791f29c1..5000fb71937 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -23,7 +23,7 @@ mod partitioning; mod polymorphize; mod util; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } fn custom_coerce_unsize_info<'tcx>( tcx: TyCtxtAt<'tcx>, diff --git a/compiler/rustc_parse/locales/en-US.ftl b/compiler/rustc_parse/messages.ftl similarity index 99% rename from compiler/rustc_parse/locales/en-US.ftl rename to compiler/rustc_parse/messages.ftl index 5c7dc1e2abf..c39ada95a4e 100644 --- a/compiler/rustc_parse/locales/en-US.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -709,7 +709,7 @@ parse_zero_chars = empty character literal parse_lone_slash = invalid trailing slash in literal .label = {parse_lone_slash} -parse_unskipped_whitespace = non-ASCII whitespace symbol '{$ch}' is not skipped +parse_unskipped_whitespace = whitespace symbol '{$ch}' is not skipped .label = {parse_unskipped_whitespace} parse_multiple_skipped_lines = multiple lines skipped by escaped newline @@ -731,3 +731,6 @@ parse_unknown_start_of_token = unknown start of token: {$escaped} [one] once more *[other] {$repeats} more times } + +parse_box_syntax_removed = `box_syntax` has been removed + .suggestion = use `Box::new()` instead diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 63e5bc50513..af0c3026c66 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2300,3 +2300,16 @@ impl HelpUseLatestEdition { } } } + +#[derive(Diagnostic)] +#[diag(parse_box_syntax_removed)] +pub struct BoxSyntaxRemoved<'a> { + #[primary_span] + #[suggestion( + code = "Box::new({code})", + applicability = "machine-applicable", + style = "verbose" + )] + pub span: Span, + pub code: &'a str, +} diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index d1c3fd0cd0f..17466cd0e6d 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -36,7 +36,7 @@ pub mod validate_attr; mod errors; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } // A bunch of utility functions of the form `parse__from_` // where includes crate, expr, item, stmt, tts, and one that diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index e00eda47c66..296eb4d653c 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -8,6 +8,7 @@ use super::{ use crate::errors; use crate::maybe_recover_from_interpolated_ty_qpath; +use ast::{Path, PathSegment}; use core::mem; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; @@ -29,6 +30,7 @@ use rustc_session::errors::{report_lit_error, ExprParenthesesNeeded}; use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_span::source_map::{self, Span, Spanned}; +use rustc_span::symbol::kw::PathRoot; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, Pos}; use thin_vec::{thin_vec, ThinVec}; @@ -636,11 +638,27 @@ impl<'a> Parser<'a> { self.parse_expr_unary(lo, UnOp::Not) } - /// Parse `box expr`. + /// Parse `box expr` - this syntax has been removed, but we still parse this + /// for now to provide an automated way to fix usages of it fn parse_expr_box(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { let (span, expr) = self.parse_expr_prefix_common(lo)?; - self.sess.gated_spans.gate(sym::box_syntax, span); - Ok((span, ExprKind::Box(expr))) + let code = self.sess.source_map().span_to_snippet(span.with_lo(lo.hi())).unwrap(); + self.sess.emit_err(errors::BoxSyntaxRemoved { span, code: code.trim() }); + // So typechecking works, parse `box ` as `::std::boxed::Box::new(expr)` + let path = Path { + span, + segments: [ + PathSegment::from_ident(Ident::with_dummy_span(PathRoot)), + PathSegment::from_ident(Ident::with_dummy_span(sym::std)), + PathSegment::from_ident(Ident::from_str("boxed")), + PathSegment::from_ident(Ident::from_str("Box")), + PathSegment::from_ident(Ident::with_dummy_span(sym::new)), + ] + .into(), + tokens: None, + }; + let path = self.mk_expr(span, ExprKind::Path(None, path)); + Ok((span, self.mk_call(path, ThinVec::from([expr])))) } fn is_mistaken_not_ident_negation(&self) -> bool { @@ -2105,7 +2123,7 @@ impl<'a> Parser<'a> { ClosureBinder::NotPresent }; - let constness = self.parse_closure_constness(Case::Sensitive); + let constness = self.parse_closure_constness(); let movability = if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable }; diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 6e9b447fa61..3251dd6d0c6 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -1196,9 +1196,13 @@ impl<'a> Parser<'a> { self.parse_constness_(case, false) } - /// Parses constness for closures - fn parse_closure_constness(&mut self, case: Case) -> Const { - self.parse_constness_(case, true) + /// Parses constness for closures (case sensitive, feature-gated) + fn parse_closure_constness(&mut self) -> Const { + let constness = self.parse_constness_(Case::Sensitive, true); + if let Const::Yes(span) = constness { + self.sess.gated_spans.gate(sym::const_closures, span); + } + constness } fn parse_constness_(&mut self, case: Case, is_closure: bool) -> Const { diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 6fe4da71f6b..3d9d2cc62e3 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -624,10 +624,12 @@ impl<'a> Parser<'a> { /// /// Note that this does *not* parse bare trait objects. fn parse_dyn_ty(&mut self, impl_dyn_multi: &mut bool) -> PResult<'a, TyKind> { + let lo = self.token.span; self.bump(); // `dyn` // parse dyn* types let syntax = if self.eat(&TokenKind::BinOp(token::Star)) { + self.sess.gated_spans.gate(sym::dyn_star, lo.to(self.prev_token.span)); TraitObjectSyntax::DynStar } else { TraitObjectSyntax::Dyn diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 8a3cedfee79..1c5410c5658 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -14,6 +14,7 @@ // We want to be able to build this crate with a stable compiler, so no // `#![feature]` attributes should be added. +use rustc_lexer::unescape; pub use Alignment::*; pub use Count::*; pub use Piece::*; @@ -234,8 +235,10 @@ pub struct Parser<'a> { last_opening_brace: Option, /// Whether the source string is comes from `println!` as opposed to `format!` or `print!` append_newline: bool, - /// Whether this formatting string is a literal or it comes from a macro. - pub is_literal: bool, + /// Whether this formatting string was written directly in the source. This controls whether we + /// can use spans to refer into it and give better error messages. + /// N.B: This does _not_ control whether implicit argument captures can be used. + pub is_source_literal: bool, /// Start position of the current line. cur_line_start: usize, /// Start and end byte offset of every line of the format string. Excludes @@ -262,7 +265,7 @@ impl<'a> Iterator for Parser<'a> { } else { let arg = self.argument(lbrace_end); if let Some(rbrace_pos) = self.must_consume('}') { - if self.is_literal { + if self.is_source_literal { let lbrace_byte_pos = self.to_span_index(pos); let rbrace_byte_pos = self.to_span_index(rbrace_pos); @@ -302,7 +305,7 @@ impl<'a> Iterator for Parser<'a> { _ => Some(String(self.string(pos))), } } else { - if self.is_literal { + if self.is_source_literal { let span = self.span(self.cur_line_start, self.input.len()); if self.line_spans.last() != Some(&span) { self.line_spans.push(span); @@ -322,8 +325,8 @@ impl<'a> Parser<'a> { append_newline: bool, mode: ParseMode, ) -> Parser<'a> { - let input_string_kind = find_width_map_from_snippet(snippet, style); - let (width_map, is_literal) = match input_string_kind { + let input_string_kind = find_width_map_from_snippet(s, snippet, style); + let (width_map, is_source_literal) = match input_string_kind { InputStringKind::Literal { width_mappings } => (width_mappings, true), InputStringKind::NotALiteral => (Vec::new(), false), }; @@ -339,7 +342,7 @@ impl<'a> Parser<'a> { width_map, last_opening_brace: None, append_newline, - is_literal, + is_source_literal, cur_line_start: 0, line_spans: vec![], } @@ -532,13 +535,13 @@ impl<'a> Parser<'a> { '{' | '}' => { return &self.input[start..pos]; } - '\n' if self.is_literal => { + '\n' if self.is_source_literal => { self.line_spans.push(self.span(self.cur_line_start, pos)); self.cur_line_start = pos + 1; self.cur.next(); } _ => { - if self.is_literal && pos == self.cur_line_start && c.is_whitespace() { + if self.is_source_literal && pos == self.cur_line_start && c.is_whitespace() { self.cur_line_start = pos + c.len_utf8(); } self.cur.next(); @@ -890,6 +893,7 @@ impl<'a> Parser<'a> { /// written code (code snippet) and the `InternedString` that gets processed in the `Parser` /// in order to properly synthesise the intra-string `Span`s for error diagnostics. fn find_width_map_from_snippet( + input: &str, snippet: Option, str_style: Option, ) -> InputStringKind { @@ -902,8 +906,27 @@ fn find_width_map_from_snippet( return InputStringKind::Literal { width_mappings: Vec::new() }; } + // Strip quotes. let snippet = &snippet[1..snippet.len() - 1]; + // Macros like `println` add a newline at the end. That technically doens't make them "literals" anymore, but it's fine + // since we will never need to point our spans there, so we lie about it here by ignoring it. + // Since there might actually be newlines in the source code, we need to normalize away all trailing newlines. + // If we only trimmed it off the input, `format!("\n")` would cause a mismatch as here we they actually match up. + // Alternatively, we could just count the trailing newlines and only trim one from the input if they don't match up. + let input_no_nl = input.trim_end_matches('\n'); + let Some(unescaped) = unescape_string(snippet) else { + return InputStringKind::NotALiteral; + }; + + let unescaped_no_nl = unescaped.trim_end_matches('\n'); + + if unescaped_no_nl != input_no_nl { + // The source string that we're pointing at isn't our input, so spans pointing at it will be incorrect. + // This can for example happen with proc macros that respan generated literals. + return InputStringKind::NotALiteral; + } + let mut s = snippet.char_indices(); let mut width_mappings = vec![]; while let Some((pos, c)) = s.next() { @@ -986,6 +1009,19 @@ fn find_width_map_from_snippet( InputStringKind::Literal { width_mappings } } +fn unescape_string(string: &str) -> Option { + let mut buf = string::String::new(); + let mut ok = true; + unescape::unescape_literal(string, unescape::Mode::Str, &mut |_, unescaped_char| { + match unescaped_char { + Ok(c) => buf.push(c), + Err(_) => ok = false, + } + }); + + ok.then_some(buf) +} + // Assert a reasonable size for `Piece` #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(Piece<'_>, 16); diff --git a/compiler/rustc_passes/locales/en-US.ftl b/compiler/rustc_passes/messages.ftl similarity index 100% rename from compiler/rustc_passes/locales/en-US.ftl rename to compiler/rustc_passes/messages.ftl diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 5ef3e13eff8..c8d371dd084 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -2215,7 +2215,7 @@ impl CheckAttrVisitor<'_> { // `fn(TokenStream) -> TokenStream` after some substitution of generic arguments. // // Properly checking this means pulling in additional `rustc` crates, so we don't. - let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsInfer }; + let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsCandidateKey }; if sig.abi != Abi::Rust { tcx.sess.emit_err(errors::ProcMacroInvalidAbi { diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 59877ebb26b..ec1e1d0054b 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -243,6 +243,12 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { continue; } + // Avoid accessing the HIR for the synthesized associated type generated for RPITITs. + if self.tcx.opt_rpitit_info(id.to_def_id()).is_some() { + self.live_symbols.insert(id); + continue; + } + // in the case of tuple struct constructors we want to check the item, not the generated // tuple struct constructor function let id = self.struct_constructors.get(&id).copied().unwrap_or(id); diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index 5e2d2d3e5a7..3e0d53029ef 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -565,7 +565,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { record_variants!( (self, e, e.kind, Id::None, ast, Expr, ExprKind), [ - Box, Array, ConstBlock, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, Let, + Array, ConstBlock, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, Let, If, While, ForLoop, Loop, Match, Closure, Block, Async, Await, TryBlock, Assign, AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret, InlineAsm, FormatArgs, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet, IncludedBytes, Err diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index 0cb8424082c..b7e07aff42b 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -42,7 +42,7 @@ pub mod stability; mod upvars; mod weak_lang_items; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } pub fn provide(providers: &mut Providers) { check_attr::provide(providers); diff --git a/compiler/rustc_plugin_impl/locales/en-US.ftl b/compiler/rustc_plugin_impl/messages.ftl similarity index 100% rename from compiler/rustc_plugin_impl/locales/en-US.ftl rename to compiler/rustc_plugin_impl/messages.ftl diff --git a/compiler/rustc_plugin_impl/src/lib.rs b/compiler/rustc_plugin_impl/src/lib.rs index 3f03eef9ee3..672189e22cf 100644 --- a/compiler/rustc_plugin_impl/src/lib.rs +++ b/compiler/rustc_plugin_impl/src/lib.rs @@ -18,7 +18,7 @@ use rustc_macros::fluent_messages; mod errors; pub mod load; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } /// Structure used to register plugins. /// diff --git a/compiler/rustc_privacy/locales/en-US.ftl b/compiler/rustc_privacy/messages.ftl similarity index 100% rename from compiler/rustc_privacy/locales/en-US.ftl rename to compiler/rustc_privacy/messages.ftl diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 99a44b0ca4d..ef7c68c1a33 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -46,7 +46,7 @@ use errors::{ UnnamedItemIsPrivate, }; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } //////////////////////////////////////////////////////////////////////////////// /// Generic infrastructure used to implement specific visitors below. diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index d7708a3bc3f..035bfe978f2 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -31,6 +31,7 @@ use rustc_span::Span; #[macro_use] mod plumbing; pub use plumbing::QueryCtxt; +use rustc_query_system::dep_graph::SerializedDepNodeIndex; use rustc_query_system::query::*; #[cfg(parallel_compiler)] pub use rustc_query_system::query::{deadlock, QueryContext}; diff --git a/compiler/rustc_query_impl/src/on_disk_cache.rs b/compiler/rustc_query_impl/src/on_disk_cache.rs index 6522b1406be..35b7e5919e4 100644 --- a/compiler/rustc_query_impl/src/on_disk_cache.rs +++ b/compiler/rustc_query_impl/src/on_disk_cache.rs @@ -388,6 +388,12 @@ impl<'sess> OnDiskCache<'sess> { debug_assert!(prev.is_none()); } + /// Return whether the cached query result can be decoded. + pub fn loadable_from_disk(&self, dep_node_index: SerializedDepNodeIndex) -> bool { + self.query_result_index.contains_key(&dep_node_index) + // with_decoder is infallible, so we can stop here + } + /// Returns the cached query result if there is something in the cache for /// the given `SerializedDepNodeIndex`; otherwise returns `None`. pub fn try_load_query_result<'tcx, T>( @@ -398,7 +404,9 @@ impl<'sess> OnDiskCache<'sess> { where T: for<'a> Decodable>, { - self.load_indexed(tcx, dep_node_index, &self.query_result_index) + let opt_value = self.load_indexed(tcx, dep_node_index, &self.query_result_index); + debug_assert_eq!(opt_value.is_some(), self.loadable_from_disk(dep_node_index)); + opt_value } /// Stores side effect emitted during computation of an anonymous query. @@ -428,8 +436,8 @@ impl<'sess> OnDiskCache<'sess> { T: for<'a> Decodable>, { let pos = index.get(&dep_node_index).cloned()?; - - self.with_decoder(tcx, pos, |decoder| Some(decode_tagged(decoder, dep_node_index))) + let value = self.with_decoder(tcx, pos, |decoder| decode_tagged(decoder, dep_node_index)); + Some(value) } fn with_decoder<'a, 'tcx, T, F: for<'s> FnOnce(&mut CacheDecoder<'s, 'tcx>) -> T>( diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 005ce16dbb9..ca3c3997df0 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -364,6 +364,14 @@ where } } +pub(crate) fn loadable_from_disk<'tcx>(tcx: QueryCtxt<'tcx>, id: SerializedDepNodeIndex) -> bool { + if let Some(cache) = tcx.on_disk_cache().as_ref() { + cache.loadable_from_disk(id) + } else { + false + } +} + pub(crate) fn try_load_from_disk<'tcx, V>( tcx: QueryCtxt<'tcx>, id: SerializedDepNodeIndex, @@ -535,6 +543,21 @@ macro_rules! define_queries { }) } + #[inline] + fn loadable_from_disk( + self, + _qcx: QueryCtxt<'tcx>, + _key: &Self::Key, + _index: SerializedDepNodeIndex, + ) -> bool { + should_ever_cache_on_disk!([$($modifiers)*] { + self.cache_on_disk(_qcx.tcx, _key) && + $crate::plumbing::loadable_from_disk(_qcx, _index) + } { + false + }) + } + #[inline(always)] fn anon(self) -> bool { is_anon!([$($modifiers)*]) diff --git a/compiler/rustc_query_system/locales/en-US.ftl b/compiler/rustc_query_system/messages.ftl similarity index 100% rename from compiler/rustc_query_system/locales/en-US.ftl rename to compiler/rustc_query_system/messages.ftl diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 59e0c359745..8a9a1238606 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -70,7 +70,7 @@ impl DepNodeColor { } } -struct DepGraphData { +pub struct DepGraphData { /// The new encoding of the dependency graph, optimized for red/green /// tracking. The `current` field is the dependency graph of only the /// current compilation session: We don't merge the previous dep-graph into @@ -168,6 +168,11 @@ impl DepGraph { DepGraph { data: None, virtual_dep_node_index: Lrc::new(AtomicU32::new(0)) } } + #[inline] + pub fn data(&self) -> Option<&DepGraphData> { + self.data.as_deref() + } + /// Returns `true` if we are actually building the full dep-graph, and `false` otherwise. #[inline] pub fn is_fully_enabled(&self) -> bool { @@ -252,6 +257,38 @@ impl DepGraph { K::with_deps(TaskDepsRef::Forbid, op) } + #[inline(always)] + pub fn with_task, A: Debug, R>( + &self, + key: DepNode, + cx: Ctxt, + arg: A, + task: fn(Ctxt, A) -> R, + hash_result: Option, &R) -> Fingerprint>, + ) -> (R, DepNodeIndex) { + match self.data() { + Some(data) => data.with_task(key, cx, arg, task, hash_result), + None => (task(cx, arg), self.next_virtual_depnode_index()), + } + } + + pub fn with_anon_task, OP, R>( + &self, + cx: Tcx, + dep_kind: K, + op: OP, + ) -> (R, DepNodeIndex) + where + OP: FnOnce() -> R, + { + match self.data() { + Some(data) => data.with_anon_task(cx, dep_kind, op), + None => (op(), self.next_virtual_depnode_index()), + } + } +} + +impl DepGraphData { /// Starts a new dep-graph task. Dep-graph tasks are specified /// using a free function (`task`) and **not** a closure -- this /// is intentional because we want to exercise tight control over @@ -288,29 +325,6 @@ impl DepGraph { task: fn(Ctxt, A) -> R, hash_result: Option, &R) -> Fingerprint>, ) -> (R, DepNodeIndex) { - if self.is_fully_enabled() { - self.with_task_impl(key, cx, arg, task, hash_result) - } else { - // Incremental compilation is turned off. We just execute the task - // without tracking. We still provide a dep-node index that uniquely - // identifies the task so that we have a cheap way of referring to - // the query for self-profiling. - (task(cx, arg), self.next_virtual_depnode_index()) - } - } - - #[inline(always)] - fn with_task_impl, A: Debug, R>( - &self, - key: DepNode, - cx: Ctxt, - arg: A, - task: fn(Ctxt, A) -> R, - hash_result: Option, &R) -> Fingerprint>, - ) -> (R, DepNodeIndex) { - // This function is only called when the graph is enabled. - let data = self.data.as_ref().unwrap(); - // If the following assertion triggers, it can have two reasons: // 1. Something is wrong with DepNode creation, either here or // in `DepGraph::try_mark_green()`. @@ -351,9 +365,9 @@ impl DepGraph { let print_status = cfg!(debug_assertions) && dcx.sess().opts.unstable_opts.dep_tasks; // Intern the new `DepNode`. - let (dep_node_index, prev_and_color) = data.current.intern_node( + let (dep_node_index, prev_and_color) = self.current.intern_node( dcx.profiler(), - &data.previous, + &self.previous, key, edges, current_fingerprint, @@ -364,12 +378,12 @@ impl DepGraph { if let Some((prev_index, color)) = prev_and_color { debug_assert!( - data.colors.get(prev_index).is_none(), + self.colors.get(prev_index).is_none(), "DepGraph::with_task() - Duplicate DepNodeColor \ insertion for {key:?}" ); - data.colors.insert(prev_index, color); + self.colors.insert(prev_index, color); } (result, dep_node_index) @@ -388,57 +402,55 @@ impl DepGraph { { debug_assert!(!cx.is_eval_always(dep_kind)); - if let Some(ref data) = self.data { - let task_deps = Lock::new(TaskDeps::default()); - let result = K::with_deps(TaskDepsRef::Allow(&task_deps), op); - let task_deps = task_deps.into_inner(); - let task_deps = task_deps.reads; + let task_deps = Lock::new(TaskDeps::default()); + let result = K::with_deps(TaskDepsRef::Allow(&task_deps), op); + let task_deps = task_deps.into_inner(); + let task_deps = task_deps.reads; - let dep_node_index = match task_deps.len() { - 0 => { - // Because the dep-node id of anon nodes is computed from the sets of its - // dependencies we already know what the ID of this dependency-less node is - // going to be (i.e. equal to the precomputed - // `SINGLETON_DEPENDENCYLESS_ANON_NODE`). As a consequence we can skip creating - // a `StableHasher` and sending the node through interning. - DepNodeIndex::SINGLETON_DEPENDENCYLESS_ANON_NODE - } - 1 => { - // When there is only one dependency, don't bother creating a node. - task_deps[0] - } - _ => { - // The dep node indices are hashed here instead of hashing the dep nodes of the - // dependencies. These indices may refer to different nodes per session, but this isn't - // a problem here because we that ensure the final dep node hash is per session only by - // combining it with the per session random number `anon_id_seed`. This hash only need - // to map the dependencies to a single value on a per session basis. - let mut hasher = StableHasher::new(); - task_deps.hash(&mut hasher); + let dep_node_index = match task_deps.len() { + 0 => { + // Because the dep-node id of anon nodes is computed from the sets of its + // dependencies we already know what the ID of this dependency-less node is + // going to be (i.e. equal to the precomputed + // `SINGLETON_DEPENDENCYLESS_ANON_NODE`). As a consequence we can skip creating + // a `StableHasher` and sending the node through interning. + DepNodeIndex::SINGLETON_DEPENDENCYLESS_ANON_NODE + } + 1 => { + // When there is only one dependency, don't bother creating a node. + task_deps[0] + } + _ => { + // The dep node indices are hashed here instead of hashing the dep nodes of the + // dependencies. These indices may refer to different nodes per session, but this isn't + // a problem here because we that ensure the final dep node hash is per session only by + // combining it with the per session random number `anon_id_seed`. This hash only need + // to map the dependencies to a single value on a per session basis. + let mut hasher = StableHasher::new(); + task_deps.hash(&mut hasher); - let target_dep_node = DepNode { - kind: dep_kind, - // Fingerprint::combine() is faster than sending Fingerprint - // through the StableHasher (at least as long as StableHasher - // is so slow). - hash: data.current.anon_id_seed.combine(hasher.finish()).into(), - }; + let target_dep_node = DepNode { + kind: dep_kind, + // Fingerprint::combine() is faster than sending Fingerprint + // through the StableHasher (at least as long as StableHasher + // is so slow). + hash: self.current.anon_id_seed.combine(hasher.finish()).into(), + }; - data.current.intern_new_node( - cx.profiler(), - target_dep_node, - task_deps, - Fingerprint::ZERO, - ) - } - }; + self.current.intern_new_node( + cx.profiler(), + target_dep_node, + task_deps, + Fingerprint::ZERO, + ) + } + }; - (result, dep_node_index) - } else { - (op(), self.next_virtual_depnode_index()) - } + (result, dep_node_index) } +} +impl DepGraph { #[inline] pub fn read_index(&self, dep_node_index: DepNodeIndex) { if let Some(ref data) = self.data { @@ -519,9 +531,9 @@ impl DepGraph { // value to an existing node. // // For sanity, we still check that the loaded stable hash and the new one match. - if let Some(dep_node_index) = self.dep_node_index_of_opt(&node) { + if let Some(dep_node_index) = data.dep_node_index_of_opt(&node) { let _current_fingerprint = - crate::query::incremental_verify_ich(cx, result, &node, hash_result); + crate::query::incremental_verify_ich(cx, data, result, &node, hash_result); #[cfg(debug_assertions)] if hash_result.is_some() { @@ -577,32 +589,57 @@ impl DepGraph { self.next_virtual_depnode_index() } } +} - #[inline] - pub fn dep_node_index_of(&self, dep_node: &DepNode) -> DepNodeIndex { - self.dep_node_index_of_opt(dep_node).unwrap() - } - +impl DepGraphData { #[inline] pub fn dep_node_index_of_opt(&self, dep_node: &DepNode) -> Option { - let data = self.data.as_ref().unwrap(); - let current = &data.current; - - if let Some(prev_index) = data.previous.node_to_index_opt(dep_node) { - current.prev_index_to_index.lock()[prev_index] + if let Some(prev_index) = self.previous.node_to_index_opt(dep_node) { + self.current.prev_index_to_index.lock()[prev_index] } else { - current.new_node_to_index.get_shard_by_value(dep_node).lock().get(dep_node).copied() + self.current + .new_node_to_index + .get_shard_by_value(dep_node) + .lock() + .get(dep_node) + .copied() } } #[inline] pub fn dep_node_exists(&self, dep_node: &DepNode) -> bool { - self.data.is_some() && self.dep_node_index_of_opt(dep_node).is_some() + self.dep_node_index_of_opt(dep_node).is_some() + } + + fn node_color(&self, dep_node: &DepNode) -> Option { + if let Some(prev_index) = self.previous.node_to_index_opt(dep_node) { + self.colors.get(prev_index) + } else { + // This is a node that did not exist in the previous compilation session. + None + } + } + + /// Returns true if the given node has been marked as green during the + /// current compilation session. Used in various assertions + pub fn is_green(&self, dep_node: &DepNode) -> bool { + self.node_color(dep_node).map_or(false, |c| c.is_green()) } #[inline] pub fn prev_fingerprint_of(&self, dep_node: &DepNode) -> Option { - self.data.as_ref().unwrap().previous.fingerprint_of(dep_node) + self.previous.fingerprint_of(dep_node) + } + + pub fn mark_debug_loaded_from_disk(&self, dep_node: DepNode) { + self.debug_loaded_from_disk.lock().insert(dep_node); + } +} + +impl DepGraph { + #[inline] + pub fn dep_node_exists(&self, dep_node: &DepNode) -> bool { + self.data.as_ref().and_then(|data| data.dep_node_index_of_opt(dep_node)).is_some() } /// Checks whether a previous work product exists for `v` and, if @@ -617,10 +654,6 @@ impl DepGraph { &self.data.as_ref().unwrap().previous_work_products } - pub fn mark_debug_loaded_from_disk(&self, dep_node: DepNode) { - self.data.as_ref().unwrap().debug_loaded_from_disk.lock().insert(dep_node); - } - pub fn debug_was_loaded_from_disk(&self, dep_node: DepNode) -> bool { self.data.as_ref().unwrap().debug_loaded_from_disk.lock().contains(&dep_node) } @@ -645,17 +678,22 @@ impl DepGraph { fn node_color(&self, dep_node: &DepNode) -> Option { if let Some(ref data) = self.data { - if let Some(prev_index) = data.previous.node_to_index_opt(dep_node) { - return data.colors.get(prev_index); - } else { - // This is a node that did not exist in the previous compilation session. - return None; - } + return data.node_color(dep_node); } None } + pub fn try_mark_green>( + &self, + qcx: Qcx, + dep_node: &DepNode, + ) -> Option<(SerializedDepNodeIndex, DepNodeIndex)> { + self.data().and_then(|data| data.try_mark_green(qcx, dep_node)) + } +} + +impl DepGraphData { /// Try to mark a node index for the node dep_node. /// /// A node will have an index, when it's already been marked green, or when we can mark it @@ -668,26 +706,23 @@ impl DepGraph { ) -> Option<(SerializedDepNodeIndex, DepNodeIndex)> { debug_assert!(!qcx.dep_context().is_eval_always(dep_node.kind)); - // Return None if the dep graph is disabled - let data = self.data.as_ref()?; - // Return None if the dep node didn't exist in the previous session - let prev_index = data.previous.node_to_index_opt(dep_node)?; + let prev_index = self.previous.node_to_index_opt(dep_node)?; - match data.colors.get(prev_index) { + match self.colors.get(prev_index) { Some(DepNodeColor::Green(dep_node_index)) => return Some((prev_index, dep_node_index)), Some(DepNodeColor::Red) => return None, None => {} } - let backtrace = backtrace_printer(qcx.dep_context().sess(), data, prev_index); + let backtrace = backtrace_printer(qcx.dep_context().sess(), self, prev_index); // This DepNode and the corresponding query invocation existed // in the previous compilation session too, so we can try to // mark it as green by recursively marking all of its // dependencies green. let ret = self - .try_mark_previous_green(qcx, data, prev_index, &dep_node) + .try_mark_previous_green(qcx, prev_index, &dep_node) .map(|dep_node_index| (prev_index, dep_node_index)); // We succeeded, no backtrace. @@ -695,16 +730,15 @@ impl DepGraph { return ret; } - #[instrument(skip(self, qcx, data, parent_dep_node_index), level = "debug")] + #[instrument(skip(self, qcx, parent_dep_node_index), level = "debug")] fn try_mark_parent_green>( &self, qcx: Qcx, - data: &DepGraphData, parent_dep_node_index: SerializedDepNodeIndex, dep_node: &DepNode, ) -> Option<()> { - let dep_dep_node_color = data.colors.get(parent_dep_node_index); - let dep_dep_node = &data.previous.index_to_node(parent_dep_node_index); + let dep_dep_node_color = self.colors.get(parent_dep_node_index); + let dep_dep_node = &self.previous.index_to_node(parent_dep_node_index); match dep_dep_node_color { Some(DepNodeColor::Green(_)) => { @@ -733,8 +767,7 @@ impl DepGraph { dep_dep_node, dep_dep_node.hash, ); - let node_index = - self.try_mark_previous_green(qcx, data, parent_dep_node_index, dep_dep_node); + let node_index = self.try_mark_previous_green(qcx, parent_dep_node_index, dep_dep_node); if node_index.is_some() { debug!("managed to MARK dependency {dep_dep_node:?} as green",); @@ -750,7 +783,7 @@ impl DepGraph { return None; } - let dep_dep_node_color = data.colors.get(parent_dep_node_index); + let dep_dep_node_color = self.colors.get(parent_dep_node_index); match dep_dep_node_color { Some(DepNodeColor::Green(_)) => { @@ -783,30 +816,29 @@ impl DepGraph { } /// Try to mark a dep-node which existed in the previous compilation session as green. - #[instrument(skip(self, qcx, data, prev_dep_node_index), level = "debug")] + #[instrument(skip(self, qcx, prev_dep_node_index), level = "debug")] fn try_mark_previous_green>( &self, qcx: Qcx, - data: &DepGraphData, prev_dep_node_index: SerializedDepNodeIndex, dep_node: &DepNode, ) -> Option { #[cfg(not(parallel_compiler))] { debug_assert!(!self.dep_node_exists(dep_node)); - debug_assert!(data.colors.get(prev_dep_node_index).is_none()); + debug_assert!(self.colors.get(prev_dep_node_index).is_none()); } // We never try to mark eval_always nodes as green debug_assert!(!qcx.dep_context().is_eval_always(dep_node.kind)); - debug_assert_eq!(data.previous.index_to_node(prev_dep_node_index), *dep_node); + debug_assert_eq!(self.previous.index_to_node(prev_dep_node_index), *dep_node); - let prev_deps = data.previous.edge_targets_from(prev_dep_node_index); + let prev_deps = self.previous.edge_targets_from(prev_dep_node_index); for &dep_dep_node_index in prev_deps { - let backtrace = backtrace_printer(qcx.dep_context().sess(), data, dep_dep_node_index); - let success = self.try_mark_parent_green(qcx, data, dep_dep_node_index, dep_node); + let backtrace = backtrace_printer(qcx.dep_context().sess(), self, dep_dep_node_index); + let success = self.try_mark_parent_green(qcx, dep_dep_node_index, dep_node); backtrace.disable(); success?; } @@ -819,9 +851,9 @@ impl DepGraph { // We allocating an entry for the node in the current dependency graph and // adding all the appropriate edges imported from the previous graph - let dep_node_index = data.current.promote_node_and_deps_to_current( + let dep_node_index = self.current.promote_node_and_deps_to_current( qcx.dep_context().profiler(), - &data.previous, + &self.previous, prev_dep_node_index, ); @@ -833,20 +865,20 @@ impl DepGraph { #[cfg(not(parallel_compiler))] debug_assert!( - data.colors.get(prev_dep_node_index).is_none(), + self.colors.get(prev_dep_node_index).is_none(), "DepGraph::try_mark_previous_green() - Duplicate DepNodeColor \ insertion for {dep_node:?}" ); if !side_effects.is_empty() { - self.with_query_deserialization(|| { - self.emit_side_effects(qcx, data, dep_node_index, side_effects) + qcx.dep_context().dep_graph().with_query_deserialization(|| { + self.emit_side_effects(qcx, dep_node_index, side_effects) }); } // ... and finally storing a "Green" entry in the color map. // Multiple threads can all write the same color here - data.colors.insert(prev_dep_node_index, DepNodeColor::Green(dep_node_index)); + self.colors.insert(prev_dep_node_index, DepNodeColor::Green(dep_node_index)); debug!("successfully marked {dep_node:?} as green"); Some(dep_node_index) @@ -859,11 +891,10 @@ impl DepGraph { fn emit_side_effects>( &self, qcx: Qcx, - data: &DepGraphData, dep_node_index: DepNodeIndex, side_effects: QuerySideEffects, ) { - let mut processed = data.processed_side_effects.lock(); + let mut processed = self.processed_side_effects.lock(); if processed.insert(dep_node_index) { // We were the first to insert the node in the set so this thread @@ -879,7 +910,9 @@ impl DepGraph { } } } +} +impl DepGraph { /// Returns true if the given node has been marked as red during the /// current compilation session. Used in various assertions pub fn is_red(&self, dep_node: &DepNode) -> bool { diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs index ba83b775631..5a7b9ae2ab4 100644 --- a/compiler/rustc_query_system/src/dep_graph/mod.rs +++ b/compiler/rustc_query_system/src/dep_graph/mod.rs @@ -6,7 +6,8 @@ mod serialized; pub use dep_node::{DepKindStruct, DepNode, DepNodeParams, WorkProductId}; pub use graph::{ - hash_result, DepGraph, DepNodeColor, DepNodeIndex, TaskDeps, TaskDepsRef, WorkProduct, + hash_result, DepGraph, DepGraphData, DepNodeColor, DepNodeIndex, TaskDeps, TaskDepsRef, + WorkProduct, }; pub use query::DepGraphQuery; pub use serialized::{SerializedDepGraph, SerializedDepNodeIndex}; diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs index 6cc4c9a7e1e..bb812b006e9 100644 --- a/compiler/rustc_query_system/src/lib.rs +++ b/compiler/rustc_query_system/src/lib.rs @@ -30,4 +30,4 @@ pub use error::LayoutOfDepth; pub use error::QueryOverflow; pub use values::Value; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_query_system/src/query/config.rs b/compiler/rustc_query_system/src/query/config.rs index e44a00ca6cb..a0aeb812af9 100644 --- a/compiler/rustc_query_system/src/query/config.rs +++ b/compiler/rustc_query_system/src/query/config.rs @@ -43,6 +43,8 @@ pub trait QueryConfig: Copy { fn try_load_from_disk(self, qcx: Qcx, idx: &Self::Key) -> TryLoadFromDisk; + fn loadable_from_disk(self, qcx: Qcx, key: &Self::Key, idx: SerializedDepNodeIndex) -> bool; + fn anon(self) -> bool; fn eval_always(self) -> bool; fn depth_limit(self) -> bool; diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 005fcd8c4cc..005512cf53e 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -2,8 +2,8 @@ //! generate the actual methods on tcx which find and execute the provider, //! manage the caches, and so forth. -use crate::dep_graph::HasDepContext; use crate::dep_graph::{DepContext, DepKind, DepNode, DepNodeIndex, DepNodeParams}; +use crate::dep_graph::{DepGraphData, HasDepContext}; use crate::ich::StableHashingContext; use crate::query::caches::QueryCache; use crate::query::job::{report_cycle, QueryInfo, QueryJob, QueryJobId, QueryJobInfo}; @@ -17,7 +17,7 @@ use rustc_data_structures::profiling::TimingGuard; #[cfg(parallel_compiler)] use rustc_data_structures::sharded::Sharded; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_data_structures::sync::Lock; +use rustc_data_structures::sync::{Lock, LockGuard}; use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, FatalError}; use rustc_session::Session; use rustc_span::{Span, DUMMY_SP}; @@ -178,16 +178,13 @@ where fn try_start<'b, Qcx>( qcx: &'b Qcx, state: &'b QueryState, + mut state_lock: LockGuard<'b, FxHashMap>>, span: Span, key: K, ) -> TryGetJob<'b, K, D> where Qcx: QueryContext + HasDepContext, { - #[cfg(parallel_compiler)] - let mut state_lock = state.active.get_shard_by_value(&key).lock(); - #[cfg(not(parallel_compiler))] - let mut state_lock = state.active.lock(); let lock = &mut *state_lock; let current_job_id = qcx.current_query_job(); @@ -362,7 +359,25 @@ where Qcx: QueryContext, { let state = query.query_state(qcx); - match JobOwner::<'_, Q::Key, Qcx::DepKind>::try_start(&qcx, state, span, key) { + #[cfg(parallel_compiler)] + let state_lock = state.active.get_shard_by_value(&key).lock(); + #[cfg(not(parallel_compiler))] + let state_lock = state.active.lock(); + + // For the parallel compiler we need to check both the query cache and query state structures + // while holding the state lock to ensure that 1) the query has not yet completed and 2) the + // query is not still executing. Without checking the query cache here, we can end up + // re-executing the query since `try_start` only checks that the query is not currently + // executing, but another thread may have already completed the query and stores it result + // in the query cache. + if cfg!(parallel_compiler) && qcx.dep_context().sess().threads() > 1 { + if let Some((value, index)) = query.query_cache(qcx).lookup(&key) { + qcx.dep_context().profiler().query_cache_hit(index.into()); + return (value, Some(index)); + } + } + + match JobOwner::<'_, Q::Key, Qcx::DepKind>::try_start(&qcx, state, state_lock, span, key) { TryGetJob::NotYetStarted(job) => { let (result, dep_node_index) = execute_job(query, qcx, key.clone(), dep_node, job.id); let cache = query.query_cache(qcx); @@ -411,32 +426,34 @@ where Qcx: QueryContext, { let dep_graph = qcx.dep_context().dep_graph(); + let dep_graph_data = match dep_graph.data() { + // Fast path for when incr. comp. is off. + None => { + // Fingerprint the key, just to assert that it doesn't + // have anything we don't consider hashable + if cfg!(debug_assertions) { + let _ = key.to_fingerprint(*qcx.dep_context()); + } - // Fast path for when incr. comp. is off. - if !dep_graph.is_fully_enabled() { - // Fingerprint the key, just to assert that it doesn't - // have anything we don't consider hashable - if cfg!(debug_assertions) { - let _ = key.to_fingerprint(*qcx.dep_context()); + let prof_timer = qcx.dep_context().profiler().query_provider(); + let result = + qcx.start_query(job_id, query.depth_limit(), None, || query.compute(qcx, key)); + let dep_node_index = dep_graph.next_virtual_depnode_index(); + prof_timer.finish_with_query_invocation_id(dep_node_index.into()); + + // Similarly, fingerprint the result to assert that + // it doesn't have anything not considered hashable. + if cfg!(debug_assertions) && let Some(hash_result) = query.hash_result() + { + qcx.dep_context().with_stable_hashing_context(|mut hcx| { + hash_result(&mut hcx, &result); + }); + } + + return (result, dep_node_index); } - - let prof_timer = qcx.dep_context().profiler().query_provider(); - let result = qcx.start_query(job_id, query.depth_limit(), None, || query.compute(qcx, key)); - let dep_node_index = dep_graph.next_virtual_depnode_index(); - prof_timer.finish_with_query_invocation_id(dep_node_index.into()); - - // Similarly, fingerprint the result to assert that - // it doesn't have anything not considered hashable. - if cfg!(debug_assertions) - && let Some(hash_result) = query.hash_result() - { - qcx.dep_context().with_stable_hashing_context(|mut hcx| { - hash_result(&mut hcx, &result); - }); - } - - return (result, dep_node_index); - } + Some(data) => data, + }; if !query.anon() && !query.eval_always() { // `to_dep_node` is expensive for some `DepKind`s. @@ -446,7 +463,7 @@ where // The diagnostics for this query will be promoted to the current session during // `try_mark_green()`, so we can ignore them here. if let Some(ret) = qcx.start_query(job_id, false, None, || { - try_load_from_disk_and_cache_in_memory(query, qcx, &key, &dep_node) + try_load_from_disk_and_cache_in_memory(query, dep_graph_data, qcx, &key, &dep_node) }) { return ret; } @@ -458,7 +475,7 @@ where let (result, dep_node_index) = qcx.start_query(job_id, query.depth_limit(), Some(&diagnostics), || { if query.anon() { - return dep_graph.with_anon_task(*qcx.dep_context(), query.dep_kind(), || { + return dep_graph_data.with_anon_task(*qcx.dep_context(), query.dep_kind(), || { query.compute(qcx, key) }); } @@ -467,7 +484,7 @@ where let dep_node = dep_node_opt.unwrap_or_else(|| query.construct_dep_node(*qcx.dep_context(), &key)); - dep_graph.with_task( + dep_graph_data.with_task( dep_node, (qcx, query), key, @@ -495,6 +512,7 @@ where #[inline(always)] fn try_load_from_disk_and_cache_in_memory( query: Q, + dep_graph_data: &DepGraphData, qcx: Qcx, key: &Q::Key, dep_node: &DepNode, @@ -506,10 +524,9 @@ where // Note this function can be called concurrently from the same query // We must ensure that this is handled correctly. - let dep_graph = qcx.dep_context().dep_graph(); - let (prev_dep_node_index, dep_node_index) = dep_graph.try_mark_green(qcx, &dep_node)?; + let (prev_dep_node_index, dep_node_index) = dep_graph_data.try_mark_green(qcx, &dep_node)?; - debug_assert!(dep_graph.is_green(dep_node)); + debug_assert!(dep_graph_data.is_green(dep_node)); // First we try to load the result from the on-disk cache. // Some things are never cached on disk. @@ -519,8 +536,10 @@ where // The call to `with_query_deserialization` enforces that no new `DepNodes` // are created during deserialization. See the docs of that method for more // details. - let result = - dep_graph.with_query_deserialization(|| try_load_from_disk(qcx, prev_dep_node_index)); + let result = qcx + .dep_context() + .dep_graph() + .with_query_deserialization(|| try_load_from_disk(qcx, prev_dep_node_index)); prof_timer.finish_with_query_invocation_id(dep_node_index.into()); @@ -528,14 +547,11 @@ where if std::intrinsics::unlikely( qcx.dep_context().sess().opts.unstable_opts.query_dep_graph, ) { - dep_graph.mark_debug_loaded_from_disk(*dep_node) + dep_graph_data.mark_debug_loaded_from_disk(*dep_node) } - let prev_fingerprint = qcx - .dep_context() - .dep_graph() - .prev_fingerprint_of(dep_node) - .unwrap_or(Fingerprint::ZERO); + let prev_fingerprint = + dep_graph_data.prev_fingerprint_of(dep_node).unwrap_or(Fingerprint::ZERO); // If `-Zincremental-verify-ich` is specified, re-hash results from // the cache and make sure that they have the expected fingerprint. // @@ -547,7 +563,13 @@ where if std::intrinsics::unlikely( try_verify || qcx.dep_context().sess().opts.unstable_opts.incremental_verify_ich, ) { - incremental_verify_ich(*qcx.dep_context(), &result, dep_node, query.hash_result()); + incremental_verify_ich( + *qcx.dep_context(), + dep_graph_data, + &result, + dep_node, + query.hash_result(), + ); } return Some((result, dep_node_index)); @@ -557,16 +579,23 @@ where // can be forced from `DepNode`. debug_assert!( !qcx.dep_context().fingerprint_style(dep_node.kind).reconstructible(), - "missing on-disk cache entry for {dep_node:?}" + "missing on-disk cache entry for reconstructible {dep_node:?}" ); } + // Sanity check for the logic in `ensure`: if the node is green and the result loadable, + // we should actually be able to load it. + debug_assert!( + !query.loadable_from_disk(qcx, &key, prev_dep_node_index), + "missing on-disk cache entry for loadable {dep_node:?}" + ); + // We could not load a result from the on-disk cache, so // recompute. let prof_timer = qcx.dep_context().profiler().query_provider(); // The dep-graph for this computation is already in-place. - let result = dep_graph.with_ignore(|| query.compute(qcx, *key)); + let result = qcx.dep_context().dep_graph().with_ignore(|| query.compute(qcx, *key)); prof_timer.finish_with_query_invocation_id(dep_node_index.into()); @@ -579,15 +608,22 @@ where // // See issue #82920 for an example of a miscompilation that would get turned into // an ICE by this check - incremental_verify_ich(*qcx.dep_context(), &result, dep_node, query.hash_result()); + incremental_verify_ich( + *qcx.dep_context(), + dep_graph_data, + &result, + dep_node, + query.hash_result(), + ); Some((result, dep_node_index)) } #[inline] -#[instrument(skip(tcx, result, hash_result), level = "debug")] +#[instrument(skip(tcx, dep_graph_data, result, hash_result), level = "debug")] pub(crate) fn incremental_verify_ich( tcx: Tcx, + dep_graph_data: &DepGraphData, result: &V, dep_node: &DepNode, hash_result: Option, &V) -> Fingerprint>, @@ -596,7 +632,7 @@ where Tcx: DepContext, { assert!( - tcx.dep_graph().is_green(dep_node), + dep_graph_data.is_green(dep_node), "fingerprint for green query instance not loaded from cache: {dep_node:?}", ); @@ -604,7 +640,7 @@ where tcx.with_stable_hashing_context(|mut hcx| f(&mut hcx, result)) }); - let old_hash = tcx.dep_graph().prev_fingerprint_of(dep_node); + let old_hash = dep_graph_data.prev_fingerprint_of(dep_node); if Some(new_hash) != old_hash { incremental_verify_ich_failed( @@ -704,6 +740,7 @@ fn ensure_must_run( query: Q, qcx: Qcx, key: &Q::Key, + check_cache: bool, ) -> (bool, Option>) where Q: QueryConfig, @@ -719,7 +756,7 @@ where let dep_node = query.construct_dep_node(*qcx.dep_context(), key); let dep_graph = qcx.dep_context().dep_graph(); - match dep_graph.try_mark_green(qcx, &dep_node) { + let serialized_dep_node_index = match dep_graph.try_mark_green(qcx, &dep_node) { None => { // A None return from `try_mark_green` means that this is either // a new dep node or that the dep node has already been marked red. @@ -727,20 +764,28 @@ where // DepNodeIndex. We must invoke the query itself. The performance cost // this introduces should be negligible as we'll immediately hit the // in-memory cache, or another query down the line will. - (true, Some(dep_node)) + return (true, Some(dep_node)); } - Some((_, dep_node_index)) => { + Some((serialized_dep_node_index, dep_node_index)) => { dep_graph.read_index(dep_node_index); qcx.dep_context().profiler().query_cache_hit(dep_node_index.into()); - (false, None) + serialized_dep_node_index } + }; + + // We do not need the value at all, so do not check the cache. + if !check_cache { + return (false, None); } + + let loadable = query.loadable_from_disk(qcx, key, serialized_dep_node_index); + (!loadable, Some(dep_node)) } #[derive(Debug)] pub enum QueryMode { Get, - Ensure, + Ensure { check_cache: bool }, } #[inline(always)] @@ -755,8 +800,8 @@ where Q: QueryConfig, Qcx: QueryContext, { - let dep_node = if let QueryMode::Ensure = mode { - let (must_run, dep_node) = ensure_must_run(query, qcx, &key); + let dep_node = if let QueryMode::Ensure { check_cache } = mode { + let (must_run, dep_node) = ensure_must_run(query, qcx, &key, check_cache); if !must_run { return None; } diff --git a/compiler/rustc_resolve/locales/en-US.ftl b/compiler/rustc_resolve/messages.ftl similarity index 100% rename from compiler/rustc_resolve/locales/en-US.ftl rename to compiler/rustc_resolve/messages.ftl diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 9526bca3df2..362ef693c48 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -29,7 +29,6 @@ use rustc_middle::metadata::ModChild; use rustc_middle::{bug, ty}; use rustc_session::cstore::CrateStore; use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind}; -use rustc_span::source_map::respan; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; @@ -130,12 +129,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { }; let expn_id = self.cstore().module_expansion_untracked(def_id, &self.tcx.sess); - let span = self.cstore().get_span_untracked(def_id, &self.tcx.sess); Some(self.new_module( parent, ModuleKind::Def(def_kind, def_id, name), expn_id, - span, + self.def_span(def_id), // FIXME: Account for `#[no_implicit_prelude]` attributes. parent.map_or(false, |module| module.no_implicit_prelude), )) @@ -328,13 +326,13 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { } } - fn insert_field_names_local(&mut self, def_id: DefId, vdata: &ast::VariantData) { - let field_names = vdata - .fields() - .iter() - .map(|field| respan(field.span, field.ident.map_or(kw::Empty, |ident| ident.name))) - .collect(); - self.r.field_names.insert(def_id, field_names); + fn insert_field_def_ids(&mut self, def_id: LocalDefId, vdata: &ast::VariantData) { + if vdata.fields().iter().any(|field| field.is_placeholder) { + // The fields are not expanded yet. + return; + } + let def_ids = vdata.fields().iter().map(|field| self.r.local_def_id(field.id).to_def_id()); + self.r.field_def_ids.insert(def_id, self.r.tcx.arena.alloc_from_iter(def_ids)); } fn insert_field_visibilities_local(&mut self, def_id: DefId, vdata: &ast::VariantData) { @@ -346,12 +344,6 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { self.r.field_visibility_spans.insert(def_id, field_vis); } - fn insert_field_names_extern(&mut self, def_id: DefId) { - let field_names = - self.r.cstore().struct_field_names_untracked(def_id, self.r.tcx.sess).collect(); - self.r.field_names.insert(def_id, field_names); - } - fn block_needs_anonymous_module(&mut self, block: &Block) -> bool { // If any statements are items, we need to create an anonymous module block @@ -749,7 +741,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion)); // Record field names for error reporting. - self.insert_field_names_local(def_id, vdata); + self.insert_field_def_ids(local_def_id, vdata); self.insert_field_visibilities_local(def_id, vdata); // If this is a tuple or unit struct, define a name @@ -789,7 +781,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { self.r .struct_constructors - .insert(def_id, (ctor_res, ctor_vis.to_def_id(), ret_fields)); + .insert(local_def_id, (ctor_res, ctor_vis.to_def_id(), ret_fields)); } } @@ -798,7 +790,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion)); // Record field names for error reporting. - self.insert_field_names_local(def_id, vdata); + self.insert_field_def_ids(local_def_id, vdata); self.insert_field_visibilities_local(def_id, vdata); } @@ -1004,32 +996,6 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { | Res::SelfCtor(..) | Res::Err => bug!("unexpected resolution: {:?}", res), } - // Record some extra data for better diagnostics. - match res { - Res::Def(DefKind::Struct, def_id) => { - let cstore = self.r.cstore(); - if let Some((ctor_kind, ctor_def_id)) = cstore.ctor_untracked(def_id) { - let ctor_res = Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id); - let ctor_vis = cstore.visibility_untracked(ctor_def_id); - let field_visibilities = - cstore.struct_field_visibilities_untracked(def_id).collect(); - drop(cstore); - self.r - .struct_constructors - .insert(def_id, (ctor_res, ctor_vis, field_visibilities)); - } else { - drop(cstore); - } - self.insert_field_names_extern(def_id) - } - Res::Def(DefKind::Union, def_id) => self.insert_field_names_extern(def_id), - Res::Def(DefKind::AssocFn, def_id) => { - if self.r.cstore().fn_has_self_parameter_untracked(def_id, self.r.tcx.sess) { - self.r.has_self.insert(def_id); - } - } - _ => {} - } } fn add_macro_use_binding( @@ -1426,7 +1392,7 @@ impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> { AssocItemKind::Const(..) => (DefKind::AssocConst, ValueNS), AssocItemKind::Fn(box Fn { ref sig, .. }) => { if sig.decl.has_self() { - self.r.has_self.insert(def_id); + self.r.has_self.insert(local_def_id); } (DefKind::AssocFn, ValueNS) } @@ -1540,7 +1506,7 @@ impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> { } // Record field names for error reporting. - self.insert_field_names_local(def_id.to_def_id(), &variant.data); + self.insert_field_def_ids(def_id, &variant.data); self.insert_field_visibilities_local(def_id.to_def_id(), &variant.data); visit::walk_variant(self, variant); diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index adec7973671..44a3d4e628e 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -12,7 +12,7 @@ use rustc_errors::{struct_span_err, SuggestionStyle}; use rustc_feature::BUILTIN_ATTRIBUTES; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, NonMacroAttrKind, PerNS}; -use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE}; +use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; use rustc_hir::PrimTy; use rustc_middle::bug; use rustc_middle::ty::TyCtxt; @@ -555,25 +555,22 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { return err; } Res::SelfTyAlias { alias_to: def_id, .. } => { - if let Some(impl_span) = self.opt_span(def_id) { - err.span_label( - reduce_impl_span_to_impl_keyword(sm, impl_span), - "`Self` type implicitly declared here, by this `impl`", - ); - } + err.span_label( + reduce_impl_span_to_impl_keyword(sm, self.def_span(def_id)), + "`Self` type implicitly declared here, by this `impl`", + ); err.span_label(span, "use a type here instead"); return err; } Res::Def(DefKind::TyParam, def_id) => { - if let Some(span) = self.opt_span(def_id) { - err.span_label(span, "type parameter from outer function"); - } + err.span_label(self.def_span(def_id), "type parameter from outer function"); def_id } Res::Def(DefKind::ConstParam, def_id) => { - if let Some(span) = self.opt_span(def_id) { - err.span_label(span, "const parameter from outer function"); - } + err.span_label( + self.def_span(def_id), + "const parameter from outer function", + ); def_id } _ => { @@ -589,7 +586,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // Try to retrieve the span of the function signature and generate a new // message with a local type or const parameter. let sugg_msg = "try using a local generic parameter instead"; - let name = self.opt_name(def_id).unwrap_or(sym::T); + let name = self.tcx.item_name(def_id); let (span, snippet) = if span.is_empty() { let snippet = format!("<{}>", name); (span, snippet) @@ -1216,15 +1213,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // a note about editions let note = if let Some(did) = did { let requires_note = !did.is_local() - && this.cstore().item_attrs_untracked(did, this.tcx.sess).any( + && this.tcx.get_attrs(did, sym::rustc_diagnostic_item).any( |attr| { - if attr.has_name(sym::rustc_diagnostic_item) { - [sym::TryInto, sym::TryFrom, sym::FromIterator] - .map(|x| Some(x)) - .contains(&attr.value_str()) - } else { - false - } + [sym::TryInto, sym::TryFrom, sym::FromIterator] + .map(|x| Some(x)) + .contains(&attr.value_str()) }, ); @@ -1373,8 +1366,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } if ident.name == kw::Default && let ModuleKind::Def(DefKind::Enum, def_id, _) = parent_scope.module.kind - && let Some(span) = self.opt_span(def_id) { + let span = self.def_span(def_id); let source_map = self.tcx.sess.source_map(); let head_span = source_map.guess_head_span(span); if let Ok(head) = source_map.span_to_snippet(head_span) { @@ -1450,11 +1443,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { Some(suggestion) if suggestion.candidate == kw::Underscore => return false, Some(suggestion) => suggestion, }; - let def_span = suggestion.res.opt_def_id().and_then(|def_id| match def_id.krate { - LOCAL_CRATE => self.opt_span(def_id), - _ => Some(self.cstore().get_span_untracked(def_id, self.tcx.sess)), - }); - if let Some(def_span) = def_span { + if let Some(def_span) = suggestion.res.opt_def_id().map(|def_id| self.def_span(def_id)) { if span.overlaps(def_span) { // Don't suggest typo suggestion for itself like in the following: // error[E0423]: expected function, tuple struct or tuple variant, found struct `X` @@ -1592,8 +1581,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { )) = binding.kind { let def_id = self.tcx.parent(ctor_def_id); - let fields = self.field_names.get(&def_id)?; - return fields.iter().map(|name| name.span).reduce(Span::to); // None for `struct Foo()` + return self + .field_def_ids(def_id)? + .iter() + .map(|&field_id| self.def_span(field_id)) + .reduce(Span::to); // None for `struct Foo()` } None } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index cc3e142a5fd..eff10e5af9f 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -3376,7 +3376,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { participle: "defined", article: res.article(), shadowed_binding: res, - shadowed_binding_span: self.r.opt_span(def_id).expect("const parameter defined outside of local crate"), + shadowed_binding_span: self.r.def_span(def_id), } ); None diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 6133e75a78f..805c2ff280d 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -19,7 +19,7 @@ use rustc_errors::{ use rustc_hir as hir; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, CtorOf, DefKind}; -use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE}; +use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; use rustc_hir::PrimTy; use rustc_session::lint; use rustc_session::parse::feature_err; @@ -166,13 +166,6 @@ impl TypoCandidate { } impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { - fn def_span(&self, def_id: DefId) -> Option { - match def_id.krate { - LOCAL_CRATE => self.r.opt_span(def_id), - _ => Some(self.r.cstore().get_span_untracked(def_id, self.r.tcx.sess)), - } - } - fn make_base_error( &mut self, path: &[Segment], @@ -191,7 +184,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { span, span_label: match res { Res::Def(kind, def_id) if kind == DefKind::TyParam => { - self.def_span(def_id).map(|span| (span, "found this type parameter")) + Some((self.r.def_span(def_id), "found this type parameter")) } _ => None, }, @@ -1295,28 +1288,30 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } PathSource::Expr(_) | PathSource::TupleStruct(..) | PathSource::Pat => { let span = find_span(&source, err); - if let Some(span) = self.def_span(def_id) { - err.span_label(span, &format!("`{}` defined here", path_str)); - } + err.span_label(self.r.def_span(def_id), &format!("`{path_str}` defined here")); let (tail, descr, applicability) = match source { PathSource::Pat | PathSource::TupleStruct(..) => { ("", "pattern", Applicability::MachineApplicable) } _ => (": val", "literal", Applicability::HasPlaceholders), }; - let (fields, applicability) = match self.r.field_names.get(&def_id) { - Some(fields) => ( - fields + + let field_ids = self.r.field_def_ids(def_id); + let (fields, applicability) = match field_ids { + Some(field_ids) => ( + field_ids .iter() - .map(|f| format!("{}{}", f.node, tail)) + .map(|&field_id| { + format!("{}{tail}", self.r.tcx.item_name(field_id)) + }) .collect::>() .join(", "), applicability, ), None => ("/* fields */".to_string(), Applicability::HasPlaceholders), }; - let pad = match self.r.field_names.get(&def_id) { - Some(fields) if fields.is_empty() => "", + let pad = match field_ids { + Some(field_ids) if field_ids.is_empty() => "", _ => " ", }; err.span_suggestion( @@ -1359,17 +1354,14 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { if self.r.tcx.sess.is_nightly_build() { let msg = "you might have meant to use `#![feature(trait_alias)]` instead of a \ `type` alias"; - if let Some(span) = self.def_span(def_id) { - if let Ok(snip) = self.r.tcx.sess.source_map().span_to_snippet(span) { - // The span contains a type alias so we should be able to - // replace `type` with `trait`. - let snip = snip.replacen("type", "trait", 1); - err.span_suggestion(span, msg, snip, Applicability::MaybeIncorrect); - } else { - err.span_help(span, msg); - } + let span = self.r.def_span(def_id); + if let Ok(snip) = self.r.tcx.sess.source_map().span_to_snippet(span) { + // The span contains a type alias so we should be able to + // replace `type` with `trait`. + let snip = snip.replacen("type", "trait", 1); + err.span_suggestion(span, msg, snip, Applicability::MaybeIncorrect); } else { - err.help(msg); + err.span_help(span, msg); } } } @@ -1408,19 +1400,38 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { self.suggest_using_enum_variant(err, source, def_id, span); } (Res::Def(DefKind::Struct, def_id), source) if ns == ValueNS => { - let (ctor_def, ctor_vis, fields) = - if let Some(struct_ctor) = self.r.struct_constructors.get(&def_id).cloned() { - if let PathSource::Expr(Some(parent)) = source { - if let ExprKind::Field(..) | ExprKind::MethodCall(..) = parent.kind { - bad_struct_syntax_suggestion(def_id); - return true; - } + let struct_ctor = match def_id.as_local() { + Some(def_id) => self.r.struct_constructors.get(&def_id).cloned(), + None => { + let ctor = self.r.cstore().ctor_untracked(def_id); + ctor.map(|(ctor_kind, ctor_def_id)| { + let ctor_res = + Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id); + let ctor_vis = self.r.tcx.visibility(ctor_def_id); + let field_visibilities = self + .r + .tcx + .associated_item_def_ids(def_id) + .iter() + .map(|field_id| self.r.tcx.visibility(field_id)) + .collect(); + (ctor_res, ctor_vis, field_visibilities) + }) + } + }; + + let (ctor_def, ctor_vis, fields) = if let Some(struct_ctor) = struct_ctor { + if let PathSource::Expr(Some(parent)) = source { + if let ExprKind::Field(..) | ExprKind::MethodCall(..) = parent.kind { + bad_struct_syntax_suggestion(def_id); + return true; } - struct_ctor - } else { - bad_struct_syntax_suggestion(def_id); - return true; - }; + } + struct_ctor + } else { + bad_struct_syntax_suggestion(def_id); + return true; + }; let is_accessible = self.r.is_accessible_from(ctor_vis, self.parent_scope.module); if !is_expected(ctor_def) || is_accessible { @@ -1444,10 +1455,12 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { ); // Use spans of the tuple struct definition. - self.r - .field_names - .get(&def_id) - .map(|fields| fields.iter().map(|f| f.span).collect::>()) + self.r.field_def_ids(def_id).map(|field_ids| { + field_ids + .iter() + .map(|&field_id| self.r.def_span(field_id)) + .collect::>() + }) } _ => None, }; @@ -1493,9 +1506,10 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { match source { PathSource::Expr(_) | PathSource::TupleStruct(..) | PathSource::Pat => { let span = find_span(&source, err); - if let Some(span) = self.def_span(def_id) { - err.span_label(span, &format!("`{}` defined here", path_str)); - } + err.span_label( + self.r.def_span(def_id), + &format!("`{path_str}` defined here"), + ); err.span_suggestion( span, "use this syntax instead", @@ -1508,12 +1522,10 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } (Res::Def(DefKind::Ctor(_, CtorKind::Fn), ctor_def_id), _) if ns == ValueNS => { let def_id = self.r.tcx.parent(ctor_def_id); - if let Some(span) = self.def_span(def_id) { - err.span_label(span, &format!("`{}` defined here", path_str)); - } - let fields = self.r.field_names.get(&def_id).map_or_else( + err.span_label(self.r.def_span(def_id), &format!("`{path_str}` defined here")); + let fields = self.r.field_def_ids(def_id).map_or_else( || "/* fields */".to_string(), - |fields| vec!["_"; fields.len()].join(", "), + |field_ids| vec!["_"; field_ids.len()].join(", "), ); err.span_suggestion( span, @@ -1594,8 +1606,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { if let Some(Res::Def(DefKind::Struct | DefKind::Union, did)) = resolution.full_res() { - if let Some(field_names) = self.r.field_names.get(&did) { - if field_names.iter().any(|&field_name| ident.name == field_name.node) { + if let Some(field_ids) = self.r.field_def_ids(did) { + if field_ids + .iter() + .any(|&field_id| ident.name == self.r.tcx.item_name(field_id)) + { return Some(AssocSuggestion::Field); } } @@ -1630,7 +1645,17 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { ) { let res = binding.res(); if filter_fn(res) { - if self.r.has_self.contains(&res.def_id()) { + let def_id = res.def_id(); + let has_self = match def_id.as_local() { + Some(def_id) => self.r.has_self.contains(&def_id), + None => self + .r + .tcx + .fn_arg_names(def_id) + .first() + .map_or(false, |ident| ident.name == kw::SelfLower), + }; + if has_self { return Some(AssocSuggestion::MethodWithSelf { called }); } else { match res { @@ -1999,11 +2024,12 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } else { let needs_placeholder = |ctor_def_id: DefId, kind: CtorKind| { let def_id = self.r.tcx.parent(ctor_def_id); - let has_no_fields = self.r.field_names.get(&def_id).map_or(false, |f| f.is_empty()); match kind { CtorKind::Const => false, - CtorKind::Fn if has_no_fields => false, - _ => true, + CtorKind::Fn => !self + .r + .field_def_ids(def_id) + .map_or(false, |field_ids| field_ids.is_empty()), } }; @@ -2064,9 +2090,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { }; if def_id.is_local() { - if let Some(span) = self.def_span(def_id) { - err.span_note(span, "the enum is defined here"); - } + err.span_note(self.r.def_span(def_id), "the enum is defined here"); } } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 5eba208e3ed..cd90fd3ef84 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -27,6 +27,7 @@ use rustc_ast::{self as ast, NodeId, CRATE_NODE_ID}; use rustc_ast::{AngleBracketedArg, Crate, Expr, ExprKind, GenericArg, GenericArgs, LitKind, Path}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::intern::Interned; +use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{Lrc, MappedReadGuard}; use rustc_errors::{ Applicability, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed, SubdiagnosticMessage, @@ -34,7 +35,7 @@ use rustc_errors::{ use rustc_expand::base::{DeriveResolutions, SyntaxExtension, SyntaxExtensionKind}; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorOf, DefKind, DocLinkResMap, LifetimeRes, PartialRes, PerNS}; -use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId}; +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LocalDefIdMap, LocalDefIdSet}; use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::definitions::DefPathData; use rustc_hir::TraitCandidate; @@ -47,10 +48,8 @@ use rustc_middle::span_bug; use rustc_middle::ty::{self, MainDefinition, RegisteredTools, TyCtxt}; use rustc_middle::ty::{ResolverGlobalCtxt, ResolverOutputs}; use rustc_query_system::ich::StableHashingContext; -use rustc_session::cstore::CrateStore; use rustc_session::lint::LintBuffer; use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind, SyntaxContext, Transparency}; -use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; @@ -80,7 +79,7 @@ mod late; mod macros; pub mod rustdoc; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } enum Weak { Yes, @@ -880,11 +879,8 @@ pub struct Resolver<'a, 'tcx> { extern_prelude: FxHashMap>, /// N.B., this is used only for better diagnostics, not name resolution itself. - has_self: FxHashSet, - - /// Names of fields of an item `DefId` accessible with dot syntax. - /// Used for hints during error reporting. - field_names: FxHashMap>>, + has_self: LocalDefIdSet, + field_def_ids: LocalDefIdMap<&'tcx [DefId]>, /// Span of the privacy modifier in fields of an item `DefId` accessible with dot syntax. /// Used for hints during error reporting. @@ -965,7 +961,7 @@ pub struct Resolver<'a, 'tcx> { /// A small map keeping true kinds of built-in macros that appear to be fn-like on /// the surface (`macro` items in libcore), but are actually attributes or derives. builtin_macro_kinds: FxHashMap, - registered_tools: RegisteredTools, + registered_tools: &'tcx RegisteredTools, macro_use_prelude: FxHashMap>, macro_map: FxHashMap, dummy_ext_bang: Lrc, @@ -1008,7 +1004,7 @@ pub struct Resolver<'a, 'tcx> { /// Table for mapping struct IDs into struct constructor IDs, /// it's not used during normal resolution, only for better error reporting. /// Also includes of list of each fields visibility - struct_constructors: DefIdMap<(Res, ty::Visibility, Vec>)>, + struct_constructors: LocalDefIdMap<(Res, ty::Visibility, Vec>)>, /// Features enabled for this crate. active_features: FxHashSet, @@ -1233,7 +1229,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } - let registered_tools = macros::registered_tools(tcx.sess, &krate.attrs); + let registered_tools = tcx.registered_tools(()); let features = tcx.sess.features_untracked(); @@ -1248,8 +1244,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { prelude: None, extern_prelude, - has_self: FxHashSet::default(), - field_names: FxHashMap::default(), + has_self: Default::default(), + field_def_ids: Default::default(), field_visibility_spans: FxHashMap::default(), determined_imports: Vec::new(), @@ -1408,7 +1404,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { trait_impls: self.trait_impls, proc_macros, confused_type_with_std_module, - registered_tools: self.registered_tools, doc_link_resolutions: self.doc_link_resolutions, doc_link_traits_in_scope: self.doc_link_traits_in_scope, all_macro_rules: self.all_macro_rules, @@ -1426,6 +1421,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { trait_map: self.trait_map, builtin_macro_kinds: self.builtin_macro_kinds, lifetime_elision_allowed: self.lifetime_elision_allowed, + lint_buffer: Steal::new(self.lint_buffer), }; ResolverOutputs { global_ctxt, ast_lowering } } @@ -1435,9 +1431,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } fn crate_loader(&mut self, f: impl FnOnce(&mut CrateLoader<'_, '_>) -> T) -> T { - let mut cstore = self.tcx.untracked().cstore.write(); - let cstore = cstore.untracked_as_any().downcast_mut().unwrap(); - f(&mut CrateLoader::new(self.tcx, &mut *cstore, &mut self.used_extern_options)) + f(&mut CrateLoader::new( + self.tcx, + &mut CStore::from_tcx_mut(self.tcx), + &mut self.used_extern_options, + )) } fn cstore(&self) -> MappedReadGuard<'_, CStore> { @@ -1849,20 +1847,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { &mut self, path_str: &str, ns: Namespace, - mut parent_scope: ParentScope<'a>, + parent_scope: ParentScope<'a>, ) -> Option { let mut segments = Vec::from_iter(path_str.split("::").map(Ident::from_str).map(Segment::from_ident)); if let Some(segment) = segments.first_mut() { - if segment.ident.name == kw::Crate { - // FIXME: `resolve_path` always resolves `crate` to the current crate root, but - // rustdoc wants it to resolve to the `parent_scope`'s crate root. This trick of - // replacing `crate` with `self` and changing the current module should achieve - // the same effect. - segment.ident.name = kw::SelfLower; - parent_scope.module = - self.expect_module(parent_scope.module.def_id().krate.as_def_id()); - } else if segment.ident.name == kw::Empty { + if segment.ident.name == kw::Empty { segment.ident.name = kw::PathRoot; } } @@ -1877,20 +1867,19 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } - /// Retrieves the span of the given `DefId` if `DefId` is in the local crate. - #[inline] - fn opt_span(&self, def_id: DefId) -> Option { - def_id.as_local().map(|def_id| self.tcx.source_span(def_id)) + /// Retrieves definition span of the given `DefId`. + fn def_span(&self, def_id: DefId) -> Span { + match def_id.as_local() { + Some(def_id) => self.tcx.source_span(def_id), + None => self.cstore().get_span_untracked(def_id, self.tcx.sess), + } } - /// Retrieves the name of the given `DefId`. - #[inline] - fn opt_name(&self, def_id: DefId) -> Option { - let def_key = match def_id.as_local() { - Some(def_id) => self.tcx.definitions_untracked().def_key(def_id), - None => self.cstore().def_key(def_id), - }; - def_key.get_opt_name() + fn field_def_ids(&self, def_id: DefId) -> Option<&'tcx [DefId]> { + match def_id.as_local() { + Some(def_id) => self.field_def_ids.get(&def_id).copied(), + None => Some(self.tcx.associated_item_def_ids(def_id)), + } } /// Checks if an expression refers to a function marked with @@ -2040,3 +2029,7 @@ impl Finalize { Finalize { node_id, path_span, root_span, report_private: true } } } + +pub fn provide(providers: &mut ty::query::Providers) { + providers.registered_tools = macros::registered_tools; +} diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index b38c11e8bb8..37153854f7e 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -8,7 +8,6 @@ use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment}; use rustc_ast::{self as ast, Inline, ItemKind, ModKind, NodeId}; use rustc_ast_pretty::pprust; use rustc_attr::StabilityLevel; -use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::intern::Interned; use rustc_data_structures::sync::Lrc; use rustc_errors::{struct_span_err, Applicability}; @@ -20,11 +19,11 @@ use rustc_hir::def::{self, DefKind, NonMacroAttrKind}; use rustc_hir::def_id::{CrateNum, LocalDefId}; use rustc_middle::middle::stability; use rustc_middle::ty::RegisteredTools; +use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::{LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE}; use rustc_session::lint::builtin::{UNUSED_MACROS, UNUSED_MACRO_RULES}; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_session::parse::feature_err; -use rustc_session::Session; use rustc_span::edition::Edition; use rustc_span::hygiene::{self, ExpnData, ExpnKind, LocalExpnId}; use rustc_span::hygiene::{AstPass, MacroKind}; @@ -111,15 +110,17 @@ fn fast_print_path(path: &ast::Path) -> Symbol { } } -pub(crate) fn registered_tools(sess: &Session, attrs: &[ast::Attribute]) -> FxHashSet { - let mut registered_tools = FxHashSet::default(); - for attr in sess.filter_by_name(attrs, sym::register_tool) { +pub(crate) fn registered_tools(tcx: TyCtxt<'_>, (): ()) -> RegisteredTools { + let mut registered_tools = RegisteredTools::default(); + let krate = tcx.crate_for_resolver(()).borrow(); + for attr in tcx.sess.filter_by_name(&krate.attrs, sym::register_tool) { for nested_meta in attr.meta_item_list().unwrap_or_default() { match nested_meta.ident() { Some(ident) => { if let Some(old_ident) = registered_tools.replace(ident) { let msg = format!("{} `{}` was already registered", "tool", ident); - sess.struct_span_err(ident.span, &msg) + tcx.sess + .struct_span_err(ident.span, &msg) .span_label(old_ident.span, "already registered here") .emit(); } @@ -127,7 +128,10 @@ pub(crate) fn registered_tools(sess: &Session, attrs: &[ast::Attribute]) -> FxHa None => { let msg = format!("`{}` only accepts identifiers", sym::register_tool); let span = nested_meta.span(); - sess.struct_span_err(span, &msg).span_label(span, "not an identifier").emit(); + tcx.sess + .struct_span_err(span, &msg) + .span_label(span, "not an identifier") + .emit(); } } } diff --git a/compiler/rustc_session/locales/en-US.ftl b/compiler/rustc_session/messages.ftl similarity index 100% rename from compiler/rustc_session/locales/en-US.ftl rename to compiler/rustc_session/messages.ftl diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index d4e4ace889b..485c3f55462 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1137,7 +1137,7 @@ impl CrateCheckConfig { } /// Fills a `CrateCheckConfig` with well-known configuration values. - fn fill_well_known_values(&mut self) { + fn fill_well_known_values(&mut self, current_target: &Target) { if !self.well_known_values { return; } @@ -1229,6 +1229,7 @@ impl CrateCheckConfig { for target in TARGETS .iter() .map(|target| Target::expect_builtin(&TargetTriple::from_triple(target))) + .chain(iter::once(current_target.clone())) { values_target_os.insert(Symbol::intern(&target.options.os)); values_target_family @@ -1243,9 +1244,9 @@ impl CrateCheckConfig { } } - pub fn fill_well_known(&mut self) { + pub fn fill_well_known(&mut self, current_target: &Target) { self.fill_well_known_names(); - self.fill_well_known_values(); + self.fill_well_known_values(current_target); } } diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index e1f1a5f6d2e..74aef785163 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -42,7 +42,7 @@ pub mod output; pub use getopts; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 2cc25e977a9..fdacf814dd6 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -224,6 +224,13 @@ pub struct PerfStats { pub normalize_projection_ty: AtomicUsize, } +#[derive(PartialEq, Eq, PartialOrd, Ord)] +pub enum MetadataKind { + None, + Uncompressed, + Compressed, +} + impl Session { pub fn miri_unleashed_feature(&self, span: Span, feature_gate: Option) { self.miri_unleashed_features.lock().push((span, feature_gate)); @@ -287,6 +294,38 @@ impl Session { self.crate_types.get().unwrap().as_slice() } + pub fn needs_crate_hash(&self) -> bool { + // Why is the crate hash needed for these configurations? + // - debug_assertions: for the "fingerprint the result" check in + // `rustc_query_system::query::plumbing::execute_job`. + // - incremental: for query lookups. + // - needs_metadata: for putting into crate metadata. + // - instrument_coverage: for putting into coverage data (see + // `hash_mir_source`). + cfg!(debug_assertions) + || self.opts.incremental.is_some() + || self.needs_metadata() + || self.instrument_coverage() + } + + pub fn metadata_kind(&self) -> MetadataKind { + self.crate_types() + .iter() + .map(|ty| match *ty { + CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => { + MetadataKind::None + } + CrateType::Rlib => MetadataKind::Uncompressed, + CrateType::Dylib | CrateType::ProcMacro => MetadataKind::Compressed, + }) + .max() + .unwrap_or(MetadataKind::None) + } + + pub fn needs_metadata(&self) -> bool { + self.metadata_kind() != MetadataKind::None + } + pub fn init_crate_types(&self, crate_types: Vec) { self.crate_types.set(crate_types).expect("`crate_types` was initialized twice") } diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 2e339a9d2d2..a1cb810a429 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -100,6 +100,9 @@ pub trait FileLoader { /// Read the contents of a UTF-8 file into memory. fn read_file(&self, path: &Path) -> io::Result; + + /// Read the contents of a potentially non-UTF-8 file into memory. + fn read_binary_file(&self, path: &Path) -> io::Result>; } /// A FileLoader that uses std::fs to load real files. @@ -113,6 +116,10 @@ impl FileLoader for RealFileLoader { fn read_file(&self, path: &Path) -> io::Result { fs::read_to_string(path) } + + fn read_binary_file(&self, path: &Path) -> io::Result> { + fs::read(path) + } } /// This is a [SourceFile] identifier that is used to correlate source files between @@ -220,9 +227,7 @@ impl SourceMap { /// Unlike `load_file`, guarantees that no normalization like BOM-removal /// takes place. pub fn load_binary_file(&self, path: &Path) -> io::Result> { - // Ideally, this should use `self.file_loader`, but it can't - // deal with binary files yet. - let bytes = fs::read(path)?; + let bytes = self.file_loader.read_binary_file(path)?; // We need to add file to the `SourceMap`, so that it is present // in dep-info. There's also an edge case that file might be both diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 4e626fd9f30..d8b5b25aaee 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -429,6 +429,7 @@ symbols! { borrowck_graphviz_format, borrowck_graphviz_postflow, box_free, + box_new, box_patterns, box_syntax, bpf_target_feature, @@ -791,7 +792,6 @@ symbols! { i64, i8, ident, - identity_future, if_let, if_let_guard, if_while_or_patterns, diff --git a/compiler/rustc_symbol_mangling/locales/en-US.ftl b/compiler/rustc_symbol_mangling/messages.ftl similarity index 100% rename from compiler/rustc_symbol_mangling/locales/en-US.ftl rename to compiler/rustc_symbol_mangling/messages.ftl diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index d9ce7373483..c2fd3304f2b 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -119,7 +119,7 @@ pub mod errors; pub mod test; pub mod typeid; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } /// This function computes the symbol name for the given `instance` and the /// given instantiating crate. That is, if you know that instance X is diff --git a/compiler/rustc_trait_selection/locales/en-US.ftl b/compiler/rustc_trait_selection/messages.ftl similarity index 100% rename from compiler/rustc_trait_selection/locales/en-US.ftl rename to compiler/rustc_trait_selection/messages.ftl diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 548b42cef43..f866cb016e2 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -44,4 +44,4 @@ pub mod infer; pub mod solve; pub mod traits; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index dec9f8016b0..891ea0cdebe 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -2,11 +2,13 @@ #[cfg(doc)] use super::trait_goals::structural_traits::*; -use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, MaybeCause, QueryResult}; +use super::EvalCtxt; use itertools::Itertools; use rustc_hir::def_id::DefId; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::util::elaborate_predicates; +use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult}; +use rustc_middle::ty::fast_reject::TreatProjections; use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::{self, Ty, TyCtxt}; use std::fmt::Debug; @@ -247,7 +249,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// /// To deal with this, we first try to normalize the self type and add the candidates for the normalized /// self type to the list of candidates in case that succeeds. Note that we can't just eagerly return in - /// this case as projections as self types add ` + /// this case as projections as self types add + // FIXME complete the unfinished sentence above fn assemble_candidates_after_normalizing_self_ty>( &mut self, goal: Goal<'tcx, G>, @@ -297,9 +300,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { candidates: &mut Vec>, ) { let tcx = self.tcx(); - tcx.for_each_relevant_impl( + tcx.for_each_relevant_impl_treating_projections( goal.predicate.trait_def_id(tcx), goal.predicate.self_ty(), + TreatProjections::NextSolverLookup, |impl_def_id| match G::consider_impl_candidate(self, goal, impl_def_id) { Ok(result) => candidates .push(Candidate { source: CandidateSource::Impl(impl_def_id), result }), diff --git a/compiler/rustc_trait_selection/src/solve/canonical/canonicalize.rs b/compiler/rustc_trait_selection/src/solve/canonical/canonicalize.rs index 981a8f45e45..7ee4f332306 100644 --- a/compiler/rustc_trait_selection/src/solve/canonical/canonicalize.rs +++ b/compiler/rustc_trait_selection/src/solve/canonical/canonicalize.rs @@ -278,16 +278,16 @@ impl<'tcx> TypeFolder> for Canonicalizer<'_, 'tcx> { Err(ui) => CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)), } } - ty::Infer(ty::IntVar(_)) => { - let nt = self.infcx.shallow_resolve(t); + ty::Infer(ty::IntVar(vid)) => { + let nt = self.infcx.opportunistic_resolve_int_var(vid); if nt != t { return self.fold_ty(nt); } else { CanonicalVarKind::Ty(CanonicalTyVarKind::Int) } } - ty::Infer(ty::FloatVar(_)) => { - let nt = self.infcx.shallow_resolve(t); + ty::Infer(ty::FloatVar(vid)) => { + let nt = self.infcx.opportunistic_resolve_float_var(vid); if nt != t { return self.fold_ty(nt); } else { diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index 95612674eb9..ca438a103cf 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -93,37 +93,42 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { }; // Guard against `>::Assoc = ?0>`. - struct ContainsTerm<'tcx> { + struct ContainsTerm<'a, 'tcx> { term: ty::Term<'tcx>, + infcx: &'a InferCtxt<'tcx>, } - impl<'tcx> TypeVisitor> for ContainsTerm<'tcx> { + impl<'tcx> TypeVisitor> for ContainsTerm<'_, 'tcx> { type BreakTy = (); fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { - if t.needs_infer() { - if ty::Term::from(t) == self.term { - ControlFlow::Break(()) - } else { - t.super_visit_with(self) - } + if let Some(vid) = t.ty_vid() + && let ty::TermKind::Ty(term) = self.term.unpack() + && let Some(term_vid) = term.ty_vid() + && self.infcx.root_var(vid) == self.infcx.root_var(term_vid) + { + ControlFlow::Break(()) + } else if t.has_non_region_infer() { + t.super_visit_with(self) } else { ControlFlow::Continue(()) } } fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow { - if c.needs_infer() { - if ty::Term::from(c) == self.term { - ControlFlow::Break(()) - } else { - c.super_visit_with(self) - } + if let ty::ConstKind::Infer(ty::InferConst::Var(vid)) = c.kind() + && let ty::TermKind::Const(term) = self.term.unpack() + && let ty::ConstKind::Infer(ty::InferConst::Var(term_vid)) = term.kind() + && self.infcx.root_const_var(vid) == self.infcx.root_const_var(term_vid) + { + ControlFlow::Break(()) + } else if c.has_non_region_infer() { + c.super_visit_with(self) } else { ControlFlow::Continue(()) } } } - let mut visitor = ContainsTerm { term: goal.predicate.term }; + let mut visitor = ContainsTerm { infcx: self.infcx, term: goal.predicate.term }; term_is_infer && goal.predicate.projection_ty.visit_with(&mut visitor).is_continue() diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 43fd415e871..55d361b1204 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -21,11 +21,13 @@ use rustc_hir::def_id::DefId; use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues}; use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt}; use rustc_infer::traits::query::NoSolution; -use rustc_infer::traits::Obligation; -use rustc_middle::traits::solve::{ExternalConstraints, ExternalConstraintsData}; +use rustc_middle::traits::solve::{ + CanonicalGoal, CanonicalResponse, Certainty, ExternalConstraints, ExternalConstraintsData, + Goal, MaybeCause, QueryResult, Response, +}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{ - CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, ToPredicate, TypeOutlivesPredicate, + CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate, }; use rustc_span::DUMMY_SP; @@ -43,45 +45,6 @@ mod trait_goals; pub use eval_ctxt::EvalCtxt; pub use fulfill::FulfillmentCtxt; -/// A goal is a statement, i.e. `predicate`, we want to prove -/// given some assumptions, i.e. `param_env`. -/// -/// Most of the time the `param_env` contains the `where`-bounds of the function -/// we're currently typechecking while the `predicate` is some trait bound. -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)] -pub struct Goal<'tcx, P> { - param_env: ty::ParamEnv<'tcx>, - predicate: P, -} - -impl<'tcx, P> Goal<'tcx, P> { - pub fn new( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - predicate: impl ToPredicate<'tcx, P>, - ) -> Goal<'tcx, P> { - Goal { param_env, predicate: predicate.to_predicate(tcx) } - } - - /// Updates the goal to one with a different `predicate` but the same `param_env`. - fn with(self, tcx: TyCtxt<'tcx>, predicate: impl ToPredicate<'tcx, Q>) -> Goal<'tcx, Q> { - Goal { param_env: self.param_env, predicate: predicate.to_predicate(tcx) } - } -} - -impl<'tcx, P> From> for Goal<'tcx, P> { - fn from(obligation: Obligation<'tcx, P>) -> Goal<'tcx, P> { - Goal { param_env: obligation.param_env, predicate: obligation.predicate } - } -} -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)] -pub struct Response<'tcx> { - pub var_values: CanonicalVarValues<'tcx>, - /// Additional constraints returned by this query. - pub external_constraints: ExternalConstraints<'tcx>, - pub certainty: Certainty, -} - trait CanonicalResponseExt { fn has_no_inference_or_external_constraints(&self) -> bool; } @@ -94,56 +57,6 @@ impl<'tcx> CanonicalResponseExt for Canonical<'tcx, Response<'tcx>> { } } -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)] -pub enum Certainty { - Yes, - Maybe(MaybeCause), -} - -impl Certainty { - pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity); - - /// When proving multiple goals using **AND**, e.g. nested obligations for an impl, - /// use this function to unify the certainty of these goals - pub fn unify_and(self, other: Certainty) -> Certainty { - match (self, other) { - (Certainty::Yes, Certainty::Yes) => Certainty::Yes, - (Certainty::Yes, Certainty::Maybe(_)) => other, - (Certainty::Maybe(_), Certainty::Yes) => self, - (Certainty::Maybe(MaybeCause::Overflow), Certainty::Maybe(MaybeCause::Overflow)) => { - Certainty::Maybe(MaybeCause::Overflow) - } - // If at least one of the goals is ambiguous, hide the overflow as the ambiguous goal - // may still result in failure. - (Certainty::Maybe(MaybeCause::Ambiguity), Certainty::Maybe(_)) - | (Certainty::Maybe(_), Certainty::Maybe(MaybeCause::Ambiguity)) => { - Certainty::Maybe(MaybeCause::Ambiguity) - } - } - } -} - -/// Why we failed to evaluate a goal. -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)] -pub enum MaybeCause { - /// We failed due to ambiguity. This ambiguity can either - /// be a true ambiguity, i.e. there are multiple different answers, - /// or we hit a case where we just don't bother, e.g. `?x: Trait` goals. - Ambiguity, - /// We gave up due to an overflow, most often by hitting the recursion limit. - Overflow, -} - -type CanonicalGoal<'tcx, T = ty::Predicate<'tcx>> = Canonical<'tcx, Goal<'tcx, T>>; -type CanonicalResponse<'tcx> = Canonical<'tcx, Response<'tcx>>; -/// The result of evaluating a canonical query. -/// -/// FIXME: We use a different type than the existing canonical queries. This is because -/// we need to add a `Certainty` for `overflow` and may want to restructure this code without -/// having to worry about changes to currently used code. Once we've made progress on this -/// solver, merge the two responses again. -pub type QueryResult<'tcx> = Result, NoSolution>; - pub trait InferCtxtEvalExt<'tcx> { /// Evaluates a goal from **outside** of the trait solver. /// diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 33c66d072e9..dbb8e722c8f 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -2,7 +2,7 @@ use crate::traits::{specialization_graph, translate_substs}; use super::assembly; use super::trait_goals::structural_traits; -use super::{Certainty, EvalCtxt, Goal, QueryResult}; +use super::EvalCtxt; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -11,6 +11,7 @@ use rustc_infer::infer::InferCtxt; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::specialization_graph::LeafDef; use rustc_infer::traits::Reveal; +use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::ProjectionPredicate; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -183,7 +184,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { let goal_trait_ref = goal.predicate.projection_ty.trait_ref(tcx); let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); - let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsPlaceholder }; + let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup }; if iter::zip(goal_trait_ref.substs, impl_trait_ref.skip_binder().substs) .any(|(goal, imp)| !drcx.generic_args_may_unify(goal, imp)) { @@ -512,7 +513,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { fn consider_builtin_dyn_upcast_candidates( _ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> Vec> { + ) -> Vec> { bug!("`Unsize` does not have an associated type: {:?}", goal); } diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs b/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs index 86b13c05f76..d1b4fa554c5 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs @@ -8,12 +8,10 @@ //! //! FIXME(@lcnr): Write that section, feel free to ping me if you need help here //! before then or if I still haven't done that before January 2023. -use super::overflow::OverflowData; use super::StackDepth; -use crate::solve::{CanonicalGoal, QueryResult}; use rustc_data_structures::fx::FxHashMap; use rustc_index::vec::IndexVec; -use rustc_middle::ty::TyCtxt; +use rustc_middle::traits::solve::{CanonicalGoal, QueryResult}; rustc_index::newtype_index! { pub struct EntryIndex {} @@ -98,26 +96,3 @@ impl<'tcx> ProvisionalCache<'tcx> { self.entries[entry_index].response } } - -pub(super) fn try_move_finished_goal_to_global_cache<'tcx>( - tcx: TyCtxt<'tcx>, - overflow_data: &mut OverflowData, - stack: &IndexVec>, - goal: CanonicalGoal<'tcx>, - response: QueryResult<'tcx>, -) { - // We move goals to the global cache if we either did not hit an overflow or if it's - // the root goal as that will now always hit the same overflow limit. - // - // NOTE: We cannot move any non-root goals to the global cache even if their final result - // isn't impacted by the overflow as that goal still has unstable query dependencies - // because it didn't go its full depth. - // - // FIXME(@lcnr): We could still cache subtrees which are not impacted by overflow though. - // Tracking that info correctly isn't trivial, so I haven't implemented it for now. - let should_cache_globally = !overflow_data.did_overflow() || stack.is_empty(); - if should_cache_globally { - // FIXME: move the provisional entry to the global cache. - let _ = (tcx, goal, response); - } -} diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs index c7eb8de6521..f1b840aac55 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs @@ -2,11 +2,12 @@ mod cache; mod overflow; use self::cache::ProvisionalEntry; -use super::{CanonicalGoal, Certainty, MaybeCause, QueryResult}; pub(super) use crate::solve::search_graph::overflow::OverflowHandler; use cache::ProvisionalCache; use overflow::OverflowData; use rustc_index::vec::IndexVec; +use rustc_middle::dep_graph::DepKind; +use rustc_middle::traits::solve::{CanonicalGoal, Certainty, MaybeCause, QueryResult}; use rustc_middle::ty::TyCtxt; use std::{collections::hash_map::Entry, mem}; @@ -139,10 +140,9 @@ impl<'tcx> SearchGraph<'tcx> { /// updated the provisional cache and we have to recompute the current goal. /// /// FIXME: Refer to the rustc-dev-guide entry once it exists. - #[instrument(level = "debug", skip(self, tcx, actual_goal), ret)] + #[instrument(level = "debug", skip(self, actual_goal), ret)] fn try_finalize_goal( &mut self, - tcx: TyCtxt<'tcx>, actual_goal: CanonicalGoal<'tcx>, response: QueryResult<'tcx>, ) -> bool { @@ -176,72 +176,87 @@ impl<'tcx> SearchGraph<'tcx> { self.stack.push(StackElem { goal, has_been_used: false }); false } else { - self.try_move_finished_goal_to_global_cache(tcx, stack_elem); true } } - fn try_move_finished_goal_to_global_cache( - &mut self, - tcx: TyCtxt<'tcx>, - stack_elem: StackElem<'tcx>, - ) { - let StackElem { goal, .. } = stack_elem; - let cache = &mut self.provisional_cache; - let provisional_entry_index = *cache.lookup_table.get(&goal).unwrap(); - let provisional_entry = &mut cache.entries[provisional_entry_index]; - let depth = provisional_entry.depth; - - // If not, we're done with this goal. - // - // Check whether that this goal doesn't depend on a goal deeper on the stack - // and if so, move it and all nested goals to the global cache. - // - // Note that if any nested goal were to depend on something deeper on the stack, - // this would have also updated the depth of the current goal. - if depth == self.stack.next_index() { - for (i, entry) in cache.entries.drain_enumerated(provisional_entry_index.index()..) { - let actual_index = cache.lookup_table.remove(&entry.goal); - debug_assert_eq!(Some(i), actual_index); - debug_assert!(entry.depth == depth); - cache::try_move_finished_goal_to_global_cache( - tcx, - &mut self.overflow_data, - &self.stack, - entry.goal, - entry.response, - ); - } - } - } - pub(super) fn with_new_goal( &mut self, tcx: TyCtxt<'tcx>, canonical_goal: CanonicalGoal<'tcx>, mut loop_body: impl FnMut(&mut Self) -> QueryResult<'tcx>, ) -> QueryResult<'tcx> { + if let Some(result) = tcx.new_solver_evaluation_cache.get(&canonical_goal, tcx) { + return result; + } + match self.try_push_stack(tcx, canonical_goal) { Ok(()) => {} // Our goal is already on the stack, eager return. Err(response) => return response, } - self.repeat_while_none( - |this| { - let result = this.deal_with_overflow(tcx, canonical_goal); - let stack_elem = this.stack.pop().unwrap(); - this.try_move_finished_goal_to_global_cache(tcx, stack_elem); - result - }, - |this| { - let result = loop_body(this); - if this.try_finalize_goal(tcx, canonical_goal, result) { - Some(result) - } else { - None - } - }, - ) + // This is for global caching, so we properly track query dependencies. + // Everything that affects the `Result` should be performed within this + // `with_anon_task` closure. + let (result, dep_node) = tcx.dep_graph.with_anon_task(tcx, DepKind::TraitSelect, || { + self.repeat_while_none( + |this| { + let result = this.deal_with_overflow(tcx, canonical_goal); + let _ = this.stack.pop().unwrap(); + result + }, + |this| { + let result = loop_body(this); + this.try_finalize_goal(canonical_goal, result).then(|| result) + }, + ) + }); + + let cache = &mut self.provisional_cache; + let provisional_entry_index = *cache.lookup_table.get(&canonical_goal).unwrap(); + let provisional_entry = &mut cache.entries[provisional_entry_index]; + let depth = provisional_entry.depth; + + // If not, we're done with this goal. + // + // Check whether that this goal doesn't depend on a goal deeper on the stack + // and if so, move it to the global cache. + // + // Note that if any nested goal were to depend on something deeper on the stack, + // this would have also updated the depth of the current goal. + if depth == self.stack.next_index() { + // If the current goal is the head of a cycle, we drop all other + // cycle participants without moving them to the global cache. + let other_cycle_participants = provisional_entry_index.index() + 1; + for (i, entry) in cache.entries.drain_enumerated(other_cycle_participants..) { + let actual_index = cache.lookup_table.remove(&entry.goal); + debug_assert_eq!(Some(i), actual_index); + debug_assert!(entry.depth == depth); + } + + let current_goal = cache.entries.pop().unwrap(); + let actual_index = cache.lookup_table.remove(¤t_goal.goal); + debug_assert_eq!(Some(provisional_entry_index), actual_index); + debug_assert!(current_goal.depth == depth); + + // We move the root goal to the global cache if we either did not hit an overflow or if it's + // the root goal as that will now always hit the same overflow limit. + // + // NOTE: We cannot move any non-root goals to the global cache. When replaying the root goal's + // dependencies, our non-root goal may no longer appear as child of the root goal. + // + // See https://github.com/rust-lang/rust/pull/108071 for some additional context. + let should_cache_globally = !self.overflow_data.did_overflow() || self.stack.is_empty(); + if should_cache_globally { + tcx.new_solver_evaluation_cache.insert( + current_goal.goal, + dep_node, + current_goal.response, + ); + } + } + + result } } diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs b/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs index 56409b0602b..7c9e63f529b 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs @@ -1,10 +1,11 @@ use rustc_infer::infer::canonical::Canonical; use rustc_infer::traits::query::NoSolution; +use rustc_middle::traits::solve::{Certainty, MaybeCause, QueryResult}; use rustc_middle::ty::TyCtxt; use rustc_session::Limit; use super::SearchGraph; -use crate::solve::{response_no_constraints, Certainty, EvalCtxt, MaybeCause, QueryResult}; +use crate::solve::{response_no_constraints, EvalCtxt}; /// When detecting a solver overflow, we return ambiguity. Overflow can be /// *hidden* by either a fatal error in an **AND** or a trivial success in an **OR**. diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 5c499c36e9b..7878539817c 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -2,13 +2,13 @@ use std::iter; -use super::assembly; -use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, QueryResult}; +use super::{assembly, EvalCtxt}; use rustc_hir::def_id::DefId; use rustc_hir::LangItem; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::util::supertraits; -use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; +use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; +use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections}; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; use rustc_middle::ty::{TraitPredicate, TypeVisitableExt}; use rustc_span::DUMMY_SP; @@ -36,7 +36,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { let tcx = ecx.tcx(); let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); - let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsPlaceholder }; + let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup }; if iter::zip(goal.predicate.trait_ref.substs, impl_trait_ref.skip_binder().substs) .any(|(goal, imp)| !drcx.generic_args_may_unify(goal, imp)) { @@ -135,9 +135,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // currently instead lint patterns which can be used to // exploit this unsoundness on stable, see #93367 for // more details. + // + // Using `TreatProjections::NextSolverLookup` is fine here because + // `instantiate_constituent_tys_for_auto_trait` returns nothing for + // projection types anyways. So it doesn't really matter what we do + // here, and this is faster. if let Some(def_id) = ecx.tcx().find_map_relevant_impl( goal.predicate.def_id(), goal.predicate.self_ty(), + TreatProjections::NextSolverLookup, Some, ) { debug!(?def_id, ?goal, "disqualified auto-trait implementation"); diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 6b688c322c7..96a4b76af55 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -75,7 +75,7 @@ pub fn overlapping_impls( // Before doing expensive operations like entering an inference context, do // a quick check via fast_reject to tell if the impl headers could possibly // unify. - let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsInfer }; + let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsCandidateKey }; let impl1_ref = tcx.impl_trait_ref(impl1_def_id); let impl2_ref = tcx.impl_trait_ref(impl2_def_id); let may_overlap = match (impl1_ref, impl2_ref) { diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index a844a1494e2..41ffaeeac1c 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -24,16 +24,15 @@ use rustc_errors::{ }; use rustc_hir as hir; use rustc_hir::def::Namespace; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; -use rustc_hir::GenericParam; -use rustc_hir::Item; -use rustc_hir::Node; +use rustc_hir::{GenericParam, Item, Node}; use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::{InferOk, TypeTrace}; use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::{ExpectedFound, TypeError}; +use rustc_middle::ty::fast_reject::TreatProjections; use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::print::{with_forced_trimmed_paths, FmtPrinter, Print}; use rustc_middle::ty::{ @@ -126,11 +125,7 @@ pub trait TypeErrCtxtExt<'tcx> { + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>, >>::Error: std::fmt::Debug; - fn report_fulfillment_errors( - &self, - errors: &[FulfillmentError<'tcx>], - body_id: Option, - ) -> ErrorGuaranteed; + fn report_fulfillment_errors(&self, errors: &[FulfillmentError<'tcx>]) -> ErrorGuaranteed; fn report_overflow_obligation( &self, @@ -388,11 +383,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { } impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { - fn report_fulfillment_errors( - &self, - errors: &[FulfillmentError<'tcx>], - body_id: Option, - ) -> ErrorGuaranteed { + fn report_fulfillment_errors(&self, errors: &[FulfillmentError<'tcx>]) -> ErrorGuaranteed { #[derive(Debug)] struct ErrorDescriptor<'tcx> { predicate: ty::Predicate<'tcx>, @@ -469,7 +460,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { for from_expansion in [false, true] { for (error, suppressed) in iter::zip(errors, &is_suppressed) { if !suppressed && error.obligation.cause.span.from_expansion() == from_expansion { - self.report_fulfillment_error(error, body_id); + self.report_fulfillment_error(error); } } } @@ -955,8 +946,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ); } - let body_hir_id = - self.tcx.hir().local_def_id_to_hir_id(obligation.cause.body_id); + let body_def_id = obligation.cause.body_id; // Try to report a help message if is_fn_trait && let Ok((implemented_kind, params)) = self.type_implements_fn_trait( @@ -1035,9 +1025,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // Can't show anything else useful, try to find similar impls. let impl_candidates = self.find_similar_impl_candidates(trait_predicate); if !self.report_similar_impl_candidates( - impl_candidates, + &impl_candidates, trait_ref, - body_hir_id, + body_def_id, &mut err, true, ) { @@ -1071,14 +1061,21 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let impl_candidates = self.find_similar_impl_candidates(trait_pred); self.report_similar_impl_candidates( - impl_candidates, + &impl_candidates, trait_ref, - body_hir_id, + body_def_id, &mut err, true, ); } } + + self.maybe_suggest_convert_to_slice( + &mut err, + trait_ref, + impl_candidates.as_slice(), + span, + ); } // Changing mutability doesn't make a difference to whether we have @@ -1494,11 +1491,7 @@ trait InferCtxtPrivExt<'tcx> { // `error` occurring implies that `cond` occurs. fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool; - fn report_fulfillment_error( - &self, - error: &FulfillmentError<'tcx>, - body_id: Option, - ); + fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>); fn report_projection_error( &self, @@ -1529,9 +1522,9 @@ trait InferCtxtPrivExt<'tcx> { fn report_similar_impl_candidates( &self, - impl_candidates: Vec>, + impl_candidates: &[ImplCandidate<'tcx>], trait_ref: ty::PolyTraitRef<'tcx>, - body_id: hir::HirId, + body_def_id: LocalDefId, err: &mut Diagnostic, other: bool, ) -> bool; @@ -1561,11 +1554,7 @@ trait InferCtxtPrivExt<'tcx> { trait_ref_and_ty: ty::Binder<'tcx, (ty::TraitPredicate<'tcx>, Ty<'tcx>)>, ) -> PredicateObligation<'tcx>; - fn maybe_report_ambiguity( - &self, - obligation: &PredicateObligation<'tcx>, - body_id: Option, - ); + fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>); fn predicate_can_apply( &self, @@ -1647,11 +1636,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } #[instrument(skip(self), level = "debug")] - fn report_fulfillment_error( - &self, - error: &FulfillmentError<'tcx>, - body_id: Option, - ) { + fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) { match error.code { FulfillmentErrorCode::CodeSelectionError(ref selection_error) => { self.report_selection_error( @@ -1664,7 +1649,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { self.report_projection_error(&error.obligation, e); } FulfillmentErrorCode::CodeAmbiguity => { - self.maybe_report_ambiguity(&error.obligation, body_id); + self.maybe_report_ambiguity(&error.obligation); } FulfillmentErrorCode::CodeSubtypeError(ref expected_found, ref err) => { self.report_mismatched_types( @@ -1815,12 +1800,17 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { }) .and_then(|(trait_assoc_item, id)| { let trait_assoc_ident = trait_assoc_item.ident(self.tcx); - self.tcx.find_map_relevant_impl(id, proj.projection_ty.self_ty(), |did| { - self.tcx - .associated_items(did) - .in_definition_order() - .find(|assoc| assoc.ident(self.tcx) == trait_assoc_ident) - }) + self.tcx.find_map_relevant_impl( + id, + proj.projection_ty.self_ty(), + TreatProjections::ForLookup, + |did| { + self.tcx + .associated_items(did) + .in_definition_order() + .find(|assoc| assoc.ident(self.tcx) == trait_assoc_ident) + }, + ) }) .and_then(|item| match self.tcx.hir().get_if_local(item.def_id) { Some( @@ -2027,9 +2017,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { fn report_similar_impl_candidates( &self, - impl_candidates: Vec>, + impl_candidates: &[ImplCandidate<'tcx>], trait_ref: ty::PolyTraitRef<'tcx>, - body_id: hir::HirId, + body_def_id: LocalDefId, err: &mut Diagnostic, other: bool, ) -> bool { @@ -2120,9 +2110,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // FIXME(compiler-errors): This could be generalized, both to // be more granular, and probably look past other `#[fundamental]` // types, too. - self.tcx - .visibility(def.did()) - .is_accessible_from(body_id.owner.def_id, self.tcx) + self.tcx.visibility(def.did()).is_accessible_from(body_def_id, self.tcx) } else { true } @@ -2138,7 +2126,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // Prefer more similar candidates first, then sort lexicographically // by their normalized string representation. let mut normalized_impl_candidates_and_similarities = impl_candidates - .into_iter() + .iter() + .copied() .map(|ImplCandidate { trait_ref, similarity }| { // FIXME(compiler-errors): This should be using `NormalizeExt::normalize` let normalized = self @@ -2193,7 +2182,12 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { trait_ref: &ty::PolyTraitRef<'tcx>, ) -> bool { let get_trait_impl = |trait_def_id| { - self.tcx.find_map_relevant_impl(trait_def_id, trait_ref.skip_binder().self_ty(), Some) + self.tcx.find_map_relevant_impl( + trait_def_id, + trait_ref.skip_binder().self_ty(), + TreatProjections::ForLookup, + Some, + ) }; let required_trait_path = self.tcx.def_path_str(trait_ref.def_id()); let traits_with_same_path: std::collections::BTreeSet<_> = self @@ -2231,11 +2225,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } #[instrument(skip(self), level = "debug")] - fn maybe_report_ambiguity( - &self, - obligation: &PredicateObligation<'tcx>, - body_id: Option, - ) { + fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>) { // Unable to successfully determine, probably means // insufficient type information, but could mean // ambiguous impls. The latter *ought* to be a @@ -2277,7 +2267,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if self.tcx.lang_items().sized_trait() == Some(trait_ref.def_id()) { if let None = self.tainted_by_errors() { self.emit_inference_failure_err( - body_id, + obligation.cause.body_id, span, trait_ref.self_ty().skip_binder().into(), ErrorCode::E0282, @@ -2304,7 +2294,13 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let subst = data.trait_ref.substs.iter().find(|s| s.has_non_region_infer()); let mut err = if let Some(subst) = subst { - self.emit_inference_failure_err(body_id, span, subst, ErrorCode::E0283, true) + self.emit_inference_failure_err( + obligation.cause.body_id, + span, + subst, + ErrorCode::E0283, + true, + ) } else { struct_span_err!( self.tcx.sess, @@ -2348,12 +2344,10 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { predicate.to_opt_poly_trait_pred().unwrap(), ); if impl_candidates.len() < 10 { - let hir = - self.tcx.hir().local_def_id_to_hir_id(obligation.cause.body_id); self.report_similar_impl_candidates( - impl_candidates, + impl_candidates.as_slice(), trait_ref, - body_id.map(|id| id.hir_id).unwrap_or(hir), + obligation.cause.body_id, &mut err, false, ); @@ -2375,9 +2369,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id()); } - if let (Some(body_id), Some(ty::subst::GenericArgKind::Type(_))) = - (body_id, subst.map(|subst| subst.unpack())) + if let Some(ty::subst::GenericArgKind::Type(_)) = subst.map(|subst| subst.unpack()) { + let body_id = self.tcx.hir().body_owned_by(obligation.cause.body_id); let mut expr_finder = FindExprBySpan::new(span); expr_finder.visit_expr(&self.tcx.hir().body(body_id).value); @@ -2473,7 +2467,13 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { return; } - self.emit_inference_failure_err(body_id, span, arg, ErrorCode::E0282, false) + self.emit_inference_failure_err( + obligation.cause.body_id, + span, + arg, + ErrorCode::E0282, + false, + ) } ty::PredicateKind::Subtype(data) => { @@ -2487,7 +2487,13 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let SubtypePredicate { a_is_expected: _, a, b } = data; // both must be type variables, or the other would've been instantiated assert!(a.is_ty_var() && b.is_ty_var()); - self.emit_inference_failure_err(body_id, span, a.into(), ErrorCode::E0282, true) + self.emit_inference_failure_err( + obligation.cause.body_id, + span, + a.into(), + ErrorCode::E0282, + true, + ) } ty::PredicateKind::Clause(ty::Clause::Projection(data)) => { if predicate.references_error() || self.tainted_by_errors().is_some() { @@ -2501,7 +2507,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { .find(|g| g.has_non_region_infer()); if let Some(subst) = subst { let mut err = self.emit_inference_failure_err( - body_id, + obligation.cause.body_id, span, subst, ErrorCode::E0284, @@ -2530,7 +2536,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let subst = data.walk().find(|g| g.is_non_region_infer()); if let Some(subst) = subst { let err = self.emit_inference_failure_err( - body_id, + obligation.cause.body_id, span, subst, ErrorCode::E0284, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 9ab753c5a48..5541c085075 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1,7 +1,7 @@ // ignore-tidy-filelength use super::{ - DefIdOrName, FindExprBySpan, Obligation, ObligationCause, ObligationCauseCode, + DefIdOrName, FindExprBySpan, ImplCandidate, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, }; @@ -382,6 +382,14 @@ pub trait TypeErrCtxtExt<'tcx> { body_id: hir::HirId, param_env: ty::ParamEnv<'tcx>, ) -> Vec))>>; + + fn maybe_suggest_convert_to_slice( + &self, + err: &mut Diagnostic, + trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + candidate_impls: &[ImplCandidate<'tcx>], + span: Span, + ); } fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -> (Span, String) { @@ -2220,7 +2228,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // - `BuiltinDerivedObligation` with a generator witness (A) // - `BuiltinDerivedObligation` with a generator (A) // - `BuiltinDerivedObligation` with `impl std::future::Future` (A) - // - `BindingObligation` with `impl_send (Send requirement) + // - `BindingObligation` with `impl_send` (Send requirement) // // The first obligation in the chain is the most useful and has the generator that captured // the type. The last generator (`outer_generator` below) has information about where the @@ -3025,8 +3033,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } }; - let identity_future = tcx.require_lang_item(LangItem::IdentityFuture, None); - // Don't print the tuple of capture types 'print: { if !is_upvar_tys_infer_tuple { @@ -3039,12 +3045,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { None => err.note(&msg), }, ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => { - // Avoid printing the future from `core::future::identity_future`, it's not helpful - if tcx.parent(*def_id) == identity_future { - break 'print; - } - - // If the previous type is `identity_future`, this is the future generated by the body of an async function. + // If the previous type is async fn, this is the future generated by the body of an async function. // Avoid printing it twice (it was already printed in the `ty::Generator` arm below). let is_future = tcx.ty_is_opaque_future(ty); debug!( @@ -3826,6 +3827,73 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } assocs_in_this_method } + + /// If the type that failed selection is an array or a reference to an array, + /// but the trait is implemented for slices, suggest that the user converts + /// the array into a slice. + fn maybe_suggest_convert_to_slice( + &self, + err: &mut Diagnostic, + trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + candidate_impls: &[ImplCandidate<'tcx>], + span: Span, + ) { + // Three cases where we can make a suggestion: + // 1. `[T; _]` (array of T) + // 2. `&[T; _]` (reference to array of T) + // 3. `&mut [T; _]` (mutable reference to array of T) + let (element_ty, mut mutability) = match *trait_ref.skip_binder().self_ty().kind() { + ty::Array(element_ty, _) => (element_ty, None), + + ty::Ref(_, pointee_ty, mutability) => match *pointee_ty.kind() { + ty::Array(element_ty, _) => (element_ty, Some(mutability)), + _ => return, + }, + + _ => return, + }; + + // Go through all the candidate impls to see if any of them is for + // slices of `element_ty` with `mutability`. + let mut is_slice = |candidate: Ty<'tcx>| match *candidate.kind() { + ty::RawPtr(ty::TypeAndMut { ty: t, mutbl: m }) | ty::Ref(_, t, m) => { + if matches!(*t.kind(), ty::Slice(e) if e == element_ty) + && m == mutability.unwrap_or(m) + { + // Use the candidate's mutability going forward. + mutability = Some(m); + true + } else { + false + } + } + _ => false, + }; + + // Grab the first candidate that matches, if any, and make a suggestion. + if let Some(slice_ty) = candidate_impls + .iter() + .map(|trait_ref| trait_ref.trait_ref.self_ty()) + .filter(|t| is_slice(*t)) + .next() + { + let msg = &format!("convert the array to a `{}` slice instead", slice_ty); + + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + let mut suggestions = vec![]; + if snippet.starts_with('&') { + } else if let Some(hir::Mutability::Mut) = mutability { + suggestions.push((span.shrink_to_lo(), "&mut ".into())); + } else { + suggestions.push((span.shrink_to_lo(), "&".into())); + } + suggestions.push((span.shrink_to_hi(), "[..]".into())); + err.multipart_suggestion_verbose(msg, suggestions, Applicability::MaybeIncorrect); + } else { + err.span_help(span, msg); + } + } + } } /// Add a hint to add a missing borrow or remove an unnecessary one. diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 62bad5b49b4..bfeda88a6d4 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -210,7 +210,7 @@ fn do_normalize_predicates<'tcx>( let predicates = match fully_normalize(&infcx, cause, elaborated_env, predicates) { Ok(predicates) => predicates, Err(errors) => { - let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None); + let reported = infcx.err_ctxt().report_fulfillment_errors(&errors); return Err(reported); } }; diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs index f183248f2d0..f84b2f4428d 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -1,9 +1,9 @@ +use rustc_middle::traits::solve::{Certainty, Goal, MaybeCause}; use rustc_middle::ty; -use rustc_session::config::TraitSolver; use crate::infer::canonical::OriginalQueryValues; use crate::infer::InferCtxt; -use crate::solve::{Certainty, Goal, InferCtxtEvalExt, MaybeCause}; +use crate::solve::InferCtxtEvalExt; use crate::traits::{EvaluationResult, OverflowError, PredicateObligation, SelectionContext}; pub trait InferCtxtExt<'tcx> { @@ -79,13 +79,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { _ => obligation.param_env.without_const(), }; - if self.tcx.sess.opts.unstable_opts.trait_solver != TraitSolver::Next { - let c_pred = self.canonicalize_query_keep_static( - param_env.and(obligation.predicate), - &mut _orig_values, - ); - self.tcx.at(obligation.cause.span()).evaluate_obligation(c_pred) - } else { + if self.tcx.trait_solver_next() { self.probe(|snapshot| { if let Ok((_, certainty)) = self.evaluate_root_goal(Goal::new(self.tcx, param_env, obligation.predicate)) @@ -110,6 +104,12 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { Ok(EvaluationResult::EvaluatedToErr) } }) + } else { + let c_pred = self.canonicalize_query_keep_static( + param_env.and(obligation.predicate), + &mut _orig_values, + ); + self.tcx.at(obligation.cause.span()).evaluate_obligation(c_pred) } } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index e91057356a2..3182af989f0 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -9,6 +9,7 @@ use hir::LangItem; use rustc_hir as hir; use rustc_infer::traits::ObligationCause; use rustc_infer::traits::{Obligation, SelectionError, TraitObligation}; +use rustc_middle::ty::fast_reject::TreatProjections; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use rustc_target::spec::abi::Abi; @@ -783,6 +784,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let relevant_impl = self.tcx().find_map_relevant_impl( self.tcx().require_lang_item(LangItem::Drop, None), obligation.predicate.skip_binder().trait_ref.self_ty(), + TreatProjections::ForLookup, Some, ); diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 21c158fd0fd..ee41d840bae 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -601,10 +601,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?obligation, "confirm_fn_pointer_candidate"); let tcx = self.tcx(); - let self_ty = self + + let Some(self_ty) = self .infcx - .shallow_resolve(obligation.self_ty().no_bound_vars()) - .expect("fn pointer should not capture bound vars from predicate"); + .shallow_resolve(obligation.self_ty().no_bound_vars()) else + { + // FIXME: Ideally we'd support `for<'a> fn(&'a ()): Fn(&'a ())`, + // but we do not currently. Luckily, such a bound is not + // particularly useful, so we don't expect users to write + // them often. + return Err(SelectionError::Unimplemented); + }; + let sig = self_ty.fn_sig(tcx); let trait_ref = closure_trait_ref_and_return_type( tcx, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 48c3b3601b4..38cdaddc1e7 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -50,7 +50,6 @@ use rustc_middle::ty::relate::TypeRelation; use rustc_middle::ty::SubstsRef; use rustc_middle::ty::{self, EarlyBinder, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate}; use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; -use rustc_session::config::TraitSolver; use rustc_span::symbol::sym; use std::cell::{Cell, RefCell}; @@ -465,14 +464,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if candidates.len() > 1 { let mut i = 0; while i < candidates.len() { - let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| { + let should_drop_i = (0..candidates.len()).filter(|&j| i != j).any(|j| { self.candidate_should_be_dropped_in_favor_of( &candidates[i], &candidates[j], needs_infer, - ) + ) == DropVictim::Yes }); - if is_dup { + if should_drop_i { debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len()); candidates.swap_remove(i); } else { @@ -545,13 +544,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &PredicateObligation<'tcx>, ) -> Result { self.evaluation_probe(|this| { - if this.tcx().sess.opts.unstable_opts.trait_solver != TraitSolver::Next { + if this.tcx().trait_solver_next() { + this.evaluate_predicates_recursively_in_new_solver([obligation.clone()]) + } else { this.evaluate_predicate_recursively( TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()), obligation.clone(), ) - } else { - this.evaluate_predicates_recursively_in_new_solver([obligation.clone()]) } }) } @@ -591,7 +590,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { where I: IntoIterator> + std::fmt::Debug, { - if self.tcx().sess.opts.unstable_opts.trait_solver != TraitSolver::Next { + if self.tcx().trait_solver_next() { + self.evaluate_predicates_recursively_in_new_solver(predicates) + } else { let mut result = EvaluatedToOk; for obligation in predicates { let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?; @@ -604,8 +605,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } Ok(result) - } else { - self.evaluate_predicates_recursively_in_new_solver(predicates) } } @@ -1842,16 +1841,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ProjectionMatchesProjection::No } } +} - /////////////////////////////////////////////////////////////////////////// - // WINNOW - // - // Winnowing is the process of attempting to resolve ambiguity by - // probing further. During the winnowing process, we unify all - // type variables and then we also attempt to evaluate recursive - // bounds to see if they are satisfied. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +enum DropVictim { + Yes, + No, +} - /// Returns `true` if `victim` should be dropped in favor of +/// ## Winnowing +/// +/// Winnowing is the process of attempting to resolve ambiguity by +/// probing further. During the winnowing process, we unify all +/// type variables and then we also attempt to evaluate recursive +/// bounds to see if they are satisfied. +impl<'tcx> SelectionContext<'_, 'tcx> { + /// Returns `DropVictim::Yes` if `victim` should be dropped in favor of /// `other`. Generally speaking we will drop duplicate /// candidates and prefer where-clause candidates. /// @@ -1861,9 +1866,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { victim: &EvaluatedCandidate<'tcx>, other: &EvaluatedCandidate<'tcx>, needs_infer: bool, - ) -> bool { + ) -> DropVictim { if victim.candidate == other.candidate { - return true; + return DropVictim::Yes; } // Check if a bound would previously have been removed when normalizing @@ -1887,11 +1892,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } // FIXME(@jswrenn): this should probably be more sophisticated - (TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => false, + (TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => DropVictim::No, // (*) - (BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_), _) => true, - (_, BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_)) => false, + (BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_), _) => { + DropVictim::Yes + } + (_, BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_)) => { + DropVictim::No + } (ParamCandidate(other), ParamCandidate(victim)) => { let same_except_bound_vars = other.skip_binder().trait_ref @@ -1905,28 +1914,27 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // or the current one if tied (they should both evaluate to the same answer). This is // probably best characterized as a "hack", since we might prefer to just do our // best to *not* create essentially duplicate candidates in the first place. - other.bound_vars().len() <= victim.bound_vars().len() + if other.bound_vars().len() <= victim.bound_vars().len() { + DropVictim::Yes + } else { + DropVictim::No + } } else if other.skip_binder().trait_ref == victim.skip_binder().trait_ref && victim.skip_binder().constness == ty::BoundConstness::NotConst && other.skip_binder().polarity == victim.skip_binder().polarity { // Drop otherwise equivalent non-const candidates in favor of const candidates. - true + DropVictim::Yes } else { - false + DropVictim::No } } // Drop otherwise equivalent non-const fn pointer candidates - (FnPointerCandidate { .. }, FnPointerCandidate { is_const: false }) => true, + (FnPointerCandidate { .. }, FnPointerCandidate { is_const: false }) => DropVictim::Yes, - // Global bounds from the where clause should be ignored - // here (see issue #50825). Otherwise, we have a where - // clause so don't go around looking for impls. - // Arbitrarily give param candidates priority - // over projection and object candidates. ( - ParamCandidate(ref cand), + ParamCandidate(ref other_cand), ImplCandidate(..) | ClosureCandidate { .. } | GeneratorCandidate @@ -1939,11 +1947,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | TraitAliasCandidate | ObjectCandidate(_) | ProjectionCandidate(..), - ) => !is_global(cand), - (ObjectCandidate(_) | ProjectionCandidate(..), ParamCandidate(ref cand)) => { + ) => { + if is_global(other_cand) { + DropVictim::No + } else { + // We have a where clause so don't go around looking + // for impls. Arbitrarily give param candidates priority + // over projection and object candidates. + // + // Global bounds from the where clause should be ignored + // here (see issue #50825). + DropVictim::Yes + } + } + (ObjectCandidate(_) | ProjectionCandidate(..), ParamCandidate(ref victim_cand)) => { // Prefer these to a global where-clause bound // (see issue #50825). - is_global(cand) + if is_global(victim_cand) { DropVictim::Yes } else { DropVictim::No } } ( ImplCandidate(_) @@ -1956,18 +1976,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } | TraitAliasCandidate, - ParamCandidate(ref cand), + ParamCandidate(ref victim_cand), ) => { // Prefer these to a global where-clause bound // (see issue #50825). - is_global(cand) && other.evaluation.must_apply_modulo_regions() + if is_global(victim_cand) && other.evaluation.must_apply_modulo_regions() { + DropVictim::Yes + } else { + DropVictim::No + } } (ProjectionCandidate(i, _), ProjectionCandidate(j, _)) | (ObjectCandidate(i), ObjectCandidate(j)) => { // Arbitrarily pick the lower numbered candidate for backwards // compatibility reasons. Don't let this affect inference. - i < j && !needs_infer + if i < j && !needs_infer { DropVictim::Yes } else { DropVictim::No } } (ObjectCandidate(_), ProjectionCandidate(..)) | (ProjectionCandidate(..), ObjectCandidate(_)) => { @@ -1987,7 +2011,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } | TraitAliasCandidate, - ) => true, + ) => DropVictim::Yes, ( ImplCandidate(..) @@ -2001,7 +2025,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | BuiltinCandidate { .. } | TraitAliasCandidate, ObjectCandidate(_) | ProjectionCandidate(..), - ) => false, + ) => DropVictim::No, (&ImplCandidate(other_def), &ImplCandidate(victim_def)) => { // See if we can toss out `victim` based on specialization. @@ -2014,7 +2038,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let tcx = self.tcx(); if other.evaluation.must_apply_modulo_regions() { if tcx.specializes((other_def, victim_def)) { - return true; + return DropVictim::Yes; } } @@ -2060,13 +2084,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // will then correctly report an inference error, since the // existence of multiple marker trait impls tells us nothing // about which one should actually apply. - !needs_infer + if needs_infer { DropVictim::No } else { DropVictim::Yes } } - Some(_) => true, - None => false, + Some(_) => DropVictim::Yes, + None => DropVictim::No, } } else { - false + DropVictim::No } } @@ -2092,10 +2116,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } | TraitAliasCandidate, - ) => false, + ) => DropVictim::No, } } +} +impl<'tcx> SelectionContext<'_, 'tcx> { fn sized_conditions( &mut self, obligation: &TraitObligation<'tcx>, @@ -2532,7 +2558,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // We can avoid creating type variables and doing the full // substitution if we find that any of the input types, when // simplified, do not match. - let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsPlaceholder }; + let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup }; iter::zip(obligation.predicate.skip_binder().trait_ref.substs, impl_trait_ref.substs) .any(|(obl, imp)| !drcx.generic_args_may_unify(obl, imp)) } diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index d1d6a7a90cf..fcfb60b2603 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -99,10 +99,10 @@ pub fn translate_substs<'tcx>( } fulfill_implication(infcx, param_env, source_trait_ref, target_impl).unwrap_or_else( - |_| { + |()| { bug!( - "When translating substitutions for specialization, the expected \ - specialization failed to hold" + "When translating substitutions from {source_impl:?} to {target_impl:?}, \ + the expected specialization failed to hold" ) }, ) diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs index 61ed9ef2ec1..cd665d9471d 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs @@ -3,7 +3,7 @@ use super::OverlapError; use crate::traits; use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::DefId; -use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams}; +use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams, TreatProjections}; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; pub use rustc_middle::traits::specialization_graph::*; @@ -49,8 +49,12 @@ impl<'tcx> ChildrenExt<'tcx> for Children { /// Insert an impl into this set of children without comparing to any existing impls. fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) { let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().skip_binder(); - if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsInfer) - { + if let Some(st) = fast_reject::simplify_type( + tcx, + trait_ref.self_ty(), + TreatParams::AsCandidateKey, + TreatProjections::AsCandidateKey, + ) { debug!("insert_blindly: impl_def_id={:?} st={:?}", impl_def_id, st); self.non_blanket_impls.entry(st).or_default().push(impl_def_id) } else { @@ -65,8 +69,12 @@ impl<'tcx> ChildrenExt<'tcx> for Children { fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) { let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().skip_binder(); let vec: &mut Vec; - if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsInfer) - { + if let Some(st) = fast_reject::simplify_type( + tcx, + trait_ref.self_ty(), + TreatParams::AsCandidateKey, + TreatProjections::AsCandidateKey, + ) { debug!("remove_existing: impl_def_id={:?} st={:?}", impl_def_id, st); vec = self.non_blanket_impls.get_mut(&st).unwrap(); } else { @@ -302,7 +310,12 @@ impl<'tcx> GraphExt<'tcx> for Graph { let mut parent = trait_def_id; let mut last_lint = None; - let simplified = fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsInfer); + let simplified = fast_reject::simplify_type( + tcx, + trait_ref.self_ty(), + TreatParams::AsCandidateKey, + TreatProjections::AsCandidateKey, + ); // Descend the specialization tree, where `parent` is the current parent node. loop { diff --git a/compiler/rustc_ty_utils/locales/en-US.ftl b/compiler/rustc_ty_utils/messages.ftl similarity index 100% rename from compiler/rustc_ty_utils/locales/en-US.ftl rename to compiler/rustc_ty_utils/messages.ftl diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index cae7f36a208..867974749d5 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -22,7 +22,7 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] { let item = tcx.hir().expect_item(def_id.expect_local()); match item.kind { hir::ItemKind::Trait(.., ref trait_item_refs) => { - if tcx.sess.opts.unstable_opts.lower_impl_trait_in_trait_to_assoc_ty { + if tcx.lower_impl_trait_in_trait_to_assoc_ty() { // We collect RPITITs for each trait method's return type and create a // corresponding associated item using associated_items_for_impl_trait_in_trait // query. @@ -53,7 +53,7 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] { } } hir::ItemKind::Impl(ref impl_) => { - if tcx.sess.opts.unstable_opts.lower_impl_trait_in_trait_to_assoc_ty { + if tcx.lower_impl_trait_in_trait_to_assoc_ty() { // We collect RPITITs for each trait method's return type, on the impl side too and // create a corresponding associated item using // associated_items_for_impl_trait_in_trait query. @@ -153,6 +153,7 @@ fn associated_item_from_trait_item_ref(trait_item_ref: &hir::TraitItemRef) -> ty trait_item_def_id: Some(owner_id.to_def_id()), container: ty::TraitContainer, fn_has_self_parameter: has_self, + opt_rpitit_info: None, } } @@ -171,6 +172,7 @@ fn associated_item_from_impl_item_ref(impl_item_ref: &hir::ImplItemRef) -> ty::A trait_item_def_id: impl_item_ref.trait_item_def_id, container: ty::ImplContainer, fn_has_self_parameter: has_self, + opt_rpitit_info: None, } } @@ -262,12 +264,6 @@ fn associated_item_for_impl_trait_in_trait( // Copy span of the opaque. trait_assoc_ty.def_ident_span(Some(span)); - // Add the def_id of the function and opaque that generated this synthesized associated type. - trait_assoc_ty.opt_rpitit_info(Some(ImplTraitInTraitData::Trait { - fn_def_id, - opaque_def_id: opaque_ty_def_id.to_def_id(), - })); - trait_assoc_ty.associated_item(ty::AssocItem { name: kw::Empty, kind: ty::AssocKind::Type, @@ -275,6 +271,10 @@ fn associated_item_for_impl_trait_in_trait( trait_item_def_id: None, container: ty::TraitContainer, fn_has_self_parameter: false, + opt_rpitit_info: Some(ImplTraitInTraitData::Trait { + fn_def_id, + opaque_def_id: opaque_ty_def_id.to_def_id(), + }), }); // Copy visility of the containing function. @@ -301,9 +301,6 @@ fn associated_item_for_impl_trait_in_trait( // There are no inferred outlives for the synthesized associated type. trait_assoc_ty.inferred_outlives_of(&[]); - // FIXME implement this. - trait_assoc_ty.explicit_item_bounds(&[]); - local_def_id } @@ -315,11 +312,12 @@ fn impl_associated_item_for_impl_trait_in_trait( trait_assoc_def_id: LocalDefId, impl_fn_def_id: LocalDefId, ) -> LocalDefId { - let impl_def_id = tcx.local_parent(impl_fn_def_id); + let impl_local_def_id = tcx.local_parent(impl_fn_def_id); + let impl_def_id = impl_local_def_id.to_def_id(); // FIXME fix the span, we probably want the def_id of the return type of the function let span = tcx.def_span(impl_fn_def_id); - let impl_assoc_ty = tcx.at(span).create_def(impl_def_id, DefPathData::ImplTraitAssocTy); + let impl_assoc_ty = tcx.at(span).create_def(impl_local_def_id, DefPathData::ImplTraitAssocTy); let local_def_id = impl_assoc_ty.def_id(); let def_id = local_def_id.to_def_id(); @@ -330,11 +328,6 @@ fn impl_associated_item_for_impl_trait_in_trait( // `opt_local_def_id_to_hir_id` with `None`. impl_assoc_ty.opt_local_def_id_to_hir_id(None); - // Add the def_id of the function that generated this synthesized associated type. - impl_assoc_ty.opt_rpitit_info(Some(ImplTraitInTraitData::Impl { - fn_def_id: impl_fn_def_id.to_def_id(), - })); - impl_assoc_ty.associated_item(ty::AssocItem { name: kw::Empty, kind: ty::AssocKind::Type, @@ -342,15 +335,56 @@ fn impl_associated_item_for_impl_trait_in_trait( trait_item_def_id: Some(trait_assoc_def_id.to_def_id()), container: ty::ImplContainer, fn_has_self_parameter: false, + opt_rpitit_info: Some(ImplTraitInTraitData::Impl { fn_def_id: impl_fn_def_id.to_def_id() }), }); + // Copy param_env of the containing function. The synthesized associated type doesn't have + // extra predicates to assume. + impl_assoc_ty.param_env(tcx.param_env(impl_fn_def_id)); + // Copy impl_defaultness of the containing function. impl_assoc_ty.impl_defaultness(tcx.impl_defaultness(impl_fn_def_id)); - // Copy generics_of the trait's associated item. - // FIXME: This is not correct, in particular the parent is going to be wrong. So we would need - // to copy from trait_assoc_def_id and adjust things. - impl_assoc_ty.generics_of(tcx.generics_of(trait_assoc_def_id).clone()); + // Copy generics_of the trait's associated item but the impl as the parent. + impl_assoc_ty.generics_of({ + let trait_assoc_generics = tcx.generics_of(trait_assoc_def_id); + let trait_assoc_parent_count = trait_assoc_generics.parent_count; + let mut params = trait_assoc_generics.params.clone(); + + let parent_generics = tcx.generics_of(impl_def_id); + let parent_count = parent_generics.parent_count + parent_generics.params.len(); + + let mut impl_fn_params = tcx.generics_of(impl_fn_def_id).params.clone(); + + for param in &mut params { + param.index = param.index + parent_count as u32 + impl_fn_params.len() as u32 + - trait_assoc_parent_count as u32; + } + + impl_fn_params.extend(params); + params = impl_fn_params; + + let param_def_id_to_index = + params.iter().map(|param| (param.def_id, param.index)).collect(); + + ty::Generics { + parent: Some(impl_def_id), + parent_count, + params, + param_def_id_to_index, + has_self: false, + has_late_bound_regions: trait_assoc_generics.has_late_bound_regions, + } + }); + + // There are no predicates for the synthesized associated type. + impl_assoc_ty.explicit_predicates_of(ty::GenericPredicates { + parent: Some(impl_def_id), + predicates: &[], + }); + + // There are no inferred outlives for the synthesized associated type. + impl_assoc_ty.inferred_outlives_of(&[]); local_def_id } diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index 35f468aa952..3865a8f3223 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -33,7 +33,7 @@ pub mod representability; mod structural_match; mod ty; -fluent_messages! { "../locales/en-US.ftl" } +fluent_messages! { "../messages.ftl" } pub fn provide(providers: &mut Providers) { abi::provide(providers); diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 70c9de91fbf..c8ed1f365f1 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -3,8 +3,8 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_index::bit_set::BitSet; use rustc_middle::ty::{ - self, Binder, EarlyBinder, Predicate, PredicateKind, ToPredicate, Ty, TyCtxt, - TypeSuperVisitable, TypeVisitable, TypeVisitor, + self, Binder, EarlyBinder, ImplTraitInTraitData, Predicate, PredicateKind, ToPredicate, Ty, + TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, }; use rustc_session::config::TraitSolver; use rustc_span::def_id::{DefId, CRATE_DEF_ID}; @@ -117,6 +117,15 @@ fn adt_sized_constraint(tcx: TyCtxt<'_>, def_id: DefId) -> &[Ty<'_>] { /// See `ParamEnv` struct definition for details. fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { + // When computing the param_env of an RPITIT, copy param_env of the containing function. The + // synthesized associated type doesn't have extra predicates to assume. + let def_id = + if let Some(ImplTraitInTraitData::Trait { fn_def_id, .. }) = tcx.opt_rpitit_info(def_id) { + fn_def_id + } else { + def_id + }; + // Compute the bounds on Self and the type parameters. let ty::InstantiatedPredicates { mut predicates, .. } = tcx.predicates_of(def_id).instantiate_identity(tcx); diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index ee4ef57c38f..3a053d4c6a9 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -18,7 +18,7 @@ //! It defines a "skeleton" of how they should be folded. //! - `TypeSuperFoldable`. This is implemented only for each type of interest, //! and defines the folding "skeleton" for these types. -//! - `TypeFolder`/`FallibleTypeFolder. One of these is implemented for each +//! - `TypeFolder`/`FallibleTypeFolder`. One of these is implemented for each //! folder. This defines how types of interest are folded. //! //! This means each fold is a mixture of (a) generic folding operations, and (b) diff --git a/compiler/rustc_type_ir/src/sty.rs b/compiler/rustc_type_ir/src/sty.rs index ebe2b76aef3..62e699eefd7 100644 --- a/compiler/rustc_type_ir/src/sty.rs +++ b/compiler/rustc_type_ir/src/sty.rs @@ -167,7 +167,7 @@ pub enum TyKind { /// lifetimes bound by the witness itself. /// /// This variant is only using when `drop_tracking_mir` is set. - /// This contains the `DefId` and the `SubstRef` of the generator. + /// This contains the `DefId` and the `SubstsRef` of the generator. /// The actual witness types are computed on MIR by the `mir_generator_witnesses` query. /// /// Looking at the following example, the witness for this generator diff --git a/config.toml.example b/config.example.toml similarity index 100% rename from config.toml.example rename to config.example.toml diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 44a37899007..241b11c3f5f 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -214,6 +214,7 @@ impl Box { #[inline(always)] #[stable(feature = "rust1", since = "1.0.0")] #[must_use] + #[rustc_diagnostic_item = "box_new"] pub fn new(x: T) -> Self { #[rustc_box] Box::new(x) diff --git a/library/alloc/src/collections/vec_deque/drain.rs b/library/alloc/src/collections/vec_deque/drain.rs index 89feb361ddc..0be274a3822 100644 --- a/library/alloc/src/collections/vec_deque/drain.rs +++ b/library/alloc/src/collections/vec_deque/drain.rs @@ -52,36 +52,22 @@ impl<'a, T, A: Allocator> Drain<'a, T, A> { } } - // Only returns pointers to the slices, as that's - // all we need to drop them. May only be called if `self.remaining != 0`. + // Only returns pointers to the slices, as that's all we need + // to drop them. May only be called if `self.remaining != 0`. unsafe fn as_slices(&self) -> (*mut [T], *mut [T]) { unsafe { let deque = self.deque.as_ref(); - // FIXME: This is doing almost exactly the same thing as the else branch in `VecDeque::slice_ranges`. - // Unfortunately, we can't just call `slice_ranges` here, as the deque's `len` is currently - // just `drain_start`, so the range check would (almost) always panic. Between temporarily - // adjusting the deques `len` to call `slice_ranges`, and just copy pasting the `slice_ranges` - // implementation, this seemed like the less hacky solution, though it might be good to - // find a better one in the future. - // because `self.remaining != 0`, we know that `self.idx < deque.original_len`, so it's a valid - // logical index. - let wrapped_start = deque.to_physical_idx(self.idx); + // We know that `self.idx + self.remaining <= deque.len <= usize::MAX`, so this won't overflow. + let logical_remaining_range = self.idx..self.idx + self.remaining; - let head_len = deque.capacity() - wrapped_start; - - let (a_range, b_range) = if head_len >= self.remaining { - (wrapped_start..wrapped_start + self.remaining, 0..0) - } else { - let tail_len = self.remaining - head_len; - (wrapped_start..deque.capacity(), 0..tail_len) - }; - - // SAFETY: the range `self.idx..self.idx+self.remaining` lies strictly inside - // the range `0..deque.original_len`. because of this, and because of the fact - // that we acquire `a_range` and `b_range` exactly like `slice_ranges` would, - // it's guaranteed that `a_range` and `b_range` represent valid ranges into - // the deques buffer. + // SAFETY: `logical_remaining_range` represents the + // range into the logical buffer of elements that + // haven't been drained yet, so they're all initialized, + // and `slice::range(start..end, end) == start..end`, + // so the preconditions for `slice_ranges` are met. + let (a_range, b_range) = + deque.slice_ranges(logical_remaining_range.clone(), logical_remaining_range.end); (deque.buffer_range(a_range), deque.buffer_range(b_range)) } } diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 8317ac431a5..48e907e402c 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -1156,7 +1156,7 @@ impl VecDeque { #[inline] #[stable(feature = "deque_extras_15", since = "1.5.0")] pub fn as_slices(&self) -> (&[T], &[T]) { - let (a_range, b_range) = self.slice_ranges(..); + let (a_range, b_range) = self.slice_ranges(.., self.len); // SAFETY: `slice_ranges` always returns valid ranges into // the physical buffer. unsafe { (&*self.buffer_range(a_range), &*self.buffer_range(b_range)) } @@ -1190,7 +1190,7 @@ impl VecDeque { #[inline] #[stable(feature = "deque_extras_15", since = "1.5.0")] pub fn as_mut_slices(&mut self) -> (&mut [T], &mut [T]) { - let (a_range, b_range) = self.slice_ranges(..); + let (a_range, b_range) = self.slice_ranges(.., self.len); // SAFETY: `slice_ranges` always returns valid ranges into // the physical buffer. unsafe { (&mut *self.buffer_range(a_range), &mut *self.buffer_range(b_range)) } @@ -1232,19 +1232,28 @@ impl VecDeque { /// Given a range into the logical buffer of the deque, this function /// return two ranges into the physical buffer that correspond to - /// the given range. - fn slice_ranges(&self, range: R) -> (Range, Range) + /// the given range. The `len` parameter should usually just be `self.len`; + /// the reason it's passed explicitly is that if the deque is wrapped in + /// a `Drain`, then `self.len` is not actually the length of the deque. + /// + /// # Safety + /// + /// This function is always safe to call. For the resulting ranges to be valid + /// ranges into the physical buffer, the caller must ensure that the result of + /// calling `slice::range(range, ..len)` represents a valid range into the + /// logical buffer, and that all elements in that range are initialized. + fn slice_ranges(&self, range: R, len: usize) -> (Range, Range) where R: RangeBounds, { - let Range { start, end } = slice::range(range, ..self.len); + let Range { start, end } = slice::range(range, ..len); let len = end - start; if len == 0 { (0..0, 0..0) } else { - // `slice::range` guarantees that `start <= end <= self.len`. - // because `len != 0`, we know that `start < end`, so `start < self.len` + // `slice::range` guarantees that `start <= end <= len`. + // because `len != 0`, we know that `start < end`, so `start < len` // and the indexing is valid. let wrapped_start = self.to_physical_idx(start); @@ -1290,7 +1299,7 @@ impl VecDeque { where R: RangeBounds, { - let (a_range, b_range) = self.slice_ranges(range); + let (a_range, b_range) = self.slice_ranges(range, self.len); // SAFETY: The ranges returned by `slice_ranges` // are valid ranges into the physical buffer, so // it's ok to pass them to `buffer_range` and @@ -1330,7 +1339,7 @@ impl VecDeque { where R: RangeBounds, { - let (a_range, b_range) = self.slice_ranges(range); + let (a_range, b_range) = self.slice_ranges(range, self.len); // SAFETY: The ranges returned by `slice_ranges` // are valid ranges into the physical buffer, so // it's ok to pass them to `buffer_range` and diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 932a537c598..77b0447b345 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -681,6 +681,24 @@ impl Rc { Err(this) } } + + /// Returns the inner value, if the `Rc` has exactly one strong reference. + /// + /// Otherwise, [`None`] is returned and the `Rc` is dropped. + /// + /// This will succeed even if there are outstanding weak references. + /// + /// If `Rc::into_inner` is called on every clone of this `Rc`, + /// it is guaranteed that exactly one of the calls returns the inner value. + /// This means in particular that the inner value is not dropped. + /// + /// This is equivalent to `Rc::try_unwrap(...).ok()`. (Note that these are not equivalent for + /// `Arc`, due to race conditions that do not apply to `Rc`.) + #[inline] + #[unstable(feature = "rc_into_inner", issue = "106894")] + pub fn into_inner(this: Self) -> Option { + Rc::try_unwrap(this).ok() + } } impl Rc<[T]> { diff --git a/library/alloc/src/rc/tests.rs b/library/alloc/src/rc/tests.rs index 32433cfbdcf..2784108e0e6 100644 --- a/library/alloc/src/rc/tests.rs +++ b/library/alloc/src/rc/tests.rs @@ -151,6 +151,21 @@ fn try_unwrap() { assert_eq!(Rc::try_unwrap(x), Ok(5)); } +#[test] +fn into_inner() { + let x = Rc::new(3); + assert_eq!(Rc::into_inner(x), Some(3)); + + let x = Rc::new(4); + let y = Rc::clone(&x); + assert_eq!(Rc::into_inner(x), None); + assert_eq!(Rc::into_inner(y), Some(4)); + + let x = Rc::new(5); + let _w = Rc::downgrade(&x); + assert_eq!(Rc::into_inner(x), Some(5)); +} + #[test] fn into_from_raw() { let x = Rc::new(Box::new("hello")); diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index fdd341a06ef..f37573c6f27 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -51,8 +51,16 @@ mod tests; /// /// Going above this limit will abort your program (although not /// necessarily) at _exactly_ `MAX_REFCOUNT + 1` references. +/// Trying to go above it might call a `panic` (if not actually going above it). +/// +/// This is a global invariant, and also applies when using a compare-exchange loop. +/// +/// See comment in `Arc::clone`. const MAX_REFCOUNT: usize = (isize::MAX) as usize; +/// The error in case either counter reaches above `MAX_REFCOUNT`, and we can `panic` safely. +const INTERNAL_OVERFLOW_ERROR: &str = "Arc counter overflow"; + #[cfg(not(sanitize = "thread"))] macro_rules! acquire { ($x:expr) => { @@ -1104,6 +1112,9 @@ impl Arc { continue; } + // We can't allow the refcount to increase much past `MAX_REFCOUNT`. + assert!(cur <= MAX_REFCOUNT, "{}", INTERNAL_OVERFLOW_ERROR); + // NOTE: this code currently ignores the possibility of overflow // into usize::MAX; in general both Rc and Arc need to be adjusted // to deal with overflow. @@ -1519,6 +1530,11 @@ impl Clone for Arc { // the worst already happened and we actually do overflow the `usize` counter. However, that // requires the counter to grow from `isize::MAX` to `usize::MAX` between the increment // above and the `abort` below, which seems exceedingly unlikely. + // + // This is a global invariant, and also applies when using a compare-exchange loop to increment + // counters in other methods. + // Otherwise, the counter could be brought to an almost-overflow using a compare-exchange loop, + // and then overflow using a few `fetch_add`s. if old_size > MAX_REFCOUNT { abort(); } @@ -2180,9 +2196,7 @@ impl Weak { return None; } // See comments in `Arc::clone` for why we do this (for `mem::forget`). - if n > MAX_REFCOUNT { - abort(); - } + assert!(n <= MAX_REFCOUNT, "{}", INTERNAL_OVERFLOW_ERROR); Some(n + 1) }) .ok() diff --git a/library/alloc/src/tests.rs b/library/alloc/src/tests.rs index 299ed156a5d..b1d3a9fa8ac 100644 --- a/library/alloc/src/tests.rs +++ b/library/alloc/src/tests.rs @@ -4,7 +4,6 @@ use core::any::Any; use core::clone::Clone; use core::convert::TryInto; use core::ops::Deref; -use core::result::Result::{Err, Ok}; use std::boxed::Box; @@ -15,7 +14,7 @@ fn test_owned_clone() { assert!(a == b); } -#[derive(PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq)] struct Test; #[test] @@ -23,24 +22,17 @@ fn any_move() { let a = Box::new(8) as Box; let b = Box::new(Test) as Box; - match a.downcast::() { - Ok(a) => { - assert!(a == Box::new(8)); - } - Err(..) => panic!(), - } - match b.downcast::() { - Ok(a) => { - assert!(a == Box::new(Test)); - } - Err(..) => panic!(), - } + let a: Box = a.downcast::().unwrap(); + assert_eq!(*a, 8); + + let b: Box = b.downcast::().unwrap(); + assert_eq!(*b, Test); let a = Box::new(8) as Box; let b = Box::new(Test) as Box; - assert!(a.downcast::>().is_err()); - assert!(b.downcast::>().is_err()); + assert!(a.downcast::>().is_err()); + assert!(b.downcast::>().is_err()); } #[test] diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 897d03595d7..9d1720acf36 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -209,6 +209,12 @@ pub use once::OnceCell; /// A mutable memory location. /// +/// # Memory layout +/// +/// `Cell` has the same [memory layout and caveats as +/// `UnsafeCell`](UnsafeCell#memory-layout). In particular, this means that +/// `Cell` has the same in-memory representation as its inner type `T`. +/// /// # Examples /// /// In this example, you can see that `Cell` enables mutation inside an diff --git a/library/core/src/convert/num.rs b/library/core/src/convert/num.rs index 4da7c323492..a74a56bc5b2 100644 --- a/library/core/src/convert/num.rs +++ b/library/core/src/convert/num.rs @@ -172,7 +172,18 @@ impl_from! { f32, f64, #[stable(feature = "lossless_float_conv", since = "1.6.0" #[stable(feature = "float_from_bool", since = "1.68.0")] #[rustc_const_unstable(feature = "const_num_from_num", issue = "87852")] impl const From for f32 { - /// Converts `bool` to `f32` losslessly. + /// Converts `bool` to `f32` losslessly. The resulting value is positive + /// `0.0` for `false` and `1.0` for `true` values. + /// + /// # Examples + /// ``` + /// let x: f32 = false.into(); + /// assert_eq!(x, 0.0); + /// assert!(x.is_sign_positive()); + /// + /// let y: f32 = true.into(); + /// assert_eq!(y, 1.0); + /// ``` #[inline] fn from(small: bool) -> Self { small as u8 as Self @@ -181,7 +192,18 @@ impl const From for f32 { #[stable(feature = "float_from_bool", since = "1.68.0")] #[rustc_const_unstable(feature = "const_num_from_num", issue = "87852")] impl const From for f64 { - /// Converts `bool` to `f64` losslessly. + /// Converts `bool` to `f64` losslessly. The resulting value is positive + /// `0.0` for `false` and `1.0` for `true` values. + /// + /// # Examples + /// ``` + /// let x: f64 = false.into(); + /// assert_eq!(x, 0.0); + /// assert!(x.is_sign_positive()); + /// + /// let y: f64 = true.into(); + /// assert_eq!(y, 1.0); + /// ``` #[inline] fn from(small: bool) -> Self { small as u8 as Self diff --git a/library/core/src/future/mod.rs b/library/core/src/future/mod.rs index 46cbcd43530..04f02d47f92 100644 --- a/library/core/src/future/mod.rs +++ b/library/core/src/future/mod.rs @@ -67,14 +67,10 @@ pub unsafe fn get_context<'a, 'b>(cx: ResumeTy) -> &'a mut Context<'b> { unsafe { &mut *cx.0.as_ptr().cast() } } -// FIXME(swatinem): This fn is currently needed to work around shortcomings -// in type and lifetime inference. -// See the comment at the bottom of `LoweringContext::make_async_expr` and -// . #[doc(hidden)] #[unstable(feature = "gen_future", issue = "50547")] #[inline] -#[lang = "identity_future"] +#[cfg_attr(bootstrap, lang = "identity_future")] pub const fn identity_future>(f: Fut) -> Fut { f } diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index fbda8f82b1b..06d22d84aed 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -1147,12 +1147,10 @@ macro_rules! nonzero_min_max_unsigned { /// # Examples /// /// ``` - /// #![feature(nonzero_min_max)] - /// #[doc = concat!("# use std::num::", stringify!($Ty), ";")] #[doc = concat!("assert_eq!(", stringify!($Ty), "::MIN.get(), 1", stringify!($Int), ");")] /// ``` - #[unstable(feature = "nonzero_min_max", issue = "89065")] + #[stable(feature = "nonzero_min_max", since = "CURRENT_RUSTC_VERSION")] pub const MIN: Self = Self::new(1).unwrap(); /// The largest value that can be represented by this non-zero @@ -1162,12 +1160,10 @@ macro_rules! nonzero_min_max_unsigned { /// # Examples /// /// ``` - /// #![feature(nonzero_min_max)] - /// #[doc = concat!("# use std::num::", stringify!($Ty), ";")] #[doc = concat!("assert_eq!(", stringify!($Ty), "::MAX.get(), ", stringify!($Int), "::MAX);")] /// ``` - #[unstable(feature = "nonzero_min_max", issue = "89065")] + #[stable(feature = "nonzero_min_max", since = "CURRENT_RUSTC_VERSION")] pub const MAX: Self = Self::new(<$Int>::MAX).unwrap(); } )+ @@ -1189,12 +1185,10 @@ macro_rules! nonzero_min_max_signed { /// # Examples /// /// ``` - /// #![feature(nonzero_min_max)] - /// #[doc = concat!("# use std::num::", stringify!($Ty), ";")] #[doc = concat!("assert_eq!(", stringify!($Ty), "::MIN.get(), ", stringify!($Int), "::MIN);")] /// ``` - #[unstable(feature = "nonzero_min_max", issue = "89065")] + #[stable(feature = "nonzero_min_max", since = "CURRENT_RUSTC_VERSION")] pub const MIN: Self = Self::new(<$Int>::MIN).unwrap(); /// The largest value that can be represented by this non-zero @@ -1208,12 +1202,10 @@ macro_rules! nonzero_min_max_signed { /// # Examples /// /// ``` - /// #![feature(nonzero_min_max)] - /// #[doc = concat!("# use std::num::", stringify!($Ty), ";")] #[doc = concat!("assert_eq!(", stringify!($Ty), "::MAX.get(), ", stringify!($Int), "::MAX);")] /// ``` - #[unstable(feature = "nonzero_min_max", issue = "89065")] + #[stable(feature = "nonzero_min_max", since = "CURRENT_RUSTC_VERSION")] pub const MAX: Self = Self::new(<$Int>::MAX).unwrap(); } )+ diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 994c08d1fb5..0f2475a8bde 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -735,22 +735,47 @@ impl Option { } } - const fn get_some_offset() -> isize { - if mem::size_of::>() == mem::size_of::() { - // niche optimization means the `T` is always stored at the same position as the Option. - 0 + /// This is a guess at how many bytes into the option the payload can be found. + /// + /// For niche-optimized types it's correct because it's pigeon-holed to only + /// one possible place. For other types, it's usually correct today, but + /// tweaks to the layout algorithm (particularly expansions of + /// `-Z randomize-layout`) might make it incorrect at any point. + /// + /// It's guaranteed to be a multiple of alignment (so will always give a + /// correctly-aligned location) and to be within the allocated object, so + /// is valid to use with `offset` and to use for a zero-sized read. + /// + /// FIXME: This is a horrible hack, but allows a nice optimization. It should + /// be replaced with `offset_of!` once that works on enum variants. + const SOME_BYTE_OFFSET_GUESS: isize = { + let some_uninit = Some(mem::MaybeUninit::::uninit()); + let payload_ref = some_uninit.as_ref().unwrap(); + // SAFETY: `as_ref` gives an address inside the existing `Option`, + // so both pointers are derived from the same thing and the result + // cannot overflow an `isize`. + let offset = unsafe { <*const _>::byte_offset_from(payload_ref, &some_uninit) }; + + // The offset is into the object, so it's guaranteed to be non-negative. + assert!(offset >= 0); + + // The payload and the overall option are aligned, + // so the offset will be a multiple of the alignment too. + assert!((offset as usize) % mem::align_of::() == 0); + + let max_offset = mem::size_of::() - mem::size_of::(); + if offset as usize <= max_offset { + // There's enough space after this offset for a `T` to exist without + // overflowing the bounds of the object, so let's try it. + offset } else { - assert!(mem::size_of::>() == mem::size_of::>>()); - let some_uninit = Some(mem::MaybeUninit::::uninit()); - // SAFETY: This gets the byte offset of the `Some(_)` value following the fact that - // niche optimization is not active, and thus Option and Option> share - // the same layout. - unsafe { - (some_uninit.as_ref().unwrap() as *const mem::MaybeUninit) - .byte_offset_from(&some_uninit as *const Option>) - } + // The offset guess is definitely wrong, so use the address + // of the original option since we have it already. + // This also correctly handles the case of layout-optimized enums + // where `max_offset == 0` and thus this is the only possibility. + 0 } - } + }; /// Returns a slice of the contained value, if any. If this is `None`, an /// empty slice is returned. This can be useful to have a single type of @@ -784,18 +809,28 @@ impl Option { #[must_use] #[unstable(feature = "option_as_slice", issue = "108545")] pub fn as_slice(&self) -> &[T] { - // SAFETY: This is sound as long as `get_some_offset` returns the - // correct offset. Though in the `None` case, the slice may be located - // at a pointer pointing into padding, the fact that the slice is - // empty, and the padding is at a properly aligned position for a - // value of that type makes it sound. - unsafe { - slice::from_raw_parts( - (self as *const Option).wrapping_byte_offset(Self::get_some_offset()) - as *const T, - self.is_some() as usize, - ) - } + let payload_ptr: *const T = + // The goal here is that both arms here are calculating exactly + // the same pointer, and thus it'll be folded away when the guessed + // offset is correct, but if the guess is wrong for some reason + // it'll at least still be sound, just no longer optimal. + if let Some(payload) = self { + payload + } else { + let self_ptr: *const Self = self; + // SAFETY: `SOME_BYTE_OFFSET_GUESS` guarantees that its value is + // such that this will be in-bounds of the object. + unsafe { self_ptr.byte_offset(Self::SOME_BYTE_OFFSET_GUESS).cast() } + }; + let len = usize::from(self.is_some()); + + // SAFETY: When the `Option` is `Some`, we're using the actual pointer + // to the payload, with a length of 1, so this is equivalent to + // `slice::from_ref`, and thus is safe. + // When the `Option` is `None`, the length used is 0, so to be safe it + // just needs to be aligned, which it is because `&self` is aligned and + // the offset used is a multiple of alignment. + unsafe { slice::from_raw_parts(payload_ptr, len) } } /// Returns a mutable slice of the contained value, if any. If this is @@ -840,17 +875,28 @@ impl Option { #[must_use] #[unstable(feature = "option_as_slice", issue = "108545")] pub fn as_mut_slice(&mut self) -> &mut [T] { - // SAFETY: This is sound as long as `get_some_offset` returns the - // correct offset. Though in the `None` case, the slice may be located - // at a pointer pointing into padding, the fact that the slice is - // empty, and the padding is at a properly aligned position for a - // value of that type makes it sound. - unsafe { - slice::from_raw_parts_mut( - (self as *mut Option).wrapping_byte_offset(Self::get_some_offset()) as *mut T, - self.is_some() as usize, - ) - } + let payload_ptr: *mut T = + // The goal here is that both arms here are calculating exactly + // the same pointer, and thus it'll be folded away when the guessed + // offset is correct, but if the guess is wrong for some reason + // it'll at least still be sound, just no longer optimal. + if let Some(payload) = self { + payload + } else { + let self_ptr: *mut Self = self; + // SAFETY: `SOME_BYTE_OFFSET_GUESS` guarantees that its value is + // such that this will be in-bounds of the object. + unsafe { self_ptr.byte_offset(Self::SOME_BYTE_OFFSET_GUESS).cast() } + }; + let len = usize::from(self.is_some()); + + // SAFETY: When the `Option` is `Some`, we're using the actual pointer + // to the payload, with a length of 1, so this is equivalent to + // `slice::from_mut`, and thus is safe. + // When the `Option` is `None`, the length used is 0, so to be safe it + // just needs to be aligned, which it is because `&self` is aligned and + // the offset used is a multiple of alignment. + unsafe { slice::from_raw_parts_mut(payload_ptr, len) } } ///////////////////////////////////////////////////////////////////////// diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 57e2ffe5d20..839afc57f85 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -61,14 +61,14 @@ impl *const T { /// Use the pointer value in a new pointer of another type. /// - /// In case `val` is a (fat) pointer to an unsized type, this operation + /// In case `meta` is a (fat) pointer to an unsized type, this operation /// will ignore the pointer part, whereas for (thin) pointers to sized /// types, this has the same effect as a simple cast. /// /// The resulting pointer will have provenance of `self`, i.e., for a fat /// pointer, this operation is semantically the same as creating a new /// fat pointer with the data pointer value of `self` but the metadata of - /// `val`. + /// `meta`. /// /// # Examples /// diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 1ad9af1549a..f41be46abc9 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -691,7 +691,7 @@ where #[inline(always)] #[must_use] #[unstable(feature = "ptr_from_ref", issue = "106116")] -pub fn from_ref(r: &T) -> *const T { +pub const fn from_ref(r: &T) -> *const T { r } @@ -702,7 +702,7 @@ pub fn from_ref(r: &T) -> *const T { #[inline(always)] #[must_use] #[unstable(feature = "ptr_from_ref", issue = "106116")] -pub fn from_mut(r: &mut T) -> *mut T { +pub const fn from_mut(r: &mut T) -> *mut T { r } diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 422d0f2b8f0..ece5244e9a9 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -60,14 +60,14 @@ impl *mut T { /// Use the pointer value in a new pointer of another type. /// - /// In case `val` is a (fat) pointer to an unsized type, this operation + /// In case `meta` is a (fat) pointer to an unsized type, this operation /// will ignore the pointer part, whereas for (thin) pointers to sized /// types, this has the same effect as a simple cast. /// /// The resulting pointer will have provenance of `self`, i.e., for a fat /// pointer, this operation is semantically the same as creating a new /// fat pointer with the data pointer value of `self` but the metadata of - /// `val`. + /// `meta`. /// /// # Examples /// diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 040a59184a6..123561873a6 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -960,6 +960,7 @@ impl AtomicBool { /// ```ignore (extern-declaration) /// # fn main() { /// use std::sync::atomic::AtomicBool; + /// /// extern "C" { /// fn my_atomic_op(arg: *mut bool); /// } @@ -971,7 +972,8 @@ impl AtomicBool { /// # } /// ``` #[inline] - #[unstable(feature = "atomic_mut_ptr", reason = "recently added", issue = "66893")] + #[stable(feature = "atomic_as_ptr", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "atomic_as_ptr", since = "CURRENT_RUSTC_VERSION")] pub const fn as_ptr(&self) -> *mut bool { self.v.get().cast() } @@ -1890,7 +1892,6 @@ impl AtomicPtr { /// # Examples /// /// ```ignore (extern-declaration) - /// #![feature(atomic_mut_ptr)] /// use std::sync::atomic::AtomicPtr; /// /// extern "C" { @@ -1906,7 +1907,8 @@ impl AtomicPtr { /// } /// ``` #[inline] - #[unstable(feature = "atomic_mut_ptr", reason = "recently added", issue = "66893")] + #[stable(feature = "atomic_as_ptr", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "atomic_as_ptr", since = "CURRENT_RUSTC_VERSION")] pub const fn as_ptr(&self) -> *mut *mut T { self.p.get() } @@ -2859,9 +2861,8 @@ macro_rules! atomic_int { /// # } /// ``` #[inline] - #[unstable(feature = "atomic_mut_ptr", - reason = "recently added", - issue = "66893")] + #[stable(feature = "atomic_as_ptr", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "atomic_as_ptr", since = "CURRENT_RUSTC_VERSION")] pub const fn as_ptr(&self) -> *mut $int_type { self.v.get() } diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 7837dd276d2..4e7b6080835 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -274,7 +274,6 @@ #![feature(utf8_chunks)] // // Library features (core): -#![feature(atomic_mut_ptr)] #![feature(char_internals)] #![feature(core_intrinsics)] #![feature(duration_constants)] diff --git a/library/std/src/path.rs b/library/std/src/path.rs index cd6b393a2ea..dbc18f7827e 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1498,7 +1498,6 @@ impl PathBuf { /// # Examples /// /// ``` - /// #![feature(path_as_mut_os_str)] /// use std::path::{Path, PathBuf}; /// /// let mut path = PathBuf::from("/foo"); @@ -1510,7 +1509,7 @@ impl PathBuf { /// path.as_mut_os_string().push("baz"); /// assert_eq!(path, Path::new("/foo/barbaz")); /// ``` - #[unstable(feature = "path_as_mut_os_str", issue = "105021")] + #[stable(feature = "path_as_mut_os_str", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] pub fn as_mut_os_string(&mut self) -> &mut OsString { @@ -2066,7 +2065,6 @@ impl Path { /// # Examples /// /// ``` - /// #![feature(path_as_mut_os_str)] /// use std::path::{Path, PathBuf}; /// /// let mut path = PathBuf::from("Foo.TXT"); @@ -2076,7 +2074,7 @@ impl Path { /// path.as_mut_os_str().make_ascii_lowercase(); /// assert_eq!(path, Path::new("foo.txt")); /// ``` - #[unstable(feature = "path_as_mut_os_str", issue = "105021")] + #[stable(feature = "path_as_mut_os_str", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] pub fn as_mut_os_str(&mut self) -> &mut OsStr { diff --git a/library/std/src/sys/common/mod.rs b/library/std/src/sys/common/mod.rs index 29fc0835d76..2b8782ddf44 100644 --- a/library/std/src/sys/common/mod.rs +++ b/library/std/src/sys/common/mod.rs @@ -12,6 +12,7 @@ pub mod alloc; pub mod small_c_string; +pub mod thread_local; #[cfg(test)] mod tests; diff --git a/library/std/src/sys/common/thread_local/fast_local.rs b/library/std/src/sys/common/thread_local/fast_local.rs new file mode 100644 index 00000000000..2addcc4a759 --- /dev/null +++ b/library/std/src/sys/common/thread_local/fast_local.rs @@ -0,0 +1,276 @@ +#[doc(hidden)] +#[macro_export] +#[allow_internal_unstable( + thread_local_internals, + cfg_target_thread_local, + thread_local, + libstd_thread_internals +)] +#[allow_internal_unsafe] +macro_rules! __thread_local_inner { + // used to generate the `LocalKey` value for const-initialized thread locals + (@key $t:ty, const $init:expr) => {{ + #[cfg_attr(not(windows), inline)] // see comments below + #[deny(unsafe_op_in_unsafe_fn)] + unsafe fn __getit( + _init: $crate::option::Option<&mut $crate::option::Option<$t>>, + ) -> $crate::option::Option<&'static $t> { + const INIT_EXPR: $t = $init; + // If the platform has support for `#[thread_local]`, use it. + #[thread_local] + static mut VAL: $t = INIT_EXPR; + + // If a dtor isn't needed we can do something "very raw" and + // just get going. + if !$crate::mem::needs_drop::<$t>() { + unsafe { + return $crate::option::Option::Some(&VAL) + } + } + + // 0 == dtor not registered + // 1 == dtor registered, dtor not run + // 2 == dtor registered and is running or has run + #[thread_local] + static mut STATE: $crate::primitive::u8 = 0; + + unsafe extern "C" fn destroy(ptr: *mut $crate::primitive::u8) { + let ptr = ptr as *mut $t; + + unsafe { + $crate::debug_assert_eq!(STATE, 1); + STATE = 2; + $crate::ptr::drop_in_place(ptr); + } + } + + unsafe { + match STATE { + // 0 == we haven't registered a destructor, so do + // so now. + 0 => { + $crate::thread::__LocalKeyInner::<$t>::register_dtor( + $crate::ptr::addr_of_mut!(VAL) as *mut $crate::primitive::u8, + destroy, + ); + STATE = 1; + $crate::option::Option::Some(&VAL) + } + // 1 == the destructor is registered and the value + // is valid, so return the pointer. + 1 => $crate::option::Option::Some(&VAL), + // otherwise the destructor has already run, so we + // can't give access. + _ => $crate::option::Option::None, + } + } + } + + unsafe { + $crate::thread::LocalKey::new(__getit) + } + }}; + + // used to generate the `LocalKey` value for `thread_local!` + (@key $t:ty, $init:expr) => { + { + #[inline] + fn __init() -> $t { $init } + + // When reading this function you might ask "why is this inlined + // everywhere other than Windows?", and that's a very reasonable + // question to ask. The short story is that it segfaults rustc if + // this function is inlined. The longer story is that Windows looks + // to not support `extern` references to thread locals across DLL + // boundaries. This appears to at least not be supported in the ABI + // that LLVM implements. + // + // Because of this we never inline on Windows, but we do inline on + // other platforms (where external references to thread locals + // across DLLs are supported). A better fix for this would be to + // inline this function on Windows, but only for "statically linked" + // components. For example if two separately compiled rlibs end up + // getting linked into a DLL then it's fine to inline this function + // across that boundary. It's only not fine to inline this function + // across a DLL boundary. Unfortunately rustc doesn't currently + // have this sort of logic available in an attribute, and it's not + // clear that rustc is even equipped to answer this (it's more of a + // Cargo question kinda). This means that, unfortunately, Windows + // gets the pessimistic path for now where it's never inlined. + // + // The issue of "should enable on Windows sometimes" is #84933 + #[cfg_attr(not(windows), inline)] + unsafe fn __getit( + init: $crate::option::Option<&mut $crate::option::Option<$t>>, + ) -> $crate::option::Option<&'static $t> { + #[thread_local] + static __KEY: $crate::thread::__LocalKeyInner<$t> = + $crate::thread::__LocalKeyInner::<$t>::new(); + + // FIXME: remove the #[allow(...)] marker when macros don't + // raise warning for missing/extraneous unsafe blocks anymore. + // See https://github.com/rust-lang/rust/issues/74838. + #[allow(unused_unsafe)] + unsafe { + __KEY.get(move || { + if let $crate::option::Option::Some(init) = init { + if let $crate::option::Option::Some(value) = init.take() { + return value; + } else if $crate::cfg!(debug_assertions) { + $crate::unreachable!("missing default value"); + } + } + __init() + }) + } + } + + unsafe { + $crate::thread::LocalKey::new(__getit) + } + } + }; + ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { + $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = + $crate::__thread_local_inner!(@key $t, $($init)*); + } +} + +#[doc(hidden)] +pub mod fast { + use super::super::lazy::LazyKeyInner; + use crate::cell::Cell; + use crate::sys::thread_local_dtor::register_dtor; + use crate::{fmt, mem, panic}; + + #[derive(Copy, Clone)] + enum DtorState { + Unregistered, + Registered, + RunningOrHasRun, + } + + // This data structure has been carefully constructed so that the fast path + // only contains one branch on x86. That optimization is necessary to avoid + // duplicated tls lookups on OSX. + // + // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722 + pub struct Key { + // If `LazyKeyInner::get` returns `None`, that indicates either: + // * The value has never been initialized + // * The value is being recursively initialized + // * The value has already been destroyed or is being destroyed + // To determine which kind of `None`, check `dtor_state`. + // + // This is very optimizer friendly for the fast path - initialized but + // not yet dropped. + inner: LazyKeyInner, + + // Metadata to keep track of the state of the destructor. Remember that + // this variable is thread-local, not global. + dtor_state: Cell, + } + + impl fmt::Debug for Key { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Key").finish_non_exhaustive() + } + } + + impl Key { + pub const fn new() -> Key { + Key { inner: LazyKeyInner::new(), dtor_state: Cell::new(DtorState::Unregistered) } + } + + // note that this is just a publicly-callable function only for the + // const-initialized form of thread locals, basically a way to call the + // free `register_dtor` function defined elsewhere in std. + pub unsafe fn register_dtor(a: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { + unsafe { + register_dtor(a, dtor); + } + } + + pub unsafe fn get T>(&self, init: F) -> Option<&'static T> { + // SAFETY: See the definitions of `LazyKeyInner::get` and + // `try_initialize` for more information. + // + // The caller must ensure no mutable references are ever active to + // the inner cell or the inner T when this is called. + // The `try_initialize` is dependant on the passed `init` function + // for this. + unsafe { + match self.inner.get() { + Some(val) => Some(val), + None => self.try_initialize(init), + } + } + } + + // `try_initialize` is only called once per fast thread local variable, + // except in corner cases where thread_local dtors reference other + // thread_local's, or it is being recursively initialized. + // + // Macos: Inlining this function can cause two `tlv_get_addr` calls to + // be performed for every call to `Key::get`. + // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722 + #[inline(never)] + unsafe fn try_initialize T>(&self, init: F) -> Option<&'static T> { + // SAFETY: See comment above (this function doc). + if !mem::needs_drop::() || unsafe { self.try_register_dtor() } { + // SAFETY: See comment above (this function doc). + Some(unsafe { self.inner.initialize(init) }) + } else { + None + } + } + + // `try_register_dtor` is only called once per fast thread local + // variable, except in corner cases where thread_local dtors reference + // other thread_local's, or it is being recursively initialized. + unsafe fn try_register_dtor(&self) -> bool { + match self.dtor_state.get() { + DtorState::Unregistered => { + // SAFETY: dtor registration happens before initialization. + // Passing `self` as a pointer while using `destroy_value` + // is safe because the function will build a pointer to a + // Key, which is the type of self and so find the correct + // size. + unsafe { register_dtor(self as *const _ as *mut u8, destroy_value::) }; + self.dtor_state.set(DtorState::Registered); + true + } + DtorState::Registered => { + // recursively initialized + true + } + DtorState::RunningOrHasRun => false, + } + } + } + + unsafe extern "C" fn destroy_value(ptr: *mut u8) { + let ptr = ptr as *mut Key; + + // SAFETY: + // + // The pointer `ptr` has been built just above and comes from + // `try_register_dtor` where it is originally a Key coming from `self`, + // making it non-NUL and of the correct type. + // + // Right before we run the user destructor be sure to set the + // `Option` to `None`, and `dtor_state` to `RunningOrHasRun`. This + // causes future calls to `get` to run `try_initialize_drop` again, + // which will now fail, and return `None`. + // + // Wrap the call in a catch to ensure unwinding is caught in the event + // a panic takes place in a destructor. + if let Err(_) = panic::catch_unwind(panic::AssertUnwindSafe(|| unsafe { + let value = (*ptr).inner.take(); + (*ptr).dtor_state.set(DtorState::RunningOrHasRun); + drop(value); + })) { + rtabort!("thread local panicked on drop"); + } + } +} diff --git a/library/std/src/sys/common/thread_local/mod.rs b/library/std/src/sys/common/thread_local/mod.rs new file mode 100644 index 00000000000..1fee84a0434 --- /dev/null +++ b/library/std/src/sys/common/thread_local/mod.rs @@ -0,0 +1,109 @@ +//! The following module declarations are outside cfg_if because the internal +//! `__thread_local_internal` macro does not seem to be exported properly when using cfg_if +#![unstable(feature = "thread_local_internals", reason = "should not be necessary", issue = "none")] + +#[cfg(all(target_thread_local, not(all(target_family = "wasm", not(target_feature = "atomics")))))] +mod fast_local; +#[cfg(all( + not(target_thread_local), + not(all(target_family = "wasm", not(target_feature = "atomics"))) +))] +mod os_local; +#[cfg(all(target_family = "wasm", not(target_feature = "atomics")))] +mod static_local; + +#[cfg(not(test))] +cfg_if::cfg_if! { + if #[cfg(all(target_family = "wasm", not(target_feature = "atomics")))] { + #[doc(hidden)] + pub use static_local::statik::Key; + } else if #[cfg(all(target_thread_local, not(all(target_family = "wasm", not(target_feature = "atomics")))))] { + #[doc(hidden)] + pub use fast_local::fast::Key; + } else if #[cfg(all(not(target_thread_local), not(all(target_family = "wasm", not(target_feature = "atomics")))))] { + #[doc(hidden)] + pub use os_local::os::Key; + } +} + +#[doc(hidden)] +#[cfg(test)] +pub use realstd::thread::__LocalKeyInner as Key; + +mod lazy { + use crate::cell::UnsafeCell; + use crate::hint; + use crate::mem; + + pub struct LazyKeyInner { + inner: UnsafeCell>, + } + + impl LazyKeyInner { + pub const fn new() -> LazyKeyInner { + LazyKeyInner { inner: UnsafeCell::new(None) } + } + + pub unsafe fn get(&self) -> Option<&'static T> { + // SAFETY: The caller must ensure no reference is ever handed out to + // the inner cell nor mutable reference to the Option inside said + // cell. This make it safe to hand a reference, though the lifetime + // of 'static is itself unsafe, making the get method unsafe. + unsafe { (*self.inner.get()).as_ref() } + } + + /// The caller must ensure that no reference is active: this method + /// needs unique access. + pub unsafe fn initialize T>(&self, init: F) -> &'static T { + // Execute the initialization up front, *then* move it into our slot, + // just in case initialization fails. + let value = init(); + let ptr = self.inner.get(); + + // SAFETY: + // + // note that this can in theory just be `*ptr = Some(value)`, but due to + // the compiler will currently codegen that pattern with something like: + // + // ptr::drop_in_place(ptr) + // ptr::write(ptr, Some(value)) + // + // Due to this pattern it's possible for the destructor of the value in + // `ptr` (e.g., if this is being recursively initialized) to re-access + // TLS, in which case there will be a `&` and `&mut` pointer to the same + // value (an aliasing violation). To avoid setting the "I'm running a + // destructor" flag we just use `mem::replace` which should sequence the + // operations a little differently and make this safe to call. + // + // The precondition also ensures that we are the only one accessing + // `self` at the moment so replacing is fine. + unsafe { + let _ = mem::replace(&mut *ptr, Some(value)); + } + + // SAFETY: With the call to `mem::replace` it is guaranteed there is + // a `Some` behind `ptr`, not a `None` so `unreachable_unchecked` + // will never be reached. + unsafe { + // After storing `Some` we want to get a reference to the contents of + // what we just stored. While we could use `unwrap` here and it should + // always work it empirically doesn't seem to always get optimized away, + // which means that using something like `try_with` can pull in + // panicking code and cause a large size bloat. + match *ptr { + Some(ref x) => x, + None => hint::unreachable_unchecked(), + } + } + } + + /// The other methods hand out references while taking &self. + /// As such, callers of this method must ensure no `&` and `&mut` are + /// available and used at the same time. + #[allow(unused)] + pub unsafe fn take(&mut self) -> Option { + // SAFETY: See doc comment for this method. + unsafe { (*self.inner.get()).take() } + } + } +} diff --git a/library/std/src/sys/common/thread_local/os_local.rs b/library/std/src/sys/common/thread_local/os_local.rs new file mode 100644 index 00000000000..6f6560c4aa9 --- /dev/null +++ b/library/std/src/sys/common/thread_local/os_local.rs @@ -0,0 +1,217 @@ +#[doc(hidden)] +#[macro_export] +#[allow_internal_unstable( + thread_local_internals, + cfg_target_thread_local, + thread_local, + libstd_thread_internals +)] +#[allow_internal_unsafe] +macro_rules! __thread_local_inner { + // used to generate the `LocalKey` value for const-initialized thread locals + (@key $t:ty, const $init:expr) => {{ + #[cfg_attr(not(windows), inline)] // see comments below + #[deny(unsafe_op_in_unsafe_fn)] + unsafe fn __getit( + _init: $crate::option::Option<&mut $crate::option::Option<$t>>, + ) -> $crate::option::Option<&'static $t> { + const INIT_EXPR: $t = $init; + + // On platforms without `#[thread_local]` we fall back to the + // same implementation as below for os thread locals. + #[inline] + const fn __init() -> $t { INIT_EXPR } + static __KEY: $crate::thread::__LocalKeyInner<$t> = + $crate::thread::__LocalKeyInner::new(); + #[allow(unused_unsafe)] + unsafe { + __KEY.get(move || { + if let $crate::option::Option::Some(init) = _init { + if let $crate::option::Option::Some(value) = init.take() { + return value; + } else if $crate::cfg!(debug_assertions) { + $crate::unreachable!("missing initial value"); + } + } + __init() + }) + } + } + + unsafe { + $crate::thread::LocalKey::new(__getit) + } + }}; + + // used to generate the `LocalKey` value for `thread_local!` + (@key $t:ty, $init:expr) => { + { + #[inline] + fn __init() -> $t { $init } + + // When reading this function you might ask "why is this inlined + // everywhere other than Windows?", and that's a very reasonable + // question to ask. The short story is that it segfaults rustc if + // this function is inlined. The longer story is that Windows looks + // to not support `extern` references to thread locals across DLL + // boundaries. This appears to at least not be supported in the ABI + // that LLVM implements. + // + // Because of this we never inline on Windows, but we do inline on + // other platforms (where external references to thread locals + // across DLLs are supported). A better fix for this would be to + // inline this function on Windows, but only for "statically linked" + // components. For example if two separately compiled rlibs end up + // getting linked into a DLL then it's fine to inline this function + // across that boundary. It's only not fine to inline this function + // across a DLL boundary. Unfortunately rustc doesn't currently + // have this sort of logic available in an attribute, and it's not + // clear that rustc is even equipped to answer this (it's more of a + // Cargo question kinda). This means that, unfortunately, Windows + // gets the pessimistic path for now where it's never inlined. + // + // The issue of "should enable on Windows sometimes" is #84933 + #[cfg_attr(not(windows), inline)] + unsafe fn __getit( + init: $crate::option::Option<&mut $crate::option::Option<$t>>, + ) -> $crate::option::Option<&'static $t> { + static __KEY: $crate::thread::__LocalKeyInner<$t> = + $crate::thread::__LocalKeyInner::new(); + + // FIXME: remove the #[allow(...)] marker when macros don't + // raise warning for missing/extraneous unsafe blocks anymore. + // See https://github.com/rust-lang/rust/issues/74838. + #[allow(unused_unsafe)] + unsafe { + __KEY.get(move || { + if let $crate::option::Option::Some(init) = init { + if let $crate::option::Option::Some(value) = init.take() { + return value; + } else if $crate::cfg!(debug_assertions) { + $crate::unreachable!("missing default value"); + } + } + __init() + }) + } + } + + unsafe { + $crate::thread::LocalKey::new(__getit) + } + } + }; + ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { + $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = + $crate::__thread_local_inner!(@key $t, $($init)*); + } +} + +#[doc(hidden)] +pub mod os { + use super::super::lazy::LazyKeyInner; + use crate::cell::Cell; + use crate::sys_common::thread_local_key::StaticKey as OsStaticKey; + use crate::{fmt, marker, panic, ptr}; + + /// Use a regular global static to store this key; the state provided will then be + /// thread-local. + pub struct Key { + // OS-TLS key that we'll use to key off. + os: OsStaticKey, + marker: marker::PhantomData>, + } + + impl fmt::Debug for Key { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Key").finish_non_exhaustive() + } + } + + unsafe impl Sync for Key {} + + struct Value { + inner: LazyKeyInner, + key: &'static Key, + } + + impl Key { + #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")] + pub const fn new() -> Key { + Key { os: OsStaticKey::new(Some(destroy_value::)), marker: marker::PhantomData } + } + + /// It is a requirement for the caller to ensure that no mutable + /// reference is active when this method is called. + pub unsafe fn get(&'static self, init: impl FnOnce() -> T) -> Option<&'static T> { + // SAFETY: See the documentation for this method. + let ptr = unsafe { self.os.get() as *mut Value }; + if ptr.addr() > 1 { + // SAFETY: the check ensured the pointer is safe (its destructor + // is not running) + it is coming from a trusted source (self). + if let Some(ref value) = unsafe { (*ptr).inner.get() } { + return Some(value); + } + } + // SAFETY: At this point we are sure we have no value and so + // initializing (or trying to) is safe. + unsafe { self.try_initialize(init) } + } + + // `try_initialize` is only called once per os thread local variable, + // except in corner cases where thread_local dtors reference other + // thread_local's, or it is being recursively initialized. + unsafe fn try_initialize(&'static self, init: impl FnOnce() -> T) -> Option<&'static T> { + // SAFETY: No mutable references are ever handed out meaning getting + // the value is ok. + let ptr = unsafe { self.os.get() as *mut Value }; + if ptr.addr() == 1 { + // destructor is running + return None; + } + + let ptr = if ptr.is_null() { + // If the lookup returned null, we haven't initialized our own + // local copy, so do that now. + let ptr = Box::into_raw(Box::new(Value { inner: LazyKeyInner::new(), key: self })); + // SAFETY: At this point we are sure there is no value inside + // ptr so setting it will not affect anyone else. + unsafe { + self.os.set(ptr as *mut u8); + } + ptr + } else { + // recursive initialization + ptr + }; + + // SAFETY: ptr has been ensured as non-NUL just above an so can be + // dereferenced safely. + unsafe { Some((*ptr).inner.initialize(init)) } + } + } + + unsafe extern "C" fn destroy_value(ptr: *mut u8) { + // SAFETY: + // + // The OS TLS ensures that this key contains a null value when this + // destructor starts to run. We set it back to a sentinel value of 1 to + // ensure that any future calls to `get` for this thread will return + // `None`. + // + // Note that to prevent an infinite loop we reset it back to null right + // before we return from the destructor ourselves. + // + // Wrap the call in a catch to ensure unwinding is caught in the event + // a panic takes place in a destructor. + if let Err(_) = panic::catch_unwind(|| unsafe { + let ptr = Box::from_raw(ptr as *mut Value); + let key = ptr.key; + key.os.set(ptr::invalid_mut(1)); + drop(ptr); + key.os.set(ptr::null_mut()); + }) { + rtabort!("thread local panicked on drop"); + } + } +} diff --git a/library/std/src/sys/common/thread_local/static_local.rs b/library/std/src/sys/common/thread_local/static_local.rs new file mode 100644 index 00000000000..ec4f2a12b7e --- /dev/null +++ b/library/std/src/sys/common/thread_local/static_local.rs @@ -0,0 +1,115 @@ +#[doc(hidden)] +#[macro_export] +#[allow_internal_unstable( + thread_local_internals, + cfg_target_thread_local, + thread_local, + libstd_thread_internals +)] +#[allow_internal_unsafe] +macro_rules! __thread_local_inner { + // used to generate the `LocalKey` value for const-initialized thread locals + (@key $t:ty, const $init:expr) => {{ + #[inline] // see comments below + #[deny(unsafe_op_in_unsafe_fn)] + unsafe fn __getit( + _init: $crate::option::Option<&mut $crate::option::Option<$t>>, + ) -> $crate::option::Option<&'static $t> { + const INIT_EXPR: $t = $init; + + // wasm without atomics maps directly to `static mut`, and dtors + // aren't implemented because thread dtors aren't really a thing + // on wasm right now + // + // FIXME(#84224) this should come after the `target_thread_local` + // block. + static mut VAL: $t = INIT_EXPR; + unsafe { $crate::option::Option::Some(&VAL) } + } + + unsafe { + $crate::thread::LocalKey::new(__getit) + } + }}; + + // used to generate the `LocalKey` value for `thread_local!` + (@key $t:ty, $init:expr) => { + { + #[inline] + fn __init() -> $t { $init } + #[inline] + unsafe fn __getit( + init: $crate::option::Option<&mut $crate::option::Option<$t>>, + ) -> $crate::option::Option<&'static $t> { + static __KEY: $crate::thread::__LocalKeyInner<$t> = + $crate::thread::__LocalKeyInner::new(); + + // FIXME: remove the #[allow(...)] marker when macros don't + // raise warning for missing/extraneous unsafe blocks anymore. + // See https://github.com/rust-lang/rust/issues/74838. + #[allow(unused_unsafe)] + unsafe { + __KEY.get(move || { + if let $crate::option::Option::Some(init) = init { + if let $crate::option::Option::Some(value) = init.take() { + return value; + } else if $crate::cfg!(debug_assertions) { + $crate::unreachable!("missing default value"); + } + } + __init() + }) + } + } + + unsafe { + $crate::thread::LocalKey::new(__getit) + } + } + }; + ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { + $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = + $crate::__thread_local_inner!(@key $t, $($init)*); + } +} + +/// On some targets like wasm there's no threads, so no need to generate +/// thread locals and we can instead just use plain statics! +#[doc(hidden)] +pub mod statik { + use super::super::lazy::LazyKeyInner; + use crate::fmt; + + pub struct Key { + inner: LazyKeyInner, + } + + unsafe impl Sync for Key {} + + impl fmt::Debug for Key { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Key").finish_non_exhaustive() + } + } + + impl Key { + pub const fn new() -> Key { + Key { inner: LazyKeyInner::new() } + } + + pub unsafe fn get(&self, init: impl FnOnce() -> T) -> Option<&'static T> { + // SAFETY: The caller must ensure no reference is ever handed out to + // the inner cell nor mutable reference to the Option inside said + // cell. This make it safe to hand a reference, though the lifetime + // of 'static is itself unsafe, making the get method unsafe. + let value = unsafe { + match self.inner.get() { + Some(ref value) => value, + None => self.inner.initialize(init), + } + }; + + Some(value) + } + } +} diff --git a/library/std/src/sys/hermit/futex.rs b/library/std/src/sys/hermit/futex.rs index b64c174b06c..427d8ff6f2e 100644 --- a/library/std/src/sys/hermit/futex.rs +++ b/library/std/src/sys/hermit/futex.rs @@ -16,7 +16,7 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) - let r = unsafe { abi::futex_wait( - futex.as_mut_ptr(), + futex.as_ptr(), expected, timespec.as_ref().map_or(null(), |t| t as *const abi::timespec), abi::FUTEX_RELATIVE_TIMEOUT, @@ -28,12 +28,12 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) - #[inline] pub fn futex_wake(futex: &AtomicU32) -> bool { - unsafe { abi::futex_wake(futex.as_mut_ptr(), 1) > 0 } + unsafe { abi::futex_wake(futex.as_ptr(), 1) > 0 } } #[inline] pub fn futex_wake_all(futex: &AtomicU32) { unsafe { - abi::futex_wake(futex.as_mut_ptr(), i32::MAX); + abi::futex_wake(futex.as_ptr(), i32::MAX); } } diff --git a/library/std/src/sys/hermit/mod.rs b/library/std/src/sys/hermit/mod.rs index d34a4cfedea..743e93a2fd4 100644 --- a/library/std/src/sys/hermit/mod.rs +++ b/library/std/src/sys/hermit/mod.rs @@ -15,7 +15,6 @@ #![allow(missing_docs, nonstandard_style, unsafe_op_in_unsafe_fn)] -use crate::intrinsics; use crate::os::raw::c_char; pub mod alloc; diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index cf7c2e05a2e..7fdf03acc14 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -173,200 +173,6 @@ macro_rules! thread_local { ); } -#[doc(hidden)] -#[unstable(feature = "thread_local_internals", reason = "should not be necessary", issue = "none")] -#[macro_export] -#[allow_internal_unstable(thread_local_internals, cfg_target_thread_local, thread_local)] -#[allow_internal_unsafe] -macro_rules! __thread_local_inner { - // used to generate the `LocalKey` value for const-initialized thread locals - (@key $t:ty, const $init:expr) => {{ - #[cfg_attr(not(windows), inline)] // see comments below - #[deny(unsafe_op_in_unsafe_fn)] - unsafe fn __getit( - _init: $crate::option::Option<&mut $crate::option::Option<$t>>, - ) -> $crate::option::Option<&'static $t> { - const INIT_EXPR: $t = $init; - - // wasm without atomics maps directly to `static mut`, and dtors - // aren't implemented because thread dtors aren't really a thing - // on wasm right now - // - // FIXME(#84224) this should come after the `target_thread_local` - // block. - #[cfg(all(target_family = "wasm", not(target_feature = "atomics")))] - { - static mut VAL: $t = INIT_EXPR; - unsafe { $crate::option::Option::Some(&VAL) } - } - - // If the platform has support for `#[thread_local]`, use it. - #[cfg(all( - target_thread_local, - not(all(target_family = "wasm", not(target_feature = "atomics"))), - ))] - { - #[thread_local] - static mut VAL: $t = INIT_EXPR; - - // If a dtor isn't needed we can do something "very raw" and - // just get going. - if !$crate::mem::needs_drop::<$t>() { - unsafe { - return $crate::option::Option::Some(&VAL) - } - } - - // 0 == dtor not registered - // 1 == dtor registered, dtor not run - // 2 == dtor registered and is running or has run - #[thread_local] - static mut STATE: $crate::primitive::u8 = 0; - - unsafe extern "C" fn destroy(ptr: *mut $crate::primitive::u8) { - let ptr = ptr as *mut $t; - - unsafe { - $crate::debug_assert_eq!(STATE, 1); - STATE = 2; - $crate::ptr::drop_in_place(ptr); - } - } - - unsafe { - match STATE { - // 0 == we haven't registered a destructor, so do - // so now. - 0 => { - $crate::thread::__FastLocalKeyInner::<$t>::register_dtor( - $crate::ptr::addr_of_mut!(VAL) as *mut $crate::primitive::u8, - destroy, - ); - STATE = 1; - $crate::option::Option::Some(&VAL) - } - // 1 == the destructor is registered and the value - // is valid, so return the pointer. - 1 => $crate::option::Option::Some(&VAL), - // otherwise the destructor has already run, so we - // can't give access. - _ => $crate::option::Option::None, - } - } - } - - // On platforms without `#[thread_local]` we fall back to the - // same implementation as below for os thread locals. - #[cfg(all( - not(target_thread_local), - not(all(target_family = "wasm", not(target_feature = "atomics"))), - ))] - { - #[inline] - const fn __init() -> $t { INIT_EXPR } - static __KEY: $crate::thread::__OsLocalKeyInner<$t> = - $crate::thread::__OsLocalKeyInner::new(); - #[allow(unused_unsafe)] - unsafe { - __KEY.get(move || { - if let $crate::option::Option::Some(init) = _init { - if let $crate::option::Option::Some(value) = init.take() { - return value; - } else if $crate::cfg!(debug_assertions) { - $crate::unreachable!("missing initial value"); - } - } - __init() - }) - } - } - } - - unsafe { - $crate::thread::LocalKey::new(__getit) - } - }}; - - // used to generate the `LocalKey` value for `thread_local!` - (@key $t:ty, $init:expr) => { - { - #[inline] - fn __init() -> $t { $init } - - // When reading this function you might ask "why is this inlined - // everywhere other than Windows?", and that's a very reasonable - // question to ask. The short story is that it segfaults rustc if - // this function is inlined. The longer story is that Windows looks - // to not support `extern` references to thread locals across DLL - // boundaries. This appears to at least not be supported in the ABI - // that LLVM implements. - // - // Because of this we never inline on Windows, but we do inline on - // other platforms (where external references to thread locals - // across DLLs are supported). A better fix for this would be to - // inline this function on Windows, but only for "statically linked" - // components. For example if two separately compiled rlibs end up - // getting linked into a DLL then it's fine to inline this function - // across that boundary. It's only not fine to inline this function - // across a DLL boundary. Unfortunately rustc doesn't currently - // have this sort of logic available in an attribute, and it's not - // clear that rustc is even equipped to answer this (it's more of a - // Cargo question kinda). This means that, unfortunately, Windows - // gets the pessimistic path for now where it's never inlined. - // - // The issue of "should enable on Windows sometimes" is #84933 - #[cfg_attr(not(windows), inline)] - unsafe fn __getit( - init: $crate::option::Option<&mut $crate::option::Option<$t>>, - ) -> $crate::option::Option<&'static $t> { - #[cfg(all(target_family = "wasm", not(target_feature = "atomics")))] - static __KEY: $crate::thread::__StaticLocalKeyInner<$t> = - $crate::thread::__StaticLocalKeyInner::new(); - - #[thread_local] - #[cfg(all( - target_thread_local, - not(all(target_family = "wasm", not(target_feature = "atomics"))), - ))] - static __KEY: $crate::thread::__FastLocalKeyInner<$t> = - $crate::thread::__FastLocalKeyInner::new(); - - #[cfg(all( - not(target_thread_local), - not(all(target_family = "wasm", not(target_feature = "atomics"))), - ))] - static __KEY: $crate::thread::__OsLocalKeyInner<$t> = - $crate::thread::__OsLocalKeyInner::new(); - - // FIXME: remove the #[allow(...)] marker when macros don't - // raise warning for missing/extraneous unsafe blocks anymore. - // See https://github.com/rust-lang/rust/issues/74838. - #[allow(unused_unsafe)] - unsafe { - __KEY.get(move || { - if let $crate::option::Option::Some(init) = init { - if let $crate::option::Option::Some(value) = init.take() { - return value; - } else if $crate::cfg!(debug_assertions) { - $crate::unreachable!("missing default value"); - } - } - __init() - }) - } - } - - unsafe { - $crate::thread::LocalKey::new(__getit) - } - } - }; - ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { - $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = - $crate::__thread_local_inner!(@key $t, $($init)*); - } -} - /// An error returned by [`LocalKey::try_with`](struct.LocalKey.html#method.try_with). #[stable(feature = "thread_local_try_with", since = "1.26.0")] #[non_exhaustive] @@ -779,376 +585,3 @@ impl LocalKey> { self.with(|cell| cell.replace(value)) } } - -mod lazy { - use crate::cell::UnsafeCell; - use crate::hint; - use crate::mem; - - pub struct LazyKeyInner { - inner: UnsafeCell>, - } - - impl LazyKeyInner { - pub const fn new() -> LazyKeyInner { - LazyKeyInner { inner: UnsafeCell::new(None) } - } - - pub unsafe fn get(&self) -> Option<&'static T> { - // SAFETY: The caller must ensure no reference is ever handed out to - // the inner cell nor mutable reference to the Option inside said - // cell. This make it safe to hand a reference, though the lifetime - // of 'static is itself unsafe, making the get method unsafe. - unsafe { (*self.inner.get()).as_ref() } - } - - /// The caller must ensure that no reference is active: this method - /// needs unique access. - pub unsafe fn initialize T>(&self, init: F) -> &'static T { - // Execute the initialization up front, *then* move it into our slot, - // just in case initialization fails. - let value = init(); - let ptr = self.inner.get(); - - // SAFETY: - // - // note that this can in theory just be `*ptr = Some(value)`, but due to - // the compiler will currently codegen that pattern with something like: - // - // ptr::drop_in_place(ptr) - // ptr::write(ptr, Some(value)) - // - // Due to this pattern it's possible for the destructor of the value in - // `ptr` (e.g., if this is being recursively initialized) to re-access - // TLS, in which case there will be a `&` and `&mut` pointer to the same - // value (an aliasing violation). To avoid setting the "I'm running a - // destructor" flag we just use `mem::replace` which should sequence the - // operations a little differently and make this safe to call. - // - // The precondition also ensures that we are the only one accessing - // `self` at the moment so replacing is fine. - unsafe { - let _ = mem::replace(&mut *ptr, Some(value)); - } - - // SAFETY: With the call to `mem::replace` it is guaranteed there is - // a `Some` behind `ptr`, not a `None` so `unreachable_unchecked` - // will never be reached. - unsafe { - // After storing `Some` we want to get a reference to the contents of - // what we just stored. While we could use `unwrap` here and it should - // always work it empirically doesn't seem to always get optimized away, - // which means that using something like `try_with` can pull in - // panicking code and cause a large size bloat. - match *ptr { - Some(ref x) => x, - None => hint::unreachable_unchecked(), - } - } - } - - /// The other methods hand out references while taking &self. - /// As such, callers of this method must ensure no `&` and `&mut` are - /// available and used at the same time. - #[allow(unused)] - pub unsafe fn take(&mut self) -> Option { - // SAFETY: See doc comment for this method. - unsafe { (*self.inner.get()).take() } - } - } -} - -/// On some targets like wasm there's no threads, so no need to generate -/// thread locals and we can instead just use plain statics! -#[doc(hidden)] -#[cfg(all(target_family = "wasm", not(target_feature = "atomics")))] -pub mod statik { - use super::lazy::LazyKeyInner; - use crate::fmt; - - pub struct Key { - inner: LazyKeyInner, - } - - unsafe impl Sync for Key {} - - impl fmt::Debug for Key { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Key").finish_non_exhaustive() - } - } - - impl Key { - pub const fn new() -> Key { - Key { inner: LazyKeyInner::new() } - } - - pub unsafe fn get(&self, init: impl FnOnce() -> T) -> Option<&'static T> { - // SAFETY: The caller must ensure no reference is ever handed out to - // the inner cell nor mutable reference to the Option inside said - // cell. This make it safe to hand a reference, though the lifetime - // of 'static is itself unsafe, making the get method unsafe. - let value = unsafe { - match self.inner.get() { - Some(ref value) => value, - None => self.inner.initialize(init), - } - }; - - Some(value) - } - } -} - -#[doc(hidden)] -#[cfg(all(target_thread_local, not(all(target_family = "wasm", not(target_feature = "atomics"))),))] -pub mod fast { - use super::lazy::LazyKeyInner; - use crate::cell::Cell; - use crate::sys::thread_local_dtor::register_dtor; - use crate::{fmt, mem, panic}; - - #[derive(Copy, Clone)] - enum DtorState { - Unregistered, - Registered, - RunningOrHasRun, - } - - // This data structure has been carefully constructed so that the fast path - // only contains one branch on x86. That optimization is necessary to avoid - // duplicated tls lookups on OSX. - // - // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722 - pub struct Key { - // If `LazyKeyInner::get` returns `None`, that indicates either: - // * The value has never been initialized - // * The value is being recursively initialized - // * The value has already been destroyed or is being destroyed - // To determine which kind of `None`, check `dtor_state`. - // - // This is very optimizer friendly for the fast path - initialized but - // not yet dropped. - inner: LazyKeyInner, - - // Metadata to keep track of the state of the destructor. Remember that - // this variable is thread-local, not global. - dtor_state: Cell, - } - - impl fmt::Debug for Key { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Key").finish_non_exhaustive() - } - } - - impl Key { - pub const fn new() -> Key { - Key { inner: LazyKeyInner::new(), dtor_state: Cell::new(DtorState::Unregistered) } - } - - // note that this is just a publicly-callable function only for the - // const-initialized form of thread locals, basically a way to call the - // free `register_dtor` function defined elsewhere in std. - pub unsafe fn register_dtor(a: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - unsafe { - register_dtor(a, dtor); - } - } - - pub unsafe fn get T>(&self, init: F) -> Option<&'static T> { - // SAFETY: See the definitions of `LazyKeyInner::get` and - // `try_initialize` for more information. - // - // The caller must ensure no mutable references are ever active to - // the inner cell or the inner T when this is called. - // The `try_initialize` is dependant on the passed `init` function - // for this. - unsafe { - match self.inner.get() { - Some(val) => Some(val), - None => self.try_initialize(init), - } - } - } - - // `try_initialize` is only called once per fast thread local variable, - // except in corner cases where thread_local dtors reference other - // thread_local's, or it is being recursively initialized. - // - // Macos: Inlining this function can cause two `tlv_get_addr` calls to - // be performed for every call to `Key::get`. - // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722 - #[inline(never)] - unsafe fn try_initialize T>(&self, init: F) -> Option<&'static T> { - // SAFETY: See comment above (this function doc). - if !mem::needs_drop::() || unsafe { self.try_register_dtor() } { - // SAFETY: See comment above (this function doc). - Some(unsafe { self.inner.initialize(init) }) - } else { - None - } - } - - // `try_register_dtor` is only called once per fast thread local - // variable, except in corner cases where thread_local dtors reference - // other thread_local's, or it is being recursively initialized. - unsafe fn try_register_dtor(&self) -> bool { - match self.dtor_state.get() { - DtorState::Unregistered => { - // SAFETY: dtor registration happens before initialization. - // Passing `self` as a pointer while using `destroy_value` - // is safe because the function will build a pointer to a - // Key, which is the type of self and so find the correct - // size. - unsafe { register_dtor(self as *const _ as *mut u8, destroy_value::) }; - self.dtor_state.set(DtorState::Registered); - true - } - DtorState::Registered => { - // recursively initialized - true - } - DtorState::RunningOrHasRun => false, - } - } - } - - unsafe extern "C" fn destroy_value(ptr: *mut u8) { - let ptr = ptr as *mut Key; - - // SAFETY: - // - // The pointer `ptr` has been built just above and comes from - // `try_register_dtor` where it is originally a Key coming from `self`, - // making it non-NUL and of the correct type. - // - // Right before we run the user destructor be sure to set the - // `Option` to `None`, and `dtor_state` to `RunningOrHasRun`. This - // causes future calls to `get` to run `try_initialize_drop` again, - // which will now fail, and return `None`. - // - // Wrap the call in a catch to ensure unwinding is caught in the event - // a panic takes place in a destructor. - if let Err(_) = panic::catch_unwind(panic::AssertUnwindSafe(|| unsafe { - let value = (*ptr).inner.take(); - (*ptr).dtor_state.set(DtorState::RunningOrHasRun); - drop(value); - })) { - rtabort!("thread local panicked on drop"); - } - } -} - -#[doc(hidden)] -#[cfg(all( - not(target_thread_local), - not(all(target_family = "wasm", not(target_feature = "atomics"))), -))] -pub mod os { - use super::lazy::LazyKeyInner; - use crate::cell::Cell; - use crate::sys_common::thread_local_key::StaticKey as OsStaticKey; - use crate::{fmt, marker, panic, ptr}; - - /// Use a regular global static to store this key; the state provided will then be - /// thread-local. - pub struct Key { - // OS-TLS key that we'll use to key off. - os: OsStaticKey, - marker: marker::PhantomData>, - } - - impl fmt::Debug for Key { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Key").finish_non_exhaustive() - } - } - - unsafe impl Sync for Key {} - - struct Value { - inner: LazyKeyInner, - key: &'static Key, - } - - impl Key { - #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")] - pub const fn new() -> Key { - Key { os: OsStaticKey::new(Some(destroy_value::)), marker: marker::PhantomData } - } - - /// It is a requirement for the caller to ensure that no mutable - /// reference is active when this method is called. - pub unsafe fn get(&'static self, init: impl FnOnce() -> T) -> Option<&'static T> { - // SAFETY: See the documentation for this method. - let ptr = unsafe { self.os.get() as *mut Value }; - if ptr.addr() > 1 { - // SAFETY: the check ensured the pointer is safe (its destructor - // is not running) + it is coming from a trusted source (self). - if let Some(ref value) = unsafe { (*ptr).inner.get() } { - return Some(value); - } - } - // SAFETY: At this point we are sure we have no value and so - // initializing (or trying to) is safe. - unsafe { self.try_initialize(init) } - } - - // `try_initialize` is only called once per os thread local variable, - // except in corner cases where thread_local dtors reference other - // thread_local's, or it is being recursively initialized. - unsafe fn try_initialize(&'static self, init: impl FnOnce() -> T) -> Option<&'static T> { - // SAFETY: No mutable references are ever handed out meaning getting - // the value is ok. - let ptr = unsafe { self.os.get() as *mut Value }; - if ptr.addr() == 1 { - // destructor is running - return None; - } - - let ptr = if ptr.is_null() { - // If the lookup returned null, we haven't initialized our own - // local copy, so do that now. - let ptr = Box::into_raw(Box::new(Value { inner: LazyKeyInner::new(), key: self })); - // SAFETY: At this point we are sure there is no value inside - // ptr so setting it will not affect anyone else. - unsafe { - self.os.set(ptr as *mut u8); - } - ptr - } else { - // recursive initialization - ptr - }; - - // SAFETY: ptr has been ensured as non-NUL just above an so can be - // dereferenced safely. - unsafe { Some((*ptr).inner.initialize(init)) } - } - } - - unsafe extern "C" fn destroy_value(ptr: *mut u8) { - // SAFETY: - // - // The OS TLS ensures that this key contains a null value when this - // destructor starts to run. We set it back to a sentinel value of 1 to - // ensure that any future calls to `get` for this thread will return - // `None`. - // - // Note that to prevent an infinite loop we reset it back to null right - // before we return from the destructor ourselves. - // - // Wrap the call in a catch to ensure unwinding is caught in the event - // a panic takes place in a destructor. - if let Err(_) = panic::catch_unwind(|| unsafe { - let ptr = Box::from_raw(ptr as *mut Value); - let key = ptr.key; - key.os.set(ptr::invalid_mut(1)); - drop(ptr); - key.os.set(ptr::null_mut()); - }) { - rtabort!("thread local panicked on drop"); - } - } -} diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 489af776798..b9aaf5f6e15 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -203,44 +203,9 @@ pub use self::local::{AccessError, LocalKey}; // by the elf linker. "static" is for single-threaded platforms where a global // static is sufficient. -#[unstable(feature = "libstd_thread_internals", issue = "none")] -#[cfg(not(test))] -#[cfg(all( - target_thread_local, - not(all(target_family = "wasm", not(target_feature = "atomics"))), -))] #[doc(hidden)] -pub use self::local::fast::Key as __FastLocalKeyInner; -// when building for tests, use real std's type #[unstable(feature = "libstd_thread_internals", issue = "none")] -#[cfg(test)] -#[cfg(all( - target_thread_local, - not(all(target_family = "wasm", not(target_feature = "atomics"))), -))] -pub use realstd::thread::__FastLocalKeyInner; - -#[unstable(feature = "libstd_thread_internals", issue = "none")] -#[cfg(not(test))] -#[cfg(all( - not(target_thread_local), - not(all(target_family = "wasm", not(target_feature = "atomics"))), -))] -#[doc(hidden)] -pub use self::local::os::Key as __OsLocalKeyInner; -// when building for tests, use real std's type -#[unstable(feature = "libstd_thread_internals", issue = "none")] -#[cfg(test)] -#[cfg(all( - not(target_thread_local), - not(all(target_family = "wasm", not(target_feature = "atomics"))), -))] -pub use realstd::thread::__OsLocalKeyInner; - -#[unstable(feature = "libstd_thread_internals", issue = "none")] -#[cfg(all(target_family = "wasm", not(target_feature = "atomics")))] -#[doc(hidden)] -pub use self::local::statik::Key as __StaticLocalKeyInner; +pub use crate::sys::common::thread_local::Key as __LocalKeyInner; //////////////////////////////////////////////////////////////////////////////// // Builder diff --git a/src/bootstrap/CHANGELOG.md b/src/bootstrap/CHANGELOG.md index 648eb553c78..54d0d8a8ec2 100644 --- a/src/bootstrap/CHANGELOG.md +++ b/src/bootstrap/CHANGELOG.md @@ -47,6 +47,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Add `--keep-stage-std`, which behaves like `keep-stage` but allows the stage 0 compiler artifacts (i.e., stage1/bin/rustc) to be rebuilt if changed [#77120](https://github.com/rust-lang/rust/pull/77120). +- File locking is now used to avoid collisions between multiple running instances of `x.py` (e.g. when using `rust-analyzer` and `x.py` at the same time). Note that Solaris and possibly other non Unix and non Windows systems don't support it [#108607](https://github.com/rust-lang/rust/pull/108607). This might possibly lead to build data corruption. ## [Version 1] - 2020-09-11 diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 663987f113c..afe3fc1741b 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -32,7 +32,6 @@ test = false [dependencies] build_helper = { path = "../tools/build_helper" } cmake = "0.1.38" -fd-lock = "3.0.8" filetime = "0.2" getopts = "0.2.19" cc = "1.0.69" @@ -56,6 +55,10 @@ walkdir = "2" # Dependencies needed by the build-metrics feature sysinfo = { version = "0.26.0", optional = true } +# Solaris doesn't support flock() and thus fd-lock is not option now +[target.'cfg(not(target_os = "solaris"))'.dependencies] +fd-lock = "3.0.8" + [target.'cfg(windows)'.dependencies.winapi] version = "0.3" features = [ diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md index 71eee8968e2..253d504d7bd 100644 --- a/src/bootstrap/README.md +++ b/src/bootstrap/README.md @@ -185,7 +185,7 @@ Some general areas that you may be interested in modifying are: If you make a major change, please remember to: + Update `VERSION` in `src/bootstrap/main.rs`. -* Update `changelog-seen = N` in `config.toml.example`. +* Update `changelog-seen = N` in `config.example.toml`. * Add an entry in `src/bootstrap/CHANGELOG.md`. A 'major change' includes diff --git a/src/bootstrap/bin/main.rs b/src/bootstrap/bin/main.rs index 3856bb64fb3..912d875e445 100644 --- a/src/bootstrap/bin/main.rs +++ b/src/bootstrap/bin/main.rs @@ -7,15 +7,18 @@ use std::env; -use bootstrap::{t, Build, Config, Subcommand, VERSION}; +#[cfg(all(any(unix, windows), not(target_os = "solaris")))] +use bootstrap::t; +use bootstrap::{Build, Config, Subcommand, VERSION}; fn main() { let args = env::args().skip(1).collect::>(); let config = Config::parse(&args); - let mut build_lock; - let _build_lock_guard; - if cfg!(any(unix, windows)) { + #[cfg(all(any(unix, windows), not(target_os = "solaris")))] + { + let mut build_lock; + let _build_lock_guard; let path = config.out.join("lock"); build_lock = fd_lock::RwLock::new(t!(std::fs::File::create(&path))); _build_lock_guard = match build_lock.try_write() { @@ -30,9 +33,9 @@ fn main() { t!(build_lock.write()) } }; - } else { - println!("warning: file locking not supported for target, not locking build directory"); } + #[cfg(any(not(any(unix, windows)), target_os = "solaris"))] + println!("warning: file locking not supported for target, not locking build directory"); // check_version warnings are not printed during setup let changelog_suggestion = @@ -44,8 +47,8 @@ fn main() { if suggest_setup { println!("warning: you have not made a `config.toml`"); println!( - "help: consider running `./x.py setup` or copying `config.toml.example` by running \ - `cp config.toml.example config.toml`" + "help: consider running `./x.py setup` or copying `config.example.toml` by running \ + `cp config.example.toml config.toml`" ); } else if let Some(suggestion) = &changelog_suggestion { println!("{}", suggestion); @@ -57,8 +60,8 @@ fn main() { if suggest_setup { println!("warning: you have not made a `config.toml`"); println!( - "help: consider running `./x.py setup` or copying `config.toml.example` by running \ - `cp config.toml.example config.toml`" + "help: consider running `./x.py setup` or copying `config.example.toml` by running \ + `cp config.example.toml config.toml`" ); } else if let Some(suggestion) = &changelog_suggestion { println!("{}", suggestion); @@ -125,7 +128,7 @@ fn get_lock_owner(f: &std::path::Path) -> Option { }) } -#[cfg(not(target_os = "linux"))] +#[cfg(not(any(target_os = "linux", target_os = "solaris")))] fn get_lock_owner(_: &std::path::Path) -> Option { // FIXME: Implement on other OS's None diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 47d742ef796..bb07ca1908e 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -741,6 +741,7 @@ impl<'a> Builder<'a> { doc::EmbeddedBook, doc::EditionGuide, doc::StyleGuide, + doc::Tidy, ), Kind::Dist => describe!( dist::Docs, diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 4f417d36511..fc5aa8a245d 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -55,7 +55,7 @@ pub enum DryRun { /// Note that this structure is not decoded directly into, but rather it is /// filled out from the decoded forms of the structs below. For documentation /// each field, see the corresponding fields in -/// `config.toml.example`. +/// `config.example.toml`. #[derive(Default)] #[cfg_attr(test, derive(Clone))] pub struct Config { @@ -325,7 +325,7 @@ impl std::str::FromStr for SplitDebuginfo { impl SplitDebuginfo { /// Returns the default `-Csplit-debuginfo` value for the current target. See the comment for - /// `rust.split-debuginfo` in `config.toml.example`. + /// `rust.split-debuginfo` in `config.example.toml`. fn default_for_platform(target: &str) -> Self { if target.contains("apple") { SplitDebuginfo::Unpacked diff --git a/src/bootstrap/config/tests.rs b/src/bootstrap/config/tests.rs index 5a105007f21..16dc8c63abc 100644 --- a/src/bootstrap/config/tests.rs +++ b/src/bootstrap/config/tests.rs @@ -1,5 +1,5 @@ use super::{Config, TomlConfig}; -use std::path::Path; +use std::{env, path::Path}; fn toml(config: &str) -> impl '_ + Fn(&Path) -> TomlConfig { |&_| toml::from_str(config).unwrap() @@ -33,4 +33,35 @@ fn download_ci_llvm() { )); } -// FIXME: add test for detecting `src` and `out` +#[test] +fn detect_src_and_out() { + let cfg = parse(""); + + // This will bring absolute form of `src/bootstrap` path + let current_dir = std::env::current_dir().unwrap(); + + // get `src` by moving into project root path + let expected_src = current_dir.ancestors().nth(2).unwrap(); + + assert_eq!(&cfg.src, expected_src); + + // This should bring output path of bootstrap in absolute form + let cargo_target_dir = env::var_os("CARGO_TARGET_DIR") + .expect("CARGO_TARGET_DIR must been provided for the test environment from bootstrap"); + + // Move to `build` from `build/bootstrap` + let expected_out = Path::new(&cargo_target_dir).parent().unwrap(); + assert_eq!(&cfg.out, expected_out); + + let args: Vec = env::args().collect(); + + // Another test for `out` as a sanity check + // + // This will bring something similar to: + // `{config_toml_place}/build/bootstrap/debug/deps/bootstrap-c7ee91d5661e2804` + // `{config_toml_place}` can be anywhere, not just in the rust project directory. + let dep = Path::new(args.first().unwrap()); + let expected_out = dep.ancestors().nth(4).unwrap(); + + assert_eq!(&cfg.out, expected_out); +} diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 5278f0c10b3..b326ae402aa 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -194,7 +194,7 @@ if '--help' in sys.argv or '-h' in sys.argv: print('') print('This configure script is a thin configuration shim over the true') print('configuration system, `config.toml`. You can explore the comments') - print('in `config.toml.example` next to this configure script to see') + print('in `config.example.toml` next to this configure script to see') print('more information about what each option is. Additionally you can') print('pass `--set` as an argument to set arbitrary key/value pairs') print('in the TOML configuration if desired') @@ -367,7 +367,7 @@ for key in known_args: set('build.configure-args', sys.argv[1:]) -# "Parse" the `config.toml.example` file into the various sections, and we'll +# "Parse" the `config.example.toml` file into the various sections, and we'll # use this as a template of a `config.toml` to write out which preserves # all the various comments and whatnot. # @@ -380,7 +380,7 @@ section_order = [None] targets = {} top_level_keys = [] -for line in open(rust_dir + '/config.toml.example').read().split("\n"): +for line in open(rust_dir + '/config.example.toml').read().split("\n"): if cur_section == None: if line.count('=') == 1: top_level_key = line.split('=')[0] diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index c9384004100..a3d9cb3e10c 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -967,7 +967,7 @@ impl Step for PlainSourceTarball { "RELEASES.md", "configure", "x.py", - "config.toml.example", + "config.example.toml", "Cargo.toml", "Cargo.lock", ]; diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index cc80763ef44..36fdd4abf4f 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -882,6 +882,7 @@ tool_doc!( // "cargo-credential-wincred", ] ); +tool_doc!(Tidy, "tidy", "src/tools/tidy", ["tidy"]); #[derive(Ord, PartialOrd, Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct ErrorIndex { diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 909e7d83a15..040e36ea5f8 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -216,7 +216,7 @@ pub(crate) fn is_ci_llvm_available(config: &Config, asserts: bool) -> bool { /// Returns true if we're running in CI with modified LLVM (and thus can't download it) pub(crate) fn is_ci_llvm_modified(config: &Config) -> bool { - CiEnv::is_ci() && { + CiEnv::is_ci() && config.rust_info.is_managed_git_subrepository() && { // We assume we have access to git, so it's okay to unconditionally pass // `true` here. let llvm_sha = detect_llvm_sha(config, true); @@ -286,7 +286,7 @@ impl Step for Llvm { (true, true) => "RelWithDebInfo", }; - // NOTE: remember to also update `config.toml.example` when changing the + // NOTE: remember to also update `config.example.toml` when changing the // defaults! let llvm_targets = match &builder.config.llvm_targets { Some(s) => s, diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index 98bd90210d6..515890aef8d 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -52,4 +52,6 @@ ENV SCRIPT python3 ../x.py --stage 2 test src/tools/expand-yaml-anchors && \ reuse lint && \ # Runs checks to ensure that there are no ES5 issues in our JS code. es-check es6 ../src/librustdoc/html/static/js/*.js && \ - eslint -c ../src/librustdoc/html/static/.eslintrc.js ../src/librustdoc/html/static/js/*.js + eslint -c ../src/librustdoc/html/static/.eslintrc.js ../src/librustdoc/html/static/js/*.js && \ + eslint -c ../src/tools/rustdoc-js/.eslintrc.js ../src/tools/rustdoc-js/tester.js && \ + eslint -c ../src/tools/rustdoc-gui/.eslintrc.js ../src/tools/rustdoc-gui/tester.js diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-14/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-14/Dockerfile index db6032f8752..a007bf183ee 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-14/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-14/Dockerfile @@ -62,6 +62,4 @@ ENV SCRIPT ../x.py --stage 2 test --exclude src/tools/tidy && \ # work. # ../x.ps1 --stage 2 test tests/ui --pass=check \ - --host='' --target=i686-unknown-linux-gnu && \ - # Run tidy at the very end, after all the other tests. - python2.7 ../x.py --stage 2 test src/tools/tidy + --host='' --target=i686-unknown-linux-gnu diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml index 11f1532bef5..b490b766663 100644 --- a/src/ci/github-actions/ci.yml +++ b/src/ci/github-actions/ci.yml @@ -673,7 +673,6 @@ jobs: --target=x86_64-pc-windows-msvc --enable-full-tools --enable-profiler - --set rust.lto=thin SCRIPT: PGO_HOST=x86_64-pc-windows-msvc python src/ci/stage-build.py python x.py dist bootstrap --include-default-paths DIST_REQUIRE_ALL_TOOLS: 1 <<: *job-windows-xl diff --git a/src/ci/github-actions/problem_matchers.json b/src/ci/github-actions/problem_matchers.json new file mode 100644 index 00000000000..37561924b7d --- /dev/null +++ b/src/ci/github-actions/problem_matchers.json @@ -0,0 +1,15 @@ +{ + "problemMatcher": [ + { + "owner": "tidy-error-file-line", + "pattern": [ + { + "regexp": "^tidy error: /checkout/(.+):(\\d+): (.+)$", + "file": 1, + "line": 2, + "message": 3 + } + ] + } + ] +} diff --git a/src/ci/scripts/collect-cpu-stats.sh b/src/ci/scripts/collect-cpu-stats.sh index 853b4628fab..44875b54ddc 100755 --- a/src/ci/scripts/collect-cpu-stats.sh +++ b/src/ci/scripts/collect-cpu-stats.sh @@ -6,4 +6,5 @@ set -euo pipefail IFS=$'\n\t' -python3 src/ci/cpu-usage-over-time.py &> cpu-usage.csv & +mkdir -p build +python3 src/ci/cpu-usage-over-time.py &> build/cpu-usage.csv & diff --git a/src/ci/scripts/run-build-from-ci.sh b/src/ci/scripts/run-build-from-ci.sh index c02117f459d..55e75800d91 100755 --- a/src/ci/scripts/run-build-from-ci.sh +++ b/src/ci/scripts/run-build-from-ci.sh @@ -10,6 +10,8 @@ source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" export CI="true" export SRC=. +echo "::add-matcher::src/ci/github-actions/problem_matchers.json" + # Remove any preexisting rustup installation since it can interfere # with the cargotest step and its auto-detection of things like Clippy in # the environment diff --git a/src/ci/scripts/upload-artifacts.sh b/src/ci/scripts/upload-artifacts.sh index ffa1859fc22..9755edb6dce 100755 --- a/src/ci/scripts/upload-artifacts.sh +++ b/src/ci/scripts/upload-artifacts.sh @@ -23,7 +23,7 @@ if [[ "${DEPLOY-0}" -eq "1" ]] || [[ "${DEPLOY_ALT-0}" -eq "1" ]]; then fi # CPU usage statistics. -cp cpu-usage.csv "${upload_dir}/cpu-${CI_JOB_NAME}.csv" +cp build/cpu-usage.csv "${upload_dir}/cpu-${CI_JOB_NAME}.csv" # Build metrics generated by x.py. cp "${build_dir}/metrics.json" "${upload_dir}/metrics-${CI_JOB_NAME}.json" diff --git a/src/doc/footer.inc b/src/doc/footer.inc index 77e151235e8..504fe51156d 100644 --- a/src/doc/footer.inc +++ b/src/doc/footer.inc @@ -1,3 +1,4 @@ +

Copyright © 2011 The Rust Project Developers. Licensed under the Apache License, Version 2.0 @@ -5,3 +6,4 @@ or the MIT license, at your op

This file may not be copied, modified, or distributed except according to those terms.

+ diff --git a/src/doc/nomicon b/src/doc/nomicon index 79b53665a7c..1f3e4cd4fd8 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit 79b53665a7c61d171fb8c5ad0b73b371f9ee6ba7 +Subproject commit 1f3e4cd4fd88b5b5d45feb86a11b6d2f93e5a974 diff --git a/src/doc/reference b/src/doc/reference index a9afb04b47a..24c87f6663a 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit a9afb04b47a84a6753e4dc657348c324c876102c +Subproject commit 24c87f6663aed55b05d2cc286878f28f21918825 diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide index b06dab84083..b1b6d693cd1 160000 --- a/src/doc/rustc-dev-guide +++ b/src/doc/rustc-dev-guide @@ -1 +1 @@ -Subproject commit b06dab84083390e0ee1e998f466545a8a1a76a9f +Subproject commit b1b6d693cd1461e53de4132c1b183ace31cd36e5 diff --git a/src/doc/rustc/src/instrument-coverage.md b/src/doc/rustc/src/instrument-coverage.md index da91e25595c..b0b2f419642 100644 --- a/src/doc/rustc/src/instrument-coverage.md +++ b/src/doc/rustc/src/instrument-coverage.md @@ -31,7 +31,7 @@ Rust's source-based code coverage requires the Rust "profiler runtime". Without The Rust `nightly` distribution channel includes the profiler runtime, by default. -> **Important**: If you are building the Rust compiler from the source distribution, the profiler runtime is _not_ enabled in the default `config.toml.example`. Edit your `config.toml` file and ensure the `profiler` feature is set it to `true` (either under the `[build]` section, or under the settings for an individual `[target.]`): +> **Important**: If you are building the Rust compiler from the source distribution, the profiler runtime is _not_ enabled in the default `config.example.toml`. Edit your `config.toml` file and ensure the `profiler` feature is set it to `true` (either under the `[build]` section, or under the settings for an individual `[target.]`): > > ```toml > # Build the profiler runtime (required when compiling with options that depend diff --git a/src/doc/unstable-book/src/language-features/box-patterns.md b/src/doc/unstable-book/src/language-features/box-patterns.md index 584f4295cba..a1ac09633b7 100644 --- a/src/doc/unstable-book/src/language-features/box-patterns.md +++ b/src/doc/unstable-book/src/language-features/box-patterns.md @@ -4,8 +4,6 @@ The tracking issue for this feature is: [#29641] [#29641]: https://github.com/rust-lang/rust/issues/29641 -See also [`box_syntax`](box-syntax.md) - ------------------------ Box patterns let you match on `Box`s: diff --git a/src/doc/unstable-book/src/language-features/box-syntax.md b/src/doc/unstable-book/src/language-features/box-syntax.md deleted file mode 100644 index 9569974d22c..00000000000 --- a/src/doc/unstable-book/src/language-features/box-syntax.md +++ /dev/null @@ -1,22 +0,0 @@ -# `box_syntax` - -The tracking issue for this feature is: [#49733] - -[#49733]: https://github.com/rust-lang/rust/issues/49733 - -See also [`box_patterns`](box-patterns.md) - ------------------------- - -Currently the only stable way to create a `Box` is via the `Box::new` method. -Also it is not possible in stable Rust to destructure a `Box` in a match -pattern. The unstable `box` keyword can be used to create a `Box`. An example -usage would be: - -```rust -#![feature(box_syntax)] - -fn main() { - let b = box 5; -} -``` diff --git a/src/doc/unstable-book/src/the-unstable-book.md b/src/doc/unstable-book/src/the-unstable-book.md index 554c52c3c9c..9090b134dc6 100644 --- a/src/doc/unstable-book/src/the-unstable-book.md +++ b/src/doc/unstable-book/src/the-unstable-book.md @@ -5,16 +5,31 @@ each one organized by a "feature flag." That is, when using an unstable feature of Rust, you must use a flag, like this: ```rust -#![feature(box_syntax)] +#![feature(generators, generator_trait)] + +use std::ops::{Generator, GeneratorState}; +use std::pin::Pin; fn main() { - let five = box 5; + let mut generator = || { + yield 1; + return "foo" + }; + + match Pin::new(&mut generator).resume(()) { + GeneratorState::Yielded(1) => {} + _ => panic!("unexpected value from resume"), + } + match Pin::new(&mut generator).resume(()) { + GeneratorState::Complete("foo") => {} + _ => panic!("unexpected value from resume"), + } } ``` -The `box_syntax` feature [has a chapter][box] describing how to use it. +The `generators` feature [has a chapter][generators] describing how to use it. -[box]: language-features/box-syntax.md +[generators]: language-features/generators.md Because this documentation relates to unstable features, we make no guarantees that what is contained here is accurate or up to date. It's developed on a diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 8dbfaf4bbc9..0295de8437e 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -346,6 +346,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { self.cache, ), aliases: item.attrs.get_doc_aliases(), + deprecation: item.deprecation(self.tcx), }); } } diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index f9ea829e388..024ea62c31a 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1,13 +1,15 @@ //! HTML formatting module //! //! This module contains a large number of `fmt::Display` implementations for -//! various types in `rustdoc::clean`. These implementations all currently -//! assume that HTML output is desired, although it may be possible to redesign -//! them in the future to instead emit any format desired. +//! various types in `rustdoc::clean`. +//! +//! These implementations all emit HTML. As an internal implementation detail, +//! some of them support an alternate format that emits text, but that should +//! not be used external to this module. use std::borrow::Cow; use std::cell::Cell; -use std::fmt; +use std::fmt::{self, Write}; use std::iter::{self, once}; use rustc_ast as ast; @@ -126,7 +128,6 @@ impl Buffer { // the fmt::Result return type imposed by fmt::Write (and avoiding the trait // import). pub(crate) fn write_fmt(&mut self, v: fmt::Arguments<'_>) { - use fmt::Write; self.buffer.write_fmt(v).unwrap(); } @@ -279,8 +280,6 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>( indent: usize, ending: Ending, ) -> impl fmt::Display + 'a + Captures<'tcx> { - use fmt::Write; - display_fn(move |f| { let mut where_predicates = gens.where_predicates.iter().filter(|pred| { !matches!(pred, clean::WherePredicate::BoundPredicate { bounds, .. } if bounds.is_empty()) @@ -1306,6 +1305,28 @@ impl clean::BareFunctionDecl { } } +// Implements Write but only counts the bytes "written". +struct WriteCounter(usize); + +impl std::fmt::Write for WriteCounter { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.0 += s.len(); + Ok(()) + } +} + +// Implements Display by emitting the given number of spaces. +struct Indent(usize); + +impl fmt::Display for Indent { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (0..self.0).for_each(|_| { + f.write_char(' ').unwrap(); + }); + Ok(()) + } +} + impl clean::FnDecl { pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>( &'a self, @@ -1345,95 +1366,80 @@ impl clean::FnDecl { indent: usize, cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { - display_fn(move |f| self.inner_full_print(header_len, indent, f, cx)) + display_fn(move |f| { + // First, generate the text form of the declaration, with no line wrapping, and count the bytes. + let mut counter = WriteCounter(0); + write!(&mut counter, "{:#}", display_fn(|f| { self.inner_full_print(None, f, cx) })) + .unwrap(); + // If the text form was over 80 characters wide, we will line-wrap our output. + let line_wrapping_indent = + if header_len + counter.0 > 80 { Some(indent) } else { None }; + // Generate the final output. This happens to accept `{:#}` formatting to get textual + // output but in practice it is only formatted with `{}` to get HTML output. + self.inner_full_print(line_wrapping_indent, f, cx) + }) } fn inner_full_print( &self, - header_len: usize, - indent: usize, + // For None, the declaration will not be line-wrapped. For Some(n), + // the declaration will be line-wrapped, with an indent of n spaces. + line_wrapping_indent: Option, f: &mut fmt::Formatter<'_>, cx: &Context<'_>, ) -> fmt::Result { let amp = if f.alternate() { "&" } else { "&" }; - let mut args = Buffer::html(); - let mut args_plain = Buffer::new(); + + write!(f, "(")?; + if let Some(n) = line_wrapping_indent { + write!(f, "\n{}", Indent(n + 4))?; + } for (i, input) in self.inputs.values.iter().enumerate() { + if i > 0 { + match line_wrapping_indent { + None => write!(f, ", ")?, + Some(n) => write!(f, ",\n{}", Indent(n + 4))?, + }; + } if let Some(selfty) = input.to_self() { match selfty { clean::SelfValue => { - args.push_str("self"); - args_plain.push_str("self"); + write!(f, "self")?; } clean::SelfBorrowed(Some(ref lt), mtbl) => { - write!(args, "{}{} {}self", amp, lt.print(), mtbl.print_with_space()); - write!(args_plain, "&{} {}self", lt.print(), mtbl.print_with_space()); + write!(f, "{}{} {}self", amp, lt.print(), mtbl.print_with_space())?; } clean::SelfBorrowed(None, mtbl) => { - write!(args, "{}{}self", amp, mtbl.print_with_space()); - write!(args_plain, "&{}self", mtbl.print_with_space()); + write!(f, "{}{}self", amp, mtbl.print_with_space())?; } clean::SelfExplicit(ref typ) => { - if f.alternate() { - write!(args, "self: {:#}", typ.print(cx)); - } else { - write!(args, "self: {}", typ.print(cx)); - } - write!(args_plain, "self: {:#}", typ.print(cx)); + write!(f, "self: ")?; + fmt::Display::fmt(&typ.print(cx), f)?; } } } else { - if i > 0 { - args.push_str("\n"); - } if input.is_const { - args.push_str("const "); - args_plain.push_str("const "); + write!(f, "const ")?; } - write!(args, "{}: ", input.name); - write!(args_plain, "{}: ", input.name); - - if f.alternate() { - write!(args, "{:#}", input.type_.print(cx)); - } else { - write!(args, "{}", input.type_.print(cx)); - } - write!(args_plain, "{:#}", input.type_.print(cx)); - } - if i + 1 < self.inputs.values.len() { - args.push_str(","); - args_plain.push_str(","); + write!(f, "{}: ", input.name)?; + fmt::Display::fmt(&input.type_.print(cx), f)?; } } - let mut args_plain = format!("({})", args_plain.into_inner()); - let mut args = args.into_inner(); - if self.c_variadic { - args.push_str(",\n ..."); - args_plain.push_str(", ..."); + match line_wrapping_indent { + None => write!(f, ", ...")?, + Some(n) => write!(f, "\n{}...", Indent(n + 4))?, + }; } - let arrow_plain = format!("{:#}", self.output.print(cx)); - let arrow = - if f.alternate() { arrow_plain.clone() } else { format!("{}", self.output.print(cx)) }; - - let declaration_len = header_len + args_plain.len() + arrow_plain.len(); - let output = if declaration_len > 80 { - let full_pad = format!("\n{}", " ".repeat(indent + 4)); - let close_pad = format!("\n{}", " ".repeat(indent)); - format!( - "({pad}{args}{close}){arrow}", - pad = if self.inputs.values.is_empty() { "" } else { &full_pad }, - args = args.replace('\n', &full_pad), - close = close_pad, - arrow = arrow - ) - } else { - format!("({args}){arrow}", args = args.replace('\n', " "), arrow = arrow) + match line_wrapping_indent { + None => write!(f, ")")?, + Some(n) => write!(f, "\n{})", Indent(n))?, }; - write!(f, "{}", output) + fmt::Display::fmt(&self.output.print(cx), f)?; + Ok(()) } } diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index ed1eb66b97c..63cd0e04a28 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -17,10 +17,11 @@ use super::print_item::{full_path, item_path, print_item}; use super::search_index::build_index; use super::write_shared::write_shared; use super::{ - collect_spans_and_sources, print_sidebar, scrape_examples_help, sidebar_module_like, AllTypes, - LinkFromSrc, StylePath, + collect_spans_and_sources, scrape_examples_help, + sidebar::print_sidebar, + sidebar::{sidebar_module_like, Sidebar}, + AllTypes, LinkFromSrc, StylePath, }; - use crate::clean::{self, types::ExternalLocation, ExternalCrate}; use crate::config::{ModuleSorting, RenderOptions}; use crate::docfs::{DocFS, PathError}; @@ -35,6 +36,7 @@ use crate::html::url_parts_builder::UrlPartsBuilder; use crate::html::{layout, sources, static_files}; use crate::scrape_examples::AllCallLocations; use crate::try_err; +use askama::Template; /// Major driving force in all rustdoc rendering. This contains information /// about where in the tree-like hierarchy rendering is occurring and controls @@ -600,15 +602,18 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { }; let all = shared.all.replace(AllTypes::new()); let mut sidebar = Buffer::html(); - write!(sidebar, "

Crate {}

", crate_name); - let mut items = Buffer::html(); - sidebar_module_like(&mut items, all.item_sections()); - if !items.is_empty() { - sidebar.push_str("
"); - sidebar.push_buffer(items); - sidebar.push_str("
"); - } + let blocks = sidebar_module_like(all.item_sections()); + let bar = Sidebar { + title_prefix: "Crate ", + title: crate_name.as_str(), + is_crate: false, + version: "", + blocks: vec![blocks], + path: String::new(), + }; + + bar.render_into(&mut sidebar).unwrap(); let v = layout::render( &shared.layout, diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index e6a040d02e5..da1f1cf5ecc 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -30,6 +30,7 @@ mod tests; mod context; mod print_item; +mod sidebar; mod span_map; mod write_shared; @@ -46,14 +47,13 @@ use std::rc::Rc; use std::str; use std::string::ToString; +use askama::Template; use rustc_ast_pretty::pprust; use rustc_attr::{ConstStability, Deprecation, StabilityLevel}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_hir::def::CtorKind; use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::Mutability; use rustc_middle::middle::stability; -use rustc_middle::ty; use rustc_middle::ty::TyCtxt; use rustc_span::{ symbol::{sym, Symbol}, @@ -104,6 +104,7 @@ pub(crate) struct IndexItem { pub(crate) parent_idx: Option, pub(crate) search_type: Option, pub(crate) aliases: Box<[Symbol]>, + pub(crate) deprecation: Option, } /// A type used for the search index. @@ -417,7 +418,7 @@ fn document( if let Some(ref name) = item.name { info!("Documenting {}", name); } - document_item_info(w, cx, item, parent); + document_item_info(cx, item, parent).render_into(w).unwrap(); if parent.is_none() { document_full_collapsible(w, item, cx, heading_offset); } else { @@ -459,7 +460,7 @@ fn document_short( parent: &clean::Item, show_def_docs: bool, ) { - document_item_info(w, cx, item, Some(parent)); + document_item_info(cx, item, Some(parent)).render_into(w).unwrap(); if !show_def_docs { return; } @@ -531,25 +532,23 @@ fn document_full_inner( } } +#[derive(Template)] +#[template(path = "item_info.html")] +struct ItemInfo { + items: Vec, +} /// Add extra information about an item such as: /// /// * Stability /// * Deprecated /// * Required features (through the `doc_cfg` feature) fn document_item_info( - w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, parent: Option<&clean::Item>, -) { - let item_infos = short_item_info(item, cx, parent); - if !item_infos.is_empty() { - w.write_str(""); - for info in item_infos { - w.write_str(&info); - } - w.write_str(""); - } +) -> ItemInfo { + let items = short_item_info(item, cx, parent); + ItemInfo { items } } fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option { @@ -567,7 +566,25 @@ fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option{}", cfg?.render_long_html())) + Some(cfg?.render_long_html()) +} + +#[derive(Template)] +#[template(path = "short_item_info.html")] +enum ShortItemInfo { + /// A message describing the deprecation of this item + Deprecation { + message: String, + }, + /// The feature corresponding to an unstable item, and optionally + /// a tracking issue URL and number. + Unstable { + feature: String, + tracking: Option<(String, u32)>, + }, + Portability { + message: String, + }, } /// Render the stability, deprecation and portability information that is displayed at the top of @@ -576,7 +593,7 @@ fn short_item_info( item: &clean::Item, cx: &mut Context<'_>, parent: Option<&clean::Item>, -) -> Vec { +) -> Vec { let mut extra_info = vec![]; if let Some(depr @ Deprecation { note, since, is_since_rustc_version: _, suggestion: _ }) = @@ -602,15 +619,10 @@ fn short_item_info( if let Some(note) = note { let note = note.as_str(); let html = MarkdownItemInfo(note, &mut cx.id_map); - message.push_str(&format!(": {}", html.into_string())); + message.push_str(": "); + message.push_str(&html.into_string()); } - extra_info.push(format!( - "
\ - 👎\ - {}\ -
", - message, - )); + extra_info.push(ShortItemInfo::Deprecation { message }); } // Render unstable items. But don't render "rustc_private" crates (internal compiler crates). @@ -621,26 +633,17 @@ fn short_item_info( .filter(|stab| stab.feature != sym::rustc_private) .map(|stab| (stab.level, stab.feature)) { - let mut message = "🔬\ - This is a nightly-only experimental API." - .to_owned(); - - let mut feature = format!("{}", Escape(feature.as_str())); - if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue) { - feature.push_str(&format!( - " #{issue}", - url = url, - issue = issue - )); - } - - message.push_str(&format!(" ({})", feature)); - - extra_info.push(format!("
{}
", message)); + let tracking = if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue) + { + Some((url.clone(), issue.get())) + } else { + None + }; + extra_info.push(ShortItemInfo::Unstable { feature: feature.to_string(), tracking }); } - if let Some(portability) = portability(item, parent) { - extra_info.push(portability); + if let Some(message) = portability(item, parent) { + extra_info.push(ShortItemInfo::Portability { message }); } extra_info @@ -1472,7 +1475,9 @@ fn render_impl( // We need the stability of the item from the trait // because impls can't have a stability. if item.doc_value().is_some() { - document_item_info(&mut info_buffer, cx, it, Some(parent)); + document_item_info(cx, it, Some(parent)) + .render_into(&mut info_buffer) + .unwrap(); document_full(&mut doc_buffer, item, cx, HeadingOffset::H5); short_documented = false; } else { @@ -1489,7 +1494,9 @@ fn render_impl( } } } else { - document_item_info(&mut info_buffer, cx, item, Some(parent)); + document_item_info(cx, item, Some(parent)) + .render_into(&mut info_buffer) + .unwrap(); if rendering_params.show_def_docs { document_full(&mut doc_buffer, item, cx, HeadingOffset::H5); short_documented = false; @@ -1862,161 +1869,17 @@ pub(crate) fn render_impl_summary( let is_trait = inner_impl.trait_.is_some(); if is_trait { if let Some(portability) = portability(&i.impl_item, Some(parent)) { - write!(w, "{}", portability); + write!( + w, + "
{}
", + portability + ); } } w.write_str(""); } -fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) { - if it.is_struct() - || it.is_trait() - || it.is_primitive() - || it.is_union() - || it.is_enum() - || it.is_mod() - || it.is_typedef() - { - write!( - buffer, - "

{}{}

", - match *it.kind { - clean::ModuleItem(..) => - if it.is_crate() { - "Crate " - } else { - "Module " - }, - _ => "", - }, - it.name.as_ref().unwrap() - ); - } - - buffer.write_str("
"); - if it.is_crate() { - write!(buffer, "
    "); - if let Some(ref version) = cx.cache().crate_version { - write!(buffer, "
  • Version {}
  • ", Escape(version)); - } - write!(buffer, "
  • All Items
  • "); - buffer.write_str("
"); - } - - match *it.kind { - clean::StructItem(ref s) => sidebar_struct(cx, buffer, it, s), - clean::TraitItem(ref t) => sidebar_trait(cx, buffer, it, t), - clean::PrimitiveItem(_) => sidebar_primitive(cx, buffer, it), - clean::UnionItem(ref u) => sidebar_union(cx, buffer, it, u), - clean::EnumItem(ref e) => sidebar_enum(cx, buffer, it, e), - clean::TypedefItem(_) => sidebar_typedef(cx, buffer, it), - clean::ModuleItem(ref m) => sidebar_module(buffer, &m.items), - clean::ForeignTypeItem => sidebar_foreign_type(cx, buffer, it), - _ => {} - } - - // The sidebar is designed to display sibling functions, modules and - // other miscellaneous information. since there are lots of sibling - // items (and that causes quadratic growth in large modules), - // we refactor common parts into a shared JavaScript file per module. - // still, we don't move everything into JS because we want to preserve - // as much HTML as possible in order to allow non-JS-enabled browsers - // to navigate the documentation (though slightly inefficiently). - - if !it.is_mod() { - let path: String = cx.current.iter().map(|s| s.as_str()).intersperse("::").collect(); - - write!(buffer, "

In {}

", path); - } - - // Closes sidebar-elems div. - buffer.write_str("
"); -} - -fn get_next_url(used_links: &mut FxHashSet, url: String) -> String { - if used_links.insert(url.clone()) { - return url; - } - let mut add = 1; - while !used_links.insert(format!("{}-{}", url, add)) { - add += 1; - } - format!("{}-{}", url, add) -} - -struct SidebarLink { - name: Symbol, - url: String, -} - -impl fmt::Display for SidebarLink { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.url, self.name) - } -} - -impl PartialEq for SidebarLink { - fn eq(&self, other: &Self) -> bool { - self.url == other.url - } -} - -impl Eq for SidebarLink {} - -impl PartialOrd for SidebarLink { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for SidebarLink { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.url.cmp(&other.url) - } -} - -fn get_methods( - i: &clean::Impl, - for_deref: bool, - used_links: &mut FxHashSet, - deref_mut: bool, - tcx: TyCtxt<'_>, -) -> Vec { - i.items - .iter() - .filter_map(|item| match item.name { - Some(name) if !name.is_empty() && item.is_method() => { - if !for_deref || should_render_item(item, deref_mut, tcx) { - Some(SidebarLink { - name, - url: get_next_url(used_links, format!("{}.{}", ItemType::Method, name)), - }) - } else { - None - } - } - _ => None, - }) - .collect::>() -} - -fn get_associated_constants( - i: &clean::Impl, - used_links: &mut FxHashSet, -) -> Vec { - i.items - .iter() - .filter_map(|item| match item.name { - Some(name) if !name.is_empty() && item.is_associated_const() => Some(SidebarLink { - name, - url: get_next_url(used_links, format!("{}.{}", ItemType::AssocConst, name)), - }), - _ => None, - }) - .collect::>() -} - pub(crate) fn small_url_encode(s: String) -> String { // These characters don't need to be escaped in a URI. // See https://url.spec.whatwg.org/#query-percent-encode-set @@ -2082,232 +1945,6 @@ pub(crate) fn small_url_encode(s: String) -> String { } } -pub(crate) fn sidebar_render_assoc_items( - cx: &Context<'_>, - out: &mut Buffer, - id_map: &mut IdMap, - concrete: Vec<&Impl>, - synthetic: Vec<&Impl>, - blanket_impl: Vec<&Impl>, -) { - let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| { - let mut links = FxHashSet::default(); - - let mut ret = impls - .iter() - .filter_map(|it| { - let trait_ = it.inner_impl().trait_.as_ref()?; - let encoded = - id_map.derive(get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx)); - - let i_display = format!("{:#}", trait_.print(cx)); - let out = Escape(&i_display); - let prefix = match it.inner_impl().polarity { - ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "", - ty::ImplPolarity::Negative => "!", - }; - let generated = format!("{}{}", encoded, prefix, out); - if links.insert(generated.clone()) { Some(generated) } else { None } - }) - .collect::>(); - ret.sort(); - ret - }; - - let concrete_format = format_impls(concrete, id_map); - let synthetic_format = format_impls(synthetic, id_map); - let blanket_format = format_impls(blanket_impl, id_map); - - if !concrete_format.is_empty() { - print_sidebar_block( - out, - "trait-implementations", - "Trait Implementations", - concrete_format.iter(), - ); - } - - if !synthetic_format.is_empty() { - print_sidebar_block( - out, - "synthetic-implementations", - "Auto Trait Implementations", - synthetic_format.iter(), - ); - } - - if !blanket_format.is_empty() { - print_sidebar_block( - out, - "blanket-implementations", - "Blanket Implementations", - blanket_format.iter(), - ); - } -} - -fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) { - let did = it.item_id.expect_def_id(); - let cache = cx.cache(); - - if let Some(v) = cache.impls.get(&did) { - let mut used_links = FxHashSet::default(); - let mut id_map = IdMap::new(); - - { - let used_links_bor = &mut used_links; - let mut assoc_consts = v - .iter() - .filter(|i| i.inner_impl().trait_.is_none()) - .flat_map(|i| get_associated_constants(i.inner_impl(), used_links_bor)) - .collect::>(); - if !assoc_consts.is_empty() { - // We want links' order to be reproducible so we don't use unstable sort. - assoc_consts.sort(); - - print_sidebar_block( - out, - "implementations", - "Associated Constants", - assoc_consts.iter(), - ); - } - let mut methods = v - .iter() - .filter(|i| i.inner_impl().trait_.is_none()) - .flat_map(|i| get_methods(i.inner_impl(), false, used_links_bor, false, cx.tcx())) - .collect::>(); - if !methods.is_empty() { - // We want links' order to be reproducible so we don't use unstable sort. - methods.sort(); - - print_sidebar_block(out, "implementations", "Methods", methods.iter()); - } - } - - if v.iter().any(|i| i.inner_impl().trait_.is_some()) { - if let Some(impl_) = - v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait()) - { - let mut derefs = DefIdSet::default(); - derefs.insert(did); - sidebar_deref_methods(cx, out, impl_, v, &mut derefs, &mut used_links); - } - - let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = - v.iter().partition::, _>(|i| i.inner_impl().kind.is_auto()); - let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) = - concrete.into_iter().partition::, _>(|i| i.inner_impl().kind.is_blanket()); - - sidebar_render_assoc_items(cx, out, &mut id_map, concrete, synthetic, blanket_impl); - } - } -} - -fn sidebar_deref_methods( - cx: &Context<'_>, - out: &mut Buffer, - impl_: &Impl, - v: &[Impl], - derefs: &mut DefIdSet, - used_links: &mut FxHashSet, -) { - let c = cx.cache(); - - debug!("found Deref: {:?}", impl_); - if let Some((target, real_target)) = - impl_.inner_impl().items.iter().find_map(|item| match *item.kind { - clean::AssocTypeItem(box ref t, _) => Some(match *t { - clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_), - _ => (&t.type_, &t.type_), - }), - _ => None, - }) - { - debug!("found target, real_target: {:?} {:?}", target, real_target); - if let Some(did) = target.def_id(c) && - let Some(type_did) = impl_.inner_impl().for_.def_id(c) && - // `impl Deref for S` - (did == type_did || !derefs.insert(did)) - { - // Avoid infinite cycles - return; - } - let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait()); - let inner_impl = target - .def_id(c) - .or_else(|| { - target.primitive_type().and_then(|prim| c.primitive_locations.get(&prim).cloned()) - }) - .and_then(|did| c.impls.get(&did)); - if let Some(impls) = inner_impl { - debug!("found inner_impl: {:?}", impls); - let mut ret = impls - .iter() - .filter(|i| i.inner_impl().trait_.is_none()) - .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx())) - .collect::>(); - if !ret.is_empty() { - let id = if let Some(target_def_id) = real_target.def_id(c) { - cx.deref_id_map.get(&target_def_id).expect("Deref section without derived id") - } else { - "deref-methods" - }; - let title = format!( - "Methods from {}<Target={}>", - Escape(&format!("{:#}", impl_.inner_impl().trait_.as_ref().unwrap().print(cx))), - Escape(&format!("{:#}", real_target.print(cx))), - ); - // We want links' order to be reproducible so we don't use unstable sort. - ret.sort(); - print_sidebar_block(out, id, &title, ret.iter()); - } - } - - // Recurse into any further impls that might exist for `target` - if let Some(target_did) = target.def_id(c) && - let Some(target_impls) = c.impls.get(&target_did) && - let Some(target_deref_impl) = target_impls.iter().find(|i| { - i.inner_impl() - .trait_ - .as_ref() - .map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait()) - .unwrap_or(false) - }) - { - sidebar_deref_methods( - cx, - out, - target_deref_impl, - target_impls, - derefs, - used_links, - ); - } - } -} - -fn sidebar_struct(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, s: &clean::Struct) { - let mut sidebar = Buffer::new(); - let fields = get_struct_fields_name(&s.fields); - - if !fields.is_empty() { - match s.ctor_kind { - None => { - print_sidebar_block(&mut sidebar, "fields", "Fields", fields.iter()); - } - Some(CtorKind::Fn) => print_sidebar_title(&mut sidebar, "fields", "Tuple Fields"), - Some(CtorKind::Const) => {} - } - } - - sidebar_assoc_items(cx, &mut sidebar, it); - - if !sidebar.is_empty() { - write!(buf, "
{}
", sidebar.into_inner()); - } -} - fn get_id_for_impl(for_: &clean::Type, trait_: Option<&clean::Path>, cx: &Context<'_>) -> String { match trait_ { Some(t) => small_url_encode(format!("impl-{:#}-for-{:#}", t.print(cx), for_.print(cx))), @@ -2328,131 +1965,6 @@ fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String } } -fn print_sidebar_title(buf: &mut Buffer, id: &str, title: &str) { - write!(buf, "

{}

", id, title); -} - -fn print_sidebar_block( - buf: &mut Buffer, - id: &str, - title: &str, - items: impl Iterator, -) { - print_sidebar_title(buf, id, title); - buf.push_str("
    "); - for item in items { - write!(buf, "
  • {}
  • ", item); - } - buf.push_str("
"); -} - -fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) { - buf.write_str("
"); - - fn print_sidebar_section( - out: &mut Buffer, - items: &[clean::Item], - id: &str, - title: &str, - filter: impl Fn(&clean::Item) -> bool, - mapper: impl Fn(&str) -> String, - ) { - let mut items: Vec<&str> = items - .iter() - .filter_map(|m| match m.name { - Some(ref name) if filter(m) => Some(name.as_str()), - _ => None, - }) - .collect::>(); - - if !items.is_empty() { - items.sort_unstable(); - print_sidebar_block(out, id, title, items.into_iter().map(mapper)); - } - } - - print_sidebar_section( - buf, - &t.items, - "required-associated-types", - "Required Associated Types", - |m| m.is_ty_associated_type(), - |sym| format!("{0}", sym, ItemType::AssocType), - ); - - print_sidebar_section( - buf, - &t.items, - "provided-associated-types", - "Provided Associated Types", - |m| m.is_associated_type(), - |sym| format!("{0}", sym, ItemType::AssocType), - ); - - print_sidebar_section( - buf, - &t.items, - "required-associated-consts", - "Required Associated Constants", - |m| m.is_ty_associated_const(), - |sym| format!("{0}", sym, ItemType::AssocConst), - ); - - print_sidebar_section( - buf, - &t.items, - "provided-associated-consts", - "Provided Associated Constants", - |m| m.is_associated_const(), - |sym| format!("{0}", sym, ItemType::AssocConst), - ); - - print_sidebar_section( - buf, - &t.items, - "required-methods", - "Required Methods", - |m| m.is_ty_method(), - |sym| format!("{0}", sym, ItemType::TyMethod), - ); - - print_sidebar_section( - buf, - &t.items, - "provided-methods", - "Provided Methods", - |m| m.is_method(), - |sym| format!("{0}", sym, ItemType::Method), - ); - - if let Some(implementors) = cx.cache().implementors.get(&it.item_id.expect_def_id()) { - let mut res = implementors - .iter() - .filter(|i| !i.is_on_local_type(cx)) - .filter_map(|i| extract_for_impl_name(&i.impl_item, cx)) - .collect::>(); - - if !res.is_empty() { - res.sort(); - print_sidebar_block( - buf, - "foreign-impls", - "Implementations on Foreign Types", - res.iter().map(|(name, id)| format!("{}", id, Escape(name))), - ); - } - } - - sidebar_assoc_items(cx, buf, it); - - print_sidebar_title(buf, "implementors", "Implementors"); - if t.is_auto(cx.tcx()) { - print_sidebar_title(buf, "synthetic-implementors", "Auto Implementors"); - } - - buf.push_str("
") -} - /// Returns the list of implementations for the primitive reference type, filtering out any /// implementations that are on concrete or partially generic types, only keeping implementations /// of the form `impl Trait for &T`. @@ -2483,89 +1995,6 @@ pub(crate) fn get_filtered_impls_for_reference<'a>( (concrete, synthetic, blanket_impl) } -fn sidebar_primitive(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) { - let mut sidebar = Buffer::new(); - - if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) { - sidebar_assoc_items(cx, &mut sidebar, it); - } else { - let shared = Rc::clone(&cx.shared); - let (concrete, synthetic, blanket_impl) = get_filtered_impls_for_reference(&shared, it); - - sidebar_render_assoc_items( - cx, - &mut sidebar, - &mut IdMap::new(), - concrete, - synthetic, - blanket_impl, - ); - } - - if !sidebar.is_empty() { - write!(buf, "
{}
", sidebar.into_inner()); - } -} - -fn sidebar_typedef(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) { - let mut sidebar = Buffer::new(); - sidebar_assoc_items(cx, &mut sidebar, it); - - if !sidebar.is_empty() { - write!(buf, "
{}
", sidebar.into_inner()); - } -} - -fn get_struct_fields_name(fields: &[clean::Item]) -> Vec { - let mut fields = fields - .iter() - .filter(|f| matches!(*f.kind, clean::StructFieldItem(..))) - .filter_map(|f| { - f.name.map(|name| format!("{name}", name = name)) - }) - .collect::>(); - fields.sort(); - fields -} - -fn sidebar_union(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, u: &clean::Union) { - let mut sidebar = Buffer::new(); - let fields = get_struct_fields_name(&u.fields); - - if !fields.is_empty() { - print_sidebar_block(&mut sidebar, "fields", "Fields", fields.iter()); - } - - sidebar_assoc_items(cx, &mut sidebar, it); - - if !sidebar.is_empty() { - write!(buf, "
{}
", sidebar.into_inner()); - } -} - -fn sidebar_enum(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, e: &clean::Enum) { - let mut sidebar = Buffer::new(); - - let mut variants = e - .variants() - .filter_map(|v| { - v.name - .as_ref() - .map(|name| format!("{name}", name = name)) - }) - .collect::>(); - if !variants.is_empty() { - variants.sort_unstable(); - print_sidebar_block(&mut sidebar, "variants", "Variants", variants.iter()); - } - - sidebar_assoc_items(cx, &mut sidebar, it); - - if !sidebar.is_empty() { - write!(buf, "
{}
", sidebar.into_inner()); - } -} - #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub(crate) enum ItemSection { Reexports, @@ -2719,54 +2148,6 @@ fn item_ty_to_section(ty: ItemType) -> ItemSection { } } -pub(crate) fn sidebar_module_like(buf: &mut Buffer, item_sections_in_use: FxHashSet) { - use std::fmt::Write as _; - - let mut sidebar = String::new(); - - for &sec in ItemSection::ALL.iter().filter(|sec| item_sections_in_use.contains(sec)) { - let _ = write!(sidebar, "
  • {}
  • ", sec.id(), sec.name()); - } - - if !sidebar.is_empty() { - write!( - buf, - "
    \ -
      {}
    \ -
    ", - sidebar - ); - } -} - -fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) { - let item_sections_in_use: FxHashSet<_> = items - .iter() - .filter(|it| { - !it.is_stripped() - && it - .name - .or_else(|| { - if let clean::ImportItem(ref i) = *it.kind && - let clean::ImportKind::Simple(s) = i.kind { Some(s) } else { None } - }) - .is_some() - }) - .map(|it| item_ty_to_section(it.type_())) - .collect(); - - sidebar_module_like(buf, item_sections_in_use); -} - -fn sidebar_foreign_type(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) { - let mut sidebar = Buffer::new(); - sidebar_assoc_items(cx, &mut sidebar, it); - - if !sidebar.is_empty() { - write!(buf, "
    {}
    ", sidebar.into_inner()); - } -} - /// Returns a list of all paths used in the type. /// This is used to help deduplicate imported impls /// for reexported types. If any of the contained diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 08796f10d92..577497868f6 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -470,10 +470,11 @@ fn extra_info_tags(item: &clean::Item, parent: &clean::Item, tcx: TyCtxt<'_>) -> // The trailing space after each tag is to space it properly against the rest of the docs. if let Some(depr) = &item.deprecation(tcx) { - let mut message = "Deprecated"; - if !stability::deprecation_in_effect(depr) { - message = "Deprecation planned"; - } + let message = if stability::deprecation_in_effect(depr) { + "Deprecated" + } else { + "Deprecation planned" + }; tags += &tag_html("deprecated", "", message); } diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index e22ac6ec19b..146221f5806 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -40,6 +40,7 @@ pub(crate) fn build_index<'tcx>( parent_idx: None, search_type: get_function_type_for_search(item, tcx, impl_generics.as_ref(), cache), aliases: item.attrs.get_doc_aliases(), + deprecation: item.deprecation(tcx), }); } } @@ -251,7 +252,17 @@ pub(crate) fn build_index<'tcx>( )?; crate_data.serialize_field( "q", - &self.items.iter().map(|item| &item.path).collect::>(), + &self + .items + .iter() + .enumerate() + // Serialize as an array of item indices and full paths + .filter_map( + |(index, item)| { + if item.path.is_empty() { None } else { Some((index, &item.path)) } + }, + ) + .collect::>(), )?; crate_data.serialize_field( "d", @@ -304,6 +315,16 @@ pub(crate) fn build_index<'tcx>( }) .collect::>(), )?; + crate_data.serialize_field( + "c", + &self + .items + .iter() + .enumerate() + // Serialize as an array of deprecated item indices + .filter_map(|(index, item)| item.deprecation.map(|_| index)) + .collect::>(), + )?; crate_data.serialize_field( "p", &self.paths.iter().map(|(it, s)| (it, s.as_str())).collect::>(), diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs new file mode 100644 index 00000000000..94ad4753d7c --- /dev/null +++ b/src/librustdoc/html/render/sidebar.rs @@ -0,0 +1,561 @@ +use std::{borrow::Cow, rc::Rc}; + +use askama::Template; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::{def::CtorKind, def_id::DefIdSet}; +use rustc_middle::ty::{self, TyCtxt}; + +use crate::{ + clean, + formats::{item_type::ItemType, Impl}, + html::{format::Buffer, markdown::IdMap}, +}; + +use super::{item_ty_to_section, Context, ItemSection}; + +#[derive(Template)] +#[template(path = "sidebar.html")] +pub(super) struct Sidebar<'a> { + pub(super) title_prefix: &'static str, + pub(super) title: &'a str, + pub(super) is_crate: bool, + pub(super) version: &'a str, + pub(super) blocks: Vec>, + pub(super) path: String, +} + +impl<'a> Sidebar<'a> { + /// Only create a `
    ` if there are any blocks + /// which should actually be rendered. + pub fn should_render_blocks(&self) -> bool { + self.blocks.iter().any(LinkBlock::should_render) + } +} + +/// A sidebar section such as 'Methods'. +pub(crate) struct LinkBlock<'a> { + /// The name of this section, e.g. 'Methods' + /// as well as the link to it, e.g. `#implementations`. + /// Will be rendered inside an `

    ` tag + heading: Link<'a>, + links: Vec>, + /// Render the heading even if there are no links + force_render: bool, +} + +impl<'a> LinkBlock<'a> { + pub fn new(heading: Link<'a>, links: Vec>) -> Self { + Self { heading, links, force_render: false } + } + + pub fn forced(heading: Link<'a>) -> Self { + Self { heading, links: vec![], force_render: true } + } + + pub fn should_render(&self) -> bool { + self.force_render || !self.links.is_empty() + } +} + +/// A link to an item. Content should not be escaped. +#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Clone)] +pub(crate) struct Link<'a> { + /// The content for the anchor tag + name: Cow<'a, str>, + /// The id of an anchor within the page (without a `#` prefix) + href: Cow<'a, str>, +} + +impl<'a> Link<'a> { + pub fn new(href: impl Into>, name: impl Into>) -> Self { + Self { href: href.into(), name: name.into() } + } + pub fn empty() -> Link<'static> { + Link::new("", "") + } +} + +pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) { + let blocks: Vec> = match *it.kind { + clean::StructItem(ref s) => sidebar_struct(cx, it, s), + clean::TraitItem(ref t) => sidebar_trait(cx, it, t), + clean::PrimitiveItem(_) => sidebar_primitive(cx, it), + clean::UnionItem(ref u) => sidebar_union(cx, it, u), + clean::EnumItem(ref e) => sidebar_enum(cx, it, e), + clean::TypedefItem(_) => sidebar_typedef(cx, it), + clean::ModuleItem(ref m) => vec![sidebar_module(&m.items)], + clean::ForeignTypeItem => sidebar_foreign_type(cx, it), + _ => vec![], + }; + // The sidebar is designed to display sibling functions, modules and + // other miscellaneous information. since there are lots of sibling + // items (and that causes quadratic growth in large modules), + // we refactor common parts into a shared JavaScript file per module. + // still, we don't move everything into JS because we want to preserve + // as much HTML as possible in order to allow non-JS-enabled browsers + // to navigate the documentation (though slightly inefficiently). + let (title_prefix, title) = if it.is_struct() + || it.is_trait() + || it.is_primitive() + || it.is_union() + || it.is_enum() + || it.is_mod() + || it.is_typedef() + { + ( + match *it.kind { + clean::ModuleItem(..) if it.is_crate() => "Crate ", + clean::ModuleItem(..) => "Module ", + _ => "", + }, + it.name.as_ref().unwrap().as_str(), + ) + } else { + ("", "") + }; + let version = if it.is_crate() { + cx.cache().crate_version.as_ref().map(String::as_str).unwrap_or_default() + } else { + "" + }; + let path: String = if !it.is_mod() { + cx.current.iter().map(|s| s.as_str()).intersperse("::").collect() + } else { + "".into() + }; + let sidebar = Sidebar { title_prefix, title, is_crate: it.is_crate(), version, blocks, path }; + sidebar.render_into(buffer).unwrap(); +} + +fn get_struct_fields_name<'a>(fields: &'a [clean::Item]) -> Vec> { + let mut fields = fields + .iter() + .filter(|f| matches!(*f.kind, clean::StructFieldItem(..))) + .filter_map(|f| { + f.name.as_ref().map(|name| Link::new(format!("structfield.{name}"), name.as_str())) + }) + .collect::>>(); + fields.sort(); + fields +} + +fn sidebar_struct<'a>( + cx: &'a Context<'_>, + it: &'a clean::Item, + s: &'a clean::Struct, +) -> Vec> { + let fields = get_struct_fields_name(&s.fields); + let field_name = match s.ctor_kind { + Some(CtorKind::Fn) => Some("Tuple Fields"), + None => Some("Fields"), + _ => None, + }; + let mut items = vec![]; + if let Some(name) = field_name { + items.push(LinkBlock::new(Link::new("fields", name), fields)); + } + sidebar_assoc_items(cx, it, &mut items); + items +} + +fn sidebar_trait<'a>( + cx: &'a Context<'_>, + it: &'a clean::Item, + t: &'a clean::Trait, +) -> Vec> { + fn filter_items<'a>( + items: &'a [clean::Item], + filt: impl Fn(&clean::Item) -> bool, + ty: &str, + ) -> Vec> { + let mut res = items + .iter() + .filter_map(|m: &clean::Item| match m.name { + Some(ref name) if filt(m) => Some(Link::new(format!("{ty}.{name}"), name.as_str())), + _ => None, + }) + .collect::>>(); + res.sort(); + res + } + + let req_assoc = filter_items(&t.items, |m| m.is_ty_associated_type(), "associatedtype"); + let prov_assoc = filter_items(&t.items, |m| m.is_associated_type(), "associatedtype"); + let req_assoc_const = + filter_items(&t.items, |m| m.is_ty_associated_const(), "associatedconstant"); + let prov_assoc_const = + filter_items(&t.items, |m| m.is_associated_const(), "associatedconstant"); + let req_method = filter_items(&t.items, |m| m.is_ty_method(), "tymethod"); + let prov_method = filter_items(&t.items, |m| m.is_method(), "method"); + let mut foreign_impls = vec![]; + if let Some(implementors) = cx.cache().implementors.get(&it.item_id.expect_def_id()) { + foreign_impls.extend( + implementors + .iter() + .filter(|i| !i.is_on_local_type(cx)) + .filter_map(|i| super::extract_for_impl_name(&i.impl_item, cx)) + .map(|(name, id)| Link::new(id, name)), + ); + foreign_impls.sort(); + } + + let mut blocks: Vec> = [ + ("required-associated-types", "Required Associated Types", req_assoc), + ("provided-associated-types", "Provided Associated Types", prov_assoc), + ("required-associated-consts", "Required Associated Constants", req_assoc_const), + ("provided-associated-consts", "Provided Associated Constants", prov_assoc_const), + ("required-methods", "Required Methods", req_method), + ("provided-methods", "Provided Methods", prov_method), + ("foreign-impls", "Implementations on Foreign Types", foreign_impls), + ] + .into_iter() + .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), items)) + .collect(); + sidebar_assoc_items(cx, it, &mut blocks); + blocks.push(LinkBlock::forced(Link::new("implementors", "Implementors"))); + if t.is_auto(cx.tcx()) { + blocks.push(LinkBlock::forced(Link::new("synthetic-implementors", "Auto Implementors"))); + } + blocks +} + +fn sidebar_primitive<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec> { + if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) { + let mut items = vec![]; + sidebar_assoc_items(cx, it, &mut items); + items + } else { + let shared = Rc::clone(&cx.shared); + let (concrete, synthetic, blanket_impl) = + super::get_filtered_impls_for_reference(&shared, it); + + sidebar_render_assoc_items(cx, &mut IdMap::new(), concrete, synthetic, blanket_impl).into() + } +} + +fn sidebar_typedef<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec> { + let mut items = vec![]; + sidebar_assoc_items(cx, it, &mut items); + items +} + +fn sidebar_union<'a>( + cx: &'a Context<'_>, + it: &'a clean::Item, + u: &'a clean::Union, +) -> Vec> { + let fields = get_struct_fields_name(&u.fields); + let mut items = vec![LinkBlock::new(Link::new("fields", "Fields"), fields)]; + sidebar_assoc_items(cx, it, &mut items); + items +} + +/// Adds trait implementations into the blocks of links +fn sidebar_assoc_items<'a>( + cx: &'a Context<'_>, + it: &'a clean::Item, + links: &mut Vec>, +) { + let did = it.item_id.expect_def_id(); + let cache = cx.cache(); + + let mut assoc_consts = Vec::new(); + let mut methods = Vec::new(); + if let Some(v) = cache.impls.get(&did) { + let mut used_links = FxHashSet::default(); + let mut id_map = IdMap::new(); + + { + let used_links_bor = &mut used_links; + assoc_consts.extend( + v.iter() + .filter(|i| i.inner_impl().trait_.is_none()) + .flat_map(|i| get_associated_constants(i.inner_impl(), used_links_bor)), + ); + // We want links' order to be reproducible so we don't use unstable sort. + assoc_consts.sort(); + + #[rustfmt::skip] // rustfmt makes the pipeline less readable + methods.extend( + v.iter() + .filter(|i| i.inner_impl().trait_.is_none()) + .flat_map(|i| get_methods(i.inner_impl(), false, used_links_bor, false, cx.tcx())), + ); + + // We want links' order to be reproducible so we don't use unstable sort. + methods.sort(); + } + + let mut deref_methods = Vec::new(); + let [concrete, synthetic, blanket] = if v.iter().any(|i| i.inner_impl().trait_.is_some()) { + if let Some(impl_) = + v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait()) + { + let mut derefs = DefIdSet::default(); + derefs.insert(did); + sidebar_deref_methods( + cx, + &mut deref_methods, + impl_, + v, + &mut derefs, + &mut used_links, + ); + } + + let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = + v.iter().partition::, _>(|i| i.inner_impl().kind.is_auto()); + let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) = + concrete.into_iter().partition::, _>(|i| i.inner_impl().kind.is_blanket()); + + sidebar_render_assoc_items(cx, &mut id_map, concrete, synthetic, blanket_impl) + } else { + std::array::from_fn(|_| LinkBlock::new(Link::empty(), vec![])) + }; + + let mut blocks = vec![ + LinkBlock::new(Link::new("implementations", "Associated Constants"), assoc_consts), + LinkBlock::new(Link::new("implementations", "Methods"), methods), + ]; + blocks.append(&mut deref_methods); + blocks.extend([concrete, synthetic, blanket]); + links.append(&mut blocks); + } +} + +fn sidebar_deref_methods<'a>( + cx: &'a Context<'_>, + out: &mut Vec>, + impl_: &Impl, + v: &[Impl], + derefs: &mut DefIdSet, + used_links: &mut FxHashSet, +) { + let c = cx.cache(); + + debug!("found Deref: {:?}", impl_); + if let Some((target, real_target)) = + impl_.inner_impl().items.iter().find_map(|item| match *item.kind { + clean::AssocTypeItem(box ref t, _) => Some(match *t { + clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_), + _ => (&t.type_, &t.type_), + }), + _ => None, + }) + { + debug!("found target, real_target: {:?} {:?}", target, real_target); + if let Some(did) = target.def_id(c) && + let Some(type_did) = impl_.inner_impl().for_.def_id(c) && + // `impl Deref for S` + (did == type_did || !derefs.insert(did)) + { + // Avoid infinite cycles + return; + } + let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait()); + let inner_impl = target + .def_id(c) + .or_else(|| { + target.primitive_type().and_then(|prim| c.primitive_locations.get(&prim).cloned()) + }) + .and_then(|did| c.impls.get(&did)); + if let Some(impls) = inner_impl { + debug!("found inner_impl: {:?}", impls); + let mut ret = impls + .iter() + .filter(|i| i.inner_impl().trait_.is_none()) + .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx())) + .collect::>(); + if !ret.is_empty() { + let id = if let Some(target_def_id) = real_target.def_id(c) { + Cow::Borrowed( + cx.deref_id_map + .get(&target_def_id) + .expect("Deref section without derived id") + .as_str(), + ) + } else { + Cow::Borrowed("deref-methods") + }; + let title = format!( + "Methods from {:#}", + impl_.inner_impl().trait_.as_ref().unwrap().print(cx), + real_target.print(cx), + ); + // We want links' order to be reproducible so we don't use unstable sort. + ret.sort(); + out.push(LinkBlock::new(Link::new(id, title), ret)); + } + } + + // Recurse into any further impls that might exist for `target` + if let Some(target_did) = target.def_id(c) && + let Some(target_impls) = c.impls.get(&target_did) && + let Some(target_deref_impl) = target_impls.iter().find(|i| { + i.inner_impl() + .trait_ + .as_ref() + .map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait()) + .unwrap_or(false) + }) + { + sidebar_deref_methods( + cx, + out, + target_deref_impl, + target_impls, + derefs, + used_links, + ); + } + } +} + +fn sidebar_enum<'a>( + cx: &'a Context<'_>, + it: &'a clean::Item, + e: &'a clean::Enum, +) -> Vec> { + let mut variants = e + .variants() + .filter_map(|v| v.name) + .map(|name| Link::new(format!("variant.{name}"), name.to_string())) + .collect::>(); + variants.sort_unstable(); + + let mut items = vec![LinkBlock::new(Link::new("variants", "Variants"), variants)]; + sidebar_assoc_items(cx, it, &mut items); + items +} + +pub(crate) fn sidebar_module_like( + item_sections_in_use: FxHashSet, +) -> LinkBlock<'static> { + let item_sections = ItemSection::ALL + .iter() + .copied() + .filter(|sec| item_sections_in_use.contains(sec)) + .map(|sec| Link::new(sec.id(), sec.name())) + .collect(); + LinkBlock::new(Link::empty(), item_sections) +} + +fn sidebar_module(items: &[clean::Item]) -> LinkBlock<'static> { + let item_sections_in_use: FxHashSet<_> = items + .iter() + .filter(|it| { + !it.is_stripped() + && it + .name + .or_else(|| { + if let clean::ImportItem(ref i) = *it.kind && + let clean::ImportKind::Simple(s) = i.kind { Some(s) } else { None } + }) + .is_some() + }) + .map(|it| item_ty_to_section(it.type_())) + .collect(); + + sidebar_module_like(item_sections_in_use) +} + +fn sidebar_foreign_type<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec> { + let mut items = vec![]; + sidebar_assoc_items(cx, it, &mut items); + items +} + +/// Renders the trait implementations for this type +fn sidebar_render_assoc_items( + cx: &Context<'_>, + id_map: &mut IdMap, + concrete: Vec<&Impl>, + synthetic: Vec<&Impl>, + blanket_impl: Vec<&Impl>, +) -> [LinkBlock<'static>; 3] { + let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| { + let mut links = FxHashSet::default(); + + let mut ret = impls + .iter() + .filter_map(|it| { + let trait_ = it.inner_impl().trait_.as_ref()?; + let encoded = + id_map.derive(super::get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx)); + + let prefix = match it.inner_impl().polarity { + ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "", + ty::ImplPolarity::Negative => "!", + }; + let generated = Link::new(encoded, format!("{prefix}{:#}", trait_.print(cx))); + if links.insert(generated.clone()) { Some(generated) } else { None } + }) + .collect::>>(); + ret.sort(); + ret + }; + + let concrete = format_impls(concrete, id_map); + let synthetic = format_impls(synthetic, id_map); + let blanket = format_impls(blanket_impl, id_map); + [ + LinkBlock::new(Link::new("trait-implementations", "Trait Implementations"), concrete), + LinkBlock::new( + Link::new("synthetic-implementations", "Auto Trait Implementations"), + synthetic, + ), + LinkBlock::new(Link::new("blanket-implementations", "Blanket Implementations"), blanket), + ] +} + +fn get_next_url(used_links: &mut FxHashSet, url: String) -> String { + if used_links.insert(url.clone()) { + return url; + } + let mut add = 1; + while !used_links.insert(format!("{}-{}", url, add)) { + add += 1; + } + format!("{}-{}", url, add) +} + +fn get_methods<'a>( + i: &'a clean::Impl, + for_deref: bool, + used_links: &mut FxHashSet, + deref_mut: bool, + tcx: TyCtxt<'_>, +) -> Vec> { + i.items + .iter() + .filter_map(|item| match item.name { + Some(ref name) if !name.is_empty() && item.is_method() => { + if !for_deref || super::should_render_item(item, deref_mut, tcx) { + Some(Link::new( + get_next_url(used_links, format!("{}.{}", ItemType::Method, name)), + name.as_str(), + )) + } else { + None + } + } + _ => None, + }) + .collect::>() +} + +fn get_associated_constants<'a>( + i: &'a clean::Impl, + used_links: &mut FxHashSet, +) -> Vec> { + i.items + .iter() + .filter_map(|item| match item.name { + Some(ref name) if !name.is_empty() && item.is_associated_const() => Some(Link::new( + get_next_url(used_links, format!("{}.{}", ItemType::AssocConst, name)), + name.as_str(), + )), + _ => None, + }) + .collect::>() +} diff --git a/src/librustdoc/html/static/COPYRIGHT.txt b/src/librustdoc/html/static/COPYRIGHT.txt index 34e48134cc3..1447df792f6 100644 --- a/src/librustdoc/html/static/COPYRIGHT.txt +++ b/src/librustdoc/html/static/COPYRIGHT.txt @@ -1,3 +1,5 @@ +# REUSE-IgnoreStart + These documentation pages include resources by third parties. This copyright file applies only to those resources. The following third party resources are included, and carry their own copyright notices and license terms: @@ -44,3 +46,5 @@ included, and carry their own copyright notices and license terms: See SourceSerif4-LICENSE.md. This copyright file is intended to be distributed with rustdoc output. + +# REUSE-IgnoreEnd diff --git a/src/librustdoc/html/static/fonts/FiraSans-LICENSE.txt b/src/librustdoc/html/static/fonts/FiraSans-LICENSE.txt index ff9afab064a..d7e9c149b7e 100644 --- a/src/librustdoc/html/static/fonts/FiraSans-LICENSE.txt +++ b/src/librustdoc/html/static/fonts/FiraSans-LICENSE.txt @@ -1,3 +1,5 @@ +// REUSE-IgnoreStart + Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A. with Reserved Font Name < Fira >, @@ -92,3 +94,5 @@ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/src/librustdoc/html/static/fonts/NanumBarunGothic-LICENSE.txt b/src/librustdoc/html/static/fonts/NanumBarunGothic-LICENSE.txt index 0bf46682b5b..4b3edc29eb9 100644 --- a/src/librustdoc/html/static/fonts/NanumBarunGothic-LICENSE.txt +++ b/src/librustdoc/html/static/fonts/NanumBarunGothic-LICENSE.txt @@ -1,3 +1,5 @@ +// REUSE-IgnoreStart + Copyright (c) 2010, NAVER Corporation (https://www.navercorp.com/), with Reserved Font Name Nanum, Naver Nanum, NanumGothic, Naver NanumGothic, @@ -97,3 +99,5 @@ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/src/librustdoc/html/static/fonts/SourceCodePro-LICENSE.txt b/src/librustdoc/html/static/fonts/SourceCodePro-LICENSE.txt index 07542572e33..0d2941e148d 100644 --- a/src/librustdoc/html/static/fonts/SourceCodePro-LICENSE.txt +++ b/src/librustdoc/html/static/fonts/SourceCodePro-LICENSE.txt @@ -1,3 +1,5 @@ +// REUSE-IgnoreStart + Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. This Font Software is licensed under the SIL Open Font License, Version 1.1. @@ -91,3 +93,5 @@ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/src/librustdoc/html/static/fonts/SourceSerif4-LICENSE.md b/src/librustdoc/html/static/fonts/SourceSerif4-LICENSE.md index 5871e1f3d1b..175fa4f47ae 100644 --- a/src/librustdoc/html/static/fonts/SourceSerif4-LICENSE.md +++ b/src/librustdoc/html/static/fonts/SourceSerif4-LICENSE.md @@ -1,3 +1,6 @@ + + +Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. Copyright 2014 - 2023 Adobe (http://www.adobe.com/), with Reserved Font Name ‘Source’. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. This Font Software is licensed under the SIL Open Font License, Version 1.1. @@ -91,3 +94,5 @@ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. + + diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index b98bced4126..c71ce2c3001 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -76,39 +76,111 @@ function printTab(nb) { } /** - * A function to compute the Levenshtein distance between two strings - * Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported - * Full License can be found at http://creativecommons.org/licenses/by-sa/3.0/legalcode - * This code is an unmodified version of the code written by Marco de Wit - * and was found at https://stackoverflow.com/a/18514751/745719 + * The [edit distance] is a metric for measuring the difference between two strings. + * + * [edit distance]: https://en.wikipedia.org/wiki/Edit_distance */ -const levenshtein_row2 = []; -function levenshtein(s1, s2) { - if (s1 === s2) { - return 0; - } - const s1_len = s1.length, s2_len = s2.length; - if (s1_len && s2_len) { - let i1 = 0, i2 = 0, a, b, c, c2; - const row = levenshtein_row2; - while (i1 < s1_len) { - row[i1] = ++i1; + +/* + * This function was translated, mostly line-for-line, from + * https://github.com/rust-lang/rust/blob/ff4b772f805ec1e/compiler/rustc_span/src/edit_distance.rs + * + * The current implementation is the restricted Damerau-Levenshtein algorithm. It is restricted + * because it does not permit modifying characters that have already been transposed. The specific + * algorithm should not matter to the caller of the methods, which is why it is not noted in the + * documentation. + */ +const editDistanceState = { + current: [], + prev: [], + prevPrev: [], + calculate: function calculate(a, b, limit) { + // Ensure that `b` is the shorter string, minimizing memory use. + if (a.length < b.length) { + const aTmp = a; + a = b; + b = aTmp; } - while (i2 < s2_len) { - c2 = s2.charCodeAt(i2); - a = i2; - ++i2; - b = i2; - for (i1 = 0; i1 < s1_len; ++i1) { - c = a + (s1.charCodeAt(i1) !== c2 ? 1 : 0); - a = row[i1]; - b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c); - row[i1] = b; + + const minDist = a.length - b.length; + // If we know the limit will be exceeded, we can return early. + if (minDist > limit) { + return limit + 1; + } + + // Strip common prefix. + // We know that `b` is the shorter string, so we don't need to check + // `a.length`. + while (b.length > 0 && b[0] === a[0]) { + a = a.substring(1); + b = b.substring(1); + } + // Strip common suffix. + while (b.length > 0 && b[b.length - 1] === a[a.length - 1]) { + a = a.substring(0, a.length - 1); + b = b.substring(0, b.length - 1); + } + + // If either string is empty, the distance is the length of the other. + // We know that `b` is the shorter string, so we don't need to check `a`. + if (b.length === 0) { + return minDist; + } + + const aLength = a.length; + const bLength = b.length; + + for (let i = 0; i <= bLength; ++i) { + this.current[i] = 0; + this.prev[i] = i; + this.prevPrev[i] = Number.MAX_VALUE; + } + + // row by row + for (let i = 1; i <= aLength; ++i) { + this.current[0] = i; + const aIdx = i - 1; + + // column by column + for (let j = 1; j <= bLength; ++j) { + const bIdx = j - 1; + + // There is no cost to substitute a character with itself. + const substitutionCost = a[aIdx] === b[bIdx] ? 0 : 1; + + this.current[j] = Math.min( + // deletion + this.prev[j] + 1, + // insertion + this.current[j - 1] + 1, + // substitution + this.prev[j - 1] + substitutionCost + ); + + if ((i > 1) && (j > 1) && (a[aIdx] === b[bIdx - 1]) && (a[aIdx - 1] === b[bIdx])) { + // transposition + this.current[j] = Math.min( + this.current[j], + this.prevPrev[j - 2] + 1 + ); + } } + + // Rotate the buffers, reusing the memory + const prevPrevTmp = this.prevPrev; + this.prevPrev = this.prev; + this.prev = this.current; + this.current = prevPrevTmp; } - return b; - } - return s1_len + s2_len; + + // `prev` because we already rotated the buffers. + const distance = this.prev[bLength]; + return distance <= limit ? distance : (limit + 1); + }, +}; + +function editDistance(a, b, limit) { + return editDistanceState.calculate(a, b, limit); } function initSearch(rawSearchIndex) { @@ -802,7 +874,7 @@ function initSearch(rawSearchIndex) { for (const result of results) { if (result.id > -1) { const obj = searchIndex[result.id]; - obj.lev = result.lev; + obj.dist = result.dist; const res = buildHrefAndPath(obj); obj.displayPath = pathSplitter(res[0]); obj.fullPath = obj.displayPath + obj.name; @@ -860,8 +932,8 @@ function initSearch(rawSearchIndex) { // Sort by distance in the path part, if specified // (less changes required to match means higher rankings) - a = aaa.path_lev; - b = bbb.path_lev; + a = aaa.path_dist; + b = bbb.path_dist; if (a !== b) { return a - b; } @@ -875,8 +947,15 @@ function initSearch(rawSearchIndex) { // Sort by distance in the name part, the last part of the path // (less changes required to match means higher rankings) - a = (aaa.lev); - b = (bbb.lev); + a = (aaa.dist); + b = (bbb.dist); + if (a !== b) { + return a - b; + } + + // sort deprecated items later + a = aaa.item.deprecated; + b = bbb.item.deprecated; if (a !== b) { return a - b; } @@ -961,19 +1040,20 @@ function initSearch(rawSearchIndex) { /** * This function checks if the object (`row`) generics match the given type (`elem`) - * generics. If there are no generics on `row`, `defaultLev` is returned. + * generics. If there are no generics on `row`, `defaultDistance` is returned. * - * @param {Row} row - The object to check. - * @param {QueryElement} elem - The element from the parsed query. - * @param {integer} defaultLev - This is the value to return in case there are no generics. + * @param {Row} row - The object to check. + * @param {QueryElement} elem - The element from the parsed query. + * @param {integer} defaultDistance - This is the value to return in case there are no + * generics. * - * @return {integer} - Returns the best match (if any) or `maxLevDistance + 1`. + * @return {integer} - Returns the best match (if any) or `maxEditDistance + 1`. */ - function checkGenerics(row, elem, defaultLev, maxLevDistance) { + function checkGenerics(row, elem, defaultDistance, maxEditDistance) { if (row.generics.length === 0) { - return elem.generics.length === 0 ? defaultLev : maxLevDistance + 1; + return elem.generics.length === 0 ? defaultDistance : maxEditDistance + 1; } else if (row.generics.length > 0 && row.generics[0].name === null) { - return checkGenerics(row.generics[0], elem, defaultLev, maxLevDistance); + return checkGenerics(row.generics[0], elem, defaultDistance, maxEditDistance); } // The names match, but we need to be sure that all generics kinda // match as well. @@ -984,8 +1064,9 @@ function initSearch(rawSearchIndex) { elem_name = entry.name; if (elem_name === "") { // Pure generic, needs to check into it. - if (checkGenerics(entry, elem, maxLevDistance + 1, maxLevDistance) !== 0) { - return maxLevDistance + 1; + if (checkGenerics(entry, elem, maxEditDistance + 1, maxEditDistance) + !== 0) { + return maxEditDistance + 1; } continue; } @@ -1012,7 +1093,7 @@ function initSearch(rawSearchIndex) { } } if (match === null) { - return maxLevDistance + 1; + return maxEditDistance + 1; } elems[match] -= 1; if (elems[match] === 0) { @@ -1021,7 +1102,7 @@ function initSearch(rawSearchIndex) { } return 0; } - return maxLevDistance + 1; + return maxEditDistance + 1; } /** @@ -1031,17 +1112,17 @@ function initSearch(rawSearchIndex) { * @param {Row} row * @param {QueryElement} elem - The element from the parsed query. * - * @return {integer} - Returns a Levenshtein distance to the best match. + * @return {integer} - Returns an edit distance to the best match. */ - function checkIfInGenerics(row, elem, maxLevDistance) { - let lev = maxLevDistance + 1; + function checkIfInGenerics(row, elem, maxEditDistance) { + let dist = maxEditDistance + 1; for (const entry of row.generics) { - lev = Math.min(checkType(entry, elem, true, maxLevDistance), lev); - if (lev === 0) { + dist = Math.min(checkType(entry, elem, true, maxEditDistance), dist); + if (dist === 0) { break; } } - return lev; + return dist; } /** @@ -1052,21 +1133,21 @@ function initSearch(rawSearchIndex) { * @param {QueryElement} elem - The element from the parsed query. * @param {boolean} literalSearch * - * @return {integer} - Returns a Levenshtein distance to the best match. If there is - * no match, returns `maxLevDistance + 1`. + * @return {integer} - Returns an edit distance to the best match. If there is + * no match, returns `maxEditDistance + 1`. */ - function checkType(row, elem, literalSearch, maxLevDistance) { + function checkType(row, elem, literalSearch, maxEditDistance) { if (row.name === null) { // This is a pure "generic" search, no need to run other checks. if (row.generics.length > 0) { - return checkIfInGenerics(row, elem, maxLevDistance); + return checkIfInGenerics(row, elem, maxEditDistance); } - return maxLevDistance + 1; + return maxEditDistance + 1; } - let lev = levenshtein(row.name, elem.name); + let dist = editDistance(row.name, elem.name, maxEditDistance); if (literalSearch) { - if (lev !== 0) { + if (dist !== 0) { // The name didn't match, let's try to check if the generics do. if (elem.generics.length === 0) { const checkGeneric = row.generics.length > 0; @@ -1075,44 +1156,44 @@ function initSearch(rawSearchIndex) { return 0; } } - return maxLevDistance + 1; + return maxEditDistance + 1; } else if (elem.generics.length > 0) { - return checkGenerics(row, elem, maxLevDistance + 1, maxLevDistance); + return checkGenerics(row, elem, maxEditDistance + 1, maxEditDistance); } return 0; } else if (row.generics.length > 0) { if (elem.generics.length === 0) { - if (lev === 0) { + if (dist === 0) { return 0; } // The name didn't match so we now check if the type we're looking for is inside // the generics! - lev = Math.min(lev, checkIfInGenerics(row, elem, maxLevDistance)); - return lev; - } else if (lev > maxLevDistance) { + dist = Math.min(dist, checkIfInGenerics(row, elem, maxEditDistance)); + return dist; + } else if (dist > maxEditDistance) { // So our item's name doesn't match at all and has generics. // // Maybe it's present in a sub generic? For example "f>>()", if we're // looking for "B", we'll need to go down. - return checkIfInGenerics(row, elem, maxLevDistance); + return checkIfInGenerics(row, elem, maxEditDistance); } else { // At this point, the name kinda match and we have generics to check, so // let's go! - const tmp_lev = checkGenerics(row, elem, lev, maxLevDistance); - if (tmp_lev > maxLevDistance) { - return maxLevDistance + 1; + const tmp_dist = checkGenerics(row, elem, dist, maxEditDistance); + if (tmp_dist > maxEditDistance) { + return maxEditDistance + 1; } // We compute the median value of both checks and return it. - return (tmp_lev + lev) / 2; + return (tmp_dist + dist) / 2; } } else if (elem.generics.length > 0) { // In this case, we were expecting generics but there isn't so we simply reject this // one. - return maxLevDistance + 1; + return maxEditDistance + 1; } // No generics on our query or on the target type so we can return without doing // anything else. - return lev; + return dist; } /** @@ -1122,27 +1203,27 @@ function initSearch(rawSearchIndex) { * @param {QueryElement} elem - The element from the parsed query. * @param {integer} typeFilter * - * @return {integer} - Returns a Levenshtein distance to the best match. If there is no - * match, returns `maxLevDistance + 1`. + * @return {integer} - Returns an edit distance to the best match. If there is no + * match, returns `maxEditDistance + 1`. */ - function findArg(row, elem, typeFilter, maxLevDistance) { - let lev = maxLevDistance + 1; + function findArg(row, elem, typeFilter, maxEditDistance) { + let dist = maxEditDistance + 1; if (row && row.type && row.type.inputs && row.type.inputs.length > 0) { for (const input of row.type.inputs) { if (!typePassesFilter(typeFilter, input.ty)) { continue; } - lev = Math.min( - lev, - checkType(input, elem, parsedQuery.literalSearch, maxLevDistance) + dist = Math.min( + dist, + checkType(input, elem, parsedQuery.literalSearch, maxEditDistance) ); - if (lev === 0) { + if (dist === 0) { return 0; } } } - return parsedQuery.literalSearch ? maxLevDistance + 1 : lev; + return parsedQuery.literalSearch ? maxEditDistance + 1 : dist; } /** @@ -1152,11 +1233,11 @@ function initSearch(rawSearchIndex) { * @param {QueryElement} elem - The element from the parsed query. * @param {integer} typeFilter * - * @return {integer} - Returns a Levenshtein distance to the best match. If there is no - * match, returns `maxLevDistance + 1`. + * @return {integer} - Returns an edit distance to the best match. If there is no + * match, returns `maxEditDistance + 1`. */ - function checkReturned(row, elem, typeFilter, maxLevDistance) { - let lev = maxLevDistance + 1; + function checkReturned(row, elem, typeFilter, maxEditDistance) { + let dist = maxEditDistance + 1; if (row && row.type && row.type.output.length > 0) { const ret = row.type.output; @@ -1164,23 +1245,23 @@ function initSearch(rawSearchIndex) { if (!typePassesFilter(typeFilter, ret_ty.ty)) { continue; } - lev = Math.min( - lev, - checkType(ret_ty, elem, parsedQuery.literalSearch, maxLevDistance) + dist = Math.min( + dist, + checkType(ret_ty, elem, parsedQuery.literalSearch, maxEditDistance) ); - if (lev === 0) { + if (dist === 0) { return 0; } } } - return parsedQuery.literalSearch ? maxLevDistance + 1 : lev; + return parsedQuery.literalSearch ? maxEditDistance + 1 : dist; } - function checkPath(contains, ty, maxLevDistance) { + function checkPath(contains, ty, maxEditDistance) { if (contains.length === 0) { return 0; } - let ret_lev = maxLevDistance + 1; + let ret_dist = maxEditDistance + 1; const path = ty.path.split("::"); if (ty.parent && ty.parent.name) { @@ -1190,27 +1271,27 @@ function initSearch(rawSearchIndex) { const length = path.length; const clength = contains.length; if (clength > length) { - return maxLevDistance + 1; + return maxEditDistance + 1; } for (let i = 0; i < length; ++i) { if (i + clength > length) { break; } - let lev_total = 0; + let dist_total = 0; let aborted = false; for (let x = 0; x < clength; ++x) { - const lev = levenshtein(path[i + x], contains[x]); - if (lev > maxLevDistance) { + const dist = editDistance(path[i + x], contains[x], maxEditDistance); + if (dist > maxEditDistance) { aborted = true; break; } - lev_total += lev; + dist_total += dist; } if (!aborted) { - ret_lev = Math.min(ret_lev, Math.round(lev_total / clength)); + ret_dist = Math.min(ret_dist, Math.round(dist_total / clength)); } } - return ret_lev; + return ret_dist; } function typePassesFilter(filter, type) { @@ -1244,6 +1325,7 @@ function initSearch(rawSearchIndex) { parent: item.parent, type: item.type, is_alias: true, + deprecated: item.deprecated, }; } @@ -1304,31 +1386,31 @@ function initSearch(rawSearchIndex) { * This function adds the given result into the provided `results` map if it matches the * following condition: * - * * If it is a "literal search" (`parsedQuery.literalSearch`), then `lev` must be 0. - * * If it is not a "literal search", `lev` must be <= `maxLevDistance`. + * * If it is a "literal search" (`parsedQuery.literalSearch`), then `dist` must be 0. + * * If it is not a "literal search", `dist` must be <= `maxEditDistance`. * * The `results` map contains information which will be used to sort the search results: * * * `fullId` is a `string`` used as the key of the object we use for the `results` map. * * `id` is the index in both `searchWords` and `searchIndex` arrays for this element. * * `index` is an `integer`` used to sort by the position of the word in the item's name. - * * `lev` is the main metric used to sort the search results. - * * `path_lev` is zero if a single-component search query is used, otherwise it's the + * * `dist` is the main metric used to sort the search results. + * * `path_dist` is zero if a single-component search query is used, otherwise it's the * distance computed for everything other than the last path component. * * @param {Results} results * @param {string} fullId * @param {integer} id * @param {integer} index - * @param {integer} lev - * @param {integer} path_lev + * @param {integer} dist + * @param {integer} path_dist */ - function addIntoResults(results, fullId, id, index, lev, path_lev, maxLevDistance) { - const inBounds = lev <= maxLevDistance || index !== -1; - if (lev === 0 || (!parsedQuery.literalSearch && inBounds)) { + function addIntoResults(results, fullId, id, index, dist, path_dist, maxEditDistance) { + const inBounds = dist <= maxEditDistance || index !== -1; + if (dist === 0 || (!parsedQuery.literalSearch && inBounds)) { if (results[fullId] !== undefined) { const result = results[fullId]; - if (result.dontValidate || result.lev <= lev) { + if (result.dontValidate || result.dist <= dist) { return; } } @@ -1336,8 +1418,8 @@ function initSearch(rawSearchIndex) { id: id, index: index, dontValidate: parsedQuery.literalSearch, - lev: lev, - path_lev: path_lev, + dist: dist, + path_dist: path_dist, }; } } @@ -1346,7 +1428,7 @@ function initSearch(rawSearchIndex) { * This function is called in case the query is only one element (with or without generics). * This element will be compared to arguments' and returned values' items and also to items. * - * Other important thing to note: since there is only one element, we use levenshtein + * Other important thing to note: since there is only one element, we use edit * distance for name comparisons. * * @param {Row} row @@ -1364,22 +1446,22 @@ function initSearch(rawSearchIndex) { results_others, results_in_args, results_returned, - maxLevDistance + maxEditDistance ) { if (!row || (filterCrates !== null && row.crate !== filterCrates)) { return; } - let lev, index = -1, path_lev = 0; + let dist, index = -1, path_dist = 0; const fullId = row.id; const searchWord = searchWords[pos]; - const in_args = findArg(row, elem, parsedQuery.typeFilter, maxLevDistance); - const returned = checkReturned(row, elem, parsedQuery.typeFilter, maxLevDistance); + const in_args = findArg(row, elem, parsedQuery.typeFilter, maxEditDistance); + const returned = checkReturned(row, elem, parsedQuery.typeFilter, maxEditDistance); - // path_lev is 0 because no parent path information is currently stored + // path_dist is 0 because no parent path information is currently stored // in the search index - addIntoResults(results_in_args, fullId, pos, -1, in_args, 0, maxLevDistance); - addIntoResults(results_returned, fullId, pos, -1, returned, 0, maxLevDistance); + addIntoResults(results_in_args, fullId, pos, -1, in_args, 0, maxEditDistance); + addIntoResults(results_returned, fullId, pos, -1, returned, 0, maxEditDistance); if (!typePassesFilter(parsedQuery.typeFilter, row.ty)) { return; @@ -1403,34 +1485,34 @@ function initSearch(rawSearchIndex) { // No need to check anything else if it's a "pure" generics search. if (elem.name.length === 0) { if (row.type !== null) { - lev = checkGenerics(row.type, elem, maxLevDistance + 1, maxLevDistance); - // path_lev is 0 because we know it's empty - addIntoResults(results_others, fullId, pos, index, lev, 0, maxLevDistance); + dist = checkGenerics(row.type, elem, maxEditDistance + 1, maxEditDistance); + // path_dist is 0 because we know it's empty + addIntoResults(results_others, fullId, pos, index, dist, 0, maxEditDistance); } return; } if (elem.fullPath.length > 1) { - path_lev = checkPath(elem.pathWithoutLast, row, maxLevDistance); - if (path_lev > maxLevDistance) { + path_dist = checkPath(elem.pathWithoutLast, row, maxEditDistance); + if (path_dist > maxEditDistance) { return; } } if (parsedQuery.literalSearch) { if (searchWord === elem.name) { - addIntoResults(results_others, fullId, pos, index, 0, path_lev); + addIntoResults(results_others, fullId, pos, index, 0, path_dist); } return; } - lev = levenshtein(searchWord, elem.pathLast); + dist = editDistance(searchWord, elem.pathLast, maxEditDistance); - if (index === -1 && lev + path_lev > maxLevDistance) { + if (index === -1 && dist + path_dist > maxEditDistance) { return; } - addIntoResults(results_others, fullId, pos, index, lev, path_lev, maxLevDistance); + addIntoResults(results_others, fullId, pos, index, dist, path_dist, maxEditDistance); } /** @@ -1442,22 +1524,22 @@ function initSearch(rawSearchIndex) { * @param {integer} pos - Position in the `searchIndex`. * @param {Object} results */ - function handleArgs(row, pos, results, maxLevDistance) { + function handleArgs(row, pos, results, maxEditDistance) { if (!row || (filterCrates !== null && row.crate !== filterCrates)) { return; } - let totalLev = 0; - let nbLev = 0; + let totalDist = 0; + let nbDist = 0; // If the result is too "bad", we return false and it ends this search. function checkArgs(elems, callback) { for (const elem of elems) { // There is more than one parameter to the query so all checks should be "exact" - const lev = callback(row, elem, NO_TYPE_FILTER, maxLevDistance); - if (lev <= 1) { - nbLev += 1; - totalLev += lev; + const dist = callback(row, elem, NO_TYPE_FILTER, maxEditDistance); + if (dist <= 1) { + nbDist += 1; + totalDist += dist; } else { return false; } @@ -1471,11 +1553,11 @@ function initSearch(rawSearchIndex) { return; } - if (nbLev === 0) { + if (nbDist === 0) { return; } - const lev = Math.round(totalLev / nbLev); - addIntoResults(results, row.id, pos, 0, lev, 0, maxLevDistance); + const dist = Math.round(totalDist / nbDist); + addIntoResults(results, row.id, pos, 0, dist, 0, maxEditDistance); } function innerRunQuery() { @@ -1488,7 +1570,7 @@ function initSearch(rawSearchIndex) { for (const elem of parsedQuery.returned) { queryLen += elem.name.length; } - const maxLevDistance = Math.floor(queryLen / 3); + const maxEditDistance = Math.floor(queryLen / 3); if (parsedQuery.foundElems === 1) { if (parsedQuery.elems.length === 1) { @@ -1503,7 +1585,7 @@ function initSearch(rawSearchIndex) { results_others, results_in_args, results_returned, - maxLevDistance + maxEditDistance ); } } else if (parsedQuery.returned.length === 1) { @@ -1515,14 +1597,14 @@ function initSearch(rawSearchIndex) { row, elem, parsedQuery.typeFilter, - maxLevDistance + maxEditDistance ); - addIntoResults(results_others, row.id, i, -1, in_returned, maxLevDistance); + addIntoResults(results_others, row.id, i, -1, in_returned, maxEditDistance); } } } else if (parsedQuery.foundElems > 0) { for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) { - handleArgs(searchIndex[i], i, results_others, maxLevDistance); + handleArgs(searchIndex[i], i, results_others, maxEditDistance); } } } @@ -1560,7 +1642,7 @@ function initSearch(rawSearchIndex) { * * @return {boolean} - Whether the result is valid or not */ - function validateResult(name, path, keys, parent, maxLevDistance) { + function validateResult(name, path, keys, parent, maxEditDistance) { if (!keys || !keys.length) { return true; } @@ -1574,8 +1656,8 @@ function initSearch(rawSearchIndex) { // next if there is a parent, check for exact parent match (parent !== undefined && parent.name !== undefined && parent.name.toLowerCase().indexOf(key) > -1) || - // lastly check to see if the name was a levenshtein match - levenshtein(name, key) <= maxLevDistance)) { + // lastly check to see if the name was an editDistance match + editDistance(name, key, maxEditDistance) <= maxEditDistance)) { return false; } } @@ -2064,10 +2146,11 @@ function initSearch(rawSearchIndex) { * n: Array, * t: String, * d: Array, - * q: Array, + * q: Array<[Number, string]>, * i: Array, * f: Array, * p: Array, + * c: Array * }} */ const crateCorpus = rawSearchIndex[crate]; @@ -2086,6 +2169,7 @@ function initSearch(rawSearchIndex) { type: null, id: id, normalizedName: crate.indexOf("_") === -1 ? crate : crate.replace(/_/g, ""), + deprecated: null, }; id += 1; searchIndex.push(crateRow); @@ -2095,14 +2179,20 @@ function initSearch(rawSearchIndex) { const itemTypes = crateCorpus.t; // an array of (String) item names const itemNames = crateCorpus.n; - // an array of (String) full paths (or empty string for previous path) - const itemPaths = crateCorpus.q; + // an array of [(Number) item index, + // (String) full path] + // an item whose index is not present will fall back to the previous present path + // i.e. if indices 4 and 11 are present, but 5-10 and 12-13 are not present, + // 5-10 will fall back to the path for 4 and 12-13 will fall back to the path for 11 + const itemPaths = new Map(crateCorpus.q); // an array of (String) descriptions const itemDescs = crateCorpus.d; // an array of (Number) the parent path index + 1 to `paths`, or 0 if none const itemParentIdxs = crateCorpus.i; // an array of (Object | null) the type of the function, if any const itemFunctionSearchTypes = crateCorpus.f; + // an array of (Number) indices for the deprecated items + const deprecatedItems = new Set(crateCorpus.c); // an array of [(Number) item type, // (String) name] const paths = crateCorpus.p; @@ -2142,12 +2232,13 @@ function initSearch(rawSearchIndex) { crate: crate, ty: itemTypes.charCodeAt(i) - charA, name: itemNames[i], - path: itemPaths[i] ? itemPaths[i] : lastPath, + path: itemPaths.has(i) ? itemPaths.get(i) : lastPath, desc: itemDescs[i], parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined, type: buildFunctionSearchType(itemFunctionSearchTypes[i], lowercasePaths), id: id, normalizedName: word.indexOf("_") === -1 ? word : word.replace(/_/g, ""), + deprecated: deprecatedItems.has(i), }; id += 1; searchIndex.push(row); diff --git a/src/librustdoc/html/templates/item_info.html b/src/librustdoc/html/templates/item_info.html new file mode 100644 index 00000000000..d2ea9bdae9c --- /dev/null +++ b/src/librustdoc/html/templates/item_info.html @@ -0,0 +1,7 @@ +{% if !items.is_empty() %} + {# #} + {% for item in items %} + {{item|safe}} {# #} + {% endfor %} + +{% endif %} diff --git a/src/librustdoc/html/templates/short_item_info.html b/src/librustdoc/html/templates/short_item_info.html new file mode 100644 index 00000000000..e3125af0e47 --- /dev/null +++ b/src/librustdoc/html/templates/short_item_info.html @@ -0,0 +1,23 @@ +{% match self %} + {% when Self::Deprecation with { message } %} +
    {# #} + 👎 {# #} + {{message}} {# #} +
    {# #} + {% when Self::Unstable with { feature, tracking } %} +
    {# #} + 🔬 {# #} + {# #} + This is a nightly-only experimental API. ({# #} + {{feature}} {# #} + {% match tracking %} + {% when Some with ((url, num)) %} +  #{{num}} {# #} + {% when None %} + {% endmatch %} + ) {# #} + {# #} +
    {# #} + {% when Self::Portability with { message } %} +
    {{message|safe}}
    {# #} +{% endmatch %} diff --git a/src/librustdoc/html/templates/sidebar.html b/src/librustdoc/html/templates/sidebar.html new file mode 100644 index 00000000000..01d476ad29f --- /dev/null +++ b/src/librustdoc/html/templates/sidebar.html @@ -0,0 +1,37 @@ +{% if !title.is_empty() %} +

    {# #} + {{title_prefix}}{{title}} {# #} +

    +{% endif %} + diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index bcb69d1a4ca..358f6ad566c 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -13,7 +13,7 @@ use rustc_hir::def::Namespace::*; use rustc_hir::def::{DefKind, Namespace, PerNS}; use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; use rustc_hir::Mutability; -use rustc_middle::ty::{Ty, TyCtxt}; +use rustc_middle::ty::{fast_reject::TreatProjections, Ty, TyCtxt}; use rustc_middle::{bug, ty}; use rustc_resolve::rustdoc::MalformedGenerics; use rustc_resolve::rustdoc::{prepare_to_doc_link_resolution, strip_generics_from_path}; @@ -735,7 +735,7 @@ fn trait_impls_for<'a>( trace!("considering explicit impl for trait {:?}", trait_); // Look at each trait implementation to see if it's an impl for `did` - tcx.find_map_relevant_impl(trait_, ty, |impl_| { + tcx.find_map_relevant_impl(trait_, ty, TreatProjections::ForLookup, |impl_| { let trait_ref = tcx.impl_trait_ref(impl_).expect("this is not an inherent impl"); // Check if these are the same type. let impl_type = trait_ref.skip_binder().self_ty(); diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 44e9b49f82a..e09a68069e8 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -224,6 +224,11 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { ) -> bool { debug!("maybe_inline_local res: {:?}", res); + if renamed == Some(kw::Underscore) { + // We never inline `_` reexports. + return false; + } + if self.cx.output_format.is_json() { return false; } @@ -346,8 +351,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { self.visit_foreign_item_inner(item, None); } } - // If we're inlining, skip private items or item reexported as "_". - _ if self.inlining && (!is_pub || renamed == Some(kw::Underscore)) => {} + // If we're inlining, skip private items. + _ if self.inlining && !is_pub => {} hir::ItemKind::GlobalAsm(..) => {} hir::ItemKind::Use(_, hir::UseKind::ListStem) => {} hir::ItemKind::Use(path, kind) => { diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 765826ed867..0abe234fc8f 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,11 +6,156 @@ document. ## Unreleased / Beta / In Rust Nightly -[d822110d...master](https://github.com/rust-lang/rust-clippy/compare/d822110d...master) +[7f27e2e7...master](https://github.com/rust-lang/rust-clippy/compare/7f27e2e7...master) + +## Rust 1.68 + +Current stable, released 2023-03-09 + +[d822110d...7f27e2e7](https://github.com/rust-lang/rust-clippy/compare/d822110d...7f27e2e7) + +### New Lints + +* [`permissions_set_readonly_false`] + [#10063](https://github.com/rust-lang/rust-clippy/pull/10063) +* [`almost_complete_range`] + [#10043](https://github.com/rust-lang/rust-clippy/pull/10043) +* [`size_of_ref`] + [#10098](https://github.com/rust-lang/rust-clippy/pull/10098) +* [`semicolon_outside_block`] + [#9826](https://github.com/rust-lang/rust-clippy/pull/9826) +* [`semicolon_inside_block`] + [#9826](https://github.com/rust-lang/rust-clippy/pull/9826) +* [`transmute_null_to_fn`] + [#10099](https://github.com/rust-lang/rust-clippy/pull/10099) +* [`fn_null_check`] + [#10099](https://github.com/rust-lang/rust-clippy/pull/10099) + +### Moves and Deprecations + +* Moved [`manual_clamp`] to `nursery` (Now allow-by-default) + [#10101](https://github.com/rust-lang/rust-clippy/pull/10101) +* Moved [`mutex_atomic`] to `restriction` + [#10115](https://github.com/rust-lang/rust-clippy/pull/10115) +* Renamed `derive_hash_xor_eq` to [`derived_hash_with_manual_eq`] + [#10184](https://github.com/rust-lang/rust-clippy/pull/10184) + +### Enhancements + +* [`collapsible_str_replace`]: Now takes MSRV into consideration. The minimal version is 1.58 + [#10047](https://github.com/rust-lang/rust-clippy/pull/10047) +* [`unused_self`]: No longer lints, if the method body contains a `todo!()` call + [#10166](https://github.com/rust-lang/rust-clippy/pull/10166) +* [`derivable_impls`]: Now suggests deriving `Default` for enums with default unit variants + [#10161](https://github.com/rust-lang/rust-clippy/pull/10161) +* [`arithmetic_side_effects`]: Added two new config values + `arithmetic-side-effects-allowed-binary` and `arithmetic-side-effects-allowed-unary` + to allow operation on user types + [#9840](https://github.com/rust-lang/rust-clippy/pull/9840) +* [`large_const_arrays`], [`large_stack_arrays`]: avoid integer overflow when calculating + total array size + [#10103](https://github.com/rust-lang/rust-clippy/pull/10103) +* [`indexing_slicing`]: add new config `suppress-restriction-lint-in-const` to enable + restriction lints, even if the suggestion might not be applicable + [#9920](https://github.com/rust-lang/rust-clippy/pull/9920) +* [`needless_borrow`], [`redundant_clone`]: Now track references better and detect more cases + [#9701](https://github.com/rust-lang/rust-clippy/pull/9701) +* [`derived_hash_with_manual_eq`]: Now allows `#[derive(PartialEq)]` with custom `Hash` + implementations + [#10184](https://github.com/rust-lang/rust-clippy/pull/10184) +* [`manual_is_ascii_check`]: Now detects ranges with `.contains()` calls + [#10053](https://github.com/rust-lang/rust-clippy/pull/10053) +* [`transmuting_null`]: Now detects `const` pointers to all types + [#10099](https://github.com/rust-lang/rust-clippy/pull/10099) +* [`needless_return`]: Now detects more cases for returns of owned values + [#10110](https://github.com/rust-lang/rust-clippy/pull/10110) + +### False Positive Fixes + +* [`field_reassign_with_default`]: No longer lints cases, where values are initializes from + closures capturing struct values + [#10143](https://github.com/rust-lang/rust-clippy/pull/10143) +* [`seek_to_start_instead_of_rewind`]: No longer lints, if the return of `seek` is used. + [#10096](https://github.com/rust-lang/rust-clippy/pull/10096) +* [`manual_filter`]: Now ignores if expressions where the else branch has side effects or + doesn't return `None` + [#10091](https://github.com/rust-lang/rust-clippy/pull/10091) +* [`implicit_clone`]: No longer lints if the type doesn't implement clone + [#10022](https://github.com/rust-lang/rust-clippy/pull/10022) +* [`match_wildcard_for_single_variants`]: No longer lints on wildcards with a guard + [#10056](https://github.com/rust-lang/rust-clippy/pull/10056) +* [`drop_ref`]: No longer lints idiomatic expression in `match` arms + [#10142](https://github.com/rust-lang/rust-clippy/pull/10142) +* [`arithmetic_side_effects`]: No longer lints on corner cases with negative number literals + [#9867](https://github.com/rust-lang/rust-clippy/pull/9867) +* [`string_lit_as_bytes`]: No longer lints in scrutinies of `match` statements + [#10012](https://github.com/rust-lang/rust-clippy/pull/10012) +* [`manual_assert`]: No longer lints in `else if` statements + [#10013](https://github.com/rust-lang/rust-clippy/pull/10013) +* [`needless_return`]: don't lint when using `do yeet` + [#10109](https://github.com/rust-lang/rust-clippy/pull/10109) +* All lints: No longer lint in enum discriminant values when the suggestion won't work in a + const context + [#10008](https://github.com/rust-lang/rust-clippy/pull/10008) +* [`single_element_loop`]: No longer lints, if the loop contains a `break` or `continue` + [#10162](https://github.com/rust-lang/rust-clippy/pull/10162) +* [`uninlined_format_args`]: No longer suggests inlining arguments in `assert!` and + `debug_assert!` macros before 2021 edition + [#10055](https://github.com/rust-lang/rust-clippy/pull/10055) +* [`explicit_counter_loop`]: No longer ignores counter changes after `continue` expressions + [#10094](https://github.com/rust-lang/rust-clippy/pull/10094) +* [`from_over_into`]: No longer lints on opaque types + [#9982](https://github.com/rust-lang/rust-clippy/pull/9982) +* [`expl_impl_clone_on_copy`]: No longer lints on `#[repr(packed)]` structs with generic + parameters + [#10189](https://github.com/rust-lang/rust-clippy/pull/10189) + +### Suggestion Fixes/Improvements + +* [`zero_ptr`]: Now suggests `core::` paths for `no_std` crates + [#10023](https://github.com/rust-lang/rust-clippy/pull/10023) +* [`useless_conversion`]: Now suggests removing calls to `into_iter()` on an expression + implementing `Iterator` + [#10020](https://github.com/rust-lang/rust-clippy/pull/10020) +* [`box_default`]: The suggestion now uses short paths + [#10153](https://github.com/rust-lang/rust-clippy/pull/10153) +* [`default_trait_access`], [`clone_on_copy`]: The suggestion now uses short paths + [#10160](https://github.com/rust-lang/rust-clippy/pull/10160) +* [`comparison_to_empty`]: The suggestion now removes unused deref operations + [#9962](https://github.com/rust-lang/rust-clippy/pull/9962) +* [`manual_let_else`]: Suggestions for or-patterns now include required brackets. + [#9966](https://github.com/rust-lang/rust-clippy/pull/9966) +* [`match_single_binding`]: suggestion no longer introduces unneeded semicolons + [#10060](https://github.com/rust-lang/rust-clippy/pull/10060) +* [`case_sensitive_file_extension_comparisons`]: Now displays a suggestion with `Path` + [#10107](https://github.com/rust-lang/rust-clippy/pull/10107) +* [`empty_structs_with_brackets`]: The suggestion is no longer machine applicable, to avoid + errors when accessing struct fields + [#10141](https://github.com/rust-lang/rust-clippy/pull/10141) +* [`identity_op`]: Removes borrows in the suggestion when needed + [#10004](https://github.com/rust-lang/rust-clippy/pull/10004) +* [`suboptimal_flops`]: The suggestion now includes parentheses when required + [#10113](https://github.com/rust-lang/rust-clippy/pull/10113) +* [`iter_kv_map`]: Now handles `mut` and reference annotations in the suggestion + [#10159](https://github.com/rust-lang/rust-clippy/pull/10159) +* [`redundant_static_lifetimes`]: The suggestion no longer removes `mut` from references + [#10006](https://github.com/rust-lang/rust-clippy/pull/10006) + +### ICE Fixes + +* [`new_ret_no_self`]: Now avoids a stack overflow for `impl Trait` types + [#10086](https://github.com/rust-lang/rust-clippy/pull/10086) +* [`unnecessary_to_owned`]: Now handles compiler generated notes better + [#10027](https://github.com/rust-lang/rust-clippy/pull/10027) + +### Others + +* `SYSROOT` and `--sysroot` can now be set at the same time + [#10149](https://github.com/rust-lang/rust-clippy/pull/10149) ## Rust 1.67 -Current stable, released 2023-01-26 +Released 2023-01-26 [4f142aa1...d822110d](https://github.com/rust-lang/rust-clippy/compare/4f142aa1...d822110d) @@ -4307,6 +4452,7 @@ Released 2018-09-13 [`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if [`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match [`collapsible_str_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace +[`collection_is_never_read`]: https://rust-lang.github.io/rust-clippy/master/index.html#collection_is_never_read [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain [`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty [`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime @@ -4497,6 +4643,7 @@ Released 2018-09-13 [`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use [`let_underscore_untyped`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped [`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value +[`let_with_type_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_with_type_underscore [`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist [`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug [`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal @@ -4560,6 +4707,7 @@ Released 2018-09-13 [`mismatching_type_param_order`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatching_type_param_order [`misnamed_getters`]: https://rust-lang.github.io/rust-clippy/master/index.html#misnamed_getters [`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op +[`missing_assert_message`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_assert_message [`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn [`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items [`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames @@ -4689,6 +4837,7 @@ Released 2018-09-13 [`read_zero_byte_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#read_zero_byte_vec [`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl [`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation +[`redundant_async_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_async_block [`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone [`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure [`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call diff --git a/src/tools/clippy/COPYRIGHT b/src/tools/clippy/COPYRIGHT index a6be75b5e31..82703b18fd7 100644 --- a/src/tools/clippy/COPYRIGHT +++ b/src/tools/clippy/COPYRIGHT @@ -1,3 +1,5 @@ +// REUSE-IgnoreStart + Copyright 2014-2022 The Rust Project Developers Licensed under the Apache License, Version 2.0 or the MIT license , at your option. All files in the project carrying such notice may not be copied, modified, or distributed except according to those terms. + +// REUSE-IgnoreEnd diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index 70d1268090f..c35dfcbd8c4 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.69" +version = "0.1.70" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md index 3e7379ace7e..b69ed8900a4 100644 --- a/src/tools/clippy/README.md +++ b/src/tools/clippy/README.md @@ -275,6 +275,8 @@ If you want to contribute to Clippy, you can find more information in [CONTRIBUT ## License + + Copyright 2014-2022 The Rust Project Developers Licensed under the Apache License, Version 2.0 , at your option. Files in the project may not be copied, modified, or distributed except according to those terms. + + diff --git a/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md b/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md index c5587c4bf90..ea4978011b1 100644 --- a/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md +++ b/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md @@ -68,13 +68,13 @@ The second part of the motivation is clippy's dependence on unstable compiler-internal data structures. Clippy lints are currently written against the compiler's AST / HIR which means that even small changes in these data structures might break a lot of lints. The second goal of this RFC is to **make -lints independant of the compiler's AST / HIR data structures**. +lints independent of the compiler's AST / HIR data structures**. # Approach A lot of complexity in writing lints currently seems to come from having to manually implement the matching logic (see code samples above). It's an -imparative style that describes *how* to match a syntax tree node instead of +imperative style that describes *how* to match a syntax tree node instead of specifying *what* should be matched against declaratively. In other areas, it's common to use declarative patterns to describe desired information and let the implementation do the actual matching. A well-known example of this approach are @@ -270,7 +270,7 @@ pattern!{ // matches if expressions that **may or may not** have an else block // Attn: `If(_, _, _)` matches only ifs that **have** an else block // - // | if with else block | if witout else block + // | if with else block | if without else block // If(_, _, _) | match | no match // If(_, _, _?) | match | match // If(_, _, ()) | no match | match @@ -568,7 +568,7 @@ another example, `Array( Lit(_)* )` is a valid pattern because the parameter of ## The IsMatch Trait -The pattern syntax and the *PatternTree* are independant of specific syntax tree +The pattern syntax and the *PatternTree* are independent of specific syntax tree implementations (rust ast / hir, syn, ...). When looking at the different pattern examples in the previous sections, it can be seen that the patterns don't contain any information specific to a certain syntax tree implementation. @@ -717,7 +717,7 @@ if false { #### Problems Extending Rust syntax (which is quite complex by itself) with additional syntax -needed for specifying patterns (alternations, sequences, repetisions, named +needed for specifying patterns (alternations, sequences, repetitions, named submatches, ...) might become difficult to read and really hard to parse properly. @@ -858,7 +858,7 @@ would be evaluated as soon as the `Block(_)#then` was matched. Another idea in this area would be to introduce a syntax for backreferences. They could be used to require that multiple parts of a pattern should match the same value. For example, the `assign_op_pattern` lint that searches for `a = a -op b` and recommends changing it to `a op= b` requires that both occurrances of +op b` and recommends changing it to `a op= b` requires that both occurrences of `a` are the same. Using `=#...` as syntax for backreferences, the lint could be implemented like this: @@ -882,7 +882,7 @@ least two return statements" could be a practical addition. For patterns like "a literal that is not a boolean literal" one currently needs to list all alternatives except the boolean case. Introducing a negation operator that allows to write `Lit(!Bool(_))` might be a good idea. This pattern -would be eqivalent to `Lit( Char(_) | Int(_) )` (given that currently only three +would be equivalent to `Lit( Char(_) | Int(_) )` (given that currently only three literal types are implemented). #### Functional composition diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index 796f1ff1695..0b3846c1316 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.69" +version = "0.1.70" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/src/tools/clippy/clippy_lints/src/almost_complete_range.rs b/src/tools/clippy/clippy_lints/src/almost_complete_range.rs index 42e14b5cd94..32d80f42e7e 100644 --- a/src/tools/clippy/clippy_lints/src/almost_complete_range.rs +++ b/src/tools/clippy/clippy_lints/src/almost_complete_range.rs @@ -24,7 +24,7 @@ declare_clippy_lint! { /// ```rust /// let _ = 'a'..='z'; /// ``` - #[clippy::version = "1.63.0"] + #[clippy::version = "1.68.0"] pub ALMOST_COMPLETE_RANGE, suspicious, "almost complete range" diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs index 627b795d6ed..1233c632a79 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -34,6 +34,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, if let ExprKind::Path(ref qpath) = fun.kind; if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); if let Some(rpk) = raw_parts_kind(cx, fun_def_id); + let ctxt = expr.span.ctxt(); + if cast_expr.span.ctxt() == ctxt; then { let func = match rpk { RawPartsKind::Immutable => "from_raw_parts", @@ -41,8 +43,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, }; let span = expr.span; let mut applicability = Applicability::MachineApplicable; - let ptr = snippet_with_applicability(cx, ptr_arg.span, "ptr", &mut applicability); - let len = snippet_with_applicability(cx, len_arg.span, "len", &mut applicability); + let ptr = snippet_with_context(cx, ptr_arg.span, ctxt, "ptr", &mut applicability).0; + let len = snippet_with_context(cx, len_arg.span, ctxt, "len", &mut applicability).0; span_lint_and_sugg( cx, CAST_SLICE_FROM_RAW_PARTS, diff --git a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs new file mode 100644 index 00000000000..10f2bef268a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs @@ -0,0 +1,122 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::visitors::for_each_expr_with_closures; +use clippy_utils::{get_enclosing_block, get_parent_node, path_to_local_id}; +use core::ops::ControlFlow; +use rustc_hir::{Block, ExprKind, HirId, Local, Node, PatKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::sym; +use rustc_span::Symbol; + +declare_clippy_lint! { + /// ### What it does + /// Checks for collections that are never queried. + /// + /// ### Why is this bad? + /// Putting effort into constructing a collection but then never querying it might indicate that + /// the author forgot to do whatever they intended to do with the collection. Example: Clone + /// a vector, sort it for iteration, but then mistakenly iterate the original vector + /// instead. + /// + /// ### Example + /// ```rust + /// # let samples = vec![3, 1, 2]; + /// let mut sorted_samples = samples.clone(); + /// sorted_samples.sort(); + /// for sample in &samples { // Oops, meant to use `sorted_samples`. + /// println!("{sample}"); + /// } + /// ``` + /// Use instead: + /// ```rust + /// # let samples = vec![3, 1, 2]; + /// let mut sorted_samples = samples.clone(); + /// sorted_samples.sort(); + /// for sample in &sorted_samples { + /// println!("{sample}"); + /// } + /// ``` + #[clippy::version = "1.69.0"] + pub COLLECTION_IS_NEVER_READ, + nursery, + "a collection is never queried" +} +declare_lint_pass!(CollectionIsNeverRead => [COLLECTION_IS_NEVER_READ]); + +static COLLECTIONS: [Symbol; 10] = [ + sym::BTreeMap, + sym::BTreeSet, + sym::BinaryHeap, + sym::HashMap, + sym::HashSet, + sym::LinkedList, + sym::Option, + sym::String, + sym::Vec, + sym::VecDeque, +]; + +impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { + // Look for local variables whose type is a container. Search surrounding bock for read access. + let ty = cx.typeck_results().pat_ty(local.pat); + if COLLECTIONS.iter().any(|&sym| is_type_diagnostic_item(cx, ty, sym)) + && let PatKind::Binding(_, local_id, _, _) = local.pat.kind + && let Some(enclosing_block) = get_enclosing_block(cx, local.hir_id) + && has_no_read_access(cx, local_id, enclosing_block) + { + span_lint(cx, COLLECTION_IS_NEVER_READ, local.span, "collection is never read"); + } + } +} + +fn has_no_read_access<'tcx>(cx: &LateContext<'tcx>, id: HirId, block: &'tcx Block<'tcx>) -> bool { + let mut has_access = false; + let mut has_read_access = false; + + // Inspect all expressions and sub-expressions in the block. + for_each_expr_with_closures(cx, block, |expr| { + // Ignore expressions that are not simply `id`. + if !path_to_local_id(expr, id) { + return ControlFlow::Continue(()); + } + + // `id` is being accessed. Investigate if it's a read access. + has_access = true; + + // `id` appearing in the left-hand side of an assignment is not a read access: + // + // id = ...; // Not reading `id`. + if let Some(Node::Expr(parent)) = get_parent_node(cx.tcx, expr.hir_id) + && let ExprKind::Assign(lhs, ..) = parent.kind + && path_to_local_id(lhs, id) + { + return ControlFlow::Continue(()); + } + + // Method call on `id` in a statement ignores any return value, so it's not a read access: + // + // id.foo(...); // Not reading `id`. + // + // Only assuming this for "official" methods defined on the type. For methods defined in extension + // traits (identified as local, based on the orphan rule), pessimistically assume that they might + // have side effects, so consider them a read. + if let Some(Node::Expr(parent)) = get_parent_node(cx.tcx, expr.hir_id) + && let ExprKind::MethodCall(_, receiver, _, _) = parent.kind + && path_to_local_id(receiver, id) + && let Some(Node::Stmt(..)) = get_parent_node(cx.tcx, parent.hir_id) + && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id) + && !method_def_id.is_local() + { + return ControlFlow::Continue(()); + } + + // Any other access to `id` is a read access. Stop searching. + has_read_access = true; + ControlFlow::Break(()) + }); + + // Ignore collections that have no access at all. Other lints should catch them. + has_access && !has_read_access +} diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index cd5dd7a5706..cc6024b87cd 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -92,6 +92,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO, crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO, crate::collapsible_if::COLLAPSIBLE_IF_INFO, + crate::collection_is_never_read::COLLECTION_IS_NEVER_READ_INFO, crate::comparison_chain::COMPARISON_CHAIN_INFO, crate::copies::BRANCHES_SHARING_CODE_INFO, crate::copies::IFS_SAME_COND_INFO, @@ -226,6 +227,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::let_underscore::LET_UNDERSCORE_LOCK_INFO, crate::let_underscore::LET_UNDERSCORE_MUST_USE_INFO, crate::let_underscore::LET_UNDERSCORE_UNTYPED_INFO, + crate::let_with_type_underscore::LET_WITH_TYPE_UNDERSCORE_INFO, crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO, crate::lifetimes::NEEDLESS_LIFETIMES_INFO, crate::literal_representation::DECIMAL_LITERAL_REPRESENTATION_INFO, @@ -416,6 +418,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::misc_early::UNSEPARATED_LITERAL_SUFFIX_INFO, crate::misc_early::ZERO_PREFIXED_LITERAL_INFO, crate::mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER_INFO, + crate::missing_assert_message::MISSING_ASSERT_MESSAGE_INFO, crate::missing_const_for_fn::MISSING_CONST_FOR_FN_INFO, crate::missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS_INFO, crate::missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES_INFO, @@ -517,6 +520,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::ranges::REVERSED_EMPTY_RANGES_INFO, crate::rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT_INFO, crate::read_zero_byte_vec::READ_ZERO_BYTE_VEC_INFO, + crate::redundant_async_block::REDUNDANT_ASYNC_BLOCK_INFO, crate::redundant_clone::REDUNDANT_CLONE_INFO, crate::redundant_closure_call::REDUNDANT_CLOSURE_CALL_INFO, crate::redundant_else::REDUNDANT_ELSE_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs index 1ad929864b2..f296b80d283 100644 --- a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs +++ b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::last_path_segment; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use clippy_utils::{match_def_path, paths}; use rustc_errors::Applicability; use rustc_hir::{def, Expr, ExprKind, GenericArg, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::SyntaxContext; declare_clippy_lint! { /// ### What it does @@ -38,9 +39,11 @@ impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty { && let QPath::Resolved(None, path) = ty_path && let def::Res::Def(_, def_id) = &path.res && match_def_path(cx, *def_id, &paths::ITER_EMPTY) + && let ctxt = expr.span.ctxt() + && ty.span.ctxt() == ctxt { let mut applicability = Applicability::MachineApplicable; - let sugg = make_sugg(cx, ty_path, &mut applicability); + let sugg = make_sugg(cx, ty_path, ctxt, &mut applicability); span_lint_and_sugg( cx, DEFAULT_INSTEAD_OF_ITER_EMPTY, @@ -54,14 +57,19 @@ impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty { } } -fn make_sugg(cx: &LateContext<'_>, ty_path: &rustc_hir::QPath<'_>, applicability: &mut Applicability) -> String { +fn make_sugg( + cx: &LateContext<'_>, + ty_path: &rustc_hir::QPath<'_>, + ctxt: SyntaxContext, + applicability: &mut Applicability, +) -> String { if let Some(last) = last_path_segment(ty_path).args && let Some(iter_ty) = last.args.iter().find_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), _ => None, }) { - format!("std::iter::empty::<{}>()", snippet_with_applicability(cx, iter_ty.span, "..", applicability)) + format!("std::iter::empty::<{}>()", snippet_with_context(cx, iter_ty.span, ctxt, "..", applicability).0) } else { "std::iter::empty()".to_owned() } diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 47501980e66..7f3f26bed7c 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -1357,10 +1357,10 @@ fn replace_types<'tcx>( && let Some(term_ty) = projection_predicate.term.ty() && let ty::Param(term_param_ty) = term_ty.kind() { - let item_def_id = projection_predicate.projection_ty.def_id; - let assoc_item = cx.tcx.associated_item(item_def_id); - let projection = cx.tcx - .mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(new_ty, [])); + let projection = cx.tcx.mk_ty_from_kind(ty::Alias( + ty::Projection, + projection_predicate.projection_ty.with_self_ty(cx.tcx, new_ty), + )); if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection) && substs[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty) diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs index c5f4e943f4f..8a5a28c6b3d 100644 --- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs +++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs @@ -8,7 +8,7 @@ use rustc_hir::{ Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, Ty, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::AdtDef; +use rustc_middle::ty::{Adt, AdtDef, SubstsRef}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; @@ -81,13 +81,18 @@ fn check_struct<'tcx>( self_ty: &Ty<'_>, func_expr: &Expr<'_>, adt_def: AdtDef<'_>, + substs: SubstsRef<'_>, ) { if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind { - if let Some(PathSegment { args: Some(a), .. }) = p.segments.last() { - for arg in a.args { - if !matches!(arg, GenericArg::Lifetime(_)) { - return; - } + if let Some(PathSegment { args, .. }) = p.segments.last() { + let args = args.map(|a| a.args).unwrap_or(&[]); + + // substs contains the generic parameters of the type declaration, while args contains the arguments + // used at instantiation time. If both len are not equal, it means that some parameters were not + // provided (which means that the default values were used); in this case we will not risk + // suggesting too broad a rewrite. We won't either if any argument is a type or a const. + if substs.len() != args.len() || args.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))) { + return; } } } @@ -184,7 +189,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir); if let ImplItemKind::Fn(_, b) = &impl_item.kind; if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b); - if let Some(adt_def) = cx.tcx.type_of(item.owner_id).subst_identity().ty_adt_def(); + if let &Adt(adt_def, substs) = cx.tcx.type_of(item.owner_id).subst_identity().kind(); if let attrs = cx.tcx.hir().attrs(item.hir_id()); if !attrs.iter().any(|attr| attr.doc_str().is_some()); if let child_attrs = cx.tcx.hir().attrs(impl_item_hir); @@ -192,7 +197,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { then { if adt_def.is_struct() { - check_struct(cx, item, self_ty, func_expr, adt_def); + check_struct(cx, item, self_ty, func_expr, adt_def, substs); } else if adt_def.is_enum() && self.msrv.meets(msrvs::DEFAULT_ENUM_ATTRIBUTE) { check_enum(cx, item, func_expr, adt_def); } diff --git a/src/tools/clippy/clippy_lints/src/exit.rs b/src/tools/clippy/clippy_lints/src/exit.rs index 9c8b0d076df..8ba6a9e4876 100644 --- a/src/tools/clippy/clippy_lints/src/exit.rs +++ b/src/tools/clippy/clippy_lints/src/exit.rs @@ -11,7 +11,7 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// Exit terminates the program at the location it is called. For unrecoverable - /// errors `panics` should be used to provide a stacktrace and potentualy other + /// errors `panics` should be used to provide a stacktrace and potentially other /// information. A normal termination or one with an error code should happen in /// the main function. /// diff --git a/src/tools/clippy/clippy_lints/src/fn_null_check.rs b/src/tools/clippy/clippy_lints/src/fn_null_check.rs index 91c8c340ce2..d8f4a5fe221 100644 --- a/src/tools/clippy/clippy_lints/src/fn_null_check.rs +++ b/src/tools/clippy/clippy_lints/src/fn_null_check.rs @@ -25,7 +25,7 @@ declare_clippy_lint! { /// /// if fn_ptr.is_none() { ... } /// ``` - #[clippy::version = "1.67.0"] + #[clippy::version = "1.68.0"] pub FN_NULL_CHECK, correctness, "`fn()` type assumed to be nullable" diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs index d0fab694960..8040938c626 100644 --- a/src/tools/clippy/clippy_lints/src/format.rs +++ b/src/tools/clippy/clippy_lints/src/format.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::Sugg; use if_chain::if_chain; use rustc_errors::Applicability; @@ -84,9 +84,9 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { _ => false, }; let sugg = if is_new_string { - snippet_with_applicability(cx, value.span, "..", &mut applicability).into_owned() + snippet_with_context(cx, value.span, call_site.ctxt(), "..", &mut applicability).0.into_owned() } else { - let sugg = Sugg::hir_with_applicability(cx, value, "", &mut applicability); + let sugg = Sugg::hir_with_context(cx, value, call_site.ctxt(), "", &mut applicability); format!("{}.to_string()", sugg.maybe_par()) }; span_useless_format(cx, call_site, sugg, applicability); diff --git a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs index 2811a73f6c1..d3d0d91c1be 100644 --- a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs +++ b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs @@ -22,7 +22,7 @@ pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body: if let Some(gen_span) = generics.span_for_param_suggestion() { diag.span_suggestion_with_style( gen_span, - "add a type paremeter", + "add a type parameter", format!(", {{ /* Generic name */ }}: {}", ¶m.name.ident().as_str()[5..]), rustc_errors::Applicability::HasPlaceholders, rustc_errors::SuggestionStyle::ShowAlways, @@ -35,7 +35,7 @@ pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body: ident.span.ctxt(), ident.span.parent(), ), - "add a type paremeter", + "add a type parameter", format!("<{{ /* Generic name */ }}: {}>", ¶m.name.ident().as_str()[5..]), rustc_errors::Applicability::HasPlaceholders, rustc_errors::SuggestionStyle::ShowAlways, diff --git a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs index 8b53ee68ebd..e5945939e60 100644 --- a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs +++ b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs @@ -97,7 +97,7 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body: let Some(correct_field) = correct_field else { // There is no field corresponding to the getter name. - // FIXME: This can be a false positive if the correct field is reachable trought deeper autodereferences than used_field is + // FIXME: This can be a false positive if the correct field is reachable through deeper autodereferences than used_field is return; }; diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs index d2852b4acad..7c5e44bb7dc 100644 --- a/src/tools/clippy/clippy_lints/src/functions/mod.rs +++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs @@ -185,7 +185,7 @@ declare_clippy_lint! { /// ### Examples /// ```rust /// // this could be annotated with `#[must_use]`. - /// fn id(t: T) -> T { t } + /// pub fn id(t: T) -> T { t } /// ``` #[clippy::version = "1.40.0"] pub MUST_USE_CANDIDATE, diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs index 6e19343931e..57e6caa8711 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs @@ -1,7 +1,7 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::get_parent_expr; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use if_chain::if_chain; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; @@ -55,6 +55,9 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd { if let ExprKind::AssignOp(op1, target, value) = ex.kind; let ty = cx.typeck_results().expr_ty(target); if Some(c) == get_int_max(ty); + let ctxt = expr.span.ctxt(); + if ex.span.ctxt() == ctxt; + if expr1.span.ctxt() == ctxt; if clippy_utils::SpanlessEq::new(cx).eq_expr(l, target); if BinOpKind::Add == op1.node; if let ExprKind::Lit(ref lit) = value.kind; @@ -62,8 +65,15 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd { if block.expr.is_none(); then { let mut app = Applicability::MachineApplicable; - let code = snippet_with_applicability(cx, target.span, "_", &mut app); - let sugg = if let Some(parent) = get_parent_expr(cx, expr) && let ExprKind::If(_cond, _then, Some(else_)) = parent.kind && else_.hir_id == expr.hir_id {format!("{{{code} = {code}.saturating_add(1); }}")} else {format!("{code} = {code}.saturating_add(1);")}; + let code = snippet_with_context(cx, target.span, ctxt, "_", &mut app).0; + let sugg = if let Some(parent) = get_parent_expr(cx, expr) + && let ExprKind::If(_cond, _then, Some(else_)) = parent.kind + && else_.hir_id == expr.hir_id + { + format!("{{{code} = {code}.saturating_add(1); }}") + } else { + format!("{code} = {code}.saturating_add(1);") + }; span_lint_and_sugg(cx, IMPLICIT_SATURATING_ADD, expr.span, "manual saturating add detected", "use instead", sugg, app); } } diff --git a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs index 668110c7cc0..34e9991582c 100644 --- a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs +++ b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{self, span_lint_and_sugg}; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source; +use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::Sugg; use clippy_utils::ty; use rustc_errors::Applicability; @@ -161,14 +161,9 @@ fn print_unchecked_duration_subtraction_sugg( ) { let mut applicability = Applicability::MachineApplicable; - let left_expr = - source::snippet_with_applicability(cx, left_expr.span, "std::time::Instant::now()", &mut applicability); - let right_expr = source::snippet_with_applicability( - cx, - right_expr.span, - "std::time::Duration::from_secs(1)", - &mut applicability, - ); + let ctxt = expr.span.ctxt(); + let left_expr = snippet_with_context(cx, left_expr.span, ctxt, "", &mut applicability).0; + let right_expr = snippet_with_context(cx, right_expr.span, ctxt, "", &mut applicability).0; diagnostics::span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index e13bc47973b..0805b4b1979 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -1,13 +1,14 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators}; +use clippy_utils::source::snippet_with_context; +use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators, sugg::Sugg}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def_id::DefIdSet; use rustc_hir::{ - def_id::DefId, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, ImplItem, ImplItemKind, ImplicitSelfKind, Item, - ItemKind, Mutability, Node, TraitItemRef, TyKind, UnOp, + def::Res, def_id::DefId, lang_items::LangItem, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg, + GenericBound, ImplItem, ImplItemKind, ImplicitSelfKind, Item, ItemKind, Mutability, Node, PathSegment, PrimTy, + QPath, TraitItemRef, TyKind, TypeBindingKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, AssocKind, FnSig, Ty}; @@ -16,7 +17,6 @@ use rustc_span::{ source_map::{Span, Spanned, Symbol}, symbol::sym, }; -use std::borrow::Cow; declare_clippy_lint! { /// ### What it does @@ -251,33 +251,98 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items } #[derive(Debug, Clone, Copy)] -enum LenOutput<'tcx> { +enum LenOutput { Integral, Option(DefId), - Result(DefId, Ty<'tcx>), + Result(DefId), } -fn parse_len_output<'tcx>(cx: &LateContext<'_>, sig: FnSig<'tcx>) -> Option> { + +fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx PathSegment<'tcx>> { + if let ty::Alias(_, alias_ty) = ty.kind() && + let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(alias_ty.def_id) && + let Item { kind: ItemKind::OpaqueTy(opaque), .. } = item && + opaque.bounds.len() == 1 && + let GenericBound::LangItemTrait(LangItem::Future, _, _, generic_args) = &opaque.bounds[0] && + generic_args.bindings.len() == 1 && + let TypeBindingKind::Equality { + term: rustc_hir::Term::Ty(rustc_hir::Ty {kind: TyKind::Path(QPath::Resolved(_, path)), .. }), + } = &generic_args.bindings[0].kind && + path.segments.len() == 1 { + return Some(&path.segments[0]); + } + + None +} + +fn is_first_generic_integral<'tcx>(segment: &'tcx PathSegment<'tcx>) -> bool { + if let Some(generic_args) = segment.args { + if generic_args.args.is_empty() { + return false; + } + let arg = &generic_args.args[0]; + if let GenericArg::Type(rustc_hir::Ty { + kind: TyKind::Path(QPath::Resolved(_, path)), + .. + }) = arg + { + let segments = &path.segments; + let segment = &segments[0]; + let res = &segment.res; + if matches!(res, Res::PrimTy(PrimTy::Uint(_))) || matches!(res, Res::PrimTy(PrimTy::Int(_))) { + return true; + } + } + } + + false +} + +fn parse_len_output<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option { + if let Some(segment) = extract_future_output(cx, sig.output()) { + let res = segment.res; + + if matches!(res, Res::PrimTy(PrimTy::Uint(_))) || matches!(res, Res::PrimTy(PrimTy::Int(_))) { + return Some(LenOutput::Integral); + } + + if let Res::Def(_, def_id) = res { + if cx.tcx.is_diagnostic_item(sym::Option, def_id) && is_first_generic_integral(segment) { + return Some(LenOutput::Option(def_id)); + } else if cx.tcx.is_diagnostic_item(sym::Result, def_id) && is_first_generic_integral(segment) { + return Some(LenOutput::Result(def_id)); + } + } + + return None; + } + match *sig.output().kind() { ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral), ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) => { subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did())) }, - ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => subs - .type_at(0) - .is_integral() - .then(|| LenOutput::Result(adt.did(), subs.type_at(1))), + ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => { + subs.type_at(0).is_integral().then(|| LenOutput::Result(adt.did())) + }, _ => None, } } -impl<'tcx> LenOutput<'tcx> { - fn matches_is_empty_output(self, ty: Ty<'tcx>) -> bool { +impl LenOutput { + fn matches_is_empty_output<'tcx>(self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + if let Some(segment) = extract_future_output(cx, ty) { + return match (self, segment.res) { + (_, Res::PrimTy(PrimTy::Bool)) => true, + (Self::Option(_), Res::Def(_, def_id)) if cx.tcx.is_diagnostic_item(sym::Option, def_id) => true, + (Self::Result(_), Res::Def(_, def_id)) if cx.tcx.is_diagnostic_item(sym::Result, def_id) => true, + _ => false, + }; + } + match (self, ty.kind()) { (_, &ty::Bool) => true, (Self::Option(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(), - (Self::Result(id, err_ty), &ty::Adt(adt, subs)) if id == adt.did() => { - subs.type_at(0).is_bool() && subs.type_at(1) == err_ty - }, + (Self::Result(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(), _ => false, } } @@ -301,9 +366,14 @@ impl<'tcx> LenOutput<'tcx> { } /// Checks if the given signature matches the expectations for `is_empty` -fn check_is_empty_sig<'tcx>(sig: FnSig<'tcx>, self_kind: ImplicitSelfKind, len_output: LenOutput<'tcx>) -> bool { +fn check_is_empty_sig<'tcx>( + cx: &LateContext<'tcx>, + sig: FnSig<'tcx>, + self_kind: ImplicitSelfKind, + len_output: LenOutput, +) -> bool { match &**sig.inputs_and_output { - [arg, res] if len_output.matches_is_empty_output(*res) => { + [arg, res] if len_output.matches_is_empty_output(cx, *res) => { matches!( (arg.kind(), self_kind), (ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::ImmRef) @@ -315,11 +385,11 @@ fn check_is_empty_sig<'tcx>(sig: FnSig<'tcx>, self_kind: ImplicitSelfKind, len_o } /// Checks if the given type has an `is_empty` method with the appropriate signature. -fn check_for_is_empty<'tcx>( - cx: &LateContext<'tcx>, +fn check_for_is_empty( + cx: &LateContext<'_>, span: Span, self_kind: ImplicitSelfKind, - output: LenOutput<'tcx>, + output: LenOutput, impl_ty: DefId, item_name: Symbol, item_kind: &str, @@ -352,6 +422,7 @@ fn check_for_is_empty<'tcx>( Some(is_empty) if !(is_empty.fn_has_self_parameter && check_is_empty_sig( + cx, cx.tcx.fn_sig(is_empty.def_id).subst_identity().skip_binder(), self_kind, output, @@ -431,7 +502,7 @@ fn check_len( &format!("using `{op}is_empty` is clearer and more explicit"), format!( "{op}{}.is_empty()", - snippet_with_applicability(cx, receiver.span, "_", &mut applicability) + snippet_with_context(cx, receiver.span, span.ctxt(), "_", &mut applicability).0, ), applicability, ); @@ -444,13 +515,7 @@ fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Ex let mut applicability = Applicability::MachineApplicable; let lit1 = peel_ref_operators(cx, lit1); - let mut lit_str = snippet_with_applicability(cx, lit1.span, "_", &mut applicability); - - // Wrap the expression in parentheses if it's a deref expression. Otherwise operator precedence will - // cause the code to dereference boolean(won't compile). - if let ExprKind::Unary(UnOp::Deref, _) = lit1.kind { - lit_str = Cow::from(format!("({lit_str})")); - } + let lit_str = Sugg::hir_with_context(cx, lit1, span.ctxt(), "_", &mut applicability).maybe_par(); span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs index 7600777fab9..51b5de27de8 100644 --- a/src/tools/clippy/clippy_lints/src/let_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs @@ -124,7 +124,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.69.0"] pub LET_UNDERSCORE_UNTYPED, - pedantic, + restriction, "non-binding `let` without a type annotation" } diff --git a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs new file mode 100644 index 00000000000..ba51973f2f9 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs @@ -0,0 +1,45 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Detects when a variable is declared with an explicit type of `_`. + /// ### Why is this bad? + /// It adds noise, `: _` provides zero clarity or utility. + /// ### Example + /// ```rust,ignore + /// let my_number: _ = 1; + /// ``` + /// Use instead: + /// ```rust,ignore + /// let my_number = 1; + /// ``` + #[clippy::version = "1.69.0"] + pub LET_WITH_TYPE_UNDERSCORE, + complexity, + "unneeded underscore type (`_`) in a variable declaration" +} +declare_lint_pass!(UnderscoreTyped => [LET_WITH_TYPE_UNDERSCORE]); + +impl LateLintPass<'_> for UnderscoreTyped { + fn check_local<'tcx>(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { + if_chain! { + if !in_external_macro(cx.tcx.sess, local.span); + if let Some(ty) = local.ty; // Ensure that it has a type defined + if let TyKind::Infer = &ty.kind; // that type is '_' + if local.span.ctxt() == ty.span.ctxt(); + then { + span_lint_and_help(cx, + LET_WITH_TYPE_UNDERSCORE, + local.span, + "variable declared with type underscore", + Some(ty.span.with_lo(local.pat.span.hi())), + "remove the explicit type `_` declaration" + ) + } + }; + } +} diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index c626e0bd998..491732be208 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -87,6 +87,7 @@ mod casts; mod checked_conversions; mod cognitive_complexity; mod collapsible_if; +mod collection_is_never_read; mod comparison_chain; mod copies; mod copy_iterator; @@ -166,6 +167,7 @@ mod large_stack_arrays; mod len_zero; mod let_if_seq; mod let_underscore; +mod let_with_type_underscore; mod lifetimes; mod literal_representation; mod loops; @@ -192,6 +194,7 @@ mod minmax; mod misc; mod misc_early; mod mismatching_type_param_order; +mod missing_assert_message; mod missing_const_for_fn; mod missing_doc; mod missing_enforced_import_rename; @@ -249,6 +252,7 @@ mod question_mark_used; mod ranges; mod rc_clone_in_vec_init; mod read_zero_byte_vec; +mod redundant_async_block; mod redundant_clone; mod redundant_closure_call; mod redundant_else; @@ -533,6 +537,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: .collect(), )) }); + store.register_early_pass(|| Box::new(utils::format_args_collector::FormatArgsCollector)); store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir)); store.register_late_pass(|_| Box::new(utils::author::Author)); let await_holding_invalid_types = conf.await_holding_invalid_types.clone(); @@ -924,6 +929,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: )) }); store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi)); + store.register_late_pass(|_| Box::new(collection_is_never_read::CollectionIsNeverRead)); + store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage)); + store.register_early_pass(|| Box::new(redundant_async_block::RedundantAsyncBlock)); + store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs index 3778eb4c732..f97c6bcb5d1 100644 --- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs +++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::match_function_call_with_def_id; use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -175,16 +174,10 @@ fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> { if_chain! { if let Some(block_expr) = block.expr; - if let Some(args) = cx - .tcx - .lang_items() - .identity_future_fn() - .and_then(|def_id| match_function_call_with_def_id(cx, block_expr, def_id)); - if args.len() == 1; if let Expr { kind: ExprKind::Closure(&Closure { body, .. }), .. - } = args[0]; + } = block_expr; let closure_body = cx.tcx.hir().body(body); if closure_body.generator_kind == Some(GeneratorKind::Async(AsyncGeneratorKind::Block)); then { diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs index 462d73cf0b9..bc815dc4a26 100644 --- a/src/tools/clippy/clippy_lints/src/manual_bits.rs +++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::get_parent_expr; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; @@ -55,13 +56,17 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits { if_chain! { if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind; if let BinOpKind::Mul = &bin_op.node; + if !in_external_macro(cx.sess(), expr.span); + let ctxt = expr.span.ctxt(); + if left_expr.span.ctxt() == ctxt; + if right_expr.span.ctxt() == ctxt; if let Some((real_ty, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr); if matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_)); if let ExprKind::Lit(lit) = &other_expr.kind; if let LitKind::Int(8, _) = lit.node; then { let mut app = Applicability::MachineApplicable; - let ty_snip = snippet_with_applicability(cx, real_ty.span, "..", &mut app); + let ty_snip = snippet_with_context(cx, real_ty.span, ctxt, "..", &mut app).0; let sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS")); span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs index 2fd32c009ea..31264261f5d 100644 --- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs +++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs @@ -1,5 +1,5 @@ use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::{diagnostics::span_lint_and_sugg, higher, in_constant, macros::root_macro_call, source::snippet}; +use clippy_utils::{diagnostics::span_lint_and_sugg, higher, in_constant, macros::root_macro_call, sugg::Sugg}; use rustc_ast::ast::RangeLimits; use rustc_ast::LitKind::{Byte, Char}; use rustc_errors::Applicability; @@ -115,15 +115,8 @@ fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &Cha CharRange::Otherwise => None, } { let default_snip = ".."; - // `snippet_with_applicability` may set applicability to `MaybeIncorrect` for - // macro span, so we check applicability manually by comparing `recv` is not default. - let recv = snippet(cx, recv.span, default_snip); - - let applicability = if recv == default_snip { - Applicability::HasPlaceholders - } else { - Applicability::MachineApplicable - }; + let mut app = Applicability::MachineApplicable; + let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_par(); span_lint_and_sugg( cx, @@ -132,7 +125,7 @@ fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &Cha "manual check for common ascii range", "try", format!("{recv}.{sugg}()"), - applicability, + app, ); } } diff --git a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs index 38f41d077c1..aafee92713f 100644 --- a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs +++ b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs @@ -1,7 +1,7 @@ use clippy_utils::consts::{constant_full_int, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use clippy_utils::{in_constant, path_to_local}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind}; @@ -60,12 +60,16 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { return; } + // (x % c + c) % c if let ExprKind::Binary(op1, expr1, right) = expr.kind && op1.node == BinOpKind::Rem + && let ctxt = expr.span.ctxt() + && expr1.span.ctxt() == ctxt && let Some(const1) = check_for_unsigned_int_constant(cx, right) && let ExprKind::Binary(op2, left, right) = expr1.kind && op2.node == BinOpKind::Add && let Some((const2, expr2)) = check_for_either_unsigned_int_constant(cx, left, right) + && expr2.span.ctxt() == ctxt && let ExprKind::Binary(op3, expr3, right) = expr2.kind && op3.node == BinOpKind::Rem && let Some(const3) = check_for_unsigned_int_constant(cx, right) @@ -86,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { }; let mut app = Applicability::MachineApplicable; - let rem_of = snippet_with_applicability(cx, expr3.span, "_", &mut app); + let rem_of = snippet_with_context(cx, expr3.span, ctxt, "_", &mut app).0; span_lint_and_sugg( cx, MANUAL_REM_EUCLID, diff --git a/src/tools/clippy/clippy_lints/src/match_result_ok.rs b/src/tools/clippy/clippy_lints/src/match_result_ok.rs index a020282d234..6ec9784038c 100644 --- a/src/tools/clippy/clippy_lints/src/match_result_ok.rs +++ b/src/tools/clippy/clippy_lints/src/match_result_ok.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher; -use clippy_utils::method_chain_args; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::is_res_lang_ctor; +use clippy_utils::source::snippet_with_context; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, PatKind, QPath}; +use rustc_hir::{Expr, ExprKind, LangItem, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -58,17 +58,18 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk { }; if_chain! { - if let ExprKind::MethodCall(ok_path, result_types_0, ..) = let_expr.kind; //check is expr.ok() has type Result.ok(, _) - if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _) = let_pat.kind; //get operation - if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() method use std::marker::Sized; - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::Result); - if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some"; - + if let ExprKind::MethodCall(ok_path, recv, [], ..) = let_expr.kind; //check is expr.ok() has type Result.ok(, _) + if let PatKind::TupleStruct(ref pat_path, [ok_pat], _) = let_pat.kind; //get operation + if ok_path.ident.as_str() == "ok"; + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); + if is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome); + let ctxt = expr.span.ctxt(); + if let_expr.span.ctxt() == ctxt; + if let_pat.span.ctxt() == ctxt; then { - let mut applicability = Applicability::MachineApplicable; - let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability); - let trimmed_ok = snippet_with_applicability(cx, let_expr.span.until(ok_path.ident.span), "", &mut applicability); + let some_expr_string = snippet_with_context(cx, ok_pat.span, ctxt, "", &mut applicability).0; + let trimmed_ok = snippet_with_context(cx, recv.span, ctxt, "", &mut applicability).0; let sugg = format!( "{ifwhile} let Ok({some_expr_string}) = {}", trimmed_ok.trim().trim_end_matches('.'), diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index 7b15a307fec..97ecca450fa 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -925,7 +925,7 @@ declare_clippy_lint! { #[clippy::version = "1.66.0"] pub MANUAL_FILTER, complexity, - "reimplentation of `filter`" + "reimplementation of `filter`" } #[derive(Default)] diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 702df4b282b..56e3988bf09 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -340,8 +340,9 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for methods with certain name prefixes and which - /// doesn't match how self is taken. The actual rules are: + /// Checks for methods with certain name prefixes or suffixes, and which + /// do not adhere to standard conventions regarding how `self` is taken. + /// The actual rules are: /// /// |Prefix |Postfix |`self` taken | `self` type | /// |-------|------------|-------------------------------|--------------| diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index df26b36b7b3..4c4c003ca46 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -369,10 +369,10 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< Node::Item(item) => { if let ItemKind::Fn(_, _, body_id) = &item.kind && let output_ty = return_ty(cx, item.owner_id) - && Inherited::build(cx.tcx, item.owner_id.def_id).enter(|inherited| { - let fn_ctxt = FnCtxt::new(inherited, cx.param_env, item.owner_id.def_id); - fn_ctxt.can_coerce(ty, output_ty) - }) { + && let inherited = Inherited::new(cx.tcx, item.owner_id.def_id) + && let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, item.owner_id.def_id) + && fn_ctxt.can_coerce(ty, output_ty) + { if has_lifetime(output_ty) && has_lifetime(ty) { return false; } diff --git a/src/tools/clippy/clippy_lints/src/missing_assert_message.rs b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs new file mode 100644 index 00000000000..2214a568d9c --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs @@ -0,0 +1,82 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::macros::{find_assert_args, find_assert_eq_args, root_macro_call_first_node, PanicExpn}; +use clippy_utils::{is_in_cfg_test, is_in_test_function}; +use rustc_hir::Expr; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks assertions without a custom panic message. + /// + /// ### Why is this bad? + /// Without a good custom message, it'd be hard to understand what went wrong when the assertion fails. + /// A good custom message should be more about why the failure of the assertion is problematic + /// and not what is failed because the assertion already conveys that. + /// + /// ### Known problems + /// This lint cannot check the quality of the custom panic messages. + /// Hence, you can suppress this lint simply by adding placeholder messages + /// like "assertion failed". However, we recommend coming up with good messages + /// that provide useful information instead of placeholder messages that + /// don't provide any extra information. + /// + /// ### Example + /// ```rust + /// # struct Service { ready: bool } + /// fn call(service: Service) { + /// assert!(service.ready); + /// } + /// ``` + /// Use instead: + /// ```rust + /// # struct Service { ready: bool } + /// fn call(service: Service) { + /// assert!(service.ready, "`service.poll_ready()` must be called first to ensure that service is ready to receive requests"); + /// } + /// ``` + #[clippy::version = "1.69.0"] + pub MISSING_ASSERT_MESSAGE, + restriction, + "checks assertions without a custom panic message" +} + +declare_lint_pass!(MissingAssertMessage => [MISSING_ASSERT_MESSAGE]); + +impl<'tcx> LateLintPass<'tcx> for MissingAssertMessage { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; + let single_argument = match cx.tcx.get_diagnostic_name(macro_call.def_id) { + Some(sym::assert_macro | sym::debug_assert_macro) => true, + Some( + sym::assert_eq_macro | sym::assert_ne_macro | sym::debug_assert_eq_macro | sym::debug_assert_ne_macro, + ) => false, + _ => return, + }; + + // This lint would be very noisy in tests, so just ignore if we're in test context + if is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id) { + return; + } + + let panic_expn = if single_argument { + let Some((_, panic_expn)) = find_assert_args(cx, expr, macro_call.expn) else { return }; + panic_expn + } else { + let Some((_, _, panic_expn)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return }; + panic_expn + }; + + if let PanicExpn::Empty = panic_expn { + span_lint_and_help( + cx, + MISSING_ASSERT_MESSAGE, + macro_call.span, + "assert without any message", + None, + "consider describing why the failing assert is problematic", + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index 5b1f03fc16c..f2773cad400 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -8,10 +8,10 @@ use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_from_proc_macro; -use hir::def_id::LocalDefId; use if_chain::if_chain; use rustc_ast::ast::{self, MetaItem, MetaItemKind}; use rustc_hir as hir; +use rustc_hir::def_id::LocalDefId; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::Visibility; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -21,8 +21,7 @@ use rustc_span::sym; declare_clippy_lint! { /// ### What it does - /// Warns if there is missing doc for any documentable item - /// (public or private). + /// Warns if there is missing doc for any private documentable item /// /// ### Why is this bad? /// Doc is good. *rustc* has a `MISSING_DOCS` @@ -32,7 +31,7 @@ declare_clippy_lint! { #[clippy::version = "pre 1.29.0"] pub MISSING_DOCS_IN_PRIVATE_ITEMS, restriction, - "detects missing documentation for public and private members" + "detects missing documentation for private members" } pub struct MissingDoc { @@ -107,11 +106,14 @@ impl MissingDoc { if vis == Visibility::Public || vis != Visibility::Restricted(CRATE_DEF_ID.into()) { return; } + } else if def_id != CRATE_DEF_ID && cx.effective_visibilities.is_exported(def_id) { + return; } let has_doc = attrs .iter() .any(|a| a.doc_str().is_some() || Self::has_include(a.meta())); + if !has_doc { span_lint( cx, diff --git a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs index 63c575fca30..5418616ded0 100644 --- a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -11,6 +11,7 @@ use rustc_ast::Mutability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; @@ -120,33 +121,15 @@ fn collect_unsafe_exprs<'tcx>( unsafe_ops.push(("raw pointer dereference occurs here", expr.span)); }, - ExprKind::Call(path_expr, _) => match path_expr.kind { - ExprKind::Path(QPath::Resolved( - _, - hir::Path { - res: Res::Def(kind, def_id), - .. - }, - )) if kind.is_fn_like() => { - let sig = cx.tcx.fn_sig(*def_id); - if sig.0.unsafety() == Unsafety::Unsafe { - unsafe_ops.push(("unsafe function call occurs here", expr.span)); - } - }, - - ExprKind::Path(QPath::TypeRelative(..)) => { - if let Some(sig) = cx - .typeck_results() - .type_dependent_def_id(path_expr.hir_id) - .map(|def_id| cx.tcx.fn_sig(def_id)) - { - if sig.0.unsafety() == Unsafety::Unsafe { - unsafe_ops.push(("unsafe function call occurs here", expr.span)); - } - } - }, - - _ => {}, + ExprKind::Call(path_expr, _) => { + let sig = match *cx.typeck_results().expr_ty(path_expr).kind() { + ty::FnDef(id, _) => cx.tcx.fn_sig(id).skip_binder(), + ty::FnPtr(sig) => sig, + _ => return Continue(Descend::Yes), + }; + if sig.unsafety() == Unsafety::Unsafe { + unsafe_ops.push(("unsafe function call occurs here", expr.span)); + } }, ExprKind::MethodCall(..) => { diff --git a/src/tools/clippy/clippy_lints/src/neg_multiply.rs b/src/tools/clippy/clippy_lints/src/neg_multiply.rs index fb9a4abd0b4..ed3e2c6e7f4 100644 --- a/src/tools/clippy/clippy_lints/src/neg_multiply.rs +++ b/src/tools/clippy/clippy_lints/src/neg_multiply.rs @@ -1,6 +1,6 @@ use clippy_utils::consts::{self, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::has_enclosing_paren; use if_chain::if_chain; use rustc_ast::util::parser::PREC_PREFIX; @@ -60,8 +60,8 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { then { let mut applicability = Applicability::MachineApplicable; - let snip = snippet_with_applicability(cx, exp.span, "..", &mut applicability); - let suggestion = if exp.precedence().order() < PREC_PREFIX && !has_enclosing_paren(&snip) { + let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability); + let suggestion = if !from_macro && exp.precedence().order() < PREC_PREFIX && !has_enclosing_paren(&snip) { format!("-({snip})") } else { format!("-{snip}") diff --git a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs index 2ecb0487484..e1de494eb41 100644 --- a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs +++ b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs @@ -53,6 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { || is_type_diagnostic_item(cx, obj_ty, sym::DirBuilder))) || (path.ident.name == sym!(set_mode) && match_type(cx, obj_ty, &paths::PERMISSIONS)); if let ExprKind::Lit(_) = param.kind; + if param.span.ctxt() == expr.span.ctxt(); then { let Some(snip) = snippet_opt(cx, param.span) else { @@ -71,6 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::PERMISSIONS_FROM_MODE); if let ExprKind::Lit(_) = param.kind; + if param.span.ctxt() == expr.span.ctxt(); if let Some(snip) = snippet_opt(cx, param.span); if !snip.starts_with("0o"); then { diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs index 87a8a2ed12b..25e8de94863 100644 --- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -143,6 +143,10 @@ impl ArithmeticSideEffects { return; } let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) { + if let hir::BinOpKind::Shl | hir::BinOpKind::Shr = op.node { + // At least for integers, shifts are already handled by the CTFE + return; + } let (actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs); let (actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs); match ( @@ -151,10 +155,13 @@ impl ArithmeticSideEffects { ) { (None, None) => false, (None, Some(n)) | (Some(n), None) => match (&op.node, n) { - (hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false, + // Division and module are always valid if applied to non-zero integers + (hir::BinOpKind::Div | hir::BinOpKind::Rem, local_n) if local_n != 0 => true, + // Addition or subtracting zeros is always a no-op (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0) - | (hir::BinOpKind::Div | hir::BinOpKind::Rem, _) - | (hir::BinOpKind::Mul, 0 | 1) => true, + // Multiplication by 1 or 0 will never overflow + | (hir::BinOpKind::Mul, 0 | 1) + => true, _ => false, }, (Some(_), Some(_)) => { diff --git a/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs b/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs index e7095ec191f..664d44d6504 100644 --- a/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs +++ b/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs @@ -21,7 +21,7 @@ declare_clippy_lint! { /// let mut permissions = metadata.permissions(); /// permissions.set_readonly(false); /// ``` - #[clippy::version = "1.66.0"] + #[clippy::version = "1.68.0"] pub PERMISSIONS_SET_READONLY_FALSE, suspicious, "Checks for calls to `std::fs::Permissions.set_readonly` with argument `false`" diff --git a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs new file mode 100644 index 00000000000..27ad4308637 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs @@ -0,0 +1,84 @@ +use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet}; +use rustc_ast::ast::*; +use rustc_ast::visit::Visitor as AstVisitor; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for `async` block that only returns `await` on a future. + /// + /// ### Why is this bad? + /// It is simpler and more efficient to use the future directly. + /// + /// ### Example + /// ```rust + /// async fn f() -> i32 { + /// 1 + 2 + /// } + /// + /// let fut = async { + /// f().await + /// }; + /// ``` + /// Use instead: + /// ```rust + /// async fn f() -> i32 { + /// 1 + 2 + /// } + /// + /// let fut = f(); + /// ``` + #[clippy::version = "1.69.0"] + pub REDUNDANT_ASYNC_BLOCK, + complexity, + "`async { future.await }` can be replaced by `future`" +} +declare_lint_pass!(RedundantAsyncBlock => [REDUNDANT_ASYNC_BLOCK]); + +impl EarlyLintPass for RedundantAsyncBlock { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if expr.span.from_expansion() { + return; + } + if let ExprKind::Async(_, _, block) = &expr.kind && block.stmts.len() == 1 && + let Some(Stmt { kind: StmtKind::Expr(last), .. }) = block.stmts.last() && + let ExprKind::Await(future) = &last.kind && + !future.span.from_expansion() && + !await_in_expr(future) + { + span_lint_and_sugg( + cx, + REDUNDANT_ASYNC_BLOCK, + expr.span, + "this async expression only awaits a single future", + "you can reduce it to", + snippet(cx, future.span, "..").into_owned(), + Applicability::MachineApplicable, + ); + } + } +} + +/// Check whether an expression contains `.await` +fn await_in_expr(expr: &Expr) -> bool { + let mut detector = AwaitDetector::default(); + detector.visit_expr(expr); + detector.await_found +} + +#[derive(Default)] +struct AwaitDetector { + await_found: bool, +} + +impl<'ast> AstVisitor<'ast> for AwaitDetector { + fn visit_expr(&mut self, ex: &'ast Expr) { + match (&ex.kind, self.await_found) { + (ExprKind::Await(_), _) => self.await_found = true, + (_, false) => rustc_ast::visit::walk_expr(self, ex), + _ => (), + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/size_of_ref.rs b/src/tools/clippy/clippy_lints/src/size_of_ref.rs index 3fcdb4288ce..8abec06c641 100644 --- a/src/tools/clippy/clippy_lints/src/size_of_ref.rs +++ b/src/tools/clippy/clippy_lints/src/size_of_ref.rs @@ -45,7 +45,7 @@ declare_clippy_lint! { /// } /// } /// ``` - #[clippy::version = "1.67.0"] + #[clippy::version = "1.68.0"] pub SIZE_OF_REF, suspicious, "Argument to `std::mem::size_of_val()` is a double-reference, which is almost certainly unintended" diff --git a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs index e111c7d2291..8aa47b62ebf 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs @@ -596,8 +596,7 @@ fn ident_difference_expr_with_base_location( | (MethodCall(_), MethodCall(_)) | (Call(_, _), Call(_, _)) | (ConstBlock(_), ConstBlock(_)) - | (Array(_), Array(_)) - | (Box(_), Box(_)) => { + | (Array(_), Array(_)) => { // keep going }, _ => { diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs index 0f062cecf88..1aeac724ab1 100644 --- a/src/tools/clippy/clippy_lints/src/swap.rs +++ b/src/tools/clippy/clippy_lints/src/swap.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{can_mut_borrow_both, eq_expr_value, in_constant, std_or_core}; @@ -10,6 +10,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Spanned; +use rustc_span::SyntaxContext; use rustc_span::{sym, symbol::Ident, Span}; declare_clippy_lint! { @@ -80,43 +81,45 @@ impl<'tcx> LateLintPass<'tcx> for Swap { } fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, span: Span, is_xor_based: bool) { + let ctxt = span.ctxt(); let mut applicability = Applicability::MachineApplicable; if !can_mut_borrow_both(cx, e1, e2) { - if let ExprKind::Index(lhs1, idx1) = e1.kind { - if let ExprKind::Index(lhs2, idx2) = e2.kind { - if eq_expr_value(cx, lhs1, lhs2) { - let ty = cx.typeck_results().expr_ty(lhs1).peel_refs(); + if let ExprKind::Index(lhs1, idx1) = e1.kind + && let ExprKind::Index(lhs2, idx2) = e2.kind + && eq_expr_value(cx, lhs1, lhs2) + && e1.span.ctxt() == ctxt + && e2.span.ctxt() == ctxt + { + let ty = cx.typeck_results().expr_ty(lhs1).peel_refs(); - if matches!(ty.kind(), ty::Slice(_)) - || matches!(ty.kind(), ty::Array(_, _)) - || is_type_diagnostic_item(cx, ty, sym::Vec) - || is_type_diagnostic_item(cx, ty, sym::VecDeque) - { - let slice = Sugg::hir_with_applicability(cx, lhs1, "", &mut applicability); - span_lint_and_sugg( - cx, - MANUAL_SWAP, - span, - &format!("this looks like you are swapping elements of `{slice}` manually"), - "try", - format!( - "{}.swap({}, {})", - slice.maybe_par(), - snippet_with_applicability(cx, idx1.span, "..", &mut applicability), - snippet_with_applicability(cx, idx2.span, "..", &mut applicability), - ), - applicability, - ); - } - } + if matches!(ty.kind(), ty::Slice(_)) + || matches!(ty.kind(), ty::Array(_, _)) + || is_type_diagnostic_item(cx, ty, sym::Vec) + || is_type_diagnostic_item(cx, ty, sym::VecDeque) + { + let slice = Sugg::hir_with_applicability(cx, lhs1, "", &mut applicability); + span_lint_and_sugg( + cx, + MANUAL_SWAP, + span, + &format!("this looks like you are swapping elements of `{slice}` manually"), + "try", + format!( + "{}.swap({}, {});", + slice.maybe_par(), + snippet_with_context(cx, idx1.span, ctxt, "..", &mut applicability).0, + snippet_with_context(cx, idx2.span, ctxt, "..", &mut applicability).0, + ), + applicability, + ); } } return; } - let first = Sugg::hir_with_applicability(cx, e1, "..", &mut applicability); - let second = Sugg::hir_with_applicability(cx, e2, "..", &mut applicability); + let first = Sugg::hir_with_context(cx, e1, ctxt, "..", &mut applicability); + let second = Sugg::hir_with_context(cx, e2, ctxt, "..", &mut applicability); let Some(sugg) = std_or_core(cx) else { return }; span_lint_and_then( @@ -128,7 +131,7 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa diag.span_suggestion( span, "try", - format!("{sugg}::mem::swap({}, {})", first.mut_addr(), second.mut_addr()), + format!("{sugg}::mem::swap({}, {});", first.mut_addr(), second.mut_addr()), applicability, ); if !is_xor_based { @@ -144,19 +147,19 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { return; } - for w in block.stmts.windows(3) { + for [s1, s2, s3] in block.stmts.array_windows::<3>() { if_chain! { // let t = foo(); - if let StmtKind::Local(tmp) = w[0].kind; + if let StmtKind::Local(tmp) = s1.kind; if let Some(tmp_init) = tmp.init; if let PatKind::Binding(.., ident, None) = tmp.pat.kind; // foo() = bar(); - if let StmtKind::Semi(first) = w[1].kind; + if let StmtKind::Semi(first) = s2.kind; if let ExprKind::Assign(lhs1, rhs1, _) = first.kind; // bar() = t; - if let StmtKind::Semi(second) = w[2].kind; + if let StmtKind::Semi(second) = s3.kind; if let ExprKind::Assign(lhs2, rhs2, _) = second.kind; if let ExprKind::Path(QPath::Resolved(None, rhs2)) = rhs2.kind; if rhs2.segments.len() == 1; @@ -164,8 +167,15 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { if ident.name == rhs2.segments[0].ident.name; if eq_expr_value(cx, tmp_init, lhs1); if eq_expr_value(cx, rhs1, lhs2); + + let ctxt = s1.span.ctxt(); + if s2.span.ctxt() == ctxt; + if s3.span.ctxt() == ctxt; + if first.span.ctxt() == ctxt; + if second.span.ctxt() == ctxt; + then { - let span = w[0].span.to(second.span); + let span = s1.span.to(s3.span); generate_swap_warning(cx, lhs1, lhs2, span, false); } } @@ -246,17 +256,20 @@ fn parse<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(ExprOrIdent<'hir>, &'a Expr< /// Implementation of the xor case for `MANUAL_SWAP` lint. fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) { - for window in block.stmts.windows(3) { + for [s1, s2, s3] in block.stmts.array_windows::<3>() { if_chain! { - if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(&window[0]); - if let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(&window[1]); - if let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(&window[2]); + let ctxt = s1.span.ctxt(); + if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(s1, ctxt); + if let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(s2, ctxt); + if let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(s3, ctxt); if eq_expr_value(cx, lhs0, rhs1); if eq_expr_value(cx, lhs2, rhs1); if eq_expr_value(cx, lhs1, rhs0); if eq_expr_value(cx, lhs1, rhs2); + if s2.span.ctxt() == ctxt; + if s3.span.ctxt() == ctxt; then { - let span = window[0].span.to(window[2].span); + let span = s1.span.to(s3.span); generate_swap_warning(cx, lhs0, rhs0, span, true); } }; @@ -264,9 +277,12 @@ fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) { } /// Returns the lhs and rhs of an xor assignment statement. -fn extract_sides_of_xor_assign<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(&'a Expr<'hir>, &'a Expr<'hir>)> { - if let StmtKind::Semi(expr) = stmt.kind { - if let ExprKind::AssignOp( +fn extract_sides_of_xor_assign<'a, 'hir>( + stmt: &'a Stmt<'hir>, + ctxt: SyntaxContext, +) -> Option<(&'a Expr<'hir>, &'a Expr<'hir>)> { + if let StmtKind::Semi(expr) = stmt.kind + && let ExprKind::AssignOp( Spanned { node: BinOpKind::BitXor, .. @@ -274,9 +290,10 @@ fn extract_sides_of_xor_assign<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(&'a Ex lhs, rhs, ) = expr.kind - { - return Some((lhs, rhs)); - } + && expr.span.ctxt() == ctxt + { + Some((lhs, rhs)) + } else { + None } - None } diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs index c01cbe5090f..0dc30f7a935 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs @@ -458,7 +458,7 @@ declare_clippy_lint! { /// ```rust /// let null_fn: Option = None; /// ``` - #[clippy::version = "1.67.0"] + #[clippy::version = "1.68.0"] pub TRANSMUTE_NULL_TO_FN, correctness, "transmute results in a null function pointer, which is undefined behavior" diff --git a/src/tools/clippy/clippy_lints/src/transmute/utils.rs b/src/tools/clippy/clippy_lints/src/transmute/utils.rs index cddaf9450ea..62efd13b8d9 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/utils.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/utils.rs @@ -33,38 +33,37 @@ pub(super) fn check_cast<'tcx>( let hir_id = e.hir_id; let local_def_id = hir_id.owner.def_id; - Inherited::build(cx.tcx, local_def_id).enter(|inherited| { - let fn_ctxt = FnCtxt::new(inherited, cx.param_env, local_def_id); + let inherited = Inherited::new(cx.tcx, local_def_id); + let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, local_def_id); - // If we already have errors, we can't be sure we can pointer cast. + // If we already have errors, we can't be sure we can pointer cast. + assert!( + !fn_ctxt.errors_reported_since_creation(), + "Newly created FnCtxt contained errors" + ); + + if let Ok(check) = cast::CastCheck::new( + &fn_ctxt, + e, + from_ty, + to_ty, + // We won't show any error to the user, so we don't care what the span is here. + DUMMY_SP, + DUMMY_SP, + hir::Constness::NotConst, + ) { + let res = check.do_check(&fn_ctxt); + + // do_check's documentation says that it might return Ok and create + // errors in the fcx instead of returning Err in some cases. Those cases + // should be filtered out before getting here. assert!( !fn_ctxt.errors_reported_since_creation(), - "Newly created FnCtxt contained errors" + "`fn_ctxt` contained errors after cast check!" ); - if let Ok(check) = cast::CastCheck::new( - &fn_ctxt, - e, - from_ty, - to_ty, - // We won't show any error to the user, so we don't care what the span is here. - DUMMY_SP, - DUMMY_SP, - hir::Constness::NotConst, - ) { - let res = check.do_check(&fn_ctxt); - - // do_check's documentation says that it might return Ok and create - // errors in the fcx instead of returning Err in some cases. Those cases - // should be filtered out before getting here. - assert!( - !fn_ctxt.errors_reported_since_creation(), - "`fn_ctxt` contained errors after cast check!" - ); - - res.ok() - } else { - None - } - }) + res.ok() + } else { + None + } } diff --git a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs index d6167a62169..3430b6e3734 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs @@ -5,7 +5,7 @@ use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Local, Node, PatKind, QPath, TyKind}; +use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Local, MatchSource, Node, PatKind, QPath, TyKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; @@ -41,6 +41,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { ); } } else { + if let ExprKind::Match(_, _, MatchSource::AwaitDesugar) = init.kind { + return + } + span_lint_and_then( cx, LET_UNIT_VALUE, diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index e7c54000684..8ea5475fb25 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -10,8 +10,8 @@ use rustc_hir::{ def::{CtorOf, DefKind, Res}, def_id::LocalDefId, intravisit::{walk_inf, walk_ty, Visitor}, - Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, - TyKind, + Expr, ExprKind, FnRetTy, FnSig, GenericArg, GenericParam, GenericParamKind, HirId, Impl, ImplItemKind, Item, + ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind, }; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; @@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that // we're in an `impl` or nested item, that we don't want to lint let stack_item = if_chain! { - if let ItemKind::Impl(Impl { self_ty, .. }) = item.kind; + if let ItemKind::Impl(Impl { self_ty, generics,.. }) = item.kind; if let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind; let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args; if parameters.as_ref().map_or(true, |params| { @@ -105,10 +105,17 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { if !item.span.from_expansion(); if !is_from_proc_macro(cx, item); // expensive, should be last check then { + // Self cannot be used inside const generic parameters + let types_to_skip = generics.params.iter().filter_map(|param| { + match param { + GenericParam { kind: GenericParamKind::Const { ty: Ty { hir_id, ..}, ..}, ..} => Some(*hir_id), + _ => None, + } + }).chain(std::iter::once(self_ty.hir_id)).collect(); StackItem::Check { impl_id: item.owner_id.def_id, in_body: 0, - types_to_skip: std::iter::once(self_ty.hir_id).collect(), + types_to_skip, } } else { StackItem::NoCheck diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index c37e5bb6716..f31c3fdb095 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -588,7 +588,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { }, } }, - ExprKind::Err(_) => kind!("Err"), + ExprKind::Err(_) => kind!("Err(_)"), ExprKind::DropTemps(expr) => { bind!(self, expr); kind!("DropTemps({expr})"); diff --git a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs new file mode 100644 index 00000000000..be56b842b98 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs @@ -0,0 +1,23 @@ +use clippy_utils::macros::collect_ast_format_args; +use rustc_ast::{Expr, ExprKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Collects [`rustc_ast::FormatArgs`] so that future late passes can call + /// [`clippy_utils::macros::find_format_args`] + pub FORMAT_ARGS_COLLECTOR, + internal_warn, + "collects `format_args` AST nodes for use in later lints" +} + +declare_lint_pass!(FormatArgsCollector => [FORMAT_ARGS_COLLECTOR]); + +impl EarlyLintPass for FormatArgsCollector { + fn check_expr(&mut self, _: &EarlyContext<'_>, expr: &Expr) { + if let ExprKind::FormatArgs(args) = &expr.kind { + collect_ast_format_args(expr.span, args); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs index b1b5164ffb3..3d0d4a52511 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -26,7 +26,7 @@ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::Ident; use rustc_span::{sym, Loc, Span, Symbol}; use serde::{ser::SerializeStruct, Serialize, Serializer}; -use std::collections::BinaryHeap; +use std::collections::{BTreeSet, BinaryHeap}; use std::fmt; use std::fmt::Write as _; use std::fs::{self, OpenOptions}; @@ -264,6 +264,9 @@ struct LintMetadata { /// This field is only used in the output and will only be /// mapped shortly before the actual output. applicability: Option, + /// All the past names of lints which have been renamed. + #[serde(skip_serializing_if = "BTreeSet::is_empty")] + former_ids: BTreeSet, } impl LintMetadata { @@ -283,6 +286,7 @@ impl LintMetadata { version, docs, applicability: None, + former_ids: BTreeSet::new(), } } } @@ -901,6 +905,7 @@ fn collect_renames(lints: &mut Vec) { if name == lint_name; if let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX); then { + lint.former_ids.insert(past_name.to_owned()); writeln!(collected, "* `{past_name}`").unwrap(); names.push(past_name.to_string()); } diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs index 787e9fd982c..dc647af264c 100644 --- a/src/tools/clippy/clippy_lints/src/utils/mod.rs +++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs @@ -1,5 +1,6 @@ pub mod author; pub mod conf; pub mod dump_hir; +pub mod format_args_collector; #[cfg(feature = "internal")] pub mod internal_lints; diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index df335038881..8114a8463fa 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn, MacroCall}; +use clippy_utils::macros::{find_format_args, format_arg_removal_span, root_macro_call_first_node, MacroCall}; use clippy_utils::source::{expand_past_previous_comma, snippet_opt}; use clippy_utils::{is_in_cfg_test, is_in_test_function}; -use rustc_ast::LitKind; +use rustc_ast::token::LitKind; +use rustc_ast::{FormatArgPosition, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder, FormatTrait}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, HirIdMap, Impl, Item, ItemKind}; +use rustc_hir::{Expr, Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, BytePos}; @@ -297,34 +298,40 @@ impl<'tcx> LateLintPass<'tcx> for Write { _ => return, } - let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, macro_call.expn) else { return }; + find_format_args(cx, expr, macro_call.expn, |format_args| { + // ignore `writeln!(w)` and `write!(v, some_macro!())` + if format_args.span.from_expansion() { + return; + } - // ignore `writeln!(w)` and `write!(v, some_macro!())` - if format_args.format_string.span.from_expansion() { - return; - } + match diag_name { + sym::print_macro | sym::eprint_macro | sym::write_macro => { + check_newline(cx, format_args, ¯o_call, name); + }, + sym::println_macro | sym::eprintln_macro | sym::writeln_macro => { + check_empty_string(cx, format_args, ¯o_call, name); + }, + _ => {}, + } - match diag_name { - sym::print_macro | sym::eprint_macro | sym::write_macro => { - check_newline(cx, &format_args, ¯o_call, name); - }, - sym::println_macro | sym::eprintln_macro | sym::writeln_macro => { - check_empty_string(cx, &format_args, ¯o_call, name); - }, - _ => {}, - } + check_literal(cx, format_args, name); - check_literal(cx, &format_args, name); - - if !self.in_debug_impl { - for arg in &format_args.args { - if arg.format.r#trait == sym::Debug { - span_lint(cx, USE_DEBUG, arg.span, "use of `Debug`-based formatting"); + if !self.in_debug_impl { + for piece in &format_args.template { + if let &FormatArgsPiece::Placeholder(FormatPlaceholder { + span: Some(span), + format_trait: FormatTrait::Debug, + .. + }) = piece + { + span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting"); + } } } - } + }); } } + fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool { if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), .. }) = &item.kind && let Some(trait_id) = trait_ref.trait_def_id() @@ -335,16 +342,18 @@ fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool { } } -fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_call: &MacroCall, name: &str) { - let format_string_parts = &format_args.format_string.parts; - let mut format_string_span = format_args.format_string.span; - - let Some(last) = format_string_parts.last() else { return }; +fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) { + let Some(FormatArgsPiece::Literal(last)) = format_args.template.last() else { return }; let count_vertical_whitespace = || { - format_string_parts + format_args + .template .iter() - .flat_map(|part| part.as_str().chars()) + .filter_map(|piece| match piece { + FormatArgsPiece::Literal(literal) => Some(literal), + FormatArgsPiece::Placeholder(_) => None, + }) + .flat_map(|literal| literal.as_str().chars()) .filter(|ch| matches!(ch, '\r' | '\n')) .count() }; @@ -352,10 +361,9 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c if last.as_str().ends_with('\n') // ignore format strings with other internal vertical whitespace && count_vertical_whitespace() == 1 - - // ignore trailing arguments: `print!("Issue\n{}", 1265);` - && format_string_parts.len() > format_args.args.len() { + let mut format_string_span = format_args.span; + let lint = if name == "write" { format_string_span = expand_past_previous_comma(cx, format_string_span); @@ -373,7 +381,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!'); let Some(format_snippet) = snippet_opt(cx, format_string_span) else { return }; - if format_string_parts.len() == 1 && last.as_str() == "\n" { + if format_args.template.len() == 1 && last.as_str() == "\n" { // print!("\n"), write!(f, "\n") diag.multipart_suggestion( @@ -398,11 +406,12 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c } } -fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_call: &MacroCall, name: &str) { - if let [part] = &format_args.format_string.parts[..] - && let mut span = format_args.format_string.span - && part.as_str() == "\n" +fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) { + if let [FormatArgsPiece::Literal(literal)] = &format_args.template[..] + && literal.as_str() == "\n" { + let mut span = format_args.span; + let lint = if name == "writeln" { span = expand_past_previous_comma(cx, span); @@ -428,33 +437,43 @@ fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, ma } } -fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: &str) { - let mut counts = HirIdMap::::default(); - for param in format_args.params() { - *counts.entry(param.value.hir_id).or_default() += 1; +fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { + let arg_index = |argument: &FormatArgPosition| argument.index.unwrap_or_else(|pos| pos); + + let mut counts = vec![0u32; format_args.arguments.all_args().len()]; + for piece in &format_args.template { + if let FormatArgsPiece::Placeholder(placeholder) = piece { + counts[arg_index(&placeholder.argument)] += 1; + } } - for arg in &format_args.args { - let value = arg.param.value; - - if counts[&value.hir_id] == 1 - && arg.format.is_default() - && let ExprKind::Lit(lit) = &value.kind - && !value.span.from_expansion() - && let Some(value_string) = snippet_opt(cx, value.span) - { - let (replacement, replace_raw) = match lit.node { - LitKind::Str(..) => extract_str_literal(&value_string), - LitKind::Char(ch) => ( - match ch { - '"' => "\\\"", - '\'' => "'", + for piece in &format_args.template { + if let FormatArgsPiece::Placeholder(FormatPlaceholder { + argument, + span: Some(placeholder_span), + format_trait: FormatTrait::Display, + format_options, + }) = piece + && *format_options == FormatOptions::default() + && let index = arg_index(argument) + && counts[index] == 1 + && let Some(arg) = format_args.arguments.by_index(index) + && let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind + && !arg.expr.span.from_expansion() + && let Some(value_string) = snippet_opt(cx, arg.expr.span) + { + let (replacement, replace_raw) = match lit.kind { + LitKind::Str | LitKind::StrRaw(_) => extract_str_literal(&value_string), + LitKind::Char => ( + match lit.symbol.as_str() { + "\"" => "\\\"", + "\\'" => "'", _ => &value_string[1..value_string.len() - 1], } .to_string(), false, ), - LitKind::Bool(b) => (b.to_string(), false), + LitKind::Bool => (lit.symbol.to_string(), false), _ => continue, }; @@ -464,7 +483,9 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: & PRINT_LITERAL }; - let format_string_is_raw = format_args.format_string.style.is_some(); + let Some(format_string_snippet) = snippet_opt(cx, format_args.span) else { continue }; + let format_string_is_raw = format_string_snippet.starts_with('r'); + let replacement = match (format_string_is_raw, replace_raw) { (false, false) => Some(replacement), (false, true) => Some(replacement.replace('"', "\\\"").replace('\\', "\\\\")), @@ -485,23 +506,24 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: & span_lint_and_then( cx, lint, - value.span, + arg.expr.span, "literal with an empty format string", |diag| { if let Some(replacement) = replacement // `format!("{}", "a")`, `format!("{named}", named = "b") // ~~~~~ ~~~~~~~~~~~~~ - && let Some(value_span) = format_args.value_with_prev_comma_span(value.hir_id) + && let Some(removal_span) = format_arg_removal_span(format_args, index) { let replacement = replacement.replace('{', "{{").replace('}', "}}"); diag.multipart_suggestion( "try this", - vec![(arg.span, replacement), (value_span, String::new())], + vec![(*placeholder_span, replacement), (removal_span, String::new())], Applicability::MachineApplicable, ); } }, ); + } } } diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index 173469f6cdc..124ebd164e6 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.69" +version = "0.1.70" edition = "2021" publish = false diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index d82098523e3..809d654603a 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -143,7 +143,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (Paren(l), _) => eq_expr(l, r), (_, Paren(r)) => eq_expr(l, r), (Err, Err) => true, - (Box(l), Box(r)) | (Try(l), Try(r)) | (Await(l), Await(r)) => eq_expr(l, r), + (Try(l), Try(r)) | (Await(l), Await(r)) => eq_expr(l, r), (Array(l), Array(r)) => over(l, r, |l, r| eq_expr(l, r)), (Tup(l), Tup(r)) => over(l, r, |l, r| eq_expr(l, r)), (Repeat(le, ls), Repeat(re, rs)) => eq_expr(le, re) && eq_expr(&ls.value, &rs.value), diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index bcfedd07ed1..29830557a44 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -617,7 +617,7 @@ fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec) -> bool { /// Peels away all the compiler generated code surrounding the body of an async function, pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> { - if let ExprKind::Call( - _, - &[ - Expr { - kind: ExprKind::Closure(&Closure { body, .. }), - .. - }, - ], - ) = body.value.kind - { + if let ExprKind::Closure(&Closure { body, .. }) = body.value.kind { if let ExprKind::Block( Block { stmts: [], diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs index be6133d3202..e135bd9feee 100644 --- a/src/tools/clippy/clippy_utils/src/macros.rs +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -6,6 +6,8 @@ use crate::visitors::{for_each_expr, Descend}; use arrayvec::ArrayVec; use itertools::{izip, Either, Itertools}; use rustc_ast::ast::LitKind; +use rustc_ast::FormatArgs; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{self as hir, Expr, ExprField, ExprKind, HirId, LangItem, Node, QPath, TyKind}; use rustc_lexer::unescape::unescape_literal; @@ -15,8 +17,10 @@ use rustc_parse_format::{self as rpf, Alignment}; use rustc_span::def_id::DefId; use rustc_span::hygiene::{self, MacroKind, SyntaxContext}; use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Pos, Span, SpanData, Symbol}; +use std::cell::RefCell; use std::iter::{once, zip}; use std::ops::ControlFlow; +use std::sync::atomic::{AtomicBool, Ordering}; const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[ sym::assert_eq_macro, @@ -213,6 +217,7 @@ pub fn is_assert_macro(cx: &LateContext<'_>, def_id: DefId) -> bool { matches!(name, sym::assert_macro | sym::debug_assert_macro) } +#[derive(Debug)] pub enum PanicExpn<'a> { /// No arguments - `panic!()` Empty, @@ -226,10 +231,7 @@ pub enum PanicExpn<'a> { impl<'a> PanicExpn<'a> { pub fn parse(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option { - if !macro_backtrace(expr.span).any(|macro_call| is_panic(cx, macro_call.def_id)) { - return None; - } - let ExprKind::Call(callee, [arg]) = &expr.kind else { return None }; + let ExprKind::Call(callee, [arg, rest @ ..]) = &expr.kind else { return None }; let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else { return None }; let result = match path.segments.last().unwrap().ident.as_str() { "panic" if arg.span.ctxt() == expr.span.ctxt() => Self::Empty, @@ -239,6 +241,21 @@ impl<'a> PanicExpn<'a> { Self::Display(e) }, "panic_fmt" => Self::Format(FormatArgsExpn::parse(cx, arg)?), + // Since Rust 1.52, `assert_{eq,ne}` macros expand to use: + // `core::panicking::assert_failed(.., left_val, right_val, None | Some(format_args!(..)));` + "assert_failed" => { + // It should have 4 arguments in total (we already matched with the first argument, + // so we're just checking for 3) + if rest.len() != 3 { + return None; + } + // `msg_arg` is either `None` (no custom message) or `Some(format_args!(..))` (custom message) + let msg_arg = &rest[2]; + match msg_arg.kind { + ExprKind::Call(_, [fmt_arg]) => Self::Format(FormatArgsExpn::parse(cx, fmt_arg)?), + _ => Self::Empty, + } + }, _ => return None, }; Some(result) @@ -251,7 +268,17 @@ pub fn find_assert_args<'a>( expr: &'a Expr<'a>, expn: ExpnId, ) -> Option<(&'a Expr<'a>, PanicExpn<'a>)> { - find_assert_args_inner(cx, expr, expn).map(|([e], p)| (e, p)) + find_assert_args_inner(cx, expr, expn).map(|([e], mut p)| { + // `assert!(..)` expands to `core::panicking::panic("assertion failed: ...")` (which we map to + // `PanicExpn::Str(..)`) and `assert!(.., "..")` expands to + // `core::panicking::panic_fmt(format_args!(".."))` (which we map to `PanicExpn::Format(..)`). + // So even we got `PanicExpn::Str(..)` that means there is no custom message provided + if let PanicExpn::Str(_) = p { + p = PanicExpn::Empty; + } + + (e, p) + }) } /// Finds the arguments of an `assert_eq!` or `debug_assert_eq!` macro call within the macro @@ -275,13 +302,12 @@ fn find_assert_args_inner<'a, const N: usize>( Some(inner_name) => find_assert_within_debug_assert(cx, expr, expn, Symbol::intern(inner_name))?, }; let mut args = ArrayVec::new(); - let mut panic_expn = None; - let _: Option = for_each_expr(expr, |e| { + let panic_expn = for_each_expr(expr, |e| { if args.is_full() { - if panic_expn.is_none() && e.span.ctxt() != expr.span.ctxt() { - panic_expn = PanicExpn::parse(cx, e); + match PanicExpn::parse(cx, e) { + Some(expn) => ControlFlow::Break(expn), + None => ControlFlow::Continue(Descend::Yes), } - ControlFlow::Continue(Descend::from(panic_expn.is_none())) } else if is_assert_arg(cx, e, expn) { args.push(e); ControlFlow::Continue(Descend::No) @@ -339,6 +365,77 @@ fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) -> } } +thread_local! { + /// We preserve the [`FormatArgs`] structs from the early pass for use in the late pass to be + /// able to access the many features of a [`LateContext`]. + /// + /// A thread local is used because [`FormatArgs`] is `!Send` and `!Sync`, we are making an + /// assumption that the early pass the populates the map and the later late passes will all be + /// running on the same thread. + static AST_FORMAT_ARGS: RefCell> = { + static CALLED: AtomicBool = AtomicBool::new(false); + debug_assert!( + !CALLED.swap(true, Ordering::SeqCst), + "incorrect assumption: `AST_FORMAT_ARGS` should only be accessed by a single thread", + ); + + RefCell::default() + }; +} + +/// Record [`rustc_ast::FormatArgs`] for use in late lint passes, this should only be called by +/// `FormatArgsCollector` +pub fn collect_ast_format_args(span: Span, format_args: &FormatArgs) { + AST_FORMAT_ARGS.with(|ast_format_args| { + ast_format_args.borrow_mut().insert(span, format_args.clone()); + }); +} + +/// Calls `callback` with an AST [`FormatArgs`] node if one is found +pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId, callback: impl FnOnce(&FormatArgs)) { + let format_args_expr = for_each_expr(start, |expr| { + let ctxt = expr.span.ctxt(); + if ctxt == start.span.ctxt() { + ControlFlow::Continue(Descend::Yes) + } else if ctxt.outer_expn().is_descendant_of(expn_id) + && macro_backtrace(expr.span) + .map(|macro_call| cx.tcx.item_name(macro_call.def_id)) + .any(|name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl)) + { + ControlFlow::Break(expr) + } else { + ControlFlow::Continue(Descend::No) + } + }); + + if let Some(format_args_expr) = format_args_expr { + AST_FORMAT_ARGS.with(|ast_format_args| { + ast_format_args.borrow().get(&format_args_expr.span).map(callback); + }); + } +} + +/// Returns the [`Span`] of the value at `index` extended to the previous comma, e.g. for the value +/// `10` +/// +/// ```ignore +/// format("{}.{}", 10, 11) +/// // ^^^^ +/// ``` +pub fn format_arg_removal_span(format_args: &FormatArgs, index: usize) -> Option { + let ctxt = format_args.span.ctxt(); + + let current = hygiene::walk_chain(format_args.arguments.by_index(index)?.expr.span, ctxt); + + let prev = if index == 0 { + format_args.span + } else { + hygiene::walk_chain(format_args.arguments.by_index(index - 1)?.expr.span, ctxt) + }; + + Some(current.with_lo(prev.hi())) +} + /// The format string doesn't exist in the HIR, so we reassemble it from source code #[derive(Debug)] pub struct FormatString { diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index 07feadca2b0..85bf28b708b 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -188,7 +188,6 @@ impl<'a> Sugg<'a> { match expr.kind { _ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0), ast::ExprKind::AddrOf(..) - | ast::ExprKind::Box(..) | ast::ExprKind::Closure { .. } | ast::ExprKind::If(..) | ast::ExprKind::Let(..) diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index 41e34eba0ad..e0ea3952785 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -16,9 +16,9 @@ use rustc_infer::infer::{ use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::ty::{ - self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, IntTy, List, ParamEnv, Predicate, - PredicateKind, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, - TypeVisitor, UintTy, VariantDef, VariantDiscr, + self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, IntTy, List, ParamEnv, Predicate, PredicateKind, + Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, + UintTy, VariantDef, VariantDiscr, }; use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_span::symbol::Ident; diff --git a/src/tools/clippy/declare_clippy_lint/Cargo.toml b/src/tools/clippy/declare_clippy_lint/Cargo.toml index 80eee368178..5c9f76dbbc6 100644 --- a/src/tools/clippy/declare_clippy_lint/Cargo.toml +++ b/src/tools/clippy/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.69" +version = "0.1.70" edition = "2021" publish = false diff --git a/src/tools/clippy/lintcheck/Cargo.toml b/src/tools/clippy/lintcheck/Cargo.toml index 653121af54d..27d32f39003 100644 --- a/src/tools/clippy/lintcheck/Cargo.toml +++ b/src/tools/clippy/lintcheck/Cargo.toml @@ -8,12 +8,16 @@ repository = "https://github.com/rust-lang/rust-clippy" categories = ["development-tools"] edition = "2021" publish = false +default-run = "lintcheck" [dependencies] +anyhow = "1.0.69" cargo_metadata = "0.15.3" -clap = "4.1.4" +clap = { version = "4.1.8", features = ["derive", "env"] } +crates_io_api = "0.8.1" crossbeam-channel = "0.5.6" flate2 = "1.0" +indicatif = "0.17.3" rayon = "1.5.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.85" @@ -24,3 +28,11 @@ walkdir = "2.3" [features] deny-warnings = [] + +[[bin]] +name = "lintcheck" +path = "src/main.rs" + +[[bin]] +name = "popular-crates" +path = "src/popular-crates.rs" diff --git a/src/tools/clippy/lintcheck/README.md b/src/tools/clippy/lintcheck/README.md index 6142de5e313..e997eb47e32 100644 --- a/src/tools/clippy/lintcheck/README.md +++ b/src/tools/clippy/lintcheck/README.md @@ -25,6 +25,15 @@ the repo root. The results will then be saved to `lintcheck-logs/custom_logs.toml`. +The `custom.toml` file may be built using recently most +downloaded crates by using the `popular-crates` binary from the `lintcheck` +directory. For example, to retrieve the 100 recently most downloaded crates: + +``` +cargo run --release --bin popular-crates -- -n 100 custom.toml +``` + + ### Configuring the Crate Sources The sources to check are saved in a `toml` file. There are three types of diff --git a/src/tools/clippy/lintcheck/src/config.rs b/src/tools/clippy/lintcheck/src/config.rs index e0244ddcecb..3f01e9bb0a7 100644 --- a/src/tools/clippy/lintcheck/src/config.rs +++ b/src/tools/clippy/lintcheck/src/config.rs @@ -1,131 +1,79 @@ -use clap::{Arg, ArgAction, ArgMatches, Command}; -use std::env; -use std::path::PathBuf; +use clap::Parser; +use std::{num::NonZeroUsize, path::PathBuf}; -fn get_clap_config() -> ArgMatches { - Command::new("lintcheck") - .about("run clippy on a set of crates and check output") - .args([ - Arg::new("only") - .action(ArgAction::Set) - .value_name("CRATE") - .long("only") - .help("Only process a single crate of the list"), - Arg::new("crates-toml") - .action(ArgAction::Set) - .value_name("CRATES-SOURCES-TOML-PATH") - .long("crates-toml") - .help("Set the path for a crates.toml where lintcheck should read the sources from"), - Arg::new("threads") - .action(ArgAction::Set) - .value_name("N") - .value_parser(clap::value_parser!(usize)) - .short('j') - .long("jobs") - .help("Number of threads to use, 0 automatic choice"), - Arg::new("fix") - .long("fix") - .help("Runs cargo clippy --fix and checks if all suggestions apply"), - Arg::new("filter") - .long("filter") - .action(ArgAction::Append) - .value_name("clippy_lint_name") - .help("Apply a filter to only collect specified lints, this also overrides `allow` attributes"), - Arg::new("markdown") - .long("markdown") - .help("Change the reports table to use markdown links"), - Arg::new("recursive") - .long("recursive") - .help("Run clippy on the dependencies of crates specified in crates-toml") - .conflicts_with("threads") - .conflicts_with("fix"), - ]) - .get_matches() -} - -#[derive(Debug, Clone)] +#[derive(Clone, Debug, Parser)] pub(crate) struct LintcheckConfig { - /// max number of jobs to spawn (default 1) + /// Number of threads to use (default: all unless --fix or --recursive) + #[clap( + long = "jobs", + short = 'j', + value_name = "N", + default_value_t = 0, + hide_default_value = true + )] pub max_jobs: usize, - /// we read the sources to check from here + /// Set the path for a crates.toml where lintcheck should read the sources from + #[clap( + long = "crates-toml", + value_name = "CRATES-SOURCES-TOML-PATH", + default_value = "lintcheck/lintcheck_crates.toml", + hide_default_value = true, + env = "LINTCHECK_TOML", + hide_env = true + )] pub sources_toml_path: PathBuf, - /// we save the clippy lint results here - pub lintcheck_results_path: PathBuf, - /// Check only a specified package + /// File to save the clippy lint results here + #[clap(skip = "")] + pub lintcheck_results_path: PathBuf, // Overridden in new() + /// Only process a single crate on the list + #[clap(long, value_name = "CRATE")] pub only: Option, - /// whether to just run --fix and not collect all the warnings + /// Runs cargo clippy --fix and checks if all suggestions apply + #[clap(long, conflicts_with("max_jobs"))] pub fix: bool, - /// A list of lints that this lintcheck run should focus on + /// Apply a filter to only collect specified lints, this also overrides `allow` attributes + #[clap(long = "filter", value_name = "clippy_lint_name", use_value_delimiter = true)] pub lint_filter: Vec, - /// Indicate if the output should support markdown syntax + /// Change the reports table to use markdown links + #[clap(long)] pub markdown: bool, - /// Run clippy on the dependencies of crates + /// Run clippy on the dependencies of crates specified in crates-toml + #[clap(long, conflicts_with("max_jobs"))] pub recursive: bool, } impl LintcheckConfig { pub fn new() -> Self { - let clap_config = get_clap_config(); - - // first, check if we got anything passed via the LINTCHECK_TOML env var, - // if not, ask clap if we got any value for --crates-toml - // if not, use the default "lintcheck/lintcheck_crates.toml" - let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| { - clap_config - .get_one::("crates-toml") - .map_or("lintcheck/lintcheck_crates.toml", |s| &**s) - .into() - }); - - let markdown = clap_config.contains_id("markdown"); - let sources_toml_path = PathBuf::from(sources_toml); + let mut config = LintcheckConfig::parse(); // for the path where we save the lint results, get the filename without extension (so for // wasd.toml, use "wasd"...) - let filename: PathBuf = sources_toml_path.file_stem().unwrap().into(); - let lintcheck_results_path = PathBuf::from(format!( + let filename: PathBuf = config.sources_toml_path.file_stem().unwrap().into(); + config.lintcheck_results_path = PathBuf::from(format!( "lintcheck-logs/{}_logs.{}", filename.display(), - if markdown { "md" } else { "txt" } + if config.markdown { "md" } else { "txt" } )); - // look at the --threads arg, if 0 is passed, ask rayon rayon how many threads it would spawn and - // use half of that for the physical core count - // by default use a single thread - let max_jobs = match clap_config.get_one::("threads") { - Some(&0) => { - // automatic choice - // Rayon seems to return thread count so half that for core count - rayon::current_num_threads() / 2 - }, - Some(&threads) => threads, - // no -j passed, use a single thread - None => 1, + // look at the --threads arg, if 0 is passed, use the threads count + if config.max_jobs == 0 { + config.max_jobs = if config.fix || config.recursive { + 1 + } else { + std::thread::available_parallelism().map_or(1, NonZeroUsize::get) + }; }; - let lint_filter: Vec = clap_config - .get_many::("filter") - .map(|iter| { - iter.map(|lint_name| { - let mut filter = lint_name.replace('_', "-"); - if !filter.starts_with("clippy::") { - filter.insert_str(0, "clippy::"); - } - filter - }) - .collect() - }) - .unwrap_or_default(); - - LintcheckConfig { - max_jobs, - sources_toml_path, - lintcheck_results_path, - only: clap_config.get_one::("only").map(String::from), - fix: clap_config.contains_id("fix"), - lint_filter, - markdown, - recursive: clap_config.contains_id("recursive"), + for lint_name in &mut config.lint_filter { + *lint_name = format!( + "clippy::{}", + lint_name + .strip_prefix("clippy::") + .unwrap_or(lint_name) + .replace('_', "-") + ); } + + config } } diff --git a/src/tools/clippy/lintcheck/src/popular-crates.rs b/src/tools/clippy/lintcheck/src/popular-crates.rs new file mode 100644 index 00000000000..fdab984ad86 --- /dev/null +++ b/src/tools/clippy/lintcheck/src/popular-crates.rs @@ -0,0 +1,65 @@ +#![deny(clippy::pedantic)] + +use clap::Parser; +use crates_io_api::{CratesQueryBuilder, Sort, SyncClient}; +use indicatif::ProgressBar; +use std::collections::HashSet; +use std::fs::File; +use std::io::{BufWriter, Write}; +use std::path::PathBuf; +use std::time::Duration; + +#[derive(Parser)] +struct Opts { + /// Output TOML file name + output: PathBuf, + /// Number of crate names to download + #[clap(short, long, default_value_t = 100)] + number: usize, + /// Do not output progress + #[clap(short, long)] + quiet: bool, +} + +fn main() -> anyhow::Result<()> { + let opts = Opts::parse(); + let mut output = BufWriter::new(File::create(opts.output)?); + output.write_all(b"[crates]\n")?; + let client = SyncClient::new( + "clippy/lintcheck (github.com/rust-lang/rust-clippy/)", + Duration::from_secs(1), + )?; + let mut seen_crates = HashSet::new(); + let pb = if opts.quiet { + None + } else { + Some(ProgressBar::new(opts.number as u64)) + }; + let mut query = CratesQueryBuilder::new() + .sort(Sort::RecentDownloads) + .page_size(100) + .build(); + while seen_crates.len() < opts.number { + let retrieved = client.crates(query.clone())?.crates; + if retrieved.is_empty() { + eprintln!("No more than {} crates available from API", seen_crates.len()); + break; + } + for c in retrieved { + if seen_crates.insert(c.name.clone()) { + output.write_all( + format!( + "{} = {{ name = '{}', versions = ['{}'] }}\n", + c.name, c.name, c.max_version + ) + .as_bytes(), + )?; + if let Some(pb) = &pb { + pb.inc(1); + } + } + } + query.set_page(query.page() + 1); + } + Ok(()) +} diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index cfe845ec78f..d788c6359d7 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-02-25" +channel = "nightly-2023-03-10" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/tools/clippy/rustc_tools_util/README.md b/src/tools/clippy/rustc_tools_util/README.md index eefc661f963..e197ea048a0 100644 --- a/src/tools/clippy/rustc_tools_util/README.md +++ b/src/tools/clippy/rustc_tools_util/README.md @@ -49,6 +49,8 @@ The changelog for `rustc_tools_util` is available under: ## License + + Copyright 2014-2022 The Rust Project Developers Licensed under the Apache License, Version 2.0 or the MIT license , at your option. All files in the project carrying such notice may not be copied, modified, or distributed except according to those terms. + + diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index dd183362f27..f08393c303e 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -176,7 +176,7 @@ Common options: --rustc Pass all args to rustc -V, --version Print version info and exit -Other options are the same as `cargo check`. +For the other options see `cargo check --help`. To allow or deny a lint from the command line you can use `cargo clippy --` with: diff --git a/src/tools/clippy/src/main.rs b/src/tools/clippy/src/main.rs index 82147eba33f..c5e9b96cf3f 100644 --- a/src/tools/clippy/src/main.rs +++ b/src/tools/clippy/src/main.rs @@ -18,7 +18,7 @@ Common options: -V, --version Print version info and exit --explain LINT Print the documentation for a given lint -Other options are the same as `cargo check`. +For the other options see `cargo check --help`. To allow or deny a lint from the command line you can use `cargo clippy --` with: diff --git a/src/tools/clippy/tests/dogfood.rs b/src/tools/clippy/tests/dogfood.rs index 6d0022f7a5c..9643c2c9707 100644 --- a/src/tools/clippy/tests/dogfood.rs +++ b/src/tools/clippy/tests/dogfood.rs @@ -7,6 +7,7 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] +use itertools::Itertools; use std::path::PathBuf; use std::process::Command; use test_utils::IS_RUSTC_TEST_SUITE; @@ -19,8 +20,10 @@ fn dogfood_clippy() { return; } + let mut failed_packages = Vec::new(); + // "" is the root package - for package in &[ + for package in [ "", "clippy_dev", "clippy_lints", @@ -28,8 +31,16 @@ fn dogfood_clippy() { "lintcheck", "rustc_tools_util", ] { - run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]); + if !run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]) { + failed_packages.push(if package.is_empty() { "root" } else { package }); + } } + + assert!( + !failed_packages.is_empty(), + "Dogfood failed for packages `{}`", + failed_packages.iter().format(", "), + ) } #[test] @@ -71,7 +82,7 @@ fn run_metadata_collection_lint() { run_clippy_for_package("clippy_lints", &["-A", "unfulfilled_lint_expectations"]); } -fn run_clippy_for_package(project: &str, args: &[&str]) { +fn run_clippy_for_package(project: &str, args: &[&str]) -> bool { let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let mut command = Command::new(&*test_utils::CARGO_CLIPPY_PATH); @@ -107,5 +118,5 @@ fn run_clippy_for_package(project: &str, args: &[&str]) { println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - assert!(output.status.success()); + output.status.success() } diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs index 2611e3a785f..ee7d2ba444b 100644 --- a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs +++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs @@ -45,24 +45,32 @@ impl_arith!( Div, Custom, Custom, div; Mul, Custom, Custom, mul; Rem, Custom, Custom, rem; + Shl, Custom, Custom, shl; + Shr, Custom, Custom, shr; Sub, Custom, Custom, sub; Add, Custom, &Custom, add; Div, Custom, &Custom, div; Mul, Custom, &Custom, mul; Rem, Custom, &Custom, rem; + Shl, Custom, &Custom, shl; + Shr, Custom, &Custom, shr; Sub, Custom, &Custom, sub; Add, &Custom, Custom, add; Div, &Custom, Custom, div; Mul, &Custom, Custom, mul; Rem, &Custom, Custom, rem; + Shl, &Custom, Custom, shl; + Shr, &Custom, Custom, shr; Sub, &Custom, Custom, sub; Add, &Custom, &Custom, add; Div, &Custom, &Custom, div; Mul, &Custom, &Custom, mul; Rem, &Custom, &Custom, rem; + Shl, &Custom, &Custom, shl; + Shr, &Custom, &Custom, shr; Sub, &Custom, &Custom, sub; ); @@ -71,24 +79,32 @@ impl_assign_arith!( DivAssign, Custom, Custom, div_assign; MulAssign, Custom, Custom, mul_assign; RemAssign, Custom, Custom, rem_assign; + ShlAssign, Custom, Custom, shl_assign; + ShrAssign, Custom, Custom, shr_assign; SubAssign, Custom, Custom, sub_assign; AddAssign, Custom, &Custom, add_assign; DivAssign, Custom, &Custom, div_assign; MulAssign, Custom, &Custom, mul_assign; RemAssign, Custom, &Custom, rem_assign; + ShlAssign, Custom, &Custom, shl_assign; + ShrAssign, Custom, &Custom, shr_assign; SubAssign, Custom, &Custom, sub_assign; AddAssign, &Custom, Custom, add_assign; DivAssign, &Custom, Custom, div_assign; MulAssign, &Custom, Custom, mul_assign; RemAssign, &Custom, Custom, rem_assign; + ShlAssign, &Custom, Custom, shl_assign; + ShrAssign, &Custom, Custom, shr_assign; SubAssign, &Custom, Custom, sub_assign; AddAssign, &Custom, &Custom, add_assign; DivAssign, &Custom, &Custom, div_assign; MulAssign, &Custom, &Custom, mul_assign; RemAssign, &Custom, &Custom, rem_assign; + ShlAssign, &Custom, &Custom, shl_assign; + ShrAssign, &Custom, &Custom, shr_assign; SubAssign, &Custom, &Custom, sub_assign; ); @@ -297,6 +313,10 @@ pub fn unknown_ops_or_runtime_ops_that_can_overflow() { _custom %= &Custom; _custom *= Custom; _custom *= &Custom; + _custom >>= Custom; + _custom >>= &Custom; + _custom <<= Custom; + _custom <<= &Custom; _custom += -Custom; _custom += &-Custom; _custom -= -Custom; @@ -307,6 +327,10 @@ pub fn unknown_ops_or_runtime_ops_that_can_overflow() { _custom %= &-Custom; _custom *= -Custom; _custom *= &-Custom; + _custom >>= -Custom; + _custom >>= &-Custom; + _custom <<= -Custom; + _custom <<= &-Custom; // Binary _n = _n + 1; @@ -347,6 +371,10 @@ pub fn unknown_ops_or_runtime_ops_that_can_overflow() { _custom = Custom + &Custom; _custom = &Custom + Custom; _custom = &Custom + &Custom; + _custom = _custom >> _custom; + _custom = _custom >> &_custom; + _custom = Custom << _custom; + _custom = &Custom << _custom; // Unary _n = -_n; diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr index 17a2448fbfc..3895f08964c 100644 --- a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr +++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr @@ -1,5 +1,5 @@ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:270:5 + --> $DIR/arithmetic_side_effects.rs:286:5 | LL | _n += 1; | ^^^^^^^ @@ -7,592 +7,640 @@ LL | _n += 1; = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings` error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:271:5 + --> $DIR/arithmetic_side_effects.rs:287:5 | LL | _n += &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:272:5 + --> $DIR/arithmetic_side_effects.rs:288:5 | LL | _n -= 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:273:5 + --> $DIR/arithmetic_side_effects.rs:289:5 | LL | _n -= &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:274:5 + --> $DIR/arithmetic_side_effects.rs:290:5 | LL | _n /= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:275:5 + --> $DIR/arithmetic_side_effects.rs:291:5 | LL | _n /= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:276:5 + --> $DIR/arithmetic_side_effects.rs:292:5 | LL | _n %= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:277:5 + --> $DIR/arithmetic_side_effects.rs:293:5 | LL | _n %= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:278:5 + --> $DIR/arithmetic_side_effects.rs:294:5 | LL | _n *= 2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:279:5 + --> $DIR/arithmetic_side_effects.rs:295:5 | LL | _n *= &2; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:280:5 + --> $DIR/arithmetic_side_effects.rs:296:5 | LL | _n += -1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:281:5 + --> $DIR/arithmetic_side_effects.rs:297:5 | LL | _n += &-1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:282:5 + --> $DIR/arithmetic_side_effects.rs:298:5 | LL | _n -= -1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:283:5 + --> $DIR/arithmetic_side_effects.rs:299:5 | LL | _n -= &-1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:284:5 + --> $DIR/arithmetic_side_effects.rs:300:5 | LL | _n /= -0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:285:5 + --> $DIR/arithmetic_side_effects.rs:301:5 | LL | _n /= &-0; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:286:5 + --> $DIR/arithmetic_side_effects.rs:302:5 | LL | _n %= -0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:287:5 + --> $DIR/arithmetic_side_effects.rs:303:5 | LL | _n %= &-0; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:288:5 + --> $DIR/arithmetic_side_effects.rs:304:5 | LL | _n *= -2; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:289:5 + --> $DIR/arithmetic_side_effects.rs:305:5 | LL | _n *= &-2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:290:5 + --> $DIR/arithmetic_side_effects.rs:306:5 | LL | _custom += Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:291:5 + --> $DIR/arithmetic_side_effects.rs:307:5 | LL | _custom += &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:292:5 + --> $DIR/arithmetic_side_effects.rs:308:5 | LL | _custom -= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:293:5 + --> $DIR/arithmetic_side_effects.rs:309:5 | LL | _custom -= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:294:5 + --> $DIR/arithmetic_side_effects.rs:310:5 | LL | _custom /= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:295:5 + --> $DIR/arithmetic_side_effects.rs:311:5 | LL | _custom /= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:296:5 + --> $DIR/arithmetic_side_effects.rs:312:5 | LL | _custom %= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:297:5 + --> $DIR/arithmetic_side_effects.rs:313:5 | LL | _custom %= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:298:5 + --> $DIR/arithmetic_side_effects.rs:314:5 | LL | _custom *= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:299:5 + --> $DIR/arithmetic_side_effects.rs:315:5 | LL | _custom *= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:300:5 + --> $DIR/arithmetic_side_effects.rs:316:5 + | +LL | _custom >>= Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:317:5 + | +LL | _custom >>= &Custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:318:5 + | +LL | _custom <<= Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:319:5 + | +LL | _custom <<= &Custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:320:5 | LL | _custom += -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:301:5 + --> $DIR/arithmetic_side_effects.rs:321:5 | LL | _custom += &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:302:5 + --> $DIR/arithmetic_side_effects.rs:322:5 | LL | _custom -= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:303:5 + --> $DIR/arithmetic_side_effects.rs:323:5 | LL | _custom -= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:304:5 + --> $DIR/arithmetic_side_effects.rs:324:5 | LL | _custom /= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:305:5 + --> $DIR/arithmetic_side_effects.rs:325:5 | LL | _custom /= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:306:5 + --> $DIR/arithmetic_side_effects.rs:326:5 | LL | _custom %= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:307:5 + --> $DIR/arithmetic_side_effects.rs:327:5 | LL | _custom %= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:308:5 + --> $DIR/arithmetic_side_effects.rs:328:5 | LL | _custom *= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:309:5 + --> $DIR/arithmetic_side_effects.rs:329:5 | LL | _custom *= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:312:10 + --> $DIR/arithmetic_side_effects.rs:330:5 + | +LL | _custom >>= -Custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:331:5 + | +LL | _custom >>= &-Custom; + | ^^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:332:5 + | +LL | _custom <<= -Custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:333:5 + | +LL | _custom <<= &-Custom; + | ^^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:336:10 | LL | _n = _n + 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:313:10 + --> $DIR/arithmetic_side_effects.rs:337:10 | LL | _n = _n + &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:314:10 + --> $DIR/arithmetic_side_effects.rs:338:10 | LL | _n = 1 + _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:315:10 + --> $DIR/arithmetic_side_effects.rs:339:10 | LL | _n = &1 + _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:316:10 + --> $DIR/arithmetic_side_effects.rs:340:10 | LL | _n = _n - 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:317:10 + --> $DIR/arithmetic_side_effects.rs:341:10 | LL | _n = _n - &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:318:10 + --> $DIR/arithmetic_side_effects.rs:342:10 | LL | _n = 1 - _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:319:10 + --> $DIR/arithmetic_side_effects.rs:343:10 | LL | _n = &1 - _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:320:10 + --> $DIR/arithmetic_side_effects.rs:344:10 | LL | _n = _n / 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:321:10 + --> $DIR/arithmetic_side_effects.rs:345:10 | LL | _n = _n / &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:322:10 + --> $DIR/arithmetic_side_effects.rs:346:10 | LL | _n = _n % 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:323:10 + --> $DIR/arithmetic_side_effects.rs:347:10 | LL | _n = _n % &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:324:10 + --> $DIR/arithmetic_side_effects.rs:348:10 | LL | _n = _n * 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:325:10 + --> $DIR/arithmetic_side_effects.rs:349:10 | LL | _n = _n * &2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:326:10 + --> $DIR/arithmetic_side_effects.rs:350:10 | LL | _n = 2 * _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:327:10 + --> $DIR/arithmetic_side_effects.rs:351:10 | LL | _n = &2 * _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:328:10 + --> $DIR/arithmetic_side_effects.rs:352:10 | LL | _n = 23 + &85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:329:10 + --> $DIR/arithmetic_side_effects.rs:353:10 | LL | _n = &23 + 85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:330:10 + --> $DIR/arithmetic_side_effects.rs:354:10 | LL | _n = &23 + &85; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:331:15 + --> $DIR/arithmetic_side_effects.rs:355:15 | LL | _custom = _custom + _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:332:15 + --> $DIR/arithmetic_side_effects.rs:356:15 | LL | _custom = _custom + &_custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:333:15 + --> $DIR/arithmetic_side_effects.rs:357:15 | LL | _custom = Custom + _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:334:15 + --> $DIR/arithmetic_side_effects.rs:358:15 | LL | _custom = &Custom + _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:335:15 + --> $DIR/arithmetic_side_effects.rs:359:15 | LL | _custom = _custom - Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:336:15 + --> $DIR/arithmetic_side_effects.rs:360:15 | LL | _custom = _custom - &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:337:15 + --> $DIR/arithmetic_side_effects.rs:361:15 | LL | _custom = Custom - _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:338:15 + --> $DIR/arithmetic_side_effects.rs:362:15 | LL | _custom = &Custom - _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:339:15 + --> $DIR/arithmetic_side_effects.rs:363:15 | LL | _custom = _custom / Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:340:15 + --> $DIR/arithmetic_side_effects.rs:364:15 | LL | _custom = _custom / &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:341:15 + --> $DIR/arithmetic_side_effects.rs:365:15 | LL | _custom = _custom % Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:342:15 + --> $DIR/arithmetic_side_effects.rs:366:15 | LL | _custom = _custom % &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:343:15 + --> $DIR/arithmetic_side_effects.rs:367:15 | LL | _custom = _custom * Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:344:15 + --> $DIR/arithmetic_side_effects.rs:368:15 | LL | _custom = _custom * &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:345:15 + --> $DIR/arithmetic_side_effects.rs:369:15 | LL | _custom = Custom * _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:346:15 + --> $DIR/arithmetic_side_effects.rs:370:15 | LL | _custom = &Custom * _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:347:15 + --> $DIR/arithmetic_side_effects.rs:371:15 | LL | _custom = Custom + &Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:348:15 + --> $DIR/arithmetic_side_effects.rs:372:15 | LL | _custom = &Custom + Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:349:15 + --> $DIR/arithmetic_side_effects.rs:373:15 | LL | _custom = &Custom + &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:352:10 + --> $DIR/arithmetic_side_effects.rs:374:15 + | +LL | _custom = _custom >> _custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:375:15 + | +LL | _custom = _custom >> &_custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:376:15 + | +LL | _custom = Custom << _custom; + | ^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:377:15 + | +LL | _custom = &Custom << _custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:380:10 | LL | _n = -_n; | ^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:353:10 + --> $DIR/arithmetic_side_effects.rs:381:10 | LL | _n = -&_n; | ^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:354:15 + --> $DIR/arithmetic_side_effects.rs:382:15 | LL | _custom = -_custom; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:355:15 + --> $DIR/arithmetic_side_effects.rs:383:15 | LL | _custom = -&_custom; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:364:5 + --> $DIR/arithmetic_side_effects.rs:392:5 | LL | 1 + i; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:365:5 + --> $DIR/arithmetic_side_effects.rs:393:5 | LL | i * 2; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:367:5 + --> $DIR/arithmetic_side_effects.rs:395:5 | LL | i - 2 + 2 - i; | ^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:368:5 + --> $DIR/arithmetic_side_effects.rs:396:5 | LL | -i; | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:369:5 - | -LL | i >> 1; - | ^^^^^^ - -error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:370:5 - | -LL | i << 1; - | ^^^^^^ - -error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:379:5 + --> $DIR/arithmetic_side_effects.rs:407:5 | LL | i += 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:380:5 + --> $DIR/arithmetic_side_effects.rs:408:5 | LL | i -= 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:381:5 + --> $DIR/arithmetic_side_effects.rs:409:5 | LL | i *= 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:383:5 + --> $DIR/arithmetic_side_effects.rs:411:5 | LL | i /= 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:385:5 + --> $DIR/arithmetic_side_effects.rs:413:5 | LL | i /= var1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:386:5 + --> $DIR/arithmetic_side_effects.rs:414:5 | LL | i /= var2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:388:5 + --> $DIR/arithmetic_side_effects.rs:416:5 | LL | i %= 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:390:5 + --> $DIR/arithmetic_side_effects.rs:418:5 | LL | i %= var1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:391:5 + --> $DIR/arithmetic_side_effects.rs:419:5 | LL | i %= var2; | ^^^^^^^^^ -error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:392:5 - | -LL | i <<= 3; - | ^^^^^^^ - -error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:393:5 - | -LL | i >>= 2; - | ^^^^^^^ - -error: aborting due to 99 previous errors +error: aborting due to 107 previous errors diff --git a/src/tools/clippy/tests/ui/async_yields_async.fixed b/src/tools/clippy/tests/ui/async_yields_async.fixed index 3cf380d2b95..579a63ea477 100644 --- a/src/tools/clippy/tests/ui/async_yields_async.fixed +++ b/src/tools/clippy/tests/ui/async_yields_async.fixed @@ -2,6 +2,7 @@ #![feature(lint_reasons)] #![feature(async_closure)] #![warn(clippy::async_yields_async)] +#![allow(clippy::redundant_async_block)] use core::future::Future; use core::pin::Pin; diff --git a/src/tools/clippy/tests/ui/async_yields_async.rs b/src/tools/clippy/tests/ui/async_yields_async.rs index dd4131b60ab..5aec2fb50f6 100644 --- a/src/tools/clippy/tests/ui/async_yields_async.rs +++ b/src/tools/clippy/tests/ui/async_yields_async.rs @@ -2,6 +2,7 @@ #![feature(lint_reasons)] #![feature(async_closure)] #![warn(clippy::async_yields_async)] +#![allow(clippy::redundant_async_block)] use core::future::Future; use core::pin::Pin; diff --git a/src/tools/clippy/tests/ui/async_yields_async.stderr b/src/tools/clippy/tests/ui/async_yields_async.stderr index 22ce1c6f647..7f72534832b 100644 --- a/src/tools/clippy/tests/ui/async_yields_async.stderr +++ b/src/tools/clippy/tests/ui/async_yields_async.stderr @@ -1,5 +1,5 @@ error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:39:9 + --> $DIR/async_yields_async.rs:40:9 | LL | let _h = async { | _____________________- @@ -19,7 +19,7 @@ LL + }.await | error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:44:9 + --> $DIR/async_yields_async.rs:45:9 | LL | let _i = async { | ____________________- @@ -32,7 +32,7 @@ LL | | }; | |_____- outer async construct error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:50:9 + --> $DIR/async_yields_async.rs:51:9 | LL | let _j = async || { | ________________________- @@ -51,7 +51,7 @@ LL + }.await | error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:55:9 + --> $DIR/async_yields_async.rs:56:9 | LL | let _k = async || { | _______________________- @@ -64,7 +64,7 @@ LL | | }; | |_____- outer async construct error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:57:23 + --> $DIR/async_yields_async.rs:58:23 | LL | let _l = async || CustomFutureType; | ^^^^^^^^^^^^^^^^ @@ -74,7 +74,7 @@ LL | let _l = async || CustomFutureType; | help: consider awaiting this value: `CustomFutureType.await` error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:63:9 + --> $DIR/async_yields_async.rs:64:9 | LL | let _m = async || { | _______________________- diff --git a/src/tools/clippy/tests/ui/author/blocks.stdout b/src/tools/clippy/tests/ui/author/blocks.stdout index c6acf24c21e..eb3e5189c82 100644 --- a/src/tools/clippy/tests/ui/author/blocks.stdout +++ b/src/tools/clippy/tests/ui/author/blocks.stdout @@ -43,11 +43,7 @@ if let ExprKind::Block(block, None) = expr.kind if let ExprKind::Closure(CaptureBy::Value, fn_decl, body_id, _, None) = expr.kind && let FnRetTy::DefaultReturn(_) = fn_decl.output && expr1 = &cx.tcx.hir().body(body_id).value - && let ExprKind::Call(func, args) = expr1.kind - && let ExprKind::Path(ref qpath) = func.kind - && matches!(qpath, QPath::LangItem(LangItem::IdentityFuture, _)) - && args.len() == 1 - && let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = args[0].kind + && let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = expr1.kind && let FnRetTy::DefaultReturn(_) = fn_decl1.output && expr2 = &cx.tcx.hir().body(body_id1).value && let ExprKind::Block(block, None) = expr2.kind diff --git a/src/tools/clippy/tests/ui/boxed_local.rs b/src/tools/clippy/tests/ui/boxed_local.rs index 4639f00a8d8..79b6d33fc77 100644 --- a/src/tools/clippy/tests/ui/boxed_local.rs +++ b/src/tools/clippy/tests/ui/boxed_local.rs @@ -1,4 +1,3 @@ -#![feature(box_syntax)] #![feature(lint_reasons)] #![allow( clippy::borrowed_box, @@ -34,7 +33,7 @@ fn ok_box_trait(boxed_trait: &Box) { } fn warn_call() { - let x = box A; + let x = Box::new(A); x.foo(); } @@ -43,41 +42,41 @@ fn warn_arg(x: Box) { } fn nowarn_closure_arg() { - let x = Some(box A); + let x = Some(Box::new(A)); x.map_or((), |x| take_ref(&x)); } fn warn_rename_call() { - let x = box A; + let x = Box::new(A); let y = x; y.foo(); // via autoderef } fn warn_notuse() { - let bz = box A; + let bz = Box::new(A); } fn warn_pass() { - let bz = box A; + let bz = Box::new(A); take_ref(&bz); // via deref coercion } fn nowarn_return() -> Box { - box A // moved out, "escapes" + Box::new(A) // moved out, "escapes" } fn nowarn_move() { - let bx = box A; + let bx = Box::new(A); drop(bx) // moved in, "escapes" } fn nowarn_call() { - let bx = box A; + let bx = Box::new(A); bx.clone(); // method only available to Box, not via autoderef } fn nowarn_pass() { - let bx = box A; + let bx = Box::new(A); take_box(&bx); // fn needs &Box } @@ -86,30 +85,20 @@ fn take_ref(x: &A) {} fn nowarn_ref_take() { // false positive, should actually warn - let x = box A; + let x = Box::new(A); let y = &x; take_box(y); } fn nowarn_match() { - let x = box A; // moved into a match + let x = Box::new(A); // moved into a match match x { y => drop(y), } } fn warn_match() { - let x = box A; - match &x { - // not moved - y => (), - } -} - -fn nowarn_large_array() { - // should not warn, is large array - // and should not be on stack - let x = box [1; 10000]; + let x = Box::new(A); match &x { // not moved y => (), diff --git a/src/tools/clippy/tests/ui/boxed_local.stderr b/src/tools/clippy/tests/ui/boxed_local.stderr index 9036529f39c..10d78fbc0ab 100644 --- a/src/tools/clippy/tests/ui/boxed_local.stderr +++ b/src/tools/clippy/tests/ui/boxed_local.stderr @@ -1,5 +1,5 @@ error: local variable doesn't need to be boxed here - --> $DIR/boxed_local.rs:41:13 + --> $DIR/boxed_local.rs:40:13 | LL | fn warn_arg(x: Box) { | ^ @@ -7,19 +7,19 @@ LL | fn warn_arg(x: Box) { = note: `-D clippy::boxed-local` implied by `-D warnings` error: local variable doesn't need to be boxed here - --> $DIR/boxed_local.rs:132:12 + --> $DIR/boxed_local.rs:121:12 | LL | pub fn new(_needs_name: Box>) -> () {} | ^^^^^^^^^^^ error: local variable doesn't need to be boxed here - --> $DIR/boxed_local.rs:196:44 + --> $DIR/boxed_local.rs:185:44 | LL | fn default_impl_x(self: Box, x: Box) -> u32 { | ^ error: local variable doesn't need to be boxed here - --> $DIR/boxed_local.rs:203:16 + --> $DIR/boxed_local.rs:192:16 | LL | fn foo(x: Box) {} | ^ diff --git a/src/tools/clippy/tests/ui/collection_is_never_read.rs b/src/tools/clippy/tests/ui/collection_is_never_read.rs new file mode 100644 index 00000000000..068a49486cf --- /dev/null +++ b/src/tools/clippy/tests/ui/collection_is_never_read.rs @@ -0,0 +1,165 @@ +#![allow(unused)] +#![warn(clippy::collection_is_never_read)] + +use std::collections::{HashMap, HashSet}; + +fn main() {} + +fn not_a_collection() { + // TODO: Expand `collection_is_never_read` beyond collections? + let mut x = 10; // Ok + x += 1; +} + +fn no_access_at_all() { + // Other lints should catch this. + let x = vec![1, 2, 3]; // Ok +} + +fn write_without_read() { + // The main use case for `collection_is_never_read`. + let mut x = HashMap::new(); // WARNING + x.insert(1, 2); +} + +fn read_without_write() { + let mut x = vec![1, 2, 3]; // Ok + let _ = x.len(); +} + +fn write_and_read() { + let mut x = vec![1, 2, 3]; // Ok + x.push(4); + let _ = x.len(); +} + +fn write_after_read() { + // TODO: Warn here, but this requires more extensive data flow analysis. + let mut x = vec![1, 2, 3]; // Ok + let _ = x.len(); + x.push(4); // Pointless +} + +fn write_before_reassign() { + // TODO: Warn here, but this requires more extensive data flow analysis. + let mut x = HashMap::new(); // Ok + x.insert(1, 2); // Pointless + x = HashMap::new(); + let _ = x.len(); +} + +fn read_in_closure() { + let mut x = HashMap::new(); // Ok + x.insert(1, 2); + let _ = || { + let _ = x.len(); + }; +} + +fn write_in_closure() { + let mut x = vec![1, 2, 3]; // WARNING + let _ = || { + x.push(4); + }; +} + +fn read_in_format() { + let mut x = HashMap::new(); // Ok + x.insert(1, 2); + format!("{x:?}"); +} + +fn shadowing_1() { + let x = HashMap::::new(); // Ok + let _ = x.len(); + let mut x = HashMap::new(); // WARNING + x.insert(1, 2); +} + +fn shadowing_2() { + let mut x = HashMap::new(); // WARNING + x.insert(1, 2); + let x = HashMap::::new(); // Ok + let _ = x.len(); +} + +#[allow(clippy::let_unit_value)] +fn fake_read() { + let mut x = vec![1, 2, 3]; // Ok + x.reverse(); + // `collection_is_never_read` gets fooled, but other lints should catch this. + let _: () = x.clear(); +} + +fn assignment() { + let mut x = vec![1, 2, 3]; // WARNING + let y = vec![4, 5, 6]; // Ok + x = y; +} + +#[allow(clippy::self_assignment)] +fn self_assignment() { + let mut x = vec![1, 2, 3]; // WARNING + x = x; +} + +fn method_argument_but_not_target() { + struct MyStruct; + impl MyStruct { + fn my_method(&self, _argument: &[usize]) {} + } + let my_struct = MyStruct; + + let mut x = vec![1, 2, 3]; // Ok + x.reverse(); + my_struct.my_method(&x); +} + +fn insert_is_not_a_read() { + let mut x = HashSet::new(); // WARNING + x.insert(5); +} + +fn insert_is_a_read() { + let mut x = HashSet::new(); // Ok + if x.insert(5) { + println!("5 was inserted"); + } +} + +fn not_read_if_return_value_not_used() { + // `is_empty` does not modify the set, so it's a query. But since the return value is not used, the + // lint does not consider it a read here. + let x = vec![1, 2, 3]; // WARNING + x.is_empty(); +} + +fn extension_traits() { + trait VecExt { + fn method_with_side_effect(&self); + fn method_without_side_effect(&self); + } + + impl VecExt for Vec { + fn method_with_side_effect(&self) { + println!("my length: {}", self.len()); + } + fn method_without_side_effect(&self) {} + } + + let x = vec![1, 2, 3]; // Ok + x.method_with_side_effect(); + + let y = vec![1, 2, 3]; // Ok (false negative) + y.method_without_side_effect(); +} + +fn function_argument() { + #[allow(clippy::ptr_arg)] + fn foo(v: &Vec) -> usize { + v.len() + } + + let x = vec![1, 2, 3]; // Ok + foo(&x); +} diff --git a/src/tools/clippy/tests/ui/collection_is_never_read.stderr b/src/tools/clippy/tests/ui/collection_is_never_read.stderr new file mode 100644 index 00000000000..7654b74be3d --- /dev/null +++ b/src/tools/clippy/tests/ui/collection_is_never_read.stderr @@ -0,0 +1,52 @@ +error: collection is never read + --> $DIR/collection_is_never_read.rs:21:5 + | +LL | let mut x = HashMap::new(); // WARNING + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::collection-is-never-read` implied by `-D warnings` + +error: collection is never read + --> $DIR/collection_is_never_read.rs:60:5 + | +LL | let mut x = vec![1, 2, 3]; // WARNING + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: collection is never read + --> $DIR/collection_is_never_read.rs:75:5 + | +LL | let mut x = HashMap::new(); // WARNING + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: collection is never read + --> $DIR/collection_is_never_read.rs:80:5 + | +LL | let mut x = HashMap::new(); // WARNING + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: collection is never read + --> $DIR/collection_is_never_read.rs:95:5 + | +LL | let mut x = vec![1, 2, 3]; // WARNING + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: collection is never read + --> $DIR/collection_is_never_read.rs:102:5 + | +LL | let mut x = vec![1, 2, 3]; // WARNING + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: collection is never read + --> $DIR/collection_is_never_read.rs:119:5 + | +LL | let mut x = HashSet::new(); // WARNING + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: collection is never read + --> $DIR/collection_is_never_read.rs:133:5 + | +LL | let x = vec![1, 2, 3]; // WARNING + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/crashes/ice-10148.rs b/src/tools/clippy/tests/ui/crashes/ice-10148.rs new file mode 100644 index 00000000000..af33b10c693 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-10148.rs @@ -0,0 +1,9 @@ +// aux-build:../../auxiliary/proc_macro_with_span.rs + +extern crate proc_macro_with_span; + +use proc_macro_with_span::with_span; + +fn main() { + println!(with_span!(""something "")); +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-10148.stderr b/src/tools/clippy/tests/ui/crashes/ice-10148.stderr new file mode 100644 index 00000000000..f23e4433f95 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-10148.stderr @@ -0,0 +1,12 @@ +error: empty string literal in `println!` + --> $DIR/ice-10148.rs:8:5 + | +LL | println!(with_span!(""something "")); + | ^^^^^^^^^^^^^^^^^^^^-----------^^^^^ + | | + | help: remove the empty string + | + = note: `-D clippy::println-empty-string` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/crashes/ice-6179.rs b/src/tools/clippy/tests/ui/crashes/ice-6179.rs index 4fe92d356c4..ce1895851e2 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-6179.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-6179.rs @@ -2,7 +2,7 @@ //! The ICE is mainly caused by using `hir_ty_to_ty`. See the discussion in the PR for details. #![warn(clippy::use_self)] -#![allow(dead_code)] +#![allow(dead_code, clippy::let_with_type_underscore)] struct Foo; diff --git a/src/tools/clippy/tests/ui/crashes/ice-rust-107877.rs b/src/tools/clippy/tests/ui/crashes/ice-rust-107877.rs new file mode 100644 index 00000000000..7f5bae60d55 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-rust-107877.rs @@ -0,0 +1,17 @@ +#![allow(dead_code)] + +struct Foo; + +impl<'a> std::convert::TryFrom<&'a String> for Foo { + type Error = std::convert::Infallible; + + fn try_from(_: &'a String) -> Result { + Ok(Foo) + } +} + +fn find(_: impl std::convert::TryInto) {} + +fn main() { + find(&String::new()); +} diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed index a370ccc7696..a9e5fd159af 100644 --- a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed +++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed @@ -9,7 +9,8 @@ clippy::unnecessary_operation, clippy::branches_sharing_code, clippy::match_single_binding, - clippy::let_unit_value + clippy::let_unit_value, + clippy::let_with_type_underscore )] #[macro_use] diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs index 2476fe95141..085f8f452b2 100644 --- a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs +++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs @@ -9,7 +9,8 @@ clippy::unnecessary_operation, clippy::branches_sharing_code, clippy::match_single_binding, - clippy::let_unit_value + clippy::let_unit_value, + clippy::let_with_type_underscore )] #[macro_use] diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr index 5df2f642388..44c6f1a9bea 100644 --- a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr +++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr @@ -1,5 +1,5 @@ error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:21:17 + --> $DIR/default_numeric_fallback_f64.rs:22:17 | LL | let x = 0.12; | ^^^^ help: consider adding suffix: `0.12_f64` @@ -7,139 +7,139 @@ LL | let x = 0.12; = note: `-D clippy::default-numeric-fallback` implied by `-D warnings` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:22:18 + --> $DIR/default_numeric_fallback_f64.rs:23:18 | LL | let x = [1., 2., 3.]; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:22:22 + --> $DIR/default_numeric_fallback_f64.rs:23:22 | LL | let x = [1., 2., 3.]; | ^^ help: consider adding suffix: `2.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:22:26 + --> $DIR/default_numeric_fallback_f64.rs:23:26 | LL | let x = [1., 2., 3.]; | ^^ help: consider adding suffix: `3.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:23:28 + --> $DIR/default_numeric_fallback_f64.rs:24:28 | LL | let x = if true { (1., 2.) } else { (3., 4.) }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:23:32 + --> $DIR/default_numeric_fallback_f64.rs:24:32 | LL | let x = if true { (1., 2.) } else { (3., 4.) }; | ^^ help: consider adding suffix: `2.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:23:46 + --> $DIR/default_numeric_fallback_f64.rs:24:46 | LL | let x = if true { (1., 2.) } else { (3., 4.) }; | ^^ help: consider adding suffix: `3.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:23:50 + --> $DIR/default_numeric_fallback_f64.rs:24:50 | LL | let x = if true { (1., 2.) } else { (3., 4.) }; | ^^ help: consider adding suffix: `4.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:24:23 + --> $DIR/default_numeric_fallback_f64.rs:25:23 | LL | let x = match 1. { | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:25:18 + --> $DIR/default_numeric_fallback_f64.rs:26:18 | LL | _ => 1., | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:44:21 + --> $DIR/default_numeric_fallback_f64.rs:45:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:52:21 + --> $DIR/default_numeric_fallback_f64.rs:53:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:58:21 + --> $DIR/default_numeric_fallback_f64.rs:59:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:66:21 + --> $DIR/default_numeric_fallback_f64.rs:67:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:78:9 + --> $DIR/default_numeric_fallback_f64.rs:79:9 | LL | 1. | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:84:27 + --> $DIR/default_numeric_fallback_f64.rs:85:27 | LL | let f = || -> _ { 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:88:29 + --> $DIR/default_numeric_fallback_f64.rs:89:29 | LL | let f = || -> f64 { 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:102:21 + --> $DIR/default_numeric_fallback_f64.rs:103:21 | LL | generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:105:32 + --> $DIR/default_numeric_fallback_f64.rs:106:32 | LL | let x: _ = generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:123:28 + --> $DIR/default_numeric_fallback_f64.rs:124:28 | LL | GenericStruct { x: 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:126:36 + --> $DIR/default_numeric_fallback_f64.rs:127:36 | LL | let _ = GenericStruct { x: 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:144:24 + --> $DIR/default_numeric_fallback_f64.rs:145:24 | LL | GenericEnum::X(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:164:23 + --> $DIR/default_numeric_fallback_f64.rs:165:23 | LL | s.generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:171:21 + --> $DIR/default_numeric_fallback_f64.rs:172:21 | LL | let x = 22.; | ^^^ help: consider adding suffix: `22.0_f64` diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed index 3f4994f0453..63ac4d5aeb6 100644 --- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed +++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed @@ -9,7 +9,8 @@ clippy::no_effect, clippy::unnecessary_operation, clippy::branches_sharing_code, - clippy::let_unit_value + clippy::let_unit_value, + clippy::let_with_type_underscore )] #[macro_use] diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs index 2df0e09787f..28e6eceb80e 100644 --- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs +++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs @@ -9,7 +9,8 @@ clippy::no_effect, clippy::unnecessary_operation, clippy::branches_sharing_code, - clippy::let_unit_value + clippy::let_unit_value, + clippy::let_with_type_underscore )] #[macro_use] diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr index 6f219c3fc2b..dd91574d5b3 100644 --- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr +++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr @@ -1,5 +1,5 @@ error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:21:17 + --> $DIR/default_numeric_fallback_i32.rs:22:17 | LL | let x = 22; | ^^ help: consider adding suffix: `22_i32` @@ -7,151 +7,151 @@ LL | let x = 22; = note: `-D clippy::default-numeric-fallback` implied by `-D warnings` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:22:18 + --> $DIR/default_numeric_fallback_i32.rs:23:18 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:22:21 + --> $DIR/default_numeric_fallback_i32.rs:23:21 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:22:24 + --> $DIR/default_numeric_fallback_i32.rs:23:24 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:23:28 + --> $DIR/default_numeric_fallback_i32.rs:24:28 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:23:31 + --> $DIR/default_numeric_fallback_i32.rs:24:31 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:23:44 + --> $DIR/default_numeric_fallback_i32.rs:24:44 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:23:47 + --> $DIR/default_numeric_fallback_i32.rs:24:47 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `4_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:24:23 + --> $DIR/default_numeric_fallback_i32.rs:25:23 | LL | let x = match 1 { | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:25:13 + --> $DIR/default_numeric_fallback_i32.rs:26:13 | LL | 1 => 1, | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:25:18 + --> $DIR/default_numeric_fallback_i32.rs:26:18 | LL | 1 => 1, | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:26:18 + --> $DIR/default_numeric_fallback_i32.rs:27:18 | LL | _ => 2, | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:45:21 + --> $DIR/default_numeric_fallback_i32.rs:46:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:53:21 + --> $DIR/default_numeric_fallback_i32.rs:54:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:59:21 + --> $DIR/default_numeric_fallback_i32.rs:60:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:67:21 + --> $DIR/default_numeric_fallback_i32.rs:68:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:79:9 + --> $DIR/default_numeric_fallback_i32.rs:80:9 | LL | 1 | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:85:27 + --> $DIR/default_numeric_fallback_i32.rs:86:27 | LL | let f = || -> _ { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:89:29 + --> $DIR/default_numeric_fallback_i32.rs:90:29 | LL | let f = || -> i32 { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:103:21 + --> $DIR/default_numeric_fallback_i32.rs:104:21 | LL | generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:106:32 + --> $DIR/default_numeric_fallback_i32.rs:107:32 | LL | let x: _ = generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:124:28 + --> $DIR/default_numeric_fallback_i32.rs:125:28 | LL | GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:127:36 + --> $DIR/default_numeric_fallback_i32.rs:128:36 | LL | let _ = GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:145:24 + --> $DIR/default_numeric_fallback_i32.rs:146:24 | LL | GenericEnum::X(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:165:23 + --> $DIR/default_numeric_fallback_i32.rs:166:23 | LL | s.generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:172:21 + --> $DIR/default_numeric_fallback_i32.rs:173:21 | LL | let x = 22; | ^^ help: consider adding suffix: `22_i32` diff --git a/src/tools/clippy/tests/ui/derivable_impls.fixed b/src/tools/clippy/tests/ui/derivable_impls.fixed index ee8456f5deb..89ec33a0d8f 100644 --- a/src/tools/clippy/tests/ui/derivable_impls.fixed +++ b/src/tools/clippy/tests/ui/derivable_impls.fixed @@ -231,4 +231,41 @@ impl Default for NonExhaustiveEnum { } } +// https://github.com/rust-lang/rust-clippy/issues/10396 + +#[derive(Default)] +struct DefaultType; + +struct GenericType { + t: T, +} + +impl Default for GenericType { + fn default() -> Self { + Self { t: Default::default() } + } +} + +struct InnerGenericType { + t: T, +} + +impl Default for InnerGenericType { + fn default() -> Self { + Self { t: Default::default() } + } +} + +struct OtherGenericType { + inner: InnerGenericType, +} + +impl Default for OtherGenericType { + fn default() -> Self { + Self { + inner: Default::default(), + } + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/derivable_impls.rs b/src/tools/clippy/tests/ui/derivable_impls.rs index 14af419bcad..def6e41162f 100644 --- a/src/tools/clippy/tests/ui/derivable_impls.rs +++ b/src/tools/clippy/tests/ui/derivable_impls.rs @@ -267,4 +267,41 @@ impl Default for NonExhaustiveEnum { } } +// https://github.com/rust-lang/rust-clippy/issues/10396 + +#[derive(Default)] +struct DefaultType; + +struct GenericType { + t: T, +} + +impl Default for GenericType { + fn default() -> Self { + Self { t: Default::default() } + } +} + +struct InnerGenericType { + t: T, +} + +impl Default for InnerGenericType { + fn default() -> Self { + Self { t: Default::default() } + } +} + +struct OtherGenericType { + inner: InnerGenericType, +} + +impl Default for OtherGenericType { + fn default() -> Self { + Self { + inner: Default::default(), + } + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/erasing_op.rs b/src/tools/clippy/tests/ui/erasing_op.rs index ae2fad0086d..74985029e00 100644 --- a/src/tools/clippy/tests/ui/erasing_op.rs +++ b/src/tools/clippy/tests/ui/erasing_op.rs @@ -31,9 +31,7 @@ impl core::ops::Mul for Vec1 { #[allow(clippy::no_effect)] #[warn(clippy::erasing_op)] -fn main() { - let x: u8 = 0; - +fn test(x: u8) { x * 0; 0 & x; 0 / x; @@ -41,3 +39,7 @@ fn main() { 0 * Vec1 { x: 5 }; Vec1 { x: 5 } * 0; } + +fn main() { + test(0) +} diff --git a/src/tools/clippy/tests/ui/erasing_op.stderr b/src/tools/clippy/tests/ui/erasing_op.stderr index 165ed9bfe58..97941252355 100644 --- a/src/tools/clippy/tests/ui/erasing_op.stderr +++ b/src/tools/clippy/tests/ui/erasing_op.stderr @@ -1,5 +1,5 @@ error: this operation will always return zero. This is likely not the intended outcome - --> $DIR/erasing_op.rs:37:5 + --> $DIR/erasing_op.rs:35:5 | LL | x * 0; | ^^^^^ @@ -7,25 +7,25 @@ LL | x * 0; = note: `-D clippy::erasing-op` implied by `-D warnings` error: this operation will always return zero. This is likely not the intended outcome - --> $DIR/erasing_op.rs:38:5 + --> $DIR/erasing_op.rs:36:5 | LL | 0 & x; | ^^^^^ error: this operation will always return zero. This is likely not the intended outcome - --> $DIR/erasing_op.rs:39:5 + --> $DIR/erasing_op.rs:37:5 | LL | 0 / x; | ^^^^^ error: this operation will always return zero. This is likely not the intended outcome - --> $DIR/erasing_op.rs:41:5 + --> $DIR/erasing_op.rs:39:5 | LL | 0 * Vec1 { x: 5 }; | ^^^^^^^^^^^^^^^^^ error: this operation will always return zero. This is likely not the intended outcome - --> $DIR/erasing_op.rs:42:5 + --> $DIR/erasing_op.rs:40:5 | LL | Vec1 { x: 5 } * 0; | ^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/format.fixed b/src/tools/clippy/tests/ui/format.fixed index cd2f70ee8b0..beedf2c1db2 100644 --- a/src/tools/clippy/tests/ui/format.fixed +++ b/src/tools/clippy/tests/ui/format.fixed @@ -1,5 +1,4 @@ // run-rustfix -// aux-build: proc_macro_with_span.rs #![warn(clippy::useless_format)] #![allow( unused_tuple_struct_fields, @@ -10,8 +9,6 @@ clippy::uninlined_format_args )] -extern crate proc_macro_with_span; - struct Foo(pub String); macro_rules! foo { @@ -90,7 +87,4 @@ fn main() { let _ = abc.to_string(); let xx = "xx"; let _ = xx.to_string(); - - // Issue #10148 - println!(proc_macro_with_span::with_span!(""something "")); } diff --git a/src/tools/clippy/tests/ui/format.rs b/src/tools/clippy/tests/ui/format.rs index c22345a79d4..e805f181889 100644 --- a/src/tools/clippy/tests/ui/format.rs +++ b/src/tools/clippy/tests/ui/format.rs @@ -1,5 +1,4 @@ // run-rustfix -// aux-build: proc_macro_with_span.rs #![warn(clippy::useless_format)] #![allow( unused_tuple_struct_fields, @@ -10,8 +9,6 @@ clippy::uninlined_format_args )] -extern crate proc_macro_with_span; - struct Foo(pub String); macro_rules! foo { @@ -92,7 +89,4 @@ fn main() { let _ = format!("{abc}"); let xx = "xx"; let _ = format!("{xx}"); - - // Issue #10148 - println!(proc_macro_with_span::with_span!(""something "")); } diff --git a/src/tools/clippy/tests/ui/format.stderr b/src/tools/clippy/tests/ui/format.stderr index a0e5d5c8ad2..0ef0ac655d3 100644 --- a/src/tools/clippy/tests/ui/format.stderr +++ b/src/tools/clippy/tests/ui/format.stderr @@ -1,5 +1,5 @@ error: useless use of `format!` - --> $DIR/format.rs:22:5 + --> $DIR/format.rs:19:5 | LL | format!("foo"); | ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` @@ -7,19 +7,19 @@ LL | format!("foo"); = note: `-D clippy::useless-format` implied by `-D warnings` error: useless use of `format!` - --> $DIR/format.rs:23:5 + --> $DIR/format.rs:20:5 | LL | format!("{{}}"); | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{}".to_string()` error: useless use of `format!` - --> $DIR/format.rs:24:5 + --> $DIR/format.rs:21:5 | LL | format!("{{}} abc {{}}"); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{} abc {}".to_string()` error: useless use of `format!` - --> $DIR/format.rs:25:5 + --> $DIR/format.rs:22:5 | LL | / format!( LL | | r##"foo {{}} @@ -34,67 +34,67 @@ LL ~ " bar"##.to_string(); | error: useless use of `format!` - --> $DIR/format.rs:30:13 + --> $DIR/format.rs:27:13 | LL | let _ = format!(""); | ^^^^^^^^^^^ help: consider using `String::new()`: `String::new()` error: useless use of `format!` - --> $DIR/format.rs:32:5 + --> $DIR/format.rs:29:5 | LL | format!("{}", "foo"); | ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` error: useless use of `format!` - --> $DIR/format.rs:40:5 + --> $DIR/format.rs:37:5 | LL | format!("{}", arg); | ^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()` error: useless use of `format!` - --> $DIR/format.rs:70:5 + --> $DIR/format.rs:67:5 | LL | format!("{}", 42.to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string()` error: useless use of `format!` - --> $DIR/format.rs:72:5 + --> $DIR/format.rs:69:5 | LL | format!("{}", x.display().to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string()` error: useless use of `format!` - --> $DIR/format.rs:76:18 + --> $DIR/format.rs:73:18 | LL | let _ = Some(format!("{}", a + "bar")); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"` error: useless use of `format!` - --> $DIR/format.rs:80:22 + --> $DIR/format.rs:77:22 | LL | let _s: String = format!("{}", &*v.join("/n")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()` error: useless use of `format!` - --> $DIR/format.rs:86:13 + --> $DIR/format.rs:83:13 | LL | let _ = format!("{x}"); | ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()` error: useless use of `format!` - --> $DIR/format.rs:88:13 + --> $DIR/format.rs:85:13 | LL | let _ = format!("{y}", y = x); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()` error: useless use of `format!` - --> $DIR/format.rs:92:13 + --> $DIR/format.rs:89:13 | LL | let _ = format!("{abc}"); | ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `abc.to_string()` error: useless use of `format!` - --> $DIR/format.rs:94:13 + --> $DIR/format.rs:91:13 | LL | let _ = format!("{xx}"); | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `xx.to_string()` diff --git a/src/tools/clippy/tests/ui/impl_trait_in_params.stderr b/src/tools/clippy/tests/ui/impl_trait_in_params.stderr index acfcc21445e..80383743525 100644 --- a/src/tools/clippy/tests/ui/impl_trait_in_params.stderr +++ b/src/tools/clippy/tests/ui/impl_trait_in_params.stderr @@ -5,7 +5,7 @@ LL | pub fn a(_: impl Trait) {} | ^^^^^^^^^^ | = note: `-D clippy::impl-trait-in-params` implied by `-D warnings` -help: add a type paremeter +help: add a type parameter | LL | pub fn a<{ /* Generic name */ }: Trait>(_: impl Trait) {} | +++++++++++++++++++++++++++++++ @@ -16,7 +16,7 @@ error: '`impl Trait` used as a function parameter' LL | pub fn c(_: C, _: impl Trait) {} | ^^^^^^^^^^ | -help: add a type paremeter +help: add a type parameter | LL | pub fn c(_: C, _: impl Trait) {} | +++++++++++++++++++++++++++++++ diff --git a/src/tools/clippy/tests/ui/implicit_clone.fixed b/src/tools/clippy/tests/ui/implicit_clone.fixed index 51b1afbe5ac..8ccc3da7b47 100644 --- a/src/tools/clippy/tests/ui/implicit_clone.fixed +++ b/src/tools/clippy/tests/ui/implicit_clone.fixed @@ -87,7 +87,7 @@ fn main() { let kitten = Kitten {}; let _ = kitten.clone(); let _ = own_same_from_ref(&kitten); - // this shouln't lint + // this shouldn't lint let _ = kitten.to_vec(); // we expect no lints for this diff --git a/src/tools/clippy/tests/ui/implicit_clone.rs b/src/tools/clippy/tests/ui/implicit_clone.rs index 8a9027433d9..59333312607 100644 --- a/src/tools/clippy/tests/ui/implicit_clone.rs +++ b/src/tools/clippy/tests/ui/implicit_clone.rs @@ -87,7 +87,7 @@ fn main() { let kitten = Kitten {}; let _ = kitten.to_owned(); let _ = own_same_from_ref(&kitten); - // this shouln't lint + // this shouldn't lint let _ = kitten.to_vec(); // we expect no lints for this diff --git a/src/tools/clippy/tests/ui/integer_arithmetic.rs b/src/tools/clippy/tests/ui/integer_arithmetic.rs index 67f24b4548a..8dfdee662b9 100644 --- a/src/tools/clippy/tests/ui/integer_arithmetic.rs +++ b/src/tools/clippy/tests/ui/integer_arithmetic.rs @@ -4,7 +4,7 @@ #[rustfmt::skip] fn main() { let mut i = 1i32; - let mut var1 = 0i32; + let mut var1 = 13i32; let mut var2 = -1i32; 1 + i; i * 2; diff --git a/src/tools/clippy/tests/ui/len_without_is_empty.rs b/src/tools/clippy/tests/ui/len_without_is_empty.rs index b5dec6c46bd..52aabefaed2 100644 --- a/src/tools/clippy/tests/ui/len_without_is_empty.rs +++ b/src/tools/clippy/tests/ui/len_without_is_empty.rs @@ -282,6 +282,87 @@ impl AsyncLen { } } +// issue #7232 +pub struct AsyncLenWithoutIsEmpty; +impl AsyncLenWithoutIsEmpty { + pub async fn async_task(&self) -> bool { + true + } + + pub async fn len(&self) -> usize { + usize::from(!self.async_task().await) + } +} + +// issue #7232 +pub struct AsyncOptionLenWithoutIsEmpty; +impl AsyncOptionLenWithoutIsEmpty { + async fn async_task(&self) -> bool { + true + } + + pub async fn len(&self) -> Option { + None + } +} + +// issue #7232 +pub struct AsyncOptionLenNonIntegral; +impl AsyncOptionLenNonIntegral { + // don't lint + pub async fn len(&self) -> Option { + None + } +} + +// issue #7232 +pub struct AsyncResultLenWithoutIsEmpty; +impl AsyncResultLenWithoutIsEmpty { + async fn async_task(&self) -> bool { + true + } + + pub async fn len(&self) -> Result { + Err(()) + } +} + +// issue #7232 +pub struct AsyncOptionLen; +impl AsyncOptionLen { + async fn async_task(&self) -> bool { + true + } + + pub async fn len(&self) -> Result { + Err(()) + } + + pub async fn is_empty(&self) -> bool { + true + } +} + +pub struct AsyncLenSyncIsEmpty; +impl AsyncLenSyncIsEmpty { + pub async fn len(&self) -> u32 { + 0 + } + + pub fn is_empty(&self) -> bool { + true + } +} + +// issue #9520 +pub struct NonStandardLen; +impl NonStandardLen { + // don't lint + pub fn len(&self, something: usize) -> usize { + something + } +} + // issue #9520 pub struct NonStandardLenAndIsEmptySignature; impl NonStandardLenAndIsEmptySignature { @@ -328,4 +409,15 @@ impl NonStandardSignatureWithGenerics { } } +pub struct DifferingErrors; +impl DifferingErrors { + pub fn len(&self) -> Result { + Ok(0) + } + + pub fn is_empty(&self) -> Result { + Ok(true) + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/len_without_is_empty.stderr b/src/tools/clippy/tests/ui/len_without_is_empty.stderr index 8e890e2e259..1bce1734b81 100644 --- a/src/tools/clippy/tests/ui/len_without_is_empty.stderr +++ b/src/tools/clippy/tests/ui/len_without_is_empty.stderr @@ -119,5 +119,23 @@ LL | pub fn len(&self) -> Result { | = help: use a custom `Error` type instead -error: aborting due to 12 previous errors +error: struct `AsyncLenWithoutIsEmpty` has a public `len` method, but no `is_empty` method + --> $DIR/len_without_is_empty.rs:292:5 + | +LL | pub async fn len(&self) -> usize { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: struct `AsyncOptionLenWithoutIsEmpty` has a public `len` method, but no `is_empty` method + --> $DIR/len_without_is_empty.rs:304:5 + | +LL | pub async fn len(&self) -> Option { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: struct `AsyncResultLenWithoutIsEmpty` has a public `len` method, but no `is_empty` method + --> $DIR/len_without_is_empty.rs:325:5 + | +LL | pub async fn len(&self) -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/let_unit.fixed b/src/tools/clippy/tests/ui/let_unit.fixed index 6343cff0f7f..76ff0645f41 100644 --- a/src/tools/clippy/tests/ui/let_unit.fixed +++ b/src/tools/clippy/tests/ui/let_unit.fixed @@ -175,3 +175,7 @@ fn attributes() { #[expect(clippy::let_unit_value)] let _ = f(); } + +async fn issue10433() { + let _pending: () = std::future::pending().await; +} diff --git a/src/tools/clippy/tests/ui/let_unit.rs b/src/tools/clippy/tests/ui/let_unit.rs index c9bb2849f5c..895ccfe366a 100644 --- a/src/tools/clippy/tests/ui/let_unit.rs +++ b/src/tools/clippy/tests/ui/let_unit.rs @@ -175,3 +175,7 @@ fn attributes() { #[expect(clippy::let_unit_value)] let _ = f(); } + +async fn issue10433() { + let _pending: () = std::future::pending().await; +} diff --git a/src/tools/clippy/tests/ui/let_with_type_underscore.rs b/src/tools/clippy/tests/ui/let_with_type_underscore.rs new file mode 100644 index 00000000000..175718b94c8 --- /dev/null +++ b/src/tools/clippy/tests/ui/let_with_type_underscore.rs @@ -0,0 +1,19 @@ +#![allow(unused)] +#![warn(clippy::let_with_type_underscore)] +#![allow(clippy::let_unit_value)] + +fn func() -> &'static str { + "" +} + +fn main() { + // Will lint + let x: _ = 1; + let _: _ = 2; + let x: _ = func(); + + let x = 1; // Will not lint, Rust inferres this to an integer before Clippy + let x = func(); + let x: Vec<_> = Vec::::new(); + let x: [_; 1] = [1]; +} diff --git a/src/tools/clippy/tests/ui/let_with_type_underscore.stderr b/src/tools/clippy/tests/ui/let_with_type_underscore.stderr new file mode 100644 index 00000000000..16bf83c708f --- /dev/null +++ b/src/tools/clippy/tests/ui/let_with_type_underscore.stderr @@ -0,0 +1,39 @@ +error: variable declared with type underscore + --> $DIR/let_with_type_underscore.rs:11:5 + | +LL | let x: _ = 1; + | ^^^^^^^^^^^^^ + | +help: remove the explicit type `_` declaration + --> $DIR/let_with_type_underscore.rs:11:10 + | +LL | let x: _ = 1; + | ^^^ + = note: `-D clippy::let-with-type-underscore` implied by `-D warnings` + +error: variable declared with type underscore + --> $DIR/let_with_type_underscore.rs:12:5 + | +LL | let _: _ = 2; + | ^^^^^^^^^^^^^ + | +help: remove the explicit type `_` declaration + --> $DIR/let_with_type_underscore.rs:12:10 + | +LL | let _: _ = 2; + | ^^^ + +error: variable declared with type underscore + --> $DIR/let_with_type_underscore.rs:13:5 + | +LL | let x: _ = func(); + | ^^^^^^^^^^^^^^^^^^ + | +help: remove the explicit type `_` declaration + --> $DIR/let_with_type_underscore.rs:13:10 + | +LL | let x: _ = func(); + | ^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_rem_euclid.fixed b/src/tools/clippy/tests/ui/manual_rem_euclid.fixed index 4cdc0546a74..6916a284a20 100644 --- a/src/tools/clippy/tests/ui/manual_rem_euclid.fixed +++ b/src/tools/clippy/tests/ui/manual_rem_euclid.fixed @@ -2,6 +2,7 @@ // aux-build:macro_rules.rs #![warn(clippy::manual_rem_euclid)] +#![allow(clippy::let_with_type_underscore)] #[macro_use] extern crate macro_rules; diff --git a/src/tools/clippy/tests/ui/manual_rem_euclid.rs b/src/tools/clippy/tests/ui/manual_rem_euclid.rs index 58a9e20f38b..412dbddb426 100644 --- a/src/tools/clippy/tests/ui/manual_rem_euclid.rs +++ b/src/tools/clippy/tests/ui/manual_rem_euclid.rs @@ -2,6 +2,7 @@ // aux-build:macro_rules.rs #![warn(clippy::manual_rem_euclid)] +#![allow(clippy::let_with_type_underscore)] #[macro_use] extern crate macro_rules; diff --git a/src/tools/clippy/tests/ui/manual_rem_euclid.stderr b/src/tools/clippy/tests/ui/manual_rem_euclid.stderr index e3122a588b6..6d06654638b 100644 --- a/src/tools/clippy/tests/ui/manual_rem_euclid.stderr +++ b/src/tools/clippy/tests/ui/manual_rem_euclid.stderr @@ -1,5 +1,5 @@ error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:19:18 + --> $DIR/manual_rem_euclid.rs:20:18 | LL | let _: i32 = ((value % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` @@ -7,31 +7,31 @@ LL | let _: i32 = ((value % 4) + 4) % 4; = note: `-D clippy::manual-rem-euclid` implied by `-D warnings` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:20:18 + --> $DIR/manual_rem_euclid.rs:21:18 | LL | let _: i32 = (4 + (value % 4)) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:21:18 + --> $DIR/manual_rem_euclid.rs:22:18 | LL | let _: i32 = (value % 4 + 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:22:18 + --> $DIR/manual_rem_euclid.rs:23:18 | LL | let _: i32 = (4 + value % 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:23:22 + --> $DIR/manual_rem_euclid.rs:24:22 | LL | let _: i32 = 1 + (4 + value % 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:12:22 + --> $DIR/manual_rem_euclid.rs:13:22 | LL | let _: i32 = ((value % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` @@ -42,25 +42,25 @@ LL | internal_rem_euclid!(); = note: this error originates in the macro `internal_rem_euclid` (in Nightly builds, run with -Z macro-backtrace for more info) error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:49:5 + --> $DIR/manual_rem_euclid.rs:50:5 | LL | ((num % 4) + 4) % 4 | ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:54:5 + --> $DIR/manual_rem_euclid.rs:55:5 | LL | ((num % 4) + 4) % 4 | ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:66:18 + --> $DIR/manual_rem_euclid.rs:67:18 | LL | let _: i32 = ((x % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:79:18 + --> $DIR/manual_rem_euclid.rs:80:18 | LL | let _: i32 = ((x % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)` diff --git a/src/tools/clippy/tests/ui/match_result_ok.fixed b/src/tools/clippy/tests/ui/match_result_ok.fixed index 8b91b9854a0..10ae1ee5245 100644 --- a/src/tools/clippy/tests/ui/match_result_ok.fixed +++ b/src/tools/clippy/tests/ui/match_result_ok.fixed @@ -16,7 +16,7 @@ fn str_to_int_ok(x: &str) -> i32 { #[rustfmt::skip] fn strange_some_no_else(x: &str) -> i32 { { - if let Ok(y) = x . parse() { + if let Ok(y) = x . parse() { return y; }; 0 diff --git a/src/tools/clippy/tests/ui/match_result_ok.stderr b/src/tools/clippy/tests/ui/match_result_ok.stderr index 98a95705ca5..cbdc56aa28c 100644 --- a/src/tools/clippy/tests/ui/match_result_ok.stderr +++ b/src/tools/clippy/tests/ui/match_result_ok.stderr @@ -18,7 +18,7 @@ LL | if let Some(y) = x . parse() . ok () { | help: consider matching on `Ok(y)` and removing the call to `ok` instead | -LL | if let Ok(y) = x . parse() { +LL | if let Ok(y) = x . parse() { | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: matching on `Some` with `ok()` is redundant diff --git a/src/tools/clippy/tests/ui/missing_assert_message.rs b/src/tools/clippy/tests/ui/missing_assert_message.rs new file mode 100644 index 00000000000..89404ca8827 --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_assert_message.rs @@ -0,0 +1,84 @@ +#![allow(unused)] +#![warn(clippy::missing_assert_message)] + +macro_rules! bar { + ($( $x:expr ),*) => { + foo() + }; +} + +fn main() {} + +// Should trigger warning +fn asserts_without_message() { + assert!(foo()); + assert_eq!(foo(), foo()); + assert_ne!(foo(), foo()); + debug_assert!(foo()); + debug_assert_eq!(foo(), foo()); + debug_assert_ne!(foo(), foo()); +} + +// Should trigger warning +fn asserts_without_message_but_with_macro_calls() { + assert!(bar!(true)); + assert!(bar!(true, false)); + assert_eq!(bar!(true), foo()); + assert_ne!(bar!(true, true), bar!(true)); +} + +// Should trigger warning +fn asserts_with_trailing_commas() { + assert!(foo(),); + assert_eq!(foo(), foo(),); + assert_ne!(foo(), foo(),); + debug_assert!(foo(),); + debug_assert_eq!(foo(), foo(),); + debug_assert_ne!(foo(), foo(),); +} + +// Should not trigger warning +fn asserts_with_message_and_with_macro_calls() { + assert!(bar!(true), "msg"); + assert!(bar!(true, false), "msg"); + assert_eq!(bar!(true), foo(), "msg"); + assert_ne!(bar!(true, true), bar!(true), "msg"); +} + +// Should not trigger warning +fn asserts_with_message() { + assert!(foo(), "msg"); + assert_eq!(foo(), foo(), "msg"); + assert_ne!(foo(), foo(), "msg"); + debug_assert!(foo(), "msg"); + debug_assert_eq!(foo(), foo(), "msg"); + debug_assert_ne!(foo(), foo(), "msg"); +} + +// Should not trigger warning +#[test] +fn asserts_without_message_but_inside_a_test_function() { + assert!(foo()); + assert_eq!(foo(), foo()); + assert_ne!(foo(), foo()); + debug_assert!(foo()); + debug_assert_eq!(foo(), foo()); + debug_assert_ne!(foo(), foo()); +} + +// Should not trigger warning +#[cfg(test)] +mod tests { + fn asserts_without_message_but_inside_a_test_module() { + assert!(foo()); + assert_eq!(foo(), foo()); + assert_ne!(foo(), foo()); + debug_assert!(foo()); + debug_assert_eq!(foo(), foo()); + debug_assert_ne!(foo(), foo()); + } +} + +fn foo() -> bool { + true +} diff --git a/src/tools/clippy/tests/ui/missing_assert_message.stderr b/src/tools/clippy/tests/ui/missing_assert_message.stderr new file mode 100644 index 00000000000..ecd03801277 --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_assert_message.stderr @@ -0,0 +1,131 @@ +error: assert without any message + --> $DIR/missing_assert_message.rs:14:5 + | +LL | assert!(foo()); + | ^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + = note: `-D clippy::missing-assert-message` implied by `-D warnings` + +error: assert without any message + --> $DIR/missing_assert_message.rs:15:5 + | +LL | assert_eq!(foo(), foo()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:16:5 + | +LL | assert_ne!(foo(), foo()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:17:5 + | +LL | debug_assert!(foo()); + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:18:5 + | +LL | debug_assert_eq!(foo(), foo()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:19:5 + | +LL | debug_assert_ne!(foo(), foo()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:24:5 + | +LL | assert!(bar!(true)); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:25:5 + | +LL | assert!(bar!(true, false)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:26:5 + | +LL | assert_eq!(bar!(true), foo()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:27:5 + | +LL | assert_ne!(bar!(true, true), bar!(true)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:32:5 + | +LL | assert!(foo(),); + | ^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:33:5 + | +LL | assert_eq!(foo(), foo(),); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:34:5 + | +LL | assert_ne!(foo(), foo(),); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:35:5 + | +LL | debug_assert!(foo(),); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:36:5 + | +LL | debug_assert_eq!(foo(), foo(),); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> $DIR/missing_assert_message.rs:37:5 + | +LL | debug_assert_ne!(foo(), foo(),); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: aborting due to 16 previous errors + diff --git a/src/tools/clippy/tests/ui/missing_doc.stderr b/src/tools/clippy/tests/ui/missing_doc.stderr index d3bef28bf64..4e8a49bf1cd 100644 --- a/src/tools/clippy/tests/ui/missing_doc.stderr +++ b/src/tools/clippy/tests/ui/missing_doc.stderr @@ -6,30 +6,12 @@ LL | type Typedef = String; | = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings` -error: missing documentation for a type alias - --> $DIR/missing_doc.rs:17:1 - | -LL | pub type PubTypedef = String; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: missing documentation for a module --> $DIR/missing_doc.rs:19:1 | LL | mod module_no_dox {} | ^^^^^^^^^^^^^^^^^^^^ -error: missing documentation for a module - --> $DIR/missing_doc.rs:20:1 - | -LL | pub mod pub_module_no_dox {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: missing documentation for a function - --> $DIR/missing_doc.rs:24:1 - | -LL | pub fn foo2() {} - | ^^^^^^^^^^^^^^^^ - error: missing documentation for a function --> $DIR/missing_doc.rs:25:1 | @@ -69,50 +51,18 @@ error: missing documentation for a variant LL | BarB, | ^^^^ -error: missing documentation for an enum - --> $DIR/missing_doc.rs:44:1 - | -LL | / pub enum PubBaz { -LL | | PubBazA { a: isize }, -LL | | } - | |_^ - -error: missing documentation for a variant - --> $DIR/missing_doc.rs:45:5 - | -LL | PubBazA { a: isize }, - | ^^^^^^^^^^^^^^^^^^^^ - -error: missing documentation for a struct field - --> $DIR/missing_doc.rs:45:15 - | -LL | PubBazA { a: isize }, - | ^^^^^^^^ - error: missing documentation for a constant --> $DIR/missing_doc.rs:65:1 | LL | const FOO: u32 = 0; | ^^^^^^^^^^^^^^^^^^^ -error: missing documentation for a constant - --> $DIR/missing_doc.rs:72:1 - | -LL | pub const FOO4: u32 = 0; - | ^^^^^^^^^^^^^^^^^^^^^^^^ - error: missing documentation for a static --> $DIR/missing_doc.rs:74:1 | LL | static BAR: u32 = 0; | ^^^^^^^^^^^^^^^^^^^^ -error: missing documentation for a static - --> $DIR/missing_doc.rs:81:1 - | -LL | pub static BAR4: u32 = 0; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - error: missing documentation for a module --> $DIR/missing_doc.rs:83:1 | @@ -125,35 +75,17 @@ LL | | } LL | | } | |_^ -error: missing documentation for a function - --> $DIR/missing_doc.rs:86:5 - | -LL | pub fn undocumented1() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: missing documentation for a function - --> $DIR/missing_doc.rs:87:5 - | -LL | pub fn undocumented2() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - error: missing documentation for a function --> $DIR/missing_doc.rs:88:5 | LL | fn undocumented3() {} | ^^^^^^^^^^^^^^^^^^^^^ -error: missing documentation for a function - --> $DIR/missing_doc.rs:93:9 - | -LL | pub fn also_undocumented1() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: missing documentation for a function --> $DIR/missing_doc.rs:94:9 | LL | fn also_undocumented2() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 24 previous errors +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/missing_doc_impl.stderr b/src/tools/clippy/tests/ui/missing_doc_impl.stderr index b410f56e167..111d6546966 100644 --- a/src/tools/clippy/tests/ui/missing_doc_impl.stderr +++ b/src/tools/clippy/tests/ui/missing_doc_impl.stderr @@ -21,60 +21,12 @@ error: missing documentation for a struct field LL | b: isize, | ^^^^^^^^ -error: missing documentation for a struct - --> $DIR/missing_doc_impl.rs:18:1 - | -LL | / pub struct PubFoo { -LL | | pub a: isize, -LL | | b: isize, -LL | | } - | |_^ - -error: missing documentation for a struct field - --> $DIR/missing_doc_impl.rs:19:5 - | -LL | pub a: isize, - | ^^^^^^^^^^^^ - error: missing documentation for a struct field --> $DIR/missing_doc_impl.rs:20:5 | LL | b: isize, | ^^^^^^^^ -error: missing documentation for a trait - --> $DIR/missing_doc_impl.rs:43:1 - | -LL | / pub trait C { -LL | | fn foo(&self); -LL | | fn foo_with_impl(&self) {} -LL | | } - | |_^ - -error: missing documentation for a method - --> $DIR/missing_doc_impl.rs:44:5 - | -LL | fn foo(&self); - | ^^^^^^^^^^^^^^ - -error: missing documentation for a method - --> $DIR/missing_doc_impl.rs:45:5 - | -LL | fn foo_with_impl(&self) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: missing documentation for an associated type - --> $DIR/missing_doc_impl.rs:55:5 - | -LL | type AssociatedType; - | ^^^^^^^^^^^^^^^^^^^^ - -error: missing documentation for an associated type - --> $DIR/missing_doc_impl.rs:56:5 - | -LL | type AssociatedTypeDef = Self; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: missing documentation for an associated function --> $DIR/missing_doc_impl.rs:67:5 | @@ -89,12 +41,6 @@ error: missing documentation for an associated function LL | fn bar() {} | ^^^^^^^^^^^ -error: missing documentation for an associated function - --> $DIR/missing_doc_impl.rs:74:5 - | -LL | pub fn foo() {} - | ^^^^^^^^^^^^^^^ - error: missing documentation for an associated function --> $DIR/missing_doc_impl.rs:78:5 | @@ -103,5 +49,5 @@ LL | | 1 LL | | } | |_____^ -error: aborting due to 15 previous errors +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs index 4511bc99c3c..5073685c9f0 100644 --- a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs +++ b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs @@ -116,4 +116,32 @@ fn issue10259() { unsafe_macro!(); } +fn _fn_ptr(x: unsafe fn()) { + unsafe { + x(); + x(); + } +} + +fn _assoc_const() { + trait X { + const X: unsafe fn(); + } + fn _f() { + unsafe { + T::X(); + T::X(); + } + } +} + +fn _field_fn_ptr(x: unsafe fn()) { + struct X(unsafe fn()); + let x = X(x); + unsafe { + x.0(); + x.0(); + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr index 303aeb7aee0..e0c1d3801f7 100644 --- a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr +++ b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr @@ -125,5 +125,65 @@ note: raw pointer dereference occurs here LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } | ^^^^^^^^^^^^^^^^^^ -error: aborting due to 5 previous errors +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> $DIR/multiple_unsafe_ops_per_block.rs:120:5 + | +LL | / unsafe { +LL | | x(); +LL | | x(); +LL | | } + | |_____^ + | +note: unsafe function call occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:121:9 + | +LL | x(); + | ^^^ +note: unsafe function call occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:122:9 + | +LL | x(); + | ^^^ + +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> $DIR/multiple_unsafe_ops_per_block.rs:131:9 + | +LL | / unsafe { +LL | | T::X(); +LL | | T::X(); +LL | | } + | |_________^ + | +note: unsafe function call occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:132:13 + | +LL | T::X(); + | ^^^^^^ +note: unsafe function call occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:133:13 + | +LL | T::X(); + | ^^^^^^ + +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> $DIR/multiple_unsafe_ops_per_block.rs:141:5 + | +LL | / unsafe { +LL | | x.0(); +LL | | x.0(); +LL | | } + | |_____^ + | +note: unsafe function call occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:142:9 + | +LL | x.0(); + | ^^^^^ +note: unsafe function call occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:143:9 + | +LL | x.0(); + | ^^^^^ + +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/new_ret_no_self.rs b/src/tools/clippy/tests/ui/new_ret_no_self.rs index beec42f08bb..a2a30c8b931 100644 --- a/src/tools/clippy/tests/ui/new_ret_no_self.rs +++ b/src/tools/clippy/tests/ui/new_ret_no_self.rs @@ -406,7 +406,7 @@ mod issue10041 { struct Bomb; impl Bomb { - // Hidden default generic paramter. + // Hidden default generic parameter. pub fn new() -> impl PartialOrd { 0i32 } diff --git a/src/tools/clippy/tests/ui/no_effect.rs b/src/tools/clippy/tests/ui/no_effect.rs index f08eb092e6b..ec8a5aa28c5 100644 --- a/src/tools/clippy/tests/ui/no_effect.rs +++ b/src/tools/clippy/tests/ui/no_effect.rs @@ -1,4 +1,4 @@ -#![feature(box_syntax, fn_traits, unboxed_closures)] +#![feature(fn_traits, unboxed_closures)] #![warn(clippy::no_effect_underscore_binding)] #![allow(dead_code, path_statements)] #![allow(clippy::deref_addrof, clippy::redundant_field_names, clippy::uninlined_format_args)] @@ -102,7 +102,6 @@ fn main() { *&42; &6; (5, 6, 7); - box 42; ..; 5..; ..5; diff --git a/src/tools/clippy/tests/ui/no_effect.stderr b/src/tools/clippy/tests/ui/no_effect.stderr index 6a1e636f9a6..92f6dbfbdba 100644 --- a/src/tools/clippy/tests/ui/no_effect.stderr +++ b/src/tools/clippy/tests/ui/no_effect.stderr @@ -81,83 +81,77 @@ LL | (5, 6, 7); error: statement with no effect --> $DIR/no_effect.rs:105:5 | -LL | box 42; - | ^^^^^^^ - -error: statement with no effect - --> $DIR/no_effect.rs:106:5 - | LL | ..; | ^^^ error: statement with no effect - --> $DIR/no_effect.rs:107:5 + --> $DIR/no_effect.rs:106:5 | LL | 5..; | ^^^^ error: statement with no effect - --> $DIR/no_effect.rs:108:5 + --> $DIR/no_effect.rs:107:5 | LL | ..5; | ^^^^ error: statement with no effect - --> $DIR/no_effect.rs:109:5 + --> $DIR/no_effect.rs:108:5 | LL | 5..6; | ^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:110:5 + --> $DIR/no_effect.rs:109:5 | LL | 5..=6; | ^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:111:5 + --> $DIR/no_effect.rs:110:5 | LL | [42, 55]; | ^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:112:5 + --> $DIR/no_effect.rs:111:5 | LL | [42, 55][1]; | ^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:113:5 + --> $DIR/no_effect.rs:112:5 | LL | (42, 55).1; | ^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:114:5 + --> $DIR/no_effect.rs:113:5 | LL | [42; 55]; | ^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:115:5 + --> $DIR/no_effect.rs:114:5 | LL | [42; 55][13]; | ^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:117:5 + --> $DIR/no_effect.rs:116:5 | LL | || x += 5; | ^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:119:5 + --> $DIR/no_effect.rs:118:5 | LL | FooString { s: s }; | ^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:120:5 + --> $DIR/no_effect.rs:119:5 | LL | let _unused = 1; | ^^^^^^^^^^^^^^^^ @@ -165,22 +159,22 @@ LL | let _unused = 1; = note: `-D clippy::no-effect-underscore-binding` implied by `-D warnings` error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:121:5 + --> $DIR/no_effect.rs:120:5 | LL | let _penguin = || println!("Some helpful closure"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:122:5 + --> $DIR/no_effect.rs:121:5 | LL | let _duck = Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:123:5 + --> $DIR/no_effect.rs:122:5 | LL | let _cat = [2, 4, 6, 8][2]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 30 previous errors +error: aborting due to 29 previous errors diff --git a/src/tools/clippy/tests/ui/overflow_check_conditional.rs b/src/tools/clippy/tests/ui/overflow_check_conditional.rs index 5db75f5291b..e1e30114081 100644 --- a/src/tools/clippy/tests/ui/overflow_check_conditional.rs +++ b/src/tools/clippy/tests/ui/overflow_check_conditional.rs @@ -1,9 +1,6 @@ #![warn(clippy::overflow_check_conditional)] -fn main() { - let a: u32 = 1; - let b: u32 = 2; - let c: u32 = 3; +fn test(a: u32, b: u32, c: u32) { if a + b < a {} if a > a + b {} if a + b < b {} @@ -23,3 +20,7 @@ fn main() { if i > i + j {} if i - j < i {} } + +fn main() { + test(1, 2, 3) +} diff --git a/src/tools/clippy/tests/ui/overflow_check_conditional.stderr b/src/tools/clippy/tests/ui/overflow_check_conditional.stderr index 1b8b146b60a..92d1d8ef911 100644 --- a/src/tools/clippy/tests/ui/overflow_check_conditional.stderr +++ b/src/tools/clippy/tests/ui/overflow_check_conditional.stderr @@ -1,5 +1,5 @@ error: you are trying to use classic C overflow conditions that will fail in Rust - --> $DIR/overflow_check_conditional.rs:7:8 + --> $DIR/overflow_check_conditional.rs:4:8 | LL | if a + b < a {} | ^^^^^^^^^ @@ -7,43 +7,43 @@ LL | if a + b < a {} = note: `-D clippy::overflow-check-conditional` implied by `-D warnings` error: you are trying to use classic C overflow conditions that will fail in Rust - --> $DIR/overflow_check_conditional.rs:8:8 + --> $DIR/overflow_check_conditional.rs:5:8 | LL | if a > a + b {} | ^^^^^^^^^ error: you are trying to use classic C overflow conditions that will fail in Rust - --> $DIR/overflow_check_conditional.rs:9:8 + --> $DIR/overflow_check_conditional.rs:6:8 | LL | if a + b < b {} | ^^^^^^^^^ error: you are trying to use classic C overflow conditions that will fail in Rust - --> $DIR/overflow_check_conditional.rs:10:8 + --> $DIR/overflow_check_conditional.rs:7:8 | LL | if b > a + b {} | ^^^^^^^^^ error: you are trying to use classic C underflow conditions that will fail in Rust - --> $DIR/overflow_check_conditional.rs:11:8 + --> $DIR/overflow_check_conditional.rs:8:8 | LL | if a - b > b {} | ^^^^^^^^^ error: you are trying to use classic C underflow conditions that will fail in Rust - --> $DIR/overflow_check_conditional.rs:12:8 + --> $DIR/overflow_check_conditional.rs:9:8 | LL | if b < a - b {} | ^^^^^^^^^ error: you are trying to use classic C underflow conditions that will fail in Rust - --> $DIR/overflow_check_conditional.rs:13:8 + --> $DIR/overflow_check_conditional.rs:10:8 | LL | if a - b > a {} | ^^^^^^^^^ error: you are trying to use classic C underflow conditions that will fail in Rust - --> $DIR/overflow_check_conditional.rs:14:8 + --> $DIR/overflow_check_conditional.rs:11:8 | LL | if a < a - b {} | ^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/redundant_async_block.fixed b/src/tools/clippy/tests/ui/redundant_async_block.fixed new file mode 100644 index 00000000000..5f9931df45e --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_async_block.fixed @@ -0,0 +1,64 @@ +// run-rustfix + +#![allow(unused)] +#![warn(clippy::redundant_async_block)] + +async fn func1(n: usize) -> usize { + n + 1 +} + +async fn func2() -> String { + let s = String::from("some string"); + let f = async { (*s).to_owned() }; + let x = f; + x.await +} + +macro_rules! await_in_macro { + ($e:expr) => { + std::convert::identity($e).await + }; +} + +async fn func3(n: usize) -> usize { + // Do not lint (suggestion would be `std::convert::identity(func1(n))` + // which copies code from inside the macro) + async move { await_in_macro!(func1(n)) }.await +} + +// This macro should never be linted as `$e` might contain `.await` +macro_rules! async_await_parameter_in_macro { + ($e:expr) => { + async { $e.await } + }; +} + +// MISSED OPPORTUNITY: this macro could be linted as the `async` block does not +// contain code coming from the parameters +macro_rules! async_await_in_macro { + ($f:expr) => { + ($f)(async { func2().await }) + }; +} + +fn main() { + let fut1 = async { 17 }; + let fut2 = fut1; + + let fut1 = async { 25 }; + let fut2 = fut1; + + let fut = async { 42 }; + + // Do not lint: not a single expression + let fut = async { + func1(10).await; + func2().await + }; + + // Do not lint: expression contains `.await` + let fut = async { func1(func2().await.len()).await }; + + let fut = async_await_parameter_in_macro!(func2()); + let fut = async_await_in_macro!(std::convert::identity); +} diff --git a/src/tools/clippy/tests/ui/redundant_async_block.rs b/src/tools/clippy/tests/ui/redundant_async_block.rs new file mode 100644 index 00000000000..de3c9970c65 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_async_block.rs @@ -0,0 +1,64 @@ +// run-rustfix + +#![allow(unused)] +#![warn(clippy::redundant_async_block)] + +async fn func1(n: usize) -> usize { + n + 1 +} + +async fn func2() -> String { + let s = String::from("some string"); + let f = async { (*s).to_owned() }; + let x = async { f.await }; + x.await +} + +macro_rules! await_in_macro { + ($e:expr) => { + std::convert::identity($e).await + }; +} + +async fn func3(n: usize) -> usize { + // Do not lint (suggestion would be `std::convert::identity(func1(n))` + // which copies code from inside the macro) + async move { await_in_macro!(func1(n)) }.await +} + +// This macro should never be linted as `$e` might contain `.await` +macro_rules! async_await_parameter_in_macro { + ($e:expr) => { + async { $e.await } + }; +} + +// MISSED OPPORTUNITY: this macro could be linted as the `async` block does not +// contain code coming from the parameters +macro_rules! async_await_in_macro { + ($f:expr) => { + ($f)(async { func2().await }) + }; +} + +fn main() { + let fut1 = async { 17 }; + let fut2 = async { fut1.await }; + + let fut1 = async { 25 }; + let fut2 = async move { fut1.await }; + + let fut = async { async { 42 }.await }; + + // Do not lint: not a single expression + let fut = async { + func1(10).await; + func2().await + }; + + // Do not lint: expression contains `.await` + let fut = async { func1(func2().await.len()).await }; + + let fut = async_await_parameter_in_macro!(func2()); + let fut = async_await_in_macro!(std::convert::identity); +} diff --git a/src/tools/clippy/tests/ui/redundant_async_block.stderr b/src/tools/clippy/tests/ui/redundant_async_block.stderr new file mode 100644 index 00000000000..b16d96dce84 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_async_block.stderr @@ -0,0 +1,28 @@ +error: this async expression only awaits a single future + --> $DIR/redundant_async_block.rs:13:13 + | +LL | let x = async { f.await }; + | ^^^^^^^^^^^^^^^^^ help: you can reduce it to: `f` + | + = note: `-D clippy::redundant-async-block` implied by `-D warnings` + +error: this async expression only awaits a single future + --> $DIR/redundant_async_block.rs:46:16 + | +LL | let fut2 = async { fut1.await }; + | ^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `fut1` + +error: this async expression only awaits a single future + --> $DIR/redundant_async_block.rs:49:16 + | +LL | let fut2 = async move { fut1.await }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `fut1` + +error: this async expression only awaits a single future + --> $DIR/redundant_async_block.rs:51:15 + | +LL | let fut = async { async { 42 }.await }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `async { 42 }` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed index c0e49ff4caa..b987fd2ce6f 100644 --- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed +++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed @@ -2,6 +2,7 @@ #![feature(async_closure)] #![warn(clippy::redundant_closure_call)] +#![allow(clippy::redundant_async_block)] #![allow(unused)] async fn something() -> u32 { diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs index 9e6e54348a8..633a2979d5d 100644 --- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs +++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs @@ -2,6 +2,7 @@ #![feature(async_closure)] #![warn(clippy::redundant_closure_call)] +#![allow(clippy::redundant_async_block)] #![allow(unused)] async fn something() -> u32 { diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr index d71bcba2a82..8a1f0771659 100644 --- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr +++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr @@ -1,5 +1,5 @@ error: try not to call a closure in the expression where it is declared - --> $DIR/redundant_closure_call_fixable.rs:16:13 + --> $DIR/redundant_closure_call_fixable.rs:17:13 | LL | let a = (|| 42)(); | ^^^^^^^^^ help: try doing something like: `42` @@ -7,7 +7,7 @@ LL | let a = (|| 42)(); = note: `-D clippy::redundant-closure-call` implied by `-D warnings` error: try not to call a closure in the expression where it is declared - --> $DIR/redundant_closure_call_fixable.rs:17:13 + --> $DIR/redundant_closure_call_fixable.rs:18:13 | LL | let b = (async || { | _____________^ @@ -27,7 +27,7 @@ LL ~ }; | error: try not to call a closure in the expression where it is declared - --> $DIR/redundant_closure_call_fixable.rs:22:13 + --> $DIR/redundant_closure_call_fixable.rs:23:13 | LL | let c = (|| { | _____________^ @@ -47,13 +47,13 @@ LL ~ }; | error: try not to call a closure in the expression where it is declared - --> $DIR/redundant_closure_call_fixable.rs:27:13 + --> $DIR/redundant_closure_call_fixable.rs:28:13 | LL | let d = (async || something().await)(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async { something().await }` error: try not to call a closure in the expression where it is declared - --> $DIR/redundant_closure_call_fixable.rs:36:13 + --> $DIR/redundant_closure_call_fixable.rs:37:13 | LL | (|| m!())() | ^^^^^^^^^^^ help: try doing something like: `m!()` @@ -64,7 +64,7 @@ LL | m2!(); = note: this error originates in the macro `m2` (in Nightly builds, run with -Z macro-backtrace for more info) error: try not to call a closure in the expression where it is declared - --> $DIR/redundant_closure_call_fixable.rs:31:13 + --> $DIR/redundant_closure_call_fixable.rs:32:13 | LL | (|| 0)() | ^^^^^^^^ help: try doing something like: `0` diff --git a/src/tools/clippy/tests/ui/swap.fixed b/src/tools/clippy/tests/ui/swap.fixed index fa89706a815..04008c0d9b3 100644 --- a/src/tools/clippy/tests/ui/swap.fixed +++ b/src/tools/clippy/tests/ui/swap.fixed @@ -65,19 +65,19 @@ fn xor_swap_locals() { // This is an xor-based swap of local variables. let mut a = 0; let mut b = 1; - std::mem::swap(&mut a, &mut b) + std::mem::swap(&mut a, &mut b); } fn xor_field_swap() { // This is an xor-based swap of fields in a struct. let mut bar = Bar { a: 0, b: 1 }; - std::mem::swap(&mut bar.a, &mut bar.b) + std::mem::swap(&mut bar.a, &mut bar.b); } fn xor_slice_swap() { // This is an xor-based swap of a slice let foo = &mut [1, 2]; - foo.swap(0, 1) + foo.swap(0, 1); } fn xor_no_swap() { diff --git a/src/tools/clippy/tests/ui/swap.stderr b/src/tools/clippy/tests/ui/swap.stderr index f0acbfe253f..825c9261e19 100644 --- a/src/tools/clippy/tests/ui/swap.stderr +++ b/src/tools/clippy/tests/ui/swap.stderr @@ -4,7 +4,7 @@ error: this looks like you are swapping `bar.a` and `bar.b` manually LL | / let temp = bar.a; LL | | bar.a = bar.b; LL | | bar.b = temp; - | |________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b)` + | |_________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b);` | = note: or maybe you should use `std::mem::replace`? = note: `-D clippy::manual-swap` implied by `-D warnings` @@ -15,7 +15,7 @@ error: this looks like you are swapping elements of `foo` manually LL | / let temp = foo[0]; LL | | foo[0] = foo[1]; LL | | foo[1] = temp; - | |_________________^ help: try: `foo.swap(0, 1)` + | |__________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping elements of `foo` manually --> $DIR/swap.rs:46:5 @@ -23,7 +23,7 @@ error: this looks like you are swapping elements of `foo` manually LL | / let temp = foo[0]; LL | | foo[0] = foo[1]; LL | | foo[1] = temp; - | |_________________^ help: try: `foo.swap(0, 1)` + | |__________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping elements of `foo` manually --> $DIR/swap.rs:65:5 @@ -31,7 +31,7 @@ error: this looks like you are swapping elements of `foo` manually LL | / let temp = foo[0]; LL | | foo[0] = foo[1]; LL | | foo[1] = temp; - | |_________________^ help: try: `foo.swap(0, 1)` + | |__________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping `a` and `b` manually --> $DIR/swap.rs:76:5 @@ -39,7 +39,7 @@ error: this looks like you are swapping `a` and `b` manually LL | / a ^= b; LL | | b ^= a; LL | | a ^= b; - | |___________^ help: try: `std::mem::swap(&mut a, &mut b)` + | |___________^ help: try: `std::mem::swap(&mut a, &mut b);` error: this looks like you are swapping `bar.a` and `bar.b` manually --> $DIR/swap.rs:84:5 @@ -47,7 +47,7 @@ error: this looks like you are swapping `bar.a` and `bar.b` manually LL | / bar.a ^= bar.b; LL | | bar.b ^= bar.a; LL | | bar.a ^= bar.b; - | |___________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b)` + | |___________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b);` error: this looks like you are swapping elements of `foo` manually --> $DIR/swap.rs:92:5 @@ -55,7 +55,7 @@ error: this looks like you are swapping elements of `foo` manually LL | / foo[0] ^= foo[1]; LL | | foo[1] ^= foo[0]; LL | | foo[0] ^= foo[1]; - | |_____________________^ help: try: `foo.swap(0, 1)` + | |_____________________^ help: try: `foo.swap(0, 1);` error: this looks like you are swapping `foo[0][1]` and `bar[1][0]` manually --> $DIR/swap.rs:121:5 @@ -63,7 +63,7 @@ error: this looks like you are swapping `foo[0][1]` and `bar[1][0]` manually LL | / let temp = foo[0][1]; LL | | foo[0][1] = bar[1][0]; LL | | bar[1][0] = temp; - | |____________________^ help: try: `std::mem::swap(&mut foo[0][1], &mut bar[1][0])` + | |_____________________^ help: try: `std::mem::swap(&mut foo[0][1], &mut bar[1][0]);` | = note: or maybe you should use `std::mem::replace`? @@ -74,7 +74,7 @@ LL | ; let t = a; | _______^ LL | | a = b; LL | | b = t; - | |_________^ help: try: `std::mem::swap(&mut a, &mut b)` + | |__________^ help: try: `std::mem::swap(&mut a, &mut b);` | = note: or maybe you should use `std::mem::replace`? @@ -85,7 +85,7 @@ LL | ; let t = c.0; | _______^ LL | | c.0 = a; LL | | a = t; - | |_________^ help: try: `std::mem::swap(&mut c.0, &mut a)` + | |__________^ help: try: `std::mem::swap(&mut c.0, &mut a);` | = note: or maybe you should use `std::mem::replace`? @@ -95,7 +95,7 @@ error: this looks like you are swapping `b` and `a` manually LL | / let t = b; LL | | b = a; LL | | a = t; - | |_________^ help: try: `std::mem::swap(&mut b, &mut a)` + | |__________^ help: try: `std::mem::swap(&mut b, &mut a);` | = note: or maybe you should use `std::mem::replace`? @@ -151,7 +151,7 @@ error: this looks like you are swapping `s.0.x` and `s.0.y` manually LL | / let t = s.0.x; LL | | s.0.x = s.0.y; LL | | s.0.y = t; - | |_____________^ help: try: `std::mem::swap(&mut s.0.x, &mut s.0.y)` + | |______________^ help: try: `std::mem::swap(&mut s.0.x, &mut s.0.y);` | = note: or maybe you should use `std::mem::replace`? diff --git a/src/tools/clippy/tests/ui/trailing_empty_array.rs b/src/tools/clippy/tests/ui/trailing_empty_array.rs index c39b0bcaf22..8e3749eef35 100644 --- a/src/tools/clippy/tests/ui/trailing_empty_array.rs +++ b/src/tools/clippy/tests/ui/trailing_empty_array.rs @@ -155,7 +155,6 @@ struct TupleStructReprC(i32, [usize; 0]); type NamedTuple = (i32, [usize; 0]); -#[rustfmt::skip] // [rustfmt#4995](https://github.com/rust-lang/rustfmt/issues/4995) struct ConstParamZeroDefault { field: i32, last: [usize; N], @@ -166,7 +165,6 @@ struct ConstParamNoDefault { last: [usize; N], } -#[rustfmt::skip] struct ConstParamNonZeroDefault { field: i32, last: [usize; N], diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.fixed b/src/tools/clippy/tests/ui/unnecessary_operation.fixed index d37163570ab..65d9c910b82 100644 --- a/src/tools/clippy/tests/ui/unnecessary_operation.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_operation.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(box_syntax)] #![allow(clippy::deref_addrof, dead_code, unused, clippy::no_effect)] #![warn(clippy::unnecessary_operation)] @@ -59,7 +58,6 @@ fn main() { 5;6;get_number(); get_number(); get_number(); - get_number(); 5;get_number(); 42;get_number(); assert!([42, 55].len() > get_usize()); diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.rs b/src/tools/clippy/tests/ui/unnecessary_operation.rs index a14fd4bca0e..4e2acd59f04 100644 --- a/src/tools/clippy/tests/ui/unnecessary_operation.rs +++ b/src/tools/clippy/tests/ui/unnecessary_operation.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(box_syntax)] #![allow(clippy::deref_addrof, dead_code, unused, clippy::no_effect)] #![warn(clippy::unnecessary_operation)] @@ -57,7 +56,6 @@ fn main() { *&get_number(); &get_number(); (5, 6, get_number()); - box get_number(); get_number()..; ..get_number(); 5..get_number(); diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.stderr b/src/tools/clippy/tests/ui/unnecessary_operation.stderr index f66d08ecb82..44cf2e01ff7 100644 --- a/src/tools/clippy/tests/ui/unnecessary_operation.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_operation.stderr @@ -1,5 +1,5 @@ error: unnecessary operation - --> $DIR/unnecessary_operation.rs:51:5 + --> $DIR/unnecessary_operation.rs:50:5 | LL | Tuple(get_number()); | ^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` @@ -7,109 +7,103 @@ LL | Tuple(get_number()); = note: `-D clippy::unnecessary-operation` implied by `-D warnings` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:52:5 + --> $DIR/unnecessary_operation.rs:51:5 | LL | Struct { field: get_number() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:53:5 + --> $DIR/unnecessary_operation.rs:52:5 | LL | Struct { ..get_struct() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_struct();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:54:5 + --> $DIR/unnecessary_operation.rs:53:5 | LL | Enum::Tuple(get_number()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:55:5 + --> $DIR/unnecessary_operation.rs:54:5 | LL | Enum::Struct { field: get_number() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:56:5 + --> $DIR/unnecessary_operation.rs:55:5 | LL | 5 + get_number(); | ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:57:5 + --> $DIR/unnecessary_operation.rs:56:5 | LL | *&get_number(); | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:58:5 + --> $DIR/unnecessary_operation.rs:57:5 | LL | &get_number(); | ^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:59:5 + --> $DIR/unnecessary_operation.rs:58:5 | LL | (5, 6, get_number()); | ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;6;get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:60:5 - | -LL | box get_number(); - | ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` - -error: unnecessary operation - --> $DIR/unnecessary_operation.rs:61:5 + --> $DIR/unnecessary_operation.rs:59:5 | LL | get_number()..; | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:62:5 + --> $DIR/unnecessary_operation.rs:60:5 | LL | ..get_number(); | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:63:5 + --> $DIR/unnecessary_operation.rs:61:5 | LL | 5..get_number(); | ^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:64:5 + --> $DIR/unnecessary_operation.rs:62:5 | LL | [42, get_number()]; | ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:65:5 + --> $DIR/unnecessary_operation.rs:63:5 | LL | [42, 55][get_usize()]; | ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42, 55].len() > get_usize());` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:66:5 + --> $DIR/unnecessary_operation.rs:64:5 | LL | (42, get_number()).1; | ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:67:5 + --> $DIR/unnecessary_operation.rs:65:5 | LL | [get_number(); 55]; | ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:68:5 + --> $DIR/unnecessary_operation.rs:66:5 | LL | [42; 55][get_usize()]; | ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42; 55].len() > get_usize());` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:69:5 + --> $DIR/unnecessary_operation.rs:67:5 | LL | / { LL | | get_number() @@ -117,12 +111,12 @@ LL | | }; | |______^ help: statement can be reduced to: `get_number();` error: unnecessary operation - --> $DIR/unnecessary_operation.rs:72:5 + --> $DIR/unnecessary_operation.rs:70:5 | LL | / FooString { LL | | s: String::from("blah"), LL | | }; | |______^ help: statement can be reduced to: `String::from("blah");` -error: aborting due to 20 previous errors +error: aborting due to 19 previous errors diff --git a/src/tools/clippy/tests/ui/use_self.fixed b/src/tools/clippy/tests/ui/use_self.fixed index 0a6166571eb..3ac6217312a 100644 --- a/src/tools/clippy/tests/ui/use_self.fixed +++ b/src/tools/clippy/tests/ui/use_self.fixed @@ -647,3 +647,13 @@ fn msrv_1_37() { } } } + +mod issue_10371 { + struct Val {} + + impl From> for i32 { + fn from(_: Val) -> Self { + todo!() + } + } +} diff --git a/src/tools/clippy/tests/ui/use_self.rs b/src/tools/clippy/tests/ui/use_self.rs index 39c2b431f7f..9dc5d1e3f9b 100644 --- a/src/tools/clippy/tests/ui/use_self.rs +++ b/src/tools/clippy/tests/ui/use_self.rs @@ -647,3 +647,13 @@ fn msrv_1_37() { } } } + +mod issue_10371 { + struct Val {} + + impl From> for i32 { + fn from(_: Val) -> Self { + todo!() + } + } +} diff --git a/src/tools/collect-license-metadata/src/licenses.rs b/src/tools/collect-license-metadata/src/licenses.rs index 1c95b1bc8e9..2855069db0d 100644 --- a/src/tools/collect-license-metadata/src/licenses.rs +++ b/src/tools/collect-license-metadata/src/licenses.rs @@ -42,6 +42,7 @@ pub(crate) struct License { impl License { fn simplify(&mut self) { self.remove_copyright_prefixes(); + self.remove_trailing_dots(); self.copyright.sort(); self.copyright.dedup(); } @@ -62,4 +63,12 @@ impl License { *copyright = stripped.into(); } } + + fn remove_trailing_dots(&mut self) { + for copyright in &mut self.copyright { + if copyright.ends_with('.') { + *copyright = copyright.trim_end_matches('.').to_string(); + } + } + } } diff --git a/src/tools/collect-license-metadata/src/path_tree.rs b/src/tools/collect-license-metadata/src/path_tree.rs index 133ff683737..7a2a440636d 100644 --- a/src/tools/collect-license-metadata/src/path_tree.rs +++ b/src/tools/collect-license-metadata/src/path_tree.rs @@ -13,7 +13,7 @@ pub(crate) enum Node { Root { childs: Vec> }, Directory { name: PathBuf, childs: Vec>, license: Option }, File { name: PathBuf, license: L }, - FileGroup { names: Vec, license: L }, + Group { files: Vec, directories: Vec, license: L }, Empty, } @@ -22,7 +22,7 @@ impl Node { self.merge_directories(); self.collapse_in_licensed_directories(); self.merge_directory_licenses(); - self.merge_file_groups(); + self.merge_groups(); self.remove_empty(); } @@ -64,8 +64,8 @@ impl Node { Node::Root { .. } => { panic!("can't have a root inside another element"); } - Node::FileGroup { .. } => { - panic!("FileGroup should not be present at this stage"); + Node::Group { .. } => { + panic!("Group should not be present at this stage"); } Node::Directory { license: Some(_), .. } => { panic!("license should not be set at this stage"); @@ -86,8 +86,8 @@ impl Node { } Node::Empty => {} Node::File { .. } => {} - Node::FileGroup { .. } => { - panic!("FileGroup should not be present at this stage"); + Node::Group { .. } => { + panic!("Group should not be present at this stage"); } Node::Directory { license: Some(_), .. } => { panic!("license should not be set at this stage"); @@ -132,7 +132,7 @@ impl Node { } } Node::File { .. } => {} - Node::FileGroup { .. } => {} + Node::Group { .. } => panic!("group should not be present at this stage"), Node::Empty => {} } } @@ -165,8 +165,8 @@ impl Node { Node::Root { .. } => { panic!("can't have a root inside another element"); } - Node::FileGroup { .. } => { - panic!("FileGroup should not be present at this stage"); + Node::Group { .. } => { + panic!("Group should not be present at this stage"); } Node::Directory { name: child_child_name, .. } => { *child_child_name = child_name.join(&child_child_name); @@ -185,38 +185,74 @@ impl Node { } Node::Empty => {} Node::File { .. } => {} - Node::FileGroup { .. } => {} + Node::Group { .. } => panic!("Group should not be present at this stage"), } } /// This pass groups multiple files in a directory with the same license into a single - /// "FileGroup", so that the license of all those files can be reported as a group. + /// "Group", so that the license of all those files can be reported as a group. + /// + /// This also merges directories *without exceptions*. /// /// Crucially this pass runs after collapse_in_licensed_directories, so the most common license /// will already be marked as the directory's license and won't be turned into a group. - fn merge_file_groups(&mut self) { + fn merge_groups(&mut self) { + #[derive(Default)] + struct Grouped { + files: Vec, + directories: Vec, + } match self { Node::Root { childs } | Node::Directory { childs, .. } => { - let mut grouped = BTreeMap::new(); + let mut grouped: BTreeMap = BTreeMap::new(); for child in &mut *childs { - child.merge_file_groups(); - if let Node::File { name, license } = child { - grouped.entry(*license).or_insert_with(Vec::new).push(name.clone()); - *child = Node::Empty; + child.merge_groups(); + match child { + Node::Directory { name, childs, license: Some(license) } => { + if childs.is_empty() { + grouped + .entry(*license) + .or_insert_with(Grouped::default) + .directories + .push(name.clone()); + *child = Node::Empty; + } + } + Node::File { name, license } => { + grouped + .entry(*license) + .or_insert_with(Grouped::default) + .files + .push(name.clone()); + *child = Node::Empty; + } + _ => {} } } - for (license, mut names) in grouped.into_iter() { - if names.len() == 1 { - childs.push(Node::File { license, name: names.pop().unwrap() }); + for (license, mut grouped) in grouped.into_iter() { + if grouped.files.len() + grouped.directories.len() <= 1 { + if let Some(name) = grouped.files.pop() { + childs.push(Node::File { license, name }); + } else if let Some(name) = grouped.directories.pop() { + childs.push(Node::Directory { + name, + childs: Vec::new(), + license: Some(license), + }); + } } else { - childs.push(Node::FileGroup { license, names }); + childs.push(Node::Group { + license, + files: grouped.files, + directories: grouped.directories, + }); } } } Node::File { .. } => {} - Node::FileGroup { .. } => panic!("FileGroup should not be present at this stage"), + Node::Group { .. } => panic!("FileGroup should not be present at this stage"), Node::Empty => {} } } @@ -231,7 +267,7 @@ impl Node { } childs.retain(|child| !matches!(child, Node::Empty)); } - Node::FileGroup { .. } => {} + Node::Group { .. } => {} Node::File { .. } => {} Node::Empty => {} } @@ -278,16 +314,22 @@ pub(crate) fn expand_interned_licenses( ) -> Node<&License> { match node { Node::Root { childs } => Node::Root { - childs: childs.into_iter().map(|child| strip_interning(child, interner)).collect(), + childs: childs + .into_iter() + .map(|child| expand_interned_licenses(child, interner)) + .collect(), }, Node::Directory { name, childs, license } => Node::Directory { - childs: childs.into_iter().map(|child| strip_interning(child, interner)).collect(), + childs: childs + .into_iter() + .map(|child| expand_interned_licenses(child, interner)) + .collect(), license: license.map(|license| interner.resolve(license)), name, }, Node::File { name, license } => Node::File { name, license: interner.resolve(license) }, - Node::FileGroup { names, license } => { - Node::FileGroup { names, license: interner.resolve(license) } + Node::Group { files, directories, license } => { + Node::Group { files, directories, license: interner.resolve(license) } } Node::Empty => Node::Empty, } diff --git a/src/tools/generate-copyright/src/main.rs b/src/tools/generate-copyright/src/main.rs index d172c9e157b..4d116c7da65 100644 --- a/src/tools/generate-copyright/src/main.rs +++ b/src/tools/generate-copyright/src/main.rs @@ -36,8 +36,8 @@ fn render_recursive(node: &Node, buffer: &mut Vec, depth: usize) -> Result<( } } } - Node::FileGroup { names, license } => { - render_license(&prefix, names.iter(), license, buffer)?; + Node::Group { files, directories, license } => { + render_license(&prefix, directories.iter().chain(files.iter()), license, buffer)?; } Node::File { name, license } => { render_license(&prefix, std::iter::once(name), license, buffer)?; @@ -76,7 +76,7 @@ pub(crate) enum Node { Root { childs: Vec }, Directory { name: String, childs: Vec, license: License }, File { name: String, license: License }, - FileGroup { names: Vec, license: License }, + Group { files: Vec, directories: Vec, license: License }, } #[derive(serde::Deserialize)] diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 301b322b285..0c6208f3ea7 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -ff4b772f805ec1e1c1bd7e189ab8d5a4e3a6ef13 +1716932743a7b3705cbf0c34db0c4e070ed1930d diff --git a/src/tools/miri/tests/fail/function_pointers/execute_memory.rs b/src/tools/miri/tests/fail/function_pointers/execute_memory.rs index 967933e769b..89d8d22a9dd 100644 --- a/src/tools/miri/tests/fail/function_pointers/execute_memory.rs +++ b/src/tools/miri/tests/fail/function_pointers/execute_memory.rs @@ -1,10 +1,8 @@ // Validation makes this fail in the wrong place //@compile-flags: -Zmiri-disable-validation -#![feature(box_syntax)] - fn main() { - let x = box 42; + let x = Box::new(42); unsafe { let f = std::mem::transmute::, fn()>(x); f() //~ ERROR: function pointer but it does not point to a function diff --git a/src/tools/miri/tests/pass/drop_empty_slice.rs b/src/tools/miri/tests/pass/drop_empty_slice.rs index 9805ce0ace3..0413ed1fd0c 100644 --- a/src/tools/miri/tests/pass/drop_empty_slice.rs +++ b/src/tools/miri/tests/pass/drop_empty_slice.rs @@ -1,7 +1,5 @@ -#![feature(box_syntax)] - fn main() { // With the nested Vec, this is calling Offset(Unique::empty(), 0) on drop. let args: Vec> = Vec::new(); - let _val = box args; + let _val = Box::new(args); } diff --git a/src/tools/miri/tests/pass/dst-struct.rs b/src/tools/miri/tests/pass/dst-struct.rs index 7191068eb2c..59763bbbfdd 100644 --- a/src/tools/miri/tests/pass/dst-struct.rs +++ b/src/tools/miri/tests/pass/dst-struct.rs @@ -1,5 +1,3 @@ -#![feature(box_syntax)] - struct Fat { f1: isize, f2: &'static str, @@ -109,7 +107,7 @@ pub fn main() { assert_eq!((*f2)[1], 2); // Nested Box. - let f1: Box> = box Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] }; + let f1: Box> = Box::new(Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] }); foo(&*f1); let f2: Box> = f1; foo(&*f2); @@ -117,6 +115,6 @@ pub fn main() { let f3: Box> = Box::>::new(Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] }); foo(&*f3); - let f4: Box> = box Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] }; + let f4: Box> = Box::new(Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] }); foo(&*f4); } diff --git a/src/tools/miri/tests/pass/heap.rs b/src/tools/miri/tests/pass/heap.rs index 44537e74b5a..44e3a60cfe1 100644 --- a/src/tools/miri/tests/pass/heap.rs +++ b/src/tools/miri/tests/pass/heap.rs @@ -1,13 +1,7 @@ -#![feature(box_syntax)] - fn make_box() -> Box<(i16, i16)> { Box::new((1, 2)) } -fn make_box_syntax() -> Box<(i16, i16)> { - box (1, 2) -} - fn allocate_reallocate() { let mut s = String::new(); @@ -29,6 +23,5 @@ fn allocate_reallocate() { fn main() { assert_eq!(*make_box(), (1, 2)); - assert_eq!(*make_box_syntax(), (1, 2)); allocate_reallocate(); } diff --git a/src/tools/miri/tests/pass/intrinsics-integer.rs b/src/tools/miri/tests/pass/intrinsics-integer.rs index 546931f6ff8..13e7bd8e1b9 100644 --- a/src/tools/miri/tests/pass/intrinsics-integer.rs +++ b/src/tools/miri/tests/pass/intrinsics-integer.rs @@ -1,12 +1,5 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. +// SPDX-License-Identifier: MIT OR Apache-2.0 +// SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org) #![feature(core_intrinsics)] use std::intrinsics::*; diff --git a/src/tools/miri/tests/pass/intrinsics-math.rs b/src/tools/miri/tests/pass/intrinsics-math.rs index 5973f4cd197..9f2dc333f33 100644 --- a/src/tools/miri/tests/pass/intrinsics-math.rs +++ b/src/tools/miri/tests/pass/intrinsics-math.rs @@ -1,12 +1,5 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. +// SPDX-License-Identifier: MIT OR Apache-2.0 +// SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org) macro_rules! assert_approx_eq { ($a:expr, $b:expr) => {{ diff --git a/src/tools/miri/tests/pass/issues/issue-30530.rs b/src/tools/miri/tests/pass/issues/issue-30530.rs index 472b42adaac..b50a43ffd83 100644 --- a/src/tools/miri/tests/pass/issues/issue-30530.rs +++ b/src/tools/miri/tests/pass/issues/issue-30530.rs @@ -1,12 +1,5 @@ -// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. +// SPDX-License-Identifier: MIT OR Apache-2.0 +// SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org) // Regression test for Issue #30530: alloca's created for storing // intermediate scratch values during brace-less match arms need to be diff --git a/src/tools/miri/tests/pass/issues/issue-3794.rs b/src/tools/miri/tests/pass/issues/issue-3794.rs index 5b5b22b5494..860d72bb586 100644 --- a/src/tools/miri/tests/pass/issues/issue-3794.rs +++ b/src/tools/miri/tests/pass/issues/issue-3794.rs @@ -1,5 +1,3 @@ -#![feature(box_syntax)] - trait T { fn print(&self); } @@ -25,7 +23,7 @@ fn print_s(s: &S) { } pub fn main() { - let s: Box = box S { s: 5 }; + let s: Box = Box::new(S { s: 5 }); print_s(&*s); let t: Box = s as Box; print_t(&*t); diff --git a/src/tools/miri/tests/pass/move-arg-2-unique.rs b/src/tools/miri/tests/pass/move-arg-2-unique.rs index 669602ac704..de21d67eb4f 100644 --- a/src/tools/miri/tests/pass/move-arg-2-unique.rs +++ b/src/tools/miri/tests/pass/move-arg-2-unique.rs @@ -1,11 +1,9 @@ -#![feature(box_syntax)] - fn test(foo: Box>) { assert_eq!((*foo)[0], 10); } pub fn main() { - let x = box vec![10]; + let x = Box::new(vec![10]); // Test forgetting a local by move-in test(x); } diff --git a/src/tools/miri/tests/pass/move-arg-3-unique.rs b/src/tools/miri/tests/pass/move-arg-3-unique.rs index 3b5c7cbbd42..6025481c32e 100644 --- a/src/tools/miri/tests/pass/move-arg-3-unique.rs +++ b/src/tools/miri/tests/pass/move-arg-3-unique.rs @@ -1,7 +1,5 @@ -#![feature(box_syntax)] - pub fn main() { - let x = box 10; + let x = Box::new(10); let y = x; assert_eq!(*y, 10); } diff --git a/src/tools/miri/tests/pass/mpsc.rs b/src/tools/miri/tests/pass/mpsc.rs index 6e3c6e771cc..3824a0de907 100644 --- a/src/tools/miri/tests/pass/mpsc.rs +++ b/src/tools/miri/tests/pass/mpsc.rs @@ -1,15 +1,13 @@ -#![feature(box_syntax)] - use std::sync::mpsc::channel; pub fn main() { let (tx, rx) = channel::>(); - tx.send(box 100).unwrap(); + tx.send(Box::new(100)).unwrap(); let v = rx.recv().unwrap(); - assert_eq!(v, box 100); + assert_eq!(v, Box::new(100)); - tx.send(box 101).unwrap(); - tx.send(box 102).unwrap(); - assert_eq!(rx.recv().unwrap(), box 101); - assert_eq!(rx.recv().unwrap(), box 102); + tx.send(Box::new(101)).unwrap(); + tx.send(Box::new(102)).unwrap(); + assert_eq!(rx.recv().unwrap(), Box::new(101)); + assert_eq!(rx.recv().unwrap(), Box::new(102)); } diff --git a/src/tools/miri/tests/pass/regions-lifetime-nonfree-late-bound.rs b/src/tools/miri/tests/pass/regions-lifetime-nonfree-late-bound.rs index c91ac36ed6b..445dd43febb 100644 --- a/src/tools/miri/tests/pass/regions-lifetime-nonfree-late-bound.rs +++ b/src/tools/miri/tests/pass/regions-lifetime-nonfree-late-bound.rs @@ -12,8 +12,6 @@ // doing region-folding, when really all clients of the region-folding // case only want to see *free* lifetime variables, not bound ones. -#![feature(box_syntax)] - pub fn main() { fn explicit() { fn test(_x: Option>) @@ -21,7 +19,7 @@ pub fn main() { F: FnMut(Box FnMut(&'a isize)>), { } - test(Some(box |_f: Box FnMut(&'a isize)>| {})); + test(Some(Box::new(|_f: Box FnMut(&'a isize)>| {}))); } // The code below is shorthand for the code above (and more likely @@ -32,7 +30,7 @@ pub fn main() { F: FnMut(Box), { } - test(Some(box |_f: Box| {})); + test(Some(Box::new(|_f: Box| {}))); } explicit(); diff --git a/src/tools/miri/tests/pass/tag-align-dyn-u64.rs b/src/tools/miri/tests/pass/tag-align-dyn-u64.rs index 72211a8d3f3..81a43cc8bcc 100644 --- a/src/tools/miri/tests/pass/tag-align-dyn-u64.rs +++ b/src/tools/miri/tests/pass/tag-align-dyn-u64.rs @@ -1,12 +1,5 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. +// SPDX-License-Identifier: MIT OR Apache-2.0 +// SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org) use std::mem; diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index ec197767259..fc77515b63b 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -572,6 +572,7 @@ dependencies = [ "chalk-recursive", "chalk-solve", "cov-mark", + "either", "ena", "expect-test", "hir-def", @@ -1714,6 +1715,7 @@ name = "syntax" version = "0.0.0" dependencies = [ "cov-mark", + "either", "expect-test", "indexmap", "itertools", diff --git a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs index 11f7b068ecb..accb14a51de 100644 --- a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs +++ b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs @@ -76,7 +76,7 @@ impl fmt::Display for FlycheckConfig { #[derive(Debug)] pub struct FlycheckHandle { // XXX: drop order is significant - sender: Sender, + sender: Sender, _thread: jod_thread::JoinHandle, id: usize, } @@ -89,7 +89,7 @@ impl FlycheckHandle { workspace_root: AbsPathBuf, ) -> FlycheckHandle { let actor = FlycheckActor::new(id, sender, config, workspace_root); - let (sender, receiver) = unbounded::(); + let (sender, receiver) = unbounded::(); let thread = jod_thread::Builder::new() .name("Flycheck".to_owned()) .spawn(move || actor.run(receiver)) @@ -99,12 +99,12 @@ impl FlycheckHandle { /// Schedule a re-start of the cargo check worker. pub fn restart(&self) { - self.sender.send(Restart::Yes).unwrap(); + self.sender.send(StateChange::Restart).unwrap(); } /// Stop this cargo check worker. pub fn cancel(&self) { - self.sender.send(Restart::No).unwrap(); + self.sender.send(StateChange::Cancel).unwrap(); } pub fn id(&self) -> usize { @@ -149,9 +149,9 @@ pub enum Progress { DidFailToRestart(String), } -enum Restart { - Yes, - No, +enum StateChange { + Restart, + Cancel, } /// A [`FlycheckActor`] is a single check instance of a workspace. @@ -172,7 +172,7 @@ struct FlycheckActor { } enum Event { - Restart(Restart), + RequestStateChange(StateChange), CheckEvent(Option), } @@ -191,30 +191,31 @@ impl FlycheckActor { self.send(Message::Progress { id: self.id, progress }); } - fn next_event(&self, inbox: &Receiver) -> Option { + fn next_event(&self, inbox: &Receiver) -> Option { let check_chan = self.cargo_handle.as_ref().map(|cargo| &cargo.receiver); if let Ok(msg) = inbox.try_recv() { // give restarts a preference so check outputs don't block a restart or stop - return Some(Event::Restart(msg)); + return Some(Event::RequestStateChange(msg)); } select! { - recv(inbox) -> msg => msg.ok().map(Event::Restart), + recv(inbox) -> msg => msg.ok().map(Event::RequestStateChange), recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())), } } - fn run(mut self, inbox: Receiver) { + fn run(mut self, inbox: Receiver) { 'event: while let Some(event) = self.next_event(&inbox) { match event { - Event::Restart(Restart::No) => { + Event::RequestStateChange(StateChange::Cancel) => { + tracing::debug!(flycheck_id = self.id, "flycheck cancelled"); self.cancel_check_process(); } - Event::Restart(Restart::Yes) => { + Event::RequestStateChange(StateChange::Restart) => { // Cancel the previously spawned process self.cancel_check_process(); while let Ok(restart) = inbox.recv_timeout(Duration::from_millis(50)) { // restart chained with a stop, so just cancel - if let Restart::No = restart { + if let StateChange::Cancel = restart { continue 'event; } } @@ -255,10 +256,20 @@ impl FlycheckActor { } Event::CheckEvent(Some(message)) => match message { CargoMessage::CompilerArtifact(msg) => { + tracing::trace!( + flycheck_id = self.id, + artifact = msg.target.name, + "artifact received" + ); self.report_progress(Progress::DidCheckCrate(msg.target.name)); } CargoMessage::Diagnostic(msg) => { + tracing::trace!( + flycheck_id = self.id, + message = msg.message, + "diagnostic received" + ); self.send(Message::AddDiagnostic { id: self.id, workspace_root: self.root.clone(), @@ -445,42 +456,56 @@ impl CargoActor { // simply skip a line if it doesn't parse, which just ignores any // erroneous output. - let mut error = String::new(); - let mut read_at_least_one_message = false; + let mut stdout_errors = String::new(); + let mut stderr_errors = String::new(); + let mut read_at_least_one_stdout_message = false; + let mut read_at_least_one_stderr_message = false; + let process_line = |line: &str, error: &mut String| { + // Try to deserialize a message from Cargo or Rustc. + let mut deserializer = serde_json::Deserializer::from_str(line); + deserializer.disable_recursion_limit(); + if let Ok(message) = JsonMessage::deserialize(&mut deserializer) { + match message { + // Skip certain kinds of messages to only spend time on what's useful + JsonMessage::Cargo(message) => match message { + cargo_metadata::Message::CompilerArtifact(artifact) if !artifact.fresh => { + self.sender.send(CargoMessage::CompilerArtifact(artifact)).unwrap(); + } + cargo_metadata::Message::CompilerMessage(msg) => { + self.sender.send(CargoMessage::Diagnostic(msg.message)).unwrap(); + } + _ => (), + }, + JsonMessage::Rustc(message) => { + self.sender.send(CargoMessage::Diagnostic(message)).unwrap(); + } + } + return true; + } + + error.push_str(line); + error.push('\n'); + return false; + }; let output = streaming_output( self.stdout, self.stderr, &mut |line| { - read_at_least_one_message = true; - - // Try to deserialize a message from Cargo or Rustc. - let mut deserializer = serde_json::Deserializer::from_str(line); - deserializer.disable_recursion_limit(); - if let Ok(message) = JsonMessage::deserialize(&mut deserializer) { - match message { - // Skip certain kinds of messages to only spend time on what's useful - JsonMessage::Cargo(message) => match message { - cargo_metadata::Message::CompilerArtifact(artifact) - if !artifact.fresh => - { - self.sender.send(CargoMessage::CompilerArtifact(artifact)).unwrap(); - } - cargo_metadata::Message::CompilerMessage(msg) => { - self.sender.send(CargoMessage::Diagnostic(msg.message)).unwrap(); - } - _ => (), - }, - JsonMessage::Rustc(message) => { - self.sender.send(CargoMessage::Diagnostic(message)).unwrap(); - } - } + if process_line(line, &mut stdout_errors) { + read_at_least_one_stdout_message = true; } }, &mut |line| { - error.push_str(line); - error.push('\n'); + if process_line(line, &mut stderr_errors) { + read_at_least_one_stderr_message = true; + } }, ); + + let read_at_least_one_message = + read_at_least_one_stdout_message || read_at_least_one_stderr_message; + let mut error = stdout_errors; + error.push_str(&stderr_errors); match output { Ok(_) => Ok((read_at_least_one_message, error)), Err(e) => Err(io::Error::new(e.kind(), format!("{e:?}: {error}"))), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs index fcd92ad3385..200072c172e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs @@ -300,6 +300,7 @@ impl AttrsWithOwner { AdtId::UnionId(it) => attrs_from_item_tree(it.lookup(db).id, db), }, AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db), + AttrDefId::TraitAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db), AttrDefId::MacroId(it) => match it { MacroId::Macro2Id(it) => attrs_from_item_tree(it.lookup(db).id, db), MacroId::MacroRulesId(it) => attrs_from_item_tree(it.lookup(db).id, db), @@ -315,26 +316,14 @@ impl AttrsWithOwner { let src = it.parent().child_source(db); RawAttrs::from_attrs_owner( db.upcast(), - src.with_value(src.value[it.local_id()].as_ref().either( - |it| match it { - ast::TypeOrConstParam::Type(it) => it as _, - ast::TypeOrConstParam::Const(it) => it as _, - }, - |it| it as _, - )), + src.with_value(&src.value[it.local_id()]), ) } GenericParamId::TypeParamId(it) => { let src = it.parent().child_source(db); RawAttrs::from_attrs_owner( db.upcast(), - src.with_value(src.value[it.local_id()].as_ref().either( - |it| match it { - ast::TypeOrConstParam::Type(it) => it as _, - ast::TypeOrConstParam::Const(it) => it as _, - }, - |it| it as _, - )), + src.with_value(&src.value[it.local_id()]), ) } GenericParamId::LifetimeParamId(it) => { @@ -404,6 +393,7 @@ impl AttrsWithOwner { AttrDefId::StaticId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), AttrDefId::ConstId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), AttrDefId::TraitId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), + AttrDefId::TraitAliasId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), AttrDefId::TypeAliasId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), AttrDefId::MacroId(id) => match id { MacroId::Macro2Id(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), @@ -412,28 +402,14 @@ impl AttrsWithOwner { }, AttrDefId::ImplId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), AttrDefId::GenericParamId(id) => match id { - GenericParamId::ConstParamId(id) => { - id.parent().child_source(db).map(|source| match &source[id.local_id()] { - Either::Left(ast::TypeOrConstParam::Type(id)) => { - ast::AnyHasAttrs::new(id.clone()) - } - Either::Left(ast::TypeOrConstParam::Const(id)) => { - ast::AnyHasAttrs::new(id.clone()) - } - Either::Right(id) => ast::AnyHasAttrs::new(id.clone()), - }) - } - GenericParamId::TypeParamId(id) => { - id.parent().child_source(db).map(|source| match &source[id.local_id()] { - Either::Left(ast::TypeOrConstParam::Type(id)) => { - ast::AnyHasAttrs::new(id.clone()) - } - Either::Left(ast::TypeOrConstParam::Const(id)) => { - ast::AnyHasAttrs::new(id.clone()) - } - Either::Right(id) => ast::AnyHasAttrs::new(id.clone()), - }) - } + GenericParamId::ConstParamId(id) => id + .parent() + .child_source(db) + .map(|source| ast::AnyHasAttrs::new(source[id.local_id()].clone())), + GenericParamId::TypeParamId(id) => id + .parent() + .child_source(db) + .map(|source| ast::AnyHasAttrs::new(source[id.local_id()].clone())), GenericParamId::LifetimeParamId(id) => id .parent .child_source(db) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs index 8fd9255b8b1..3be477d4877 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs @@ -24,7 +24,7 @@ use syntax::{ast, AstPtr, SyntaxNode, SyntaxNodePtr}; use crate::{ attr::Attrs, db::DefDatabase, - expr::{dummy_expr_id, Expr, ExprId, Label, LabelId, Pat, PatId}, + expr::{dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId}, item_scope::BuiltinShadowMode, macro_id_to_def_id, nameres::DefMap, @@ -270,7 +270,7 @@ pub struct Mark { pub struct Body { pub exprs: Arena, pub pats: Arena, - pub or_pats: FxHashMap>, + pub bindings: Arena, pub labels: Arena
    { + Ok(self.place_addr_and_ty(p, locals)?.0) + } + + fn ptr_size(&self) -> usize { + match self.db.target_data_layout(self.crate_id) { + Some(x) => x.pointer_size.bytes_usize(), + None => 8, + } + } + + fn place_addr_and_ty<'a>(&'a self, p: &Place, locals: &'a Locals<'a>) -> Result<(Address, Ty)> { + let mut addr = locals.ptr[p.local]; + let mut ty: Ty = + self.ty_filler(&locals.body.locals[p.local].ty, locals.subst, locals.body.owner)?; + for proj in &p.projection { + match proj { + ProjectionElem::Deref => { + ty = match &ty.data(Interner).kind { + TyKind::Raw(_, inner) | TyKind::Ref(_, _, inner) => inner.clone(), + _ => { + return Err(MirEvalError::TypeError( + "Overloaded deref in MIR is disallowed", + )) + } + }; + let x = from_bytes!(usize, self.read_memory(addr, self.ptr_size())?); + addr = Address::from_usize(x); + } + ProjectionElem::Index(op) => { + let offset = + from_bytes!(usize, self.read_memory(locals.ptr[*op], self.ptr_size())?); + match &ty.data(Interner).kind { + TyKind::Ref(_, _, inner) => match &inner.data(Interner).kind { + TyKind::Slice(inner) => { + ty = inner.clone(); + let ty_size = self.size_of_sized( + &ty, + locals, + "slice inner type should be sized", + )?; + let value = self.read_memory(addr, self.ptr_size() * 2)?; + addr = Address::from_bytes(&value[0..8])?.offset(ty_size * offset); + } + x => not_supported!("MIR index for ref type {x:?}"), + }, + TyKind::Array(inner, _) | TyKind::Slice(inner) => { + ty = inner.clone(); + let ty_size = self.size_of_sized( + &ty, + locals, + "array inner type should be sized", + )?; + addr = addr.offset(ty_size * offset); + } + x => not_supported!("MIR index for type {x:?}"), + } + } + &ProjectionElem::TupleField(f) => match &ty.data(Interner).kind { + TyKind::Tuple(_, subst) => { + let layout = self.layout(&ty)?; + ty = subst + .as_slice(Interner) + .get(f) + .ok_or(MirEvalError::TypeError("not enough tuple fields"))? + .assert_ty_ref(Interner) + .clone(); + let offset = layout.fields.offset(f).bytes_usize(); + addr = addr.offset(offset); + } + _ => return Err(MirEvalError::TypeError("Only tuple has tuple fields")), + }, + ProjectionElem::Field(f) => match &ty.data(Interner).kind { + TyKind::Adt(adt, subst) => { + let layout = self.layout_adt(adt.0, subst.clone())?; + let variant_layout = match &layout.variants { + Variants::Single { .. } => &layout, + Variants::Multiple { variants, .. } => { + &variants[match f.parent { + hir_def::VariantId::EnumVariantId(x) => { + RustcEnumVariantIdx(x.local_id) + } + _ => { + return Err(MirEvalError::TypeError( + "Multivariant layout only happens for enums", + )) + } + }] + } + }; + ty = self.db.field_types(f.parent)[f.local_id] + .clone() + .substitute(Interner, subst); + let offset = variant_layout + .fields + .offset(u32::from(f.local_id.into_raw()) as usize) + .bytes_usize(); + addr = addr.offset(offset); + } + _ => return Err(MirEvalError::TypeError("Only adt has fields")), + }, + ProjectionElem::ConstantIndex { .. } => { + not_supported!("constant index") + } + ProjectionElem::Subslice { .. } => not_supported!("subslice"), + ProjectionElem::OpaqueCast(_) => not_supported!("opaque cast"), + } + } + Ok((addr, ty)) + } + + fn layout(&self, ty: &Ty) -> Result { + layout_of_ty(self.db, ty, self.crate_id) + .map_err(|e| MirEvalError::LayoutError(e, ty.clone())) + } + + fn layout_adt(&self, adt: AdtId, subst: Substitution) -> Result { + self.db.layout_of_adt(adt, subst.clone()).map_err(|e| { + MirEvalError::LayoutError(e, TyKind::Adt(chalk_ir::AdtId(adt), subst).intern(Interner)) + }) + } + + fn place_ty<'a>(&'a self, p: &Place, locals: &'a Locals<'a>) -> Result { + Ok(self.place_addr_and_ty(p, locals)?.1) + } + + fn operand_ty<'a>(&'a self, o: &'a Operand, locals: &'a Locals<'a>) -> Result { + Ok(match o { + Operand::Copy(p) | Operand::Move(p) => self.place_ty(p, locals)?, + Operand::Constant(c) => c.data(Interner).ty.clone(), + }) + } + + fn interpret_mir( + &mut self, + body: &MirBody, + args: impl Iterator>, + subst: Substitution, + ) -> Result> { + if let Some(x) = self.stack_depth_limit.checked_sub(1) { + self.stack_depth_limit = x; + } else { + return Err(MirEvalError::StackOverflow); + } + let mut current_block_idx = body.start_block; + let mut locals = Locals { ptr: &ArenaMap::new(), body: &body, subst: &subst }; + let (locals_ptr, stack_size) = { + let mut stack_ptr = self.stack.len(); + let addr = body + .locals + .iter() + .map(|(id, x)| { + let size = + self.size_of_sized(&x.ty, &locals, "no unsized local in extending stack")?; + let my_ptr = stack_ptr; + stack_ptr += size; + Ok((id, Stack(my_ptr))) + }) + .collect::>>()?; + let stack_size = stack_ptr - self.stack.len(); + (addr, stack_size) + }; + locals.ptr = &locals_ptr; + self.stack.extend(iter::repeat(0).take(stack_size)); + let mut remain_args = body.arg_count; + for ((_, addr), value) in locals_ptr.iter().skip(1).zip(args) { + self.write_memory(*addr, &value)?; + if remain_args == 0 { + return Err(MirEvalError::TypeError("more arguments provided")); + } + remain_args -= 1; + } + if remain_args > 0 { + return Err(MirEvalError::TypeError("not enough arguments provided")); + } + loop { + let current_block = &body.basic_blocks[current_block_idx]; + if let Some(x) = self.execution_limit.checked_sub(1) { + self.execution_limit = x; + } else { + return Err(MirEvalError::ExecutionLimitExceeded); + } + for statement in ¤t_block.statements { + match &statement.kind { + StatementKind::Assign(l, r) => { + let addr = self.place_addr(l, &locals)?; + let result = self.eval_rvalue(r, &locals)?.to_vec(&self)?; + self.write_memory(addr, &result)?; + } + StatementKind::Deinit(_) => not_supported!("de-init statement"), + StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + | StatementKind::Nop => (), + } + } + let Some(terminator) = current_block.terminator.as_ref() else { + not_supported!("block without terminator"); + }; + match terminator { + Terminator::Goto { target } => { + current_block_idx = *target; + } + Terminator::Call { + func, + args, + destination, + target, + cleanup: _, + from_hir_call: _, + } => { + let fn_ty = self.operand_ty(func, &locals)?; + match &fn_ty.data(Interner).kind { + TyKind::FnDef(def, generic_args) => { + let def: CallableDefId = from_chalk(self.db, *def); + let generic_args = self.subst_filler(generic_args, &locals); + match def { + CallableDefId::FunctionId(def) => { + let arg_bytes = args + .iter() + .map(|x| { + Ok(self + .eval_operand(x, &locals)? + .get(&self)? + .to_owned()) + }) + .collect::>>()? + .into_iter(); + let function_data = self.db.function_data(def); + let is_intrinsic = match &function_data.abi { + Some(abi) => *abi == Interned::new_str("rust-intrinsic"), + None => match def.lookup(self.db.upcast()).container { + hir_def::ItemContainerId::ExternBlockId(block) => { + let id = block.lookup(self.db.upcast()).id; + id.item_tree(self.db.upcast())[id.value] + .abi + .as_deref() + == Some("rust-intrinsic") + } + _ => false, + }, + }; + let result = if is_intrinsic { + self.exec_intrinsic( + function_data + .name + .as_text() + .unwrap_or_default() + .as_str(), + arg_bytes, + generic_args, + &locals, + )? + } else if let Some(x) = self.detect_lang_function(def) { + self.exec_lang_item(x, arg_bytes)? + } else { + let trait_env = { + let Some(d) = body.owner.as_generic_def_id() else { + not_supported!("trait resolving in non generic def id"); + }; + self.db.trait_environment(d) + }; + let (imp, generic_args) = lookup_impl_method( + self.db, + trait_env, + def, + generic_args.clone(), + ); + let generic_args = + self.subst_filler(&generic_args, &locals); + let def = imp.into(); + let mir_body = self + .db + .mir_body(def) + .map_err(|e| MirEvalError::MirLowerError(imp, e))?; + self.interpret_mir(&mir_body, arg_bytes, generic_args) + .map_err(|e| { + MirEvalError::InFunction(imp, Box::new(e)) + })? + }; + let dest_addr = self.place_addr(destination, &locals)?; + self.write_memory(dest_addr, &result)?; + } + CallableDefId::StructId(id) => { + let (size, variant_layout, tag) = self.layout_of_variant( + id.into(), + generic_args.clone(), + &locals, + )?; + let result = self.make_by_layout( + size, + &variant_layout, + tag, + args, + &locals, + )?; + let dest_addr = self.place_addr(destination, &locals)?; + self.write_memory(dest_addr, &result)?; + } + CallableDefId::EnumVariantId(id) => { + let (size, variant_layout, tag) = self.layout_of_variant( + id.into(), + generic_args.clone(), + &locals, + )?; + let result = self.make_by_layout( + size, + &variant_layout, + tag, + args, + &locals, + )?; + let dest_addr = self.place_addr(destination, &locals)?; + self.write_memory(dest_addr, &result)?; + } + } + current_block_idx = + target.expect("broken mir, function without target"); + } + _ => not_supported!("unknown function type"), + } + } + Terminator::SwitchInt { discr, targets } => { + let val = u128::from_le_bytes(pad16( + self.eval_operand(discr, &locals)?.get(&self)?, + false, + )); + current_block_idx = targets.target_for_value(val); + } + Terminator::Return => { + let ty = body.locals[return_slot()].ty.clone(); + self.stack_depth_limit += 1; + return Ok(self + .read_memory( + locals.ptr[return_slot()], + self.size_of_sized(&ty, &locals, "return type")?, + )? + .to_owned()); + } + Terminator::Unreachable => { + return Err(MirEvalError::UndefinedBehavior("unreachable executed")) + } + _ => not_supported!("unknown terminator"), + } + } + } + + fn eval_rvalue<'a>( + &'a mut self, + r: &'a Rvalue, + locals: &'a Locals<'a>, + ) -> Result { + use IntervalOrOwned::*; + Ok(match r { + Rvalue::Use(x) => Borrowed(self.eval_operand(x, locals)?), + Rvalue::Ref(_, p) => { + let addr = self.place_addr(p, locals)?; + Owned(addr.to_bytes()) + } + Rvalue::Len(_) => not_supported!("rvalue len"), + Rvalue::UnaryOp(op, val) => { + let mut c = self.eval_operand(val, locals)?.get(&self)?; + let mut ty = self.operand_ty(val, locals)?; + while let TyKind::Ref(_, _, z) = ty.kind(Interner) { + ty = z.clone(); + let size = self.size_of_sized(&ty, locals, "operand of unary op")?; + c = self.read_memory(Address::from_bytes(c)?, size)?; + } + let mut c = c.to_vec(); + if ty.as_builtin() == Some(BuiltinType::Bool) { + c[0] = 1 - c[0]; + } else { + match op { + UnOp::Not => c.iter_mut().for_each(|x| *x = !*x), + UnOp::Neg => { + c.iter_mut().for_each(|x| *x = !*x); + for k in c.iter_mut() { + let o; + (*k, o) = k.overflowing_add(1); + if !o { + break; + } + } + } + } + } + Owned(c) + } + Rvalue::CheckedBinaryOp(op, lhs, rhs) => { + let lc = self.eval_operand(lhs, locals)?; + let rc = self.eval_operand(rhs, locals)?; + let mut lc = lc.get(&self)?; + let mut rc = rc.get(&self)?; + let mut ty = self.operand_ty(lhs, locals)?; + while let TyKind::Ref(_, _, z) = ty.kind(Interner) { + ty = z.clone(); + let size = self.size_of_sized(&ty, locals, "operand of binary op")?; + lc = self.read_memory(Address::from_bytes(lc)?, size)?; + rc = self.read_memory(Address::from_bytes(rc)?, size)?; + } + let is_signed = matches!(ty.as_builtin(), Some(BuiltinType::Int(_))); + let l128 = i128::from_le_bytes(pad16(lc, is_signed)); + let r128 = i128::from_le_bytes(pad16(rc, is_signed)); + match op { + BinOp::Ge | BinOp::Gt | BinOp::Le | BinOp::Lt | BinOp::Eq | BinOp::Ne => { + let r = match op { + BinOp::Ge => l128 >= r128, + BinOp::Gt => l128 > r128, + BinOp::Le => l128 <= r128, + BinOp::Lt => l128 < r128, + BinOp::Eq => l128 == r128, + BinOp::Ne => l128 != r128, + _ => unreachable!(), + }; + let r = r as u8; + Owned(vec![r]) + } + BinOp::BitAnd + | BinOp::BitOr + | BinOp::BitXor + | BinOp::Add + | BinOp::Mul + | BinOp::Div + | BinOp::Rem + | BinOp::Sub => { + let r = match op { + BinOp::Add => l128.overflowing_add(r128).0, + BinOp::Mul => l128.overflowing_mul(r128).0, + BinOp::Div => l128.checked_div(r128).ok_or(MirEvalError::Panic)?, + BinOp::Rem => l128.checked_rem(r128).ok_or(MirEvalError::Panic)?, + BinOp::Sub => l128.overflowing_sub(r128).0, + BinOp::BitAnd => l128 & r128, + BinOp::BitOr => l128 | r128, + BinOp::BitXor => l128 ^ r128, + _ => unreachable!(), + }; + let r = r.to_le_bytes(); + for &k in &r[lc.len()..] { + if k != 0 && (k != 255 || !is_signed) { + return Err(MirEvalError::Panic); + } + } + Owned(r[0..lc.len()].into()) + } + BinOp::Shl | BinOp::Shr => { + let shift_amout = if r128 < 0 { + return Err(MirEvalError::Panic); + } else if r128 > 128 { + return Err(MirEvalError::Panic); + } else { + r128 as u8 + }; + let r = match op { + BinOp::Shl => l128 << shift_amout, + BinOp::Shr => l128 >> shift_amout, + _ => unreachable!(), + }; + Owned(r.to_le_bytes()[0..lc.len()].into()) + } + BinOp::Offset => not_supported!("offset binop"), + } + } + Rvalue::Discriminant(p) => { + let ty = self.place_ty(p, locals)?; + let bytes = self.eval_place(p, locals)?.get(&self)?; + let layout = self.layout(&ty)?; + match layout.variants { + Variants::Single { .. } => Owned(0u128.to_le_bytes().to_vec()), + Variants::Multiple { tag, tag_encoding, .. } => { + let Some(target_data_layout) = self.db.target_data_layout(self.crate_id) else { + not_supported!("missing target data layout"); + }; + let size = tag.size(&*target_data_layout).bytes_usize(); + let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field + match tag_encoding { + TagEncoding::Direct => { + let tag = &bytes[offset..offset + size]; + Owned(pad16(tag, false).to_vec()) + } + TagEncoding::Niche { untagged_variant, niche_start, .. } => { + let tag = &bytes[offset..offset + size]; + let candidate_discriminant = i128::from_le_bytes(pad16(tag, false)) + .wrapping_sub(niche_start as i128); + let enum_id = match ty.kind(Interner) { + TyKind::Adt(e, _) => match e.0 { + AdtId::EnumId(e) => e, + _ => not_supported!("Non enum with multi variant layout"), + }, + _ => not_supported!("Non adt with multi variant layout"), + }; + let enum_data = self.db.enum_data(enum_id); + let result = 'b: { + for (local_id, _) in enum_data.variants.iter() { + if candidate_discriminant + == self.db.const_eval_discriminant(EnumVariantId { + parent: enum_id, + local_id, + })? + { + break 'b candidate_discriminant; + } + } + self.db.const_eval_discriminant(EnumVariantId { + parent: enum_id, + local_id: untagged_variant.0, + })? + }; + Owned(result.to_le_bytes().to_vec()) + } + } + } + } + } + Rvalue::ShallowInitBox(_, _) => not_supported!("shallow init box"), + Rvalue::CopyForDeref(_) => not_supported!("copy for deref"), + Rvalue::Aggregate(kind, values) => match kind { + AggregateKind::Array(_) => { + let mut r = vec![]; + for x in values { + let value = self.eval_operand(x, locals)?.get(&self)?; + r.extend(value); + } + Owned(r) + } + AggregateKind::Tuple(ty) => { + let layout = self.layout(&ty)?; + Owned(self.make_by_layout( + layout.size.bytes_usize(), + &layout, + None, + values, + locals, + )?) + } + AggregateKind::Union(x, f) => { + let layout = self.layout_adt((*x).into(), Substitution::empty(Interner))?; + let offset = layout + .fields + .offset(u32::from(f.local_id.into_raw()) as usize) + .bytes_usize(); + let op = self.eval_operand(&values[0], locals)?.get(&self)?; + let mut result = vec![0; layout.size.bytes_usize()]; + result[offset..offset + op.len()].copy_from_slice(op); + Owned(result) + } + AggregateKind::Adt(x, subst) => { + let (size, variant_layout, tag) = + self.layout_of_variant(*x, subst.clone(), locals)?; + Owned(self.make_by_layout(size, &variant_layout, tag, values, locals)?) + } + }, + Rvalue::Cast(kind, operand, target_ty) => match kind { + CastKind::PointerExposeAddress => not_supported!("exposing pointer address"), + CastKind::PointerFromExposedAddress => { + not_supported!("creating pointer from exposed address") + } + CastKind::Pointer(cast) => match cast { + PointerCast::Unsize => { + let current_ty = self.operand_ty(operand, locals)?; + match &target_ty.data(Interner).kind { + TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => { + match &ty.data(Interner).kind { + TyKind::Slice(_) => match ¤t_ty.data(Interner).kind { + TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => { + match &ty.data(Interner).kind { + TyKind::Array(_, size) => { + let addr = self + .eval_operand(operand, locals)? + .get(&self)?; + let len = const_as_usize(size); + let mut r = Vec::with_capacity(16); + r.extend(addr.iter().copied()); + r.extend(len.to_le_bytes().into_iter()); + Owned(r) + } + _ => { + not_supported!("slice unsizing from non arrays") + } + } + } + _ => not_supported!("slice unsizing from non pointers"), + }, + TyKind::Dyn(_) => not_supported!("dyn pointer unsize cast"), + _ => not_supported!("unknown unsized cast"), + } + } + _ => not_supported!("unsized cast on unknown pointer type"), + } + } + x => not_supported!("pointer cast {x:?}"), + }, + CastKind::DynStar => not_supported!("dyn star cast"), + CastKind::IntToInt => { + // FIXME: handle signed cast + let current = pad16(self.eval_operand(operand, locals)?.get(&self)?, false); + let dest_size = + self.size_of_sized(target_ty, locals, "destination of int to int cast")?; + Owned(current[0..dest_size].to_vec()) + } + CastKind::FloatToInt => not_supported!("float to int cast"), + CastKind::FloatToFloat => not_supported!("float to float cast"), + CastKind::IntToFloat => not_supported!("float to int cast"), + CastKind::PtrToPtr => not_supported!("ptr to ptr cast"), + CastKind::FnPtrToPtr => not_supported!("fn ptr to ptr cast"), + }, + }) + } + + fn layout_of_variant( + &mut self, + x: VariantId, + subst: Substitution, + locals: &Locals<'_>, + ) -> Result<(usize, Layout, Option<(usize, usize, i128)>)> { + let adt = x.adt_id(); + if let DefWithBodyId::VariantId(f) = locals.body.owner { + if let VariantId::EnumVariantId(x) = x { + if AdtId::from(f.parent) == adt { + // Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and + // infinite sized type errors) we use a dummy layout + let i = self.db.const_eval_discriminant(x)?; + return Ok((16, self.layout(&TyBuilder::unit())?, Some((0, 16, i)))); + } + } + } + let layout = self.layout_adt(adt, subst)?; + Ok(match layout.variants { + Variants::Single { .. } => (layout.size.bytes_usize(), layout, None), + Variants::Multiple { variants, tag, tag_encoding, .. } => { + let cx = self + .db + .target_data_layout(self.crate_id) + .ok_or(MirEvalError::TargetDataLayoutNotAvailable)?; + let enum_variant_id = match x { + VariantId::EnumVariantId(x) => x, + _ => not_supported!("multi variant layout for non-enums"), + }; + let rustc_enum_variant_idx = RustcEnumVariantIdx(enum_variant_id.local_id); + let mut discriminant = self.db.const_eval_discriminant(enum_variant_id)?; + let variant_layout = variants[rustc_enum_variant_idx].clone(); + let have_tag = match tag_encoding { + TagEncoding::Direct => true, + TagEncoding::Niche { untagged_variant, niche_variants: _, niche_start } => { + discriminant = discriminant.wrapping_add(niche_start as i128); + untagged_variant != rustc_enum_variant_idx + } + }; + ( + layout.size.bytes_usize(), + variant_layout, + if have_tag { + Some(( + layout.fields.offset(0).bytes_usize(), + tag.size(&*cx).bytes_usize(), + discriminant, + )) + } else { + None + }, + ) + } + }) + } + + fn make_by_layout( + &mut self, + size: usize, // Not neccessarily equal to variant_layout.size + variant_layout: &Layout, + tag: Option<(usize, usize, i128)>, + values: &Vec, + locals: &Locals<'_>, + ) -> Result> { + let mut result = vec![0; size]; + if let Some((offset, size, value)) = tag { + result[offset..offset + size].copy_from_slice(&value.to_le_bytes()[0..size]); + } + for (i, op) in values.iter().enumerate() { + let offset = variant_layout.fields.offset(i).bytes_usize(); + let op = self.eval_operand(op, locals)?.get(&self)?; + result[offset..offset + op.len()].copy_from_slice(op); + } + Ok(result) + } + + fn eval_operand(&mut self, x: &Operand, locals: &Locals<'_>) -> Result { + Ok(match x { + Operand::Copy(p) | Operand::Move(p) => self.eval_place(p, locals)?, + Operand::Constant(konst) => { + let data = &konst.data(Interner); + match &data.value { + chalk_ir::ConstValue::BoundVar(b) => { + let c = locals + .subst + .as_slice(Interner) + .get(b.index) + .ok_or(MirEvalError::TypeError("missing generic arg"))? + .assert_const_ref(Interner); + self.eval_operand(&Operand::Constant(c.clone()), locals)? + } + chalk_ir::ConstValue::InferenceVar(_) => { + not_supported!("inference var constant") + } + chalk_ir::ConstValue::Placeholder(_) => not_supported!("placeholder constant"), + chalk_ir::ConstValue::Concrete(c) => match &c.interned { + ConstScalar::Bytes(v, memory_map) => { + let mut v: Cow<'_, [u8]> = Cow::Borrowed(v); + let patch_map = memory_map.transform_addresses(|b| { + let addr = self.heap_allocate(b.len()); + self.write_memory(addr, b)?; + Ok(addr.to_usize()) + })?; + let size = self.size_of(&data.ty, locals)?.unwrap_or(v.len()); + if size != v.len() { + // Handle self enum + if size == 16 && v.len() < 16 { + v = Cow::Owned(pad16(&v, false).to_vec()); + } else if size < 16 && v.len() == 16 { + v = Cow::Owned(v[0..size].to_vec()); + } else { + return Err(MirEvalError::InvalidConst(konst.clone())); + } + } + let addr = self.heap_allocate(size); + self.write_memory(addr, &v)?; + self.patch_addresses(&patch_map, addr, &data.ty, locals)?; + Interval::new(addr, size) + } + ConstScalar::Unknown => not_supported!("evaluating unknown const"), + }, + } + } + }) + } + + fn eval_place(&mut self, p: &Place, locals: &Locals<'_>) -> Result { + let addr = self.place_addr(p, locals)?; + Ok(Interval::new( + addr, + self.size_of_sized(&self.place_ty(p, locals)?, locals, "type of this place")?, + )) + } + + fn read_memory(&self, addr: Address, size: usize) -> Result<&[u8]> { + let (mem, pos) = match addr { + Stack(x) => (&self.stack, x), + Heap(x) => (&self.heap, x), + }; + mem.get(pos..pos + size).ok_or(MirEvalError::UndefinedBehavior("out of bound memory read")) + } + + fn write_memory(&mut self, addr: Address, r: &[u8]) -> Result<()> { + let (mem, pos) = match addr { + Stack(x) => (&mut self.stack, x), + Heap(x) => (&mut self.heap, x), + }; + mem.get_mut(pos..pos + r.len()) + .ok_or(MirEvalError::UndefinedBehavior("out of bound memory write"))? + .copy_from_slice(r); + Ok(()) + } + + fn size_of(&self, ty: &Ty, locals: &Locals<'_>) -> Result> { + if let DefWithBodyId::VariantId(f) = locals.body.owner { + if let Some((adt, _)) = ty.as_adt() { + if AdtId::from(f.parent) == adt { + // Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and + // infinite sized type errors) we use a dummy size + return Ok(Some(16)); + } + } + } + let ty = &self.ty_filler(ty, locals.subst, locals.body.owner)?; + let layout = self.layout(ty); + if self.assert_placeholder_ty_is_unused { + if matches!(layout, Err(MirEvalError::LayoutError(LayoutError::HasPlaceholder, _))) { + return Ok(Some(0)); + } + } + let layout = layout?; + Ok(layout.is_sized().then(|| layout.size.bytes_usize())) + } + + /// A version of `self.size_of` which returns error if the type is unsized. `what` argument should + /// be something that complete this: `error: type {ty} was unsized. {what} should be sized` + fn size_of_sized(&self, ty: &Ty, locals: &Locals<'_>, what: &'static str) -> Result { + match self.size_of(ty, locals)? { + Some(x) => Ok(x), + None => Err(MirEvalError::TypeIsUnsized(ty.clone(), what)), + } + } + + /// Uses `ty_filler` to fill an entire subst + fn subst_filler(&self, subst: &Substitution, locals: &Locals<'_>) -> Substitution { + Substitution::from_iter( + Interner, + subst.iter(Interner).map(|x| match x.data(Interner) { + chalk_ir::GenericArgData::Ty(ty) => { + let Ok(ty) = self.ty_filler(ty, locals.subst, locals.body.owner) else { + return x.clone(); + }; + chalk_ir::GenericArgData::Ty(ty).intern(Interner) + } + _ => x.clone(), + }), + ) + } + + /// This function substitutes placeholders of the body with the provided subst, effectively plays + /// the rule of monomorphization. In addition to placeholders, it substitutes opaque types (return + /// position impl traits) with their underlying type. + fn ty_filler(&self, ty: &Ty, subst: &Substitution, owner: DefWithBodyId) -> Result { + struct Filler<'a> { + db: &'a dyn HirDatabase, + subst: &'a Substitution, + skip_params: usize, + } + impl FallibleTypeFolder for Filler<'_> { + type Error = MirEvalError; + + fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder { + self + } + + fn interner(&self) -> Interner { + Interner + } + + fn try_fold_ty( + &mut self, + ty: Ty, + outer_binder: DebruijnIndex, + ) -> std::result::Result { + match ty.kind(Interner) { + TyKind::OpaqueType(id, subst) => { + let impl_trait_id = self.db.lookup_intern_impl_trait_id((*id).into()); + match impl_trait_id { + crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { + let infer = self.db.infer(func.into()); + let filler = &mut Filler { db: self.db, subst, skip_params: 0 }; + filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder) + } + crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { + not_supported!("async block impl trait"); + } + } + } + _ => ty.try_super_fold_with(self.as_dyn(), outer_binder), + } + } + + fn try_fold_free_placeholder_ty( + &mut self, + idx: chalk_ir::PlaceholderIndex, + _outer_binder: DebruijnIndex, + ) -> std::result::Result { + let x = from_placeholder_idx(self.db, idx); + Ok(self + .subst + .as_slice(Interner) + .get((u32::from(x.local_id.into_raw()) as usize) + self.skip_params) + .and_then(|x| x.ty(Interner)) + .ok_or(MirEvalError::TypeError("Generic arg not provided"))? + .clone()) + } + } + let filler = &mut Filler { db: self.db, subst, skip_params: 0 }; + Ok(normalize(self.db, owner, ty.clone().try_fold_with(filler, DebruijnIndex::INNERMOST)?)) + } + + fn heap_allocate(&mut self, s: usize) -> Address { + let pos = self.heap.len(); + self.heap.extend(iter::repeat(0).take(s)); + Address::Heap(pos) + } + + pub fn interpret_mir_with_no_arg(&mut self, body: &MirBody) -> Result> { + self.interpret_mir(&body, vec![].into_iter(), Substitution::empty(Interner)) + } + + fn detect_lang_function(&self, def: FunctionId) -> Option { + let candidate = lang_attr(self.db.upcast(), def)?; + // filter normal lang functions out + if [LangItem::IntoIterIntoIter, LangItem::IteratorNext].contains(&candidate) { + return None; + } + Some(candidate) + } + + fn create_memory_map(&self, bytes: &[u8], ty: &Ty, locals: &Locals<'_>) -> Result { + // FIXME: support indirect references + let mut mm = MemoryMap::default(); + match ty.kind(Interner) { + TyKind::Ref(_, _, t) => { + let size = self.size_of(t, locals)?; + match size { + Some(size) => { + let addr_usize = from_bytes!(usize, bytes); + mm.insert( + addr_usize, + self.read_memory(Address::from_usize(addr_usize), size)?.to_vec(), + ) + } + None => { + let element_size = match t.kind(Interner) { + TyKind::Str => 1, + TyKind::Slice(t) => { + self.size_of_sized(t, locals, "slice inner type")? + } + _ => return Ok(mm), // FIXME: support other kind of unsized types + }; + let (addr, meta) = bytes.split_at(bytes.len() / 2); + let size = element_size * from_bytes!(usize, meta); + let addr = Address::from_bytes(addr)?; + mm.insert(addr.to_usize(), self.read_memory(addr, size)?.to_vec()); + } + } + } + _ => (), + } + Ok(mm) + } + + fn patch_addresses( + &mut self, + patch_map: &HashMap, + addr: Address, + ty: &Ty, + locals: &Locals<'_>, + ) -> Result<()> { + // FIXME: support indirect references + let my_size = self.size_of_sized(ty, locals, "value to patch address")?; + match ty.kind(Interner) { + TyKind::Ref(_, _, t) => { + let size = self.size_of(t, locals)?; + match size { + Some(_) => { + let current = from_bytes!(usize, self.read_memory(addr, my_size)?); + if let Some(x) = patch_map.get(¤t) { + self.write_memory(addr, &x.to_le_bytes())?; + } + } + None => { + let current = from_bytes!(usize, self.read_memory(addr, my_size / 2)?); + if let Some(x) = patch_map.get(¤t) { + self.write_memory(addr, &x.to_le_bytes())?; + } + } + } + } + _ => (), + } + Ok(()) + } + + fn exec_intrinsic( + &self, + as_str: &str, + _arg_bytes: impl Iterator>, + generic_args: Substitution, + locals: &Locals<'_>, + ) -> Result> { + match as_str { + "size_of" => { + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + return Err(MirEvalError::TypeError("size_of generic arg is not provided")); + }; + let size = self.size_of(ty, locals)?; + match size { + Some(x) => Ok(x.to_le_bytes().to_vec()), + None => return Err(MirEvalError::TypeError("size_of arg is unsized")), + } + } + _ => not_supported!("unknown intrinsic {as_str}"), + } + } + + pub(crate) fn exec_lang_item( + &self, + x: LangItem, + mut args: std::vec::IntoIter>, + ) -> Result> { + use LangItem::*; + match x { + PanicFmt | BeginPanic => Err(MirEvalError::Panic), + SliceLen => { + let arg = args + .next() + .ok_or(MirEvalError::TypeError("argument of <[T]>::len() is not provided"))?; + let ptr_size = arg.len() / 2; + Ok(arg[ptr_size..].into()) + } + x => not_supported!("Executing lang item {x:?}"), + } + } +} + +pub fn pad16(x: &[u8], is_signed: bool) -> [u8; 16] { + let is_negative = is_signed && x.last().unwrap_or(&0) > &128; + let fill_with = if is_negative { 255 } else { 0 }; + x.iter() + .copied() + .chain(iter::repeat(fill_with)) + .take(16) + .collect::>() + .try_into() + .expect("iterator take is not working") +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs new file mode 100644 index 00000000000..435a914088b --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -0,0 +1,1577 @@ +//! This module generates a polymorphic MIR from a hir body + +use std::{iter, mem, sync::Arc}; + +use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind}; +use hir_def::{ + body::Body, + expr::{ + Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId, + RecordLitField, + }, + lang_item::{LangItem, LangItemTarget}, + layout::LayoutError, + path::Path, + resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, + DefWithBodyId, EnumVariantId, HasModule, +}; +use hir_expand::name::Name; +use la_arena::ArenaMap; + +use crate::{ + consteval::ConstEvalError, db::HirDatabase, display::HirDisplay, infer::TypeMismatch, + inhabitedness::is_ty_uninhabited_from, layout::layout_of_ty, mapping::ToChalk, static_lifetime, + utils::generics, Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt, +}; + +use super::*; + +mod as_place; + +#[derive(Debug, Clone, Copy)] +struct LoopBlocks { + begin: BasicBlockId, + /// `None` for loops that are not terminating + end: Option, +} + +struct MirLowerCtx<'a> { + result: MirBody, + owner: DefWithBodyId, + current_loop_blocks: Option, + discr_temp: Option, + db: &'a dyn HirDatabase, + body: &'a Body, + infer: &'a InferenceResult, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum MirLowerError { + ConstEvalError(Box), + LayoutError(LayoutError), + IncompleteExpr, + UnresolvedName(String), + RecordLiteralWithoutPath, + UnresolvedMethod, + UnresolvedField, + MissingFunctionDefinition, + TypeMismatch(TypeMismatch), + /// This should be never happen. Type mismatch should catch everything. + TypeError(&'static str), + NotSupported(String), + ContinueWithoutLoop, + BreakWithoutLoop, + Loop, + /// Something that should never happen and is definitely a bug, but we don't want to panic if it happened + ImplementationError(&'static str), + LangItemNotFound(LangItem), + MutatingRvalue, +} + +macro_rules! not_supported { + ($x: expr) => { + return Err(MirLowerError::NotSupported(format!($x))) + }; +} + +macro_rules! implementation_error { + ($x: expr) => {{ + ::stdx::never!("MIR lower implementation bug: {}", $x); + return Err(MirLowerError::ImplementationError($x)); + }}; +} + +impl From for MirLowerError { + fn from(value: ConstEvalError) -> Self { + match value { + ConstEvalError::MirLowerError(e) => e, + _ => MirLowerError::ConstEvalError(Box::new(value)), + } + } +} + +impl From for MirLowerError { + fn from(value: LayoutError) -> Self { + MirLowerError::LayoutError(value) + } +} + +impl MirLowerError { + fn unresolved_path(db: &dyn HirDatabase, p: &Path) -> Self { + Self::UnresolvedName(p.display(db).to_string()) + } +} + +type Result = std::result::Result; + +impl MirLowerCtx<'_> { + fn temp(&mut self, ty: Ty) -> Result { + if matches!(ty.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) { + implementation_error!("unsized temporaries"); + } + Ok(self.result.locals.alloc(Local { ty })) + } + + fn lower_expr_to_some_operand( + &mut self, + expr_id: ExprId, + current: BasicBlockId, + ) -> Result> { + if !self.has_adjustments(expr_id) { + match &self.body.exprs[expr_id] { + Expr::Literal(l) => { + let ty = self.expr_ty(expr_id); + return Ok(Some((self.lower_literal_to_operand(ty, l)?, current))); + } + _ => (), + } + } + let Some((p, current)) = self.lower_expr_as_place(current, expr_id, true)? else { + return Ok(None); + }; + Ok(Some((Operand::Copy(p), current))) + } + + fn lower_expr_to_place_with_adjust( + &mut self, + expr_id: ExprId, + place: Place, + current: BasicBlockId, + adjustments: &[Adjustment], + ) -> Result> { + match adjustments.split_last() { + Some((last, rest)) => match &last.kind { + Adjust::NeverToAny => { + let temp = self.temp(TyKind::Never.intern(Interner))?; + self.lower_expr_to_place_with_adjust(expr_id, temp.into(), current, rest) + } + Adjust::Deref(_) => { + let Some((p, current)) = self.lower_expr_as_place_with_adjust(current, expr_id, true, adjustments)? else { + return Ok(None); + }; + self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into()); + Ok(Some(current)) + } + Adjust::Borrow(AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m)) => { + let Some((p, current)) = self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)? else { + return Ok(None); + }; + let bk = BorrowKind::from_chalk(*m); + self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into()); + Ok(Some(current)) + } + Adjust::Pointer(cast) => { + let Some((p, current)) = self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)? else { + return Ok(None); + }; + self.push_assignment( + current, + place, + Rvalue::Cast( + CastKind::Pointer(cast.clone()), + Operand::Copy(p).into(), + last.target.clone(), + ), + expr_id.into(), + ); + Ok(Some(current)) + } + }, + None => self.lower_expr_to_place_without_adjust(expr_id, place, current), + } + } + + fn lower_expr_to_place( + &mut self, + expr_id: ExprId, + place: Place, + prev_block: BasicBlockId, + ) -> Result> { + if let Some(adjustments) = self.infer.expr_adjustments.get(&expr_id) { + return self.lower_expr_to_place_with_adjust(expr_id, place, prev_block, adjustments); + } + self.lower_expr_to_place_without_adjust(expr_id, place, prev_block) + } + + fn lower_expr_to_place_without_adjust( + &mut self, + expr_id: ExprId, + place: Place, + mut current: BasicBlockId, + ) -> Result> { + match &self.body.exprs[expr_id] { + Expr::Missing => Err(MirLowerError::IncompleteExpr), + Expr::Path(p) => { + let unresolved_name = || MirLowerError::unresolved_path(self.db, p); + let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id); + let pr = resolver + .resolve_path_in_value_ns(self.db.upcast(), p.mod_path()) + .ok_or_else(unresolved_name)?; + let pr = match pr { + ResolveValueResult::ValueNs(v) => v, + ResolveValueResult::Partial(..) => { + if let Some(assoc) = self + .infer + .assoc_resolutions_for_expr(expr_id) + { + match assoc.0 { + hir_def::AssocItemId::ConstId(c) => { + self.lower_const(c, current, place, expr_id.into())?; + return Ok(Some(current)) + }, + _ => not_supported!("associated functions and types"), + } + } else if let Some(variant) = self + .infer + .variant_resolution_for_expr(expr_id) + { + match variant { + VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e), + VariantId::StructId(s) => ValueNs::StructId(s), + VariantId::UnionId(_) => implementation_error!("Union variant as path"), + } + } else { + return Err(unresolved_name()); + } + } + }; + match pr { + ValueNs::LocalBinding(pat_id) => { + self.push_assignment( + current, + place, + Operand::Copy(self.result.binding_locals[pat_id].into()).into(), + expr_id.into(), + ); + Ok(Some(current)) + } + ValueNs::ConstId(const_id) => { + self.lower_const(const_id, current, place, expr_id.into())?; + Ok(Some(current)) + } + ValueNs::EnumVariantId(variant_id) => { + let ty = self.infer.type_of_expr[expr_id].clone(); + let current = self.lower_enum_variant( + variant_id, + current, + place, + ty, + vec![], + expr_id.into(), + )?; + Ok(Some(current)) + } + ValueNs::GenericParam(p) => { + let Some(def) = self.owner.as_generic_def_id() else { + not_supported!("owner without generic def id"); + }; + let gen = generics(self.db.upcast(), def); + let ty = self.expr_ty(expr_id); + self.push_assignment( + current, + place, + Operand::Constant( + ConstData { + ty, + value: chalk_ir::ConstValue::BoundVar(BoundVar::new( + DebruijnIndex::INNERMOST, + gen.param_idx(p.into()).ok_or(MirLowerError::TypeError( + "fail to lower const generic param", + ))?, + )), + } + .intern(Interner), + ) + .into(), + expr_id.into(), + ); + Ok(Some(current)) + } + ValueNs::StructId(_) => { + // It's probably a unit struct or a zero sized function, so no action is needed. + Ok(Some(current)) + } + x => { + not_supported!("unknown name {x:?} in value name space"); + } + } + } + Expr::If { condition, then_branch, else_branch } => { + let Some((discr, current)) = self.lower_expr_to_some_operand(*condition, current)? else { + return Ok(None); + }; + let start_of_then = self.new_basic_block(); + let end_of_then = + self.lower_expr_to_place(*then_branch, place.clone(), start_of_then)?; + let start_of_else = self.new_basic_block(); + let end_of_else = if let Some(else_branch) = else_branch { + self.lower_expr_to_place(*else_branch, place, start_of_else)? + } else { + Some(start_of_else) + }; + self.set_terminator( + current, + Terminator::SwitchInt { + discr, + targets: SwitchTargets::static_if(1, start_of_then, start_of_else), + }, + ); + Ok(self.merge_blocks(end_of_then, end_of_else)) + } + Expr::Let { pat, expr } => { + let Some((cond_place, current)) = self.lower_expr_as_place(current, *expr, true)? else { + return Ok(None); + }; + let (then_target, else_target) = self.pattern_match( + current, + None, + cond_place, + self.expr_ty_after_adjustments(*expr), + *pat, + BindingAnnotation::Unannotated, + )?; + self.write_bytes_to_place( + then_target, + place.clone(), + vec![1], + TyBuilder::bool(), + MirSpan::Unknown, + )?; + if let Some(else_target) = else_target { + self.write_bytes_to_place( + else_target, + place, + vec![0], + TyBuilder::bool(), + MirSpan::Unknown, + )?; + } + Ok(self.merge_blocks(Some(then_target), else_target)) + } + Expr::Unsafe { id: _, statements, tail } => { + self.lower_block_to_place(None, statements, current, *tail, place) + } + Expr::Block { id: _, statements, tail, label } => { + self.lower_block_to_place(*label, statements, current, *tail, place) + } + Expr::Loop { body, label } => self.lower_loop(current, *label, |this, begin| { + if let Some((_, block)) = this.lower_expr_as_place(begin, *body, true)? { + this.set_goto(block, begin); + } + Ok(()) + }), + Expr::While { condition, body, label } => { + self.lower_loop(current, *label, |this, begin| { + let Some((discr, to_switch)) = this.lower_expr_to_some_operand(*condition, begin)? else { + return Ok(()); + }; + let end = this.current_loop_end()?; + let after_cond = this.new_basic_block(); + this.set_terminator( + to_switch, + Terminator::SwitchInt { + discr, + targets: SwitchTargets::static_if(1, after_cond, end), + }, + ); + if let Some((_, block)) = this.lower_expr_as_place(after_cond, *body, true)? { + this.set_goto(block, begin); + } + Ok(()) + }) + } + &Expr::For { iterable, pat, body, label } => { + let into_iter_fn = self.resolve_lang_item(LangItem::IntoIterIntoIter)? + .as_function().ok_or(MirLowerError::LangItemNotFound(LangItem::IntoIterIntoIter))?; + let iter_next_fn = self.resolve_lang_item(LangItem::IteratorNext)? + .as_function().ok_or(MirLowerError::LangItemNotFound(LangItem::IteratorNext))?; + let option_some = self.resolve_lang_item(LangItem::OptionSome)? + .as_enum_variant().ok_or(MirLowerError::LangItemNotFound(LangItem::OptionSome))?; + let option = option_some.parent; + let into_iter_fn_op = Operand::const_zst( + TyKind::FnDef( + self.db.intern_callable_def(CallableDefId::FunctionId(into_iter_fn)).into(), + Substitution::from1(Interner, self.expr_ty(iterable)) + ).intern(Interner)); + let iter_next_fn_op = Operand::const_zst( + TyKind::FnDef( + self.db.intern_callable_def(CallableDefId::FunctionId(iter_next_fn)).into(), + Substitution::from1(Interner, self.expr_ty(iterable)) + ).intern(Interner)); + let &Some(iterator_ty) = &self.infer.type_of_for_iterator.get(&expr_id) else { + return Err(MirLowerError::TypeError("unknown for loop iterator type")); + }; + let ref_mut_iterator_ty = TyKind::Ref(Mutability::Mut, static_lifetime(), iterator_ty.clone()).intern(Interner); + let item_ty = &self.infer.type_of_pat[pat]; + let option_item_ty = TyKind::Adt(chalk_ir::AdtId(option.into()), Substitution::from1(Interner, item_ty.clone())).intern(Interner); + let iterator_place: Place = self.temp(iterator_ty.clone())?.into(); + let option_item_place: Place = self.temp(option_item_ty.clone())?.into(); + let ref_mut_iterator_place: Place = self.temp(ref_mut_iterator_ty)?.into(); + let Some(current) = self.lower_call_and_args(into_iter_fn_op, Some(iterable).into_iter(), iterator_place.clone(), current, false)? + else { + return Ok(None); + }; + self.push_assignment(current, ref_mut_iterator_place.clone(), Rvalue::Ref(BorrowKind::Mut { allow_two_phase_borrow: false }, iterator_place), expr_id.into()); + self.lower_loop(current, label, |this, begin| { + let Some(current) = this.lower_call(iter_next_fn_op, vec![Operand::Copy(ref_mut_iterator_place)], option_item_place.clone(), begin, false)? + else { + return Ok(()); + }; + let end = this.current_loop_end()?; + let (current, _) = this.pattern_matching_variant( + option_item_ty.clone(), + BindingAnnotation::Unannotated, + option_item_place.into(), + option_some.into(), + current, + pat.into(), + Some(end), + &[pat], &None)?; + if let Some((_, block)) = this.lower_expr_as_place(current, body, true)? { + this.set_goto(block, begin); + } + Ok(()) + }) + }, + Expr::Call { callee, args, .. } => { + let callee_ty = self.expr_ty_after_adjustments(*callee); + match &callee_ty.data(Interner).kind { + chalk_ir::TyKind::FnDef(..) => { + let func = Operand::from_bytes(vec![], callee_ty.clone()); + self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id)) + } + TyKind::Scalar(_) + | TyKind::Tuple(_, _) + | TyKind::Array(_, _) + | TyKind::Adt(_, _) + | TyKind::Str + | TyKind::Foreign(_) + | TyKind::Slice(_) => { + return Err(MirLowerError::TypeError("function call on data type")) + } + TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition), + TyKind::AssociatedType(_, _) + | TyKind::Raw(_, _) + | TyKind::Ref(_, _, _) + | TyKind::OpaqueType(_, _) + | TyKind::Never + | TyKind::Closure(_, _) + | TyKind::Generator(_, _) + | TyKind::GeneratorWitness(_, _) + | TyKind::Placeholder(_) + | TyKind::Dyn(_) + | TyKind::Alias(_) + | TyKind::Function(_) + | TyKind::BoundVar(_) + | TyKind::InferenceVar(_, _) => not_supported!("dynamic function call"), + } + } + Expr::MethodCall { receiver, args, .. } => { + let (func_id, generic_args) = + self.infer.method_resolution(expr_id).ok_or(MirLowerError::UnresolvedMethod)?; + let ty = chalk_ir::TyKind::FnDef( + CallableDefId::FunctionId(func_id).to_chalk(self.db), + generic_args, + ) + .intern(Interner); + let func = Operand::from_bytes(vec![], ty); + self.lower_call_and_args( + func, + iter::once(*receiver).chain(args.iter().copied()), + place, + current, + self.is_uninhabited(expr_id), + ) + } + Expr::Match { expr, arms } => { + let Some((cond_place, mut current)) = self.lower_expr_as_place(current, *expr, true)? + else { + return Ok(None); + }; + let cond_ty = self.expr_ty_after_adjustments(*expr); + let mut end = None; + for MatchArm { pat, guard, expr } in arms.iter() { + if guard.is_some() { + not_supported!("pattern matching with guard"); + } + let (then, otherwise) = self.pattern_match( + current, + None, + cond_place.clone(), + cond_ty.clone(), + *pat, + BindingAnnotation::Unannotated, + )?; + if let Some(block) = self.lower_expr_to_place(*expr, place.clone(), then)? { + let r = end.get_or_insert_with(|| self.new_basic_block()); + self.set_goto(block, *r); + } + match otherwise { + Some(o) => current = o, + None => { + // The current pattern was irrefutable, so there is no need to generate code + // for the rest of patterns + break; + } + } + } + if self.is_unterminated(current) { + self.set_terminator(current, Terminator::Unreachable); + } + Ok(end) + } + Expr::Continue { label } => match label { + Some(_) => not_supported!("continue with label"), + None => { + let loop_data = + self.current_loop_blocks.ok_or(MirLowerError::ContinueWithoutLoop)?; + self.set_goto(current, loop_data.begin); + Ok(None) + } + }, + Expr::Break { expr, label } => { + if expr.is_some() { + not_supported!("break with value"); + } + match label { + Some(_) => not_supported!("break with label"), + None => { + let end = + self.current_loop_end()?; + self.set_goto(current, end); + Ok(None) + } + } + } + Expr::Return { expr } => { + if let Some(expr) = expr { + if let Some(c) = self.lower_expr_to_place(*expr, return_slot().into(), current)? { + current = c; + } else { + return Ok(None); + } + } + self.set_terminator(current, Terminator::Return); + Ok(None) + } + Expr::Yield { .. } => not_supported!("yield"), + Expr::RecordLit { fields, path, .. } => { + let variant_id = self + .infer + .variant_resolution_for_expr(expr_id) + .ok_or_else(|| match path { + Some(p) => MirLowerError::UnresolvedName(p.display(self.db).to_string()), + None => MirLowerError::RecordLiteralWithoutPath, + })?; + let subst = match self.expr_ty(expr_id).kind(Interner) { + TyKind::Adt(_, s) => s.clone(), + _ => not_supported!("Non ADT record literal"), + }; + let variant_data = variant_id.variant_data(self.db.upcast()); + match variant_id { + VariantId::EnumVariantId(_) | VariantId::StructId(_) => { + let mut operands = vec![None; variant_data.fields().len()]; + for RecordLitField { name, expr } in fields.iter() { + let field_id = + variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?; + let Some((op, c)) = self.lower_expr_to_some_operand(*expr, current)? else { + return Ok(None); + }; + current = c; + operands[u32::from(field_id.into_raw()) as usize] = Some(op); + } + self.push_assignment( + current, + place, + Rvalue::Aggregate( + AggregateKind::Adt(variant_id, subst), + operands.into_iter().map(|x| x).collect::>().ok_or( + MirLowerError::TypeError("missing field in record literal"), + )?, + ), + expr_id.into(), + ); + Ok(Some(current)) + } + VariantId::UnionId(union_id) => { + let [RecordLitField { name, expr }] = fields.as_ref() else { + not_supported!("Union record literal with more than one field"); + }; + let local_id = + variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?; + let mut place = place; + place + .projection + .push(PlaceElem::Field(FieldId { parent: union_id.into(), local_id })); + self.lower_expr_to_place(*expr, place, current) + } + } + } + Expr::Await { .. } => not_supported!("await"), + Expr::Try { .. } => not_supported!("? operator"), + Expr::Yeet { .. } => not_supported!("yeet"), + Expr::TryBlock { .. } => not_supported!("try block"), + Expr::Async { .. } => not_supported!("async block"), + Expr::Const { .. } => not_supported!("anonymous const block"), + Expr::Cast { expr, type_ref: _ } => { + let Some((x, current)) = self.lower_expr_to_some_operand(*expr, current)? else { + return Ok(None); + }; + let source_ty = self.infer[*expr].clone(); + let target_ty = self.infer[expr_id].clone(); + self.push_assignment( + current, + place, + Rvalue::Cast(cast_kind(&source_ty, &target_ty)?, x, target_ty), + expr_id.into(), + ); + Ok(Some(current)) + } + Expr::Ref { expr, rawness: _, mutability } => { + let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else { + return Ok(None); + }; + let bk = BorrowKind::from_hir(*mutability); + self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into()); + Ok(Some(current)) + } + Expr::Box { .. } => not_supported!("box expression"), + Expr::Field { .. } | Expr::Index { .. } | Expr::UnaryOp { op: hir_def::expr::UnaryOp::Deref, .. } => { + let Some((p, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, true)? else { + return Ok(None); + }; + self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into()); + Ok(Some(current)) + } + Expr::UnaryOp { expr, op: op @ (hir_def::expr::UnaryOp::Not | hir_def::expr::UnaryOp::Neg) } => { + let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? else { + return Ok(None); + }; + let operation = match op { + hir_def::expr::UnaryOp::Not => UnOp::Not, + hir_def::expr::UnaryOp::Neg => UnOp::Neg, + _ => unreachable!(), + }; + self.push_assignment( + current, + place, + Rvalue::UnaryOp(operation, operand), + expr_id.into(), + ); + Ok(Some(current)) + }, + Expr::BinaryOp { lhs, rhs, op } => { + let op = op.ok_or(MirLowerError::IncompleteExpr)?; + if let hir_def::expr::BinaryOp::Assignment { op } = op { + if op.is_some() { + not_supported!("assignment with arith op (like +=)"); + } + let Some((lhs_place, current)) = + self.lower_expr_as_place(current, *lhs, false)? + else { + return Ok(None); + }; + let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else { + return Ok(None); + }; + self.push_assignment(current, lhs_place, rhs_op.into(), expr_id.into()); + return Ok(Some(current)); + } + let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)? else { + return Ok(None); + }; + let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else { + return Ok(None); + }; + self.push_assignment( + current, + place, + Rvalue::CheckedBinaryOp( + match op { + hir_def::expr::BinaryOp::LogicOp(op) => match op { + hir_def::expr::LogicOp::And => BinOp::BitAnd, // FIXME: make these short circuit + hir_def::expr::LogicOp::Or => BinOp::BitOr, + }, + hir_def::expr::BinaryOp::ArithOp(op) => BinOp::from(op), + hir_def::expr::BinaryOp::CmpOp(op) => BinOp::from(op), + hir_def::expr::BinaryOp::Assignment { .. } => unreachable!(), // handled above + }, + lhs_op, + rhs_op, + ), + expr_id.into(), + ); + Ok(Some(current)) + } + Expr::Range { .. } => not_supported!("range"), + Expr::Closure { .. } => not_supported!("closure"), + Expr::Tuple { exprs, is_assignee_expr: _ } => { + let Some(values) = exprs + .iter() + .map(|x| { + let Some((o, c)) = self.lower_expr_to_some_operand(*x, current)? else { + return Ok(None); + }; + current = c; + Ok(Some(o)) + }) + .collect::>>()? + else { + return Ok(None); + }; + let r = Rvalue::Aggregate( + AggregateKind::Tuple(self.expr_ty(expr_id)), + values, + ); + self.push_assignment(current, place, r, expr_id.into()); + Ok(Some(current)) + } + Expr::Array(l) => match l { + Array::ElementList { elements, .. } => { + let elem_ty = match &self.expr_ty(expr_id).data(Interner).kind { + TyKind::Array(ty, _) => ty.clone(), + _ => { + return Err(MirLowerError::TypeError( + "Array expression with non array type", + )) + } + }; + let Some(values) = elements + .iter() + .map(|x| { + let Some((o, c)) = self.lower_expr_to_some_operand(*x, current)? else { + return Ok(None); + }; + current = c; + Ok(Some(o)) + }) + .collect::>>()? + else { + return Ok(None); + }; + let r = Rvalue::Aggregate( + AggregateKind::Array(elem_ty), + values, + ); + self.push_assignment(current, place, r, expr_id.into()); + Ok(Some(current)) + } + Array::Repeat { .. } => not_supported!("array repeat"), + }, + Expr::Literal(l) => { + let ty = self.expr_ty(expr_id); + let op = self.lower_literal_to_operand(ty, l)?; + self.push_assignment(current, place, op.into(), expr_id.into()); + Ok(Some(current)) + } + Expr::Underscore => not_supported!("underscore"), + } + } + + fn push_field_projection(&self, place: &mut Place, expr_id: ExprId) -> Result<()> { + if let Expr::Field { expr, name } = &self.body[expr_id] { + if let TyKind::Tuple(..) = self.expr_ty_after_adjustments(*expr).kind(Interner) { + let index = name + .as_tuple_index() + .ok_or(MirLowerError::TypeError("named field on tuple"))?; + place.projection.push(ProjectionElem::TupleField(index)) + } else { + let field = + self.infer.field_resolution(expr_id).ok_or(MirLowerError::UnresolvedField)?; + place.projection.push(ProjectionElem::Field(field)); + } + } else { + not_supported!("") + } + Ok(()) + } + + fn lower_literal_to_operand(&mut self, ty: Ty, l: &Literal) -> Result { + let size = layout_of_ty(self.db, &ty, self.owner.module(self.db.upcast()).krate())? + .size + .bytes_usize(); + let bytes = match l { + hir_def::expr::Literal::String(b) => { + let b = b.as_bytes(); + let mut data = vec![]; + data.extend(0usize.to_le_bytes()); + data.extend(b.len().to_le_bytes()); + let mut mm = MemoryMap::default(); + mm.insert(0, b.to_vec()); + return Ok(Operand::from_concrete_const(data, mm, ty)); + } + hir_def::expr::Literal::ByteString(b) => { + let mut data = vec![]; + data.extend(0usize.to_le_bytes()); + data.extend(b.len().to_le_bytes()); + let mut mm = MemoryMap::default(); + mm.insert(0, b.to_vec()); + return Ok(Operand::from_concrete_const(data, mm, ty)); + } + hir_def::expr::Literal::Char(c) => u32::from(*c).to_le_bytes().into(), + hir_def::expr::Literal::Bool(b) => vec![*b as u8], + hir_def::expr::Literal::Int(x, _) => x.to_le_bytes()[0..size].into(), + hir_def::expr::Literal::Uint(x, _) => x.to_le_bytes()[0..size].into(), + hir_def::expr::Literal::Float(f, _) => match size { + 8 => f.into_f64().to_le_bytes().into(), + 4 => f.into_f32().to_le_bytes().into(), + _ => { + return Err(MirLowerError::TypeError("float with size other than 4 or 8 bytes")) + } + }, + }; + Ok(Operand::from_concrete_const(bytes, MemoryMap::default(), ty)) + } + + fn new_basic_block(&mut self) -> BasicBlockId { + self.result.basic_blocks.alloc(BasicBlock::default()) + } + + fn lower_const( + &mut self, + const_id: hir_def::ConstId, + prev_block: BasicBlockId, + place: Place, + span: MirSpan, + ) -> Result<()> { + let c = self.db.const_eval(const_id)?; + self.write_const_to_place(c, prev_block, place, span) + } + + fn write_const_to_place( + &mut self, + c: Const, + prev_block: BasicBlockId, + place: Place, + span: MirSpan, + ) -> Result<()> { + self.push_assignment(prev_block, place, Operand::Constant(c).into(), span); + Ok(()) + } + + fn write_bytes_to_place( + &mut self, + prev_block: BasicBlockId, + place: Place, + cv: Vec, + ty: Ty, + span: MirSpan, + ) -> Result<()> { + self.push_assignment(prev_block, place, Operand::from_bytes(cv, ty).into(), span); + Ok(()) + } + + fn lower_enum_variant( + &mut self, + variant_id: EnumVariantId, + prev_block: BasicBlockId, + place: Place, + ty: Ty, + fields: Vec, + span: MirSpan, + ) -> Result { + let subst = match ty.kind(Interner) { + TyKind::Adt(_, subst) => subst.clone(), + _ => not_supported!("Non ADT enum"), + }; + self.push_assignment( + prev_block, + place, + Rvalue::Aggregate(AggregateKind::Adt(variant_id.into(), subst), fields), + span, + ); + Ok(prev_block) + } + + fn lower_call_and_args( + &mut self, + func: Operand, + args: impl Iterator, + place: Place, + mut current: BasicBlockId, + is_uninhabited: bool, + ) -> Result> { + let Some(args) = args + .map(|arg| { + if let Some((temp, c)) = self.lower_expr_to_some_operand(arg, current)? { + current = c; + Ok(Some(temp)) + } else { + Ok(None) + } + }) + .collect::>>>()? + else { + return Ok(None); + }; + self.lower_call(func, args, place, current, is_uninhabited) + } + + fn lower_call( + &mut self, + func: Operand, + args: Vec, + place: Place, + current: BasicBlockId, + is_uninhabited: bool, + ) -> Result> { + let b = if is_uninhabited { None } else { Some(self.new_basic_block()) }; + self.set_terminator( + current, + Terminator::Call { + func, + args, + destination: place, + target: b, + cleanup: None, + from_hir_call: true, + }, + ); + Ok(b) + } + + fn is_unterminated(&mut self, source: BasicBlockId) -> bool { + self.result.basic_blocks[source].terminator.is_none() + } + + fn set_terminator(&mut self, source: BasicBlockId, terminator: Terminator) { + self.result.basic_blocks[source].terminator = Some(terminator); + } + + fn set_goto(&mut self, source: BasicBlockId, target: BasicBlockId) { + self.set_terminator(source, Terminator::Goto { target }); + } + + fn expr_ty(&self, e: ExprId) -> Ty { + self.infer[e].clone() + } + + fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty { + let mut ty = None; + if let Some(x) = self.infer.expr_adjustments.get(&e) { + if let Some(x) = x.last() { + ty = Some(x.target.clone()); + } + } + ty.unwrap_or_else(|| self.expr_ty(e)) + } + + fn push_statement(&mut self, block: BasicBlockId, statement: Statement) { + self.result.basic_blocks[block].statements.push(statement); + } + + fn push_assignment( + &mut self, + block: BasicBlockId, + place: Place, + rvalue: Rvalue, + span: MirSpan, + ) { + self.push_statement(block, StatementKind::Assign(place, rvalue).with_span(span)); + } + + /// It gets a `current` unterminated block, appends some statements and possibly a terminator to it to check if + /// the pattern matches and write bindings, and returns two unterminated blocks, one for the matched path (which + /// can be the `current` block) and one for the mismatched path. If the input pattern is irrefutable, the + /// mismatched path block is `None`. + /// + /// By default, it will create a new block for mismatched path. If you already have one, you can provide it with + /// `current_else` argument to save an unneccessary jump. If `current_else` isn't `None`, the result mismatched path + /// wouldn't be `None` as well. Note that this function will add jumps to the beginning of the `current_else` block, + /// so it should be an empty block. + fn pattern_match( + &mut self, + mut current: BasicBlockId, + mut current_else: Option, + mut cond_place: Place, + mut cond_ty: Ty, + pattern: PatId, + mut binding_mode: BindingAnnotation, + ) -> Result<(BasicBlockId, Option)> { + Ok(match &self.body.pats[pattern] { + Pat::Missing => return Err(MirLowerError::IncompleteExpr), + Pat::Wild => (current, current_else), + Pat::Tuple { args, ellipsis } => { + pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place); + let subst = match cond_ty.kind(Interner) { + TyKind::Tuple(_, s) => s, + _ => { + return Err(MirLowerError::TypeError( + "non tuple type matched with tuple pattern", + )) + } + }; + self.pattern_match_tuple_like( + current, + current_else, + args.iter().enumerate().map(|(i, x)| { + ( + PlaceElem::TupleField(i), + *x, + subst.at(Interner, i).assert_ty_ref(Interner).clone(), + ) + }), + *ellipsis, + &cond_place, + binding_mode, + )? + } + Pat::Or(pats) => { + let then_target = self.new_basic_block(); + let mut finished = false; + for pat in &**pats { + let (next, next_else) = self.pattern_match( + current, + None, + cond_place.clone(), + cond_ty.clone(), + *pat, + binding_mode, + )?; + self.set_goto(next, then_target); + match next_else { + Some(t) => { + current = t; + } + None => { + finished = true; + break; + } + } + } + if !finished { + let ce = *current_else.get_or_insert_with(|| self.new_basic_block()); + self.set_goto(current, ce); + } + (then_target, current_else) + } + Pat::Record { .. } => not_supported!("record pattern"), + Pat::Range { .. } => not_supported!("range pattern"), + Pat::Slice { .. } => not_supported!("slice pattern"), + Pat::Path(_) => { + let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else { + not_supported!("unresolved variant"); + }; + self.pattern_matching_variant( + cond_ty, + binding_mode, + cond_place, + variant, + current, + pattern.into(), + current_else, + &[], + &None, + )? + } + Pat::Lit(l) => { + let then_target = self.new_basic_block(); + let else_target = current_else.unwrap_or_else(|| self.new_basic_block()); + match &self.body.exprs[*l] { + Expr::Literal(l) => match l { + hir_def::expr::Literal::Int(x, _) => { + self.set_terminator( + current, + Terminator::SwitchInt { + discr: Operand::Copy(cond_place), + targets: SwitchTargets::static_if( + *x as u128, + then_target, + else_target, + ), + }, + ); + } + hir_def::expr::Literal::Uint(x, _) => { + self.set_terminator( + current, + Terminator::SwitchInt { + discr: Operand::Copy(cond_place), + targets: SwitchTargets::static_if(*x, then_target, else_target), + }, + ); + } + _ => not_supported!("non int path literal"), + }, + _ => not_supported!("expression path literal"), + } + (then_target, Some(else_target)) + } + Pat::Bind { id, subpat } => { + let target_place = self.result.binding_locals[*id]; + let mode = self.body.bindings[*id].mode; + if let Some(subpat) = subpat { + (current, current_else) = self.pattern_match( + current, + current_else, + cond_place.clone(), + cond_ty, + *subpat, + binding_mode, + )? + } + if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) { + binding_mode = mode; + } + self.push_storage_live(*id, current)?; + self.push_assignment( + current, + target_place.into(), + match binding_mode { + BindingAnnotation::Unannotated | BindingAnnotation::Mutable => { + Operand::Copy(cond_place).into() + } + BindingAnnotation::Ref => Rvalue::Ref(BorrowKind::Shared, cond_place), + BindingAnnotation::RefMut => Rvalue::Ref( + BorrowKind::Mut { allow_two_phase_borrow: false }, + cond_place, + ), + }, + pattern.into(), + ); + (current, current_else) + } + Pat::TupleStruct { path: _, args, ellipsis } => { + let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else { + not_supported!("unresolved variant"); + }; + self.pattern_matching_variant( + cond_ty, + binding_mode, + cond_place, + variant, + current, + pattern.into(), + current_else, + args, + ellipsis, + )? + } + Pat::Ref { .. } => not_supported!("& pattern"), + Pat::Box { .. } => not_supported!("box pattern"), + Pat::ConstBlock(_) => not_supported!("const block pattern"), + }) + } + + fn pattern_matching_variant( + &mut self, + mut cond_ty: Ty, + mut binding_mode: BindingAnnotation, + mut cond_place: Place, + variant: VariantId, + current: BasicBlockId, + span: MirSpan, + current_else: Option, + args: &[PatId], + ellipsis: &Option, + ) -> Result<(BasicBlockId, Option)> { + pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place); + let subst = match cond_ty.kind(Interner) { + TyKind::Adt(_, s) => s, + _ => return Err(MirLowerError::TypeError("non adt type matched with tuple struct")), + }; + let fields_type = self.db.field_types(variant); + Ok(match variant { + VariantId::EnumVariantId(v) => { + let e = self.db.const_eval_discriminant(v)? as u128; + let next = self.new_basic_block(); + let tmp = self.discr_temp_place(); + self.push_assignment( + current, + tmp.clone(), + Rvalue::Discriminant(cond_place.clone()), + span, + ); + let else_target = current_else.unwrap_or_else(|| self.new_basic_block()); + self.set_terminator( + current, + Terminator::SwitchInt { + discr: Operand::Copy(tmp), + targets: SwitchTargets::static_if(e, next, else_target), + }, + ); + let enum_data = self.db.enum_data(v.parent); + let fields = + enum_data.variants[v.local_id].variant_data.fields().iter().map(|(x, _)| { + ( + PlaceElem::Field(FieldId { parent: v.into(), local_id: x }), + fields_type[x].clone().substitute(Interner, subst), + ) + }); + self.pattern_match_tuple_like( + next, + Some(else_target), + args.iter().zip(fields).map(|(x, y)| (y.0, *x, y.1)), + *ellipsis, + &cond_place, + binding_mode, + )? + } + VariantId::StructId(s) => { + let struct_data = self.db.struct_data(s); + let fields = struct_data.variant_data.fields().iter().map(|(x, _)| { + ( + PlaceElem::Field(FieldId { parent: s.into(), local_id: x }), + fields_type[x].clone().substitute(Interner, subst), + ) + }); + self.pattern_match_tuple_like( + current, + current_else, + args.iter().zip(fields).map(|(x, y)| (y.0, *x, y.1)), + *ellipsis, + &cond_place, + binding_mode, + )? + } + VariantId::UnionId(_) => { + return Err(MirLowerError::TypeError("pattern matching on union")) + } + }) + } + + fn pattern_match_tuple_like( + &mut self, + mut current: BasicBlockId, + mut current_else: Option, + args: impl Iterator, + ellipsis: Option, + cond_place: &Place, + binding_mode: BindingAnnotation, + ) -> Result<(BasicBlockId, Option)> { + if ellipsis.is_some() { + not_supported!("tuple like pattern with ellipsis"); + } + for (proj, arg, ty) in args { + let mut cond_place = cond_place.clone(); + cond_place.projection.push(proj); + (current, current_else) = + self.pattern_match(current, current_else, cond_place, ty, arg, binding_mode)?; + } + Ok((current, current_else)) + } + + fn discr_temp_place(&mut self) -> Place { + match &self.discr_temp { + Some(x) => x.clone(), + None => { + let tmp: Place = + self.temp(TyBuilder::discr_ty()).expect("discr_ty is never unsized").into(); + self.discr_temp = Some(tmp.clone()); + tmp + } + } + } + + fn lower_loop( + &mut self, + prev_block: BasicBlockId, + label: Option, + f: impl FnOnce(&mut MirLowerCtx<'_>, BasicBlockId) -> Result<()>, + ) -> Result> { + if label.is_some() { + not_supported!("loop with label"); + } + let begin = self.new_basic_block(); + let prev = + mem::replace(&mut self.current_loop_blocks, Some(LoopBlocks { begin, end: None })); + self.set_goto(prev_block, begin); + f(self, begin)?; + let my = mem::replace(&mut self.current_loop_blocks, prev) + .ok_or(MirLowerError::ImplementationError("current_loop_blocks is corrupt"))?; + Ok(my.end) + } + + fn has_adjustments(&self, expr_id: ExprId) -> bool { + !self.infer.expr_adjustments.get(&expr_id).map(|x| x.is_empty()).unwrap_or(true) + } + + fn merge_blocks( + &mut self, + b1: Option, + b2: Option, + ) -> Option { + match (b1, b2) { + (None, None) => None, + (None, Some(b)) | (Some(b), None) => Some(b), + (Some(b1), Some(b2)) => { + let bm = self.new_basic_block(); + self.set_goto(b1, bm); + self.set_goto(b2, bm); + Some(bm) + } + } + } + + fn current_loop_end(&mut self) -> Result { + let r = match self + .current_loop_blocks + .as_mut() + .ok_or(MirLowerError::ImplementationError("Current loop access out of loop"))? + .end + { + Some(x) => x, + None => { + let s = self.new_basic_block(); + self.current_loop_blocks + .as_mut() + .ok_or(MirLowerError::ImplementationError("Current loop access out of loop"))? + .end = Some(s); + s + } + }; + Ok(r) + } + + fn is_uninhabited(&self, expr_id: ExprId) -> bool { + is_ty_uninhabited_from(&self.infer[expr_id], self.owner.module(self.db.upcast()), self.db) + } + + /// This function push `StorageLive` statements for each binding in the pattern. + fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) -> Result<()> { + // Current implementation is wrong. It adds no `StorageDead` at the end of scope, and before each break + // and continue. It just add a `StorageDead` before the `StorageLive`, which is not wrong, but unneeeded in + // the proper implementation. Due this limitation, implementing a borrow checker on top of this mir will falsely + // allow this: + // + // ``` + // let x; + // loop { + // let y = 2; + // x = &y; + // if some_condition { + // break; // we need to add a StorageDead(y) above this to kill the x borrow + // } + // } + // use(x) + // ``` + // But I think this approach work for mutability analysis, as user can't write code which mutates a binding + // after StorageDead, except loops, which are handled by this hack. + let span = self.body.bindings[b] + .definitions + .first() + .copied() + .map(MirSpan::PatId) + .unwrap_or(MirSpan::Unknown); + let l = self.result.binding_locals[b]; + self.push_statement(current, StatementKind::StorageDead(l).with_span(span)); + self.push_statement(current, StatementKind::StorageLive(l).with_span(span)); + Ok(()) + } + + fn resolve_lang_item(&self, item: LangItem) -> Result { + let crate_id = self.owner.module(self.db.upcast()).krate(); + self.db.lang_item(crate_id, item).ok_or(MirLowerError::LangItemNotFound(item)) + } + + fn lower_block_to_place( + &mut self, + label: Option, + statements: &[hir_def::expr::Statement], + mut current: BasicBlockId, + tail: Option, + place: Place, + ) -> Result>> { + if label.is_some() { + not_supported!("block with label"); + } + for statement in statements.iter() { + match statement { + hir_def::expr::Statement::Let { pat, initializer, else_branch, type_ref: _ } => { + if let Some(expr_id) = initializer { + let else_block; + let Some((init_place, c)) = + self.lower_expr_as_place(current, *expr_id, true)? + else { + return Ok(None); + }; + current = c; + (current, else_block) = self.pattern_match( + current, + None, + init_place, + self.expr_ty_after_adjustments(*expr_id), + *pat, + BindingAnnotation::Unannotated, + )?; + match (else_block, else_branch) { + (None, _) => (), + (Some(else_block), None) => { + self.set_terminator(else_block, Terminator::Unreachable); + } + (Some(else_block), Some(else_branch)) => { + if let Some((_, b)) = + self.lower_expr_as_place(else_block, *else_branch, true)? + { + self.set_terminator(b, Terminator::Unreachable); + } + } + } + } + } + hir_def::expr::Statement::Expr { expr, has_semi: _ } => { + let Some((_, c)) = self.lower_expr_as_place(current, *expr, true)? else { + return Ok(None); + }; + current = c; + } + } + } + match tail { + Some(tail) => self.lower_expr_to_place(tail, place, current), + None => Ok(Some(current)), + } + } +} + +fn pattern_matching_dereference( + cond_ty: &mut Ty, + binding_mode: &mut BindingAnnotation, + cond_place: &mut Place, +) { + while let Some((ty, _, mu)) = cond_ty.as_reference() { + if mu == Mutability::Mut && *binding_mode != BindingAnnotation::Ref { + *binding_mode = BindingAnnotation::RefMut; + } else { + *binding_mode = BindingAnnotation::Ref; + } + *cond_ty = ty.clone(); + cond_place.projection.push(ProjectionElem::Deref); + } +} + +fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result { + Ok(match (source_ty.kind(Interner), target_ty.kind(Interner)) { + (TyKind::Scalar(s), TyKind::Scalar(t)) => match (s, t) { + (chalk_ir::Scalar::Float(_), chalk_ir::Scalar::Float(_)) => CastKind::FloatToFloat, + (chalk_ir::Scalar::Float(_), _) => CastKind::FloatToInt, + (_, chalk_ir::Scalar::Float(_)) => CastKind::IntToFloat, + (_, _) => CastKind::IntToInt, + }, + // Enum to int casts + (TyKind::Scalar(_), TyKind::Adt(..)) | (TyKind::Adt(..), TyKind::Scalar(_)) => { + CastKind::IntToInt + } + (a, b) => not_supported!("Unknown cast between {a:?} and {b:?}"), + }) +} + +pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result> { + let _p = profile::span("mir_body_query").detail(|| match def { + DefWithBodyId::FunctionId(it) => db.function_data(it).name.to_string(), + DefWithBodyId::StaticId(it) => db.static_data(it).name.clone().to_string(), + DefWithBodyId::ConstId(it) => { + db.const_data(it).name.clone().unwrap_or_else(Name::missing).to_string() + } + DefWithBodyId::VariantId(it) => { + db.enum_data(it.parent).variants[it.local_id].name.to_string() + } + }); + let body = db.body(def); + let infer = db.infer(def); + let result = lower_to_mir(db, def, &body, &infer, body.body_expr)?; + Ok(Arc::new(result)) +} + +pub fn mir_body_recover( + _db: &dyn HirDatabase, + _cycle: &[String], + _def: &DefWithBodyId, +) -> Result> { + Err(MirLowerError::Loop) +} + +pub fn lower_to_mir( + db: &dyn HirDatabase, + owner: DefWithBodyId, + body: &Body, + infer: &InferenceResult, + // FIXME: root_expr should always be the body.body_expr, but since `X` in `[(); X]` doesn't have its own specific body yet, we + // need to take this input explicitly. + root_expr: ExprId, +) -> Result { + if let Some((_, x)) = infer.type_mismatches().next() { + return Err(MirLowerError::TypeMismatch(x.clone())); + } + let mut basic_blocks = Arena::new(); + let start_block = + basic_blocks.alloc(BasicBlock { statements: vec![], terminator: None, is_cleanup: false }); + let mut locals = Arena::new(); + // 0 is return local + locals.alloc(Local { ty: infer[root_expr].clone() }); + let mut binding_locals: ArenaMap = ArenaMap::new(); + // 1 to param_len is for params + let param_locals: Vec = if let DefWithBodyId::FunctionId(fid) = owner { + let substs = TyBuilder::placeholder_subst(db, fid); + let callable_sig = db.callable_item_signature(fid.into()).substitute(Interner, &substs); + body.params + .iter() + .zip(callable_sig.params().iter()) + .map(|(&x, ty)| { + let local_id = locals.alloc(Local { ty: ty.clone() }); + if let Pat::Bind { id, subpat: None } = body[x] { + if matches!( + body.bindings[id].mode, + BindingAnnotation::Unannotated | BindingAnnotation::Mutable + ) { + binding_locals.insert(id, local_id); + } + } + local_id + }) + .collect() + } else { + if !body.params.is_empty() { + return Err(MirLowerError::TypeError("Unexpected parameter for non function body")); + } + vec![] + }; + // and then rest of bindings + for (id, _) in body.bindings.iter() { + if !binding_locals.contains_idx(id) { + binding_locals.insert(id, locals.alloc(Local { ty: infer[id].clone() })); + } + } + let mir = MirBody { + basic_blocks, + locals, + start_block, + binding_locals, + param_locals, + owner, + arg_count: body.params.len(), + }; + let mut ctx = MirLowerCtx { + result: mir, + db, + infer, + body, + owner, + current_loop_blocks: None, + discr_temp: None, + }; + let mut current = start_block; + for (¶m, local) in body.params.iter().zip(ctx.result.param_locals.clone().into_iter()) { + if let Pat::Bind { id, .. } = body[param] { + if local == ctx.result.binding_locals[id] { + continue; + } + } + let r = ctx.pattern_match( + current, + None, + local.into(), + ctx.result.locals[local].ty.clone(), + param, + BindingAnnotation::Unannotated, + )?; + if let Some(b) = r.1 { + ctx.set_terminator(b, Terminator::Unreachable); + } + current = r.0; + } + if let Some(b) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? { + ctx.result.basic_blocks[b].terminator = Some(Terminator::Return); + } + Ok(ctx.result) +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs new file mode 100644 index 00000000000..fe8147dcd3e --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs @@ -0,0 +1,237 @@ +//! MIR lowering for places + +use super::*; +use hir_expand::name; + +macro_rules! not_supported { + ($x: expr) => { + return Err(MirLowerError::NotSupported(format!($x))) + }; +} + +impl MirLowerCtx<'_> { + fn lower_expr_to_some_place_without_adjust( + &mut self, + expr_id: ExprId, + prev_block: BasicBlockId, + ) -> Result> { + let ty = self.expr_ty(expr_id); + let place = self.temp(ty)?; + let Some(current) = self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)? else { + return Ok(None); + }; + Ok(Some((place.into(), current))) + } + + fn lower_expr_to_some_place_with_adjust( + &mut self, + expr_id: ExprId, + prev_block: BasicBlockId, + adjustments: &[Adjustment], + ) -> Result> { + let ty = + adjustments.last().map(|x| x.target.clone()).unwrap_or_else(|| self.expr_ty(expr_id)); + let place = self.temp(ty)?; + let Some(current) = self.lower_expr_to_place_with_adjust(expr_id, place.into(), prev_block, adjustments)? else { + return Ok(None); + }; + Ok(Some((place.into(), current))) + } + + pub(super) fn lower_expr_as_place_with_adjust( + &mut self, + current: BasicBlockId, + expr_id: ExprId, + upgrade_rvalue: bool, + adjustments: &[Adjustment], + ) -> Result> { + let try_rvalue = |this: &mut MirLowerCtx<'_>| { + if !upgrade_rvalue { + return Err(MirLowerError::MutatingRvalue); + } + this.lower_expr_to_some_place_with_adjust(expr_id, current, adjustments) + }; + if let Some((last, rest)) = adjustments.split_last() { + match last.kind { + Adjust::Deref(None) => { + let Some(mut x) = self.lower_expr_as_place_with_adjust( + current, + expr_id, + upgrade_rvalue, + rest, + )? else { + return Ok(None); + }; + x.0.projection.push(ProjectionElem::Deref); + Ok(Some(x)) + } + Adjust::Deref(Some(od)) => { + let Some((r, current)) = self.lower_expr_as_place_with_adjust( + current, + expr_id, + upgrade_rvalue, + rest, + )? else { + return Ok(None); + }; + self.lower_overloaded_deref( + current, + r, + rest.last() + .map(|x| x.target.clone()) + .unwrap_or_else(|| self.expr_ty(expr_id)), + last.target.clone(), + expr_id.into(), + match od.0 { + Some(Mutability::Mut) => true, + Some(Mutability::Not) => false, + None => { + not_supported!("implicit overloaded deref with unknown mutability") + } + }, + ) + } + Adjust::NeverToAny | Adjust::Borrow(_) | Adjust::Pointer(_) => try_rvalue(self), + } + } else { + self.lower_expr_as_place_without_adjust(current, expr_id, upgrade_rvalue) + } + } + + pub(super) fn lower_expr_as_place( + &mut self, + current: BasicBlockId, + expr_id: ExprId, + upgrade_rvalue: bool, + ) -> Result> { + match self.infer.expr_adjustments.get(&expr_id) { + Some(a) => self.lower_expr_as_place_with_adjust(current, expr_id, upgrade_rvalue, a), + None => self.lower_expr_as_place_without_adjust(current, expr_id, upgrade_rvalue), + } + } + + pub(super) fn lower_expr_as_place_without_adjust( + &mut self, + current: BasicBlockId, + expr_id: ExprId, + upgrade_rvalue: bool, + ) -> Result> { + let try_rvalue = |this: &mut MirLowerCtx<'_>| { + if !upgrade_rvalue { + return Err(MirLowerError::MutatingRvalue); + } + this.lower_expr_to_some_place_without_adjust(expr_id, current) + }; + match &self.body.exprs[expr_id] { + Expr::Path(p) => { + let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id); + let Some(pr) = resolver.resolve_path_in_value_ns(self.db.upcast(), p.mod_path()) else { + return Err(MirLowerError::unresolved_path(self.db, p)); + }; + let pr = match pr { + ResolveValueResult::ValueNs(v) => v, + ResolveValueResult::Partial(..) => return try_rvalue(self), + }; + match pr { + ValueNs::LocalBinding(pat_id) => { + Ok(Some((self.result.binding_locals[pat_id].into(), current))) + } + _ => try_rvalue(self), + } + } + Expr::UnaryOp { expr, op } => match op { + hir_def::expr::UnaryOp::Deref => { + if !matches!( + self.expr_ty(*expr).kind(Interner), + TyKind::Ref(..) | TyKind::Raw(..) + ) { + let Some(_) = self.lower_expr_as_place(current, *expr, true)? else { + return Ok(None); + }; + not_supported!("explicit overloaded deref"); + } + let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else { + return Ok(None); + }; + r.projection.push(ProjectionElem::Deref); + Ok(Some((r, current))) + } + _ => try_rvalue(self), + }, + Expr::Field { expr, .. } => { + let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else { + return Ok(None); + }; + self.push_field_projection(&mut r, expr_id)?; + Ok(Some((r, current))) + } + Expr::Index { base, index } => { + let base_ty = self.expr_ty_after_adjustments(*base); + let index_ty = self.expr_ty_after_adjustments(*index); + if index_ty != TyBuilder::usize() + || !matches!(base_ty.kind(Interner), TyKind::Array(..) | TyKind::Slice(..)) + { + not_supported!("overloaded index"); + } + let Some((mut p_base, current)) = + self.lower_expr_as_place(current, *base, true)? else { + return Ok(None); + }; + let l_index = self.temp(self.expr_ty_after_adjustments(*index))?; + let Some(current) = self.lower_expr_to_place(*index, l_index.into(), current)? else { + return Ok(None); + }; + p_base.projection.push(ProjectionElem::Index(l_index)); + Ok(Some((p_base, current))) + } + _ => try_rvalue(self), + } + } + + fn lower_overloaded_deref( + &mut self, + current: BasicBlockId, + place: Place, + source_ty: Ty, + target_ty: Ty, + span: MirSpan, + mutability: bool, + ) -> Result> { + let (chalk_mut, trait_lang_item, trait_method_name, borrow_kind) = if !mutability { + (Mutability::Not, LangItem::Deref, name![deref], BorrowKind::Shared) + } else { + ( + Mutability::Mut, + LangItem::DerefMut, + name![deref_mut], + BorrowKind::Mut { allow_two_phase_borrow: false }, + ) + }; + let ty_ref = TyKind::Ref(chalk_mut, static_lifetime(), source_ty.clone()).intern(Interner); + let target_ty_ref = TyKind::Ref(chalk_mut, static_lifetime(), target_ty).intern(Interner); + let ref_place: Place = self.temp(ty_ref)?.into(); + self.push_assignment(current, ref_place.clone(), Rvalue::Ref(borrow_kind, place), span); + let deref_trait = self + .resolve_lang_item(trait_lang_item)? + .as_trait() + .ok_or(MirLowerError::LangItemNotFound(trait_lang_item))?; + let deref_fn = self + .db + .trait_data(deref_trait) + .method_by_name(&trait_method_name) + .ok_or(MirLowerError::LangItemNotFound(trait_lang_item))?; + let deref_fn_op = Operand::const_zst( + TyKind::FnDef( + self.db.intern_callable_def(CallableDefId::FunctionId(deref_fn)).into(), + Substitution::from1(Interner, source_ty), + ) + .intern(Interner), + ); + let mut result: Place = self.temp(target_ty_ref)?.into(); + let Some(current) = self.lower_call(deref_fn_op, vec![Operand::Copy(ref_place)], result.clone(), current, false)? else { + return Ok(None); + }; + result.projection.push(ProjectionElem::Deref); + Ok(Some((result, current))) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs new file mode 100644 index 00000000000..ffc08b7e346 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs @@ -0,0 +1,348 @@ +//! A pretty-printer for MIR. + +use std::fmt::{Display, Write}; + +use hir_def::{body::Body, expr::BindingId}; +use hir_expand::name::Name; +use la_arena::ArenaMap; + +use crate::{ + db::HirDatabase, + display::HirDisplay, + mir::{PlaceElem, ProjectionElem, StatementKind, Terminator}, +}; + +use super::{ + AggregateKind, BasicBlockId, BorrowKind, LocalId, MirBody, Operand, Place, Rvalue, UnOp, +}; + +impl MirBody { + pub fn pretty_print(&self, db: &dyn HirDatabase) -> String { + let hir_body = db.body(self.owner); + let mut ctx = MirPrettyCtx::new(self, &hir_body, db); + ctx.for_body(); + ctx.result + } +} + +struct MirPrettyCtx<'a> { + body: &'a MirBody, + hir_body: &'a Body, + db: &'a dyn HirDatabase, + result: String, + ident: String, + local_to_binding: ArenaMap, +} + +macro_rules! w { + ($dst:expr, $($arg:tt)*) => { + { let _ = write!($dst, $($arg)*); } + }; +} + +macro_rules! wln { + ($dst:expr) => { + { let _ = writeln!($dst); } + }; + ($dst:expr, $($arg:tt)*) => { + { let _ = writeln!($dst, $($arg)*); } + }; +} + +impl Write for MirPrettyCtx<'_> { + fn write_str(&mut self, s: &str) -> std::fmt::Result { + let mut it = s.split('\n'); // note: `.lines()` is wrong here + self.write(it.next().unwrap_or_default()); + for line in it { + self.write_line(); + self.write(line); + } + Ok(()) + } +} + +enum LocalName { + Unknown(LocalId), + Binding(Name, LocalId), +} + +impl Display for LocalName { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + LocalName::Unknown(l) => write!(f, "_{}", u32::from(l.into_raw())), + LocalName::Binding(n, l) => write!(f, "{n}_{}", u32::from(l.into_raw())), + } + } +} + +impl<'a> MirPrettyCtx<'a> { + fn for_body(&mut self) { + self.with_block(|this| { + this.locals(); + wln!(this); + this.blocks(); + }); + } + + fn with_block(&mut self, f: impl FnOnce(&mut MirPrettyCtx<'_>)) { + self.ident += " "; + wln!(self, "{{"); + f(self); + for _ in 0..4 { + self.result.pop(); + self.ident.pop(); + } + wln!(self, "}}"); + } + + fn new(body: &'a MirBody, hir_body: &'a Body, db: &'a dyn HirDatabase) -> Self { + let local_to_binding = body.binding_locals.iter().map(|(x, y)| (*y, x)).collect(); + MirPrettyCtx { + body, + db, + result: String::new(), + ident: String::new(), + local_to_binding, + hir_body, + } + } + + fn write_line(&mut self) { + self.result.push('\n'); + self.result += &self.ident; + } + + fn write(&mut self, line: &str) { + self.result += line; + } + + fn locals(&mut self) { + for (id, local) in self.body.locals.iter() { + wln!(self, "let {}: {};", self.local_name(id), local.ty.display(self.db)); + } + } + + fn local_name(&self, local: LocalId) -> LocalName { + match self.local_to_binding.get(local) { + Some(b) => LocalName::Binding(self.hir_body.bindings[*b].name.clone(), local), + None => LocalName::Unknown(local), + } + } + + fn basic_block_id(&self, basic_block_id: BasicBlockId) -> String { + format!("'bb{}", u32::from(basic_block_id.into_raw())) + } + + fn blocks(&mut self) { + for (id, block) in self.body.basic_blocks.iter() { + wln!(self); + w!(self, "{}: ", self.basic_block_id(id)); + self.with_block(|this| { + for statement in &block.statements { + match &statement.kind { + StatementKind::Assign(l, r) => { + this.place(l); + w!(this, " = "); + this.rvalue(r); + wln!(this, ";"); + } + StatementKind::StorageDead(p) => { + wln!(this, "StorageDead({})", this.local_name(*p)); + } + StatementKind::StorageLive(p) => { + wln!(this, "StorageLive({})", this.local_name(*p)); + } + StatementKind::Deinit(p) => { + w!(this, "Deinit("); + this.place(p); + wln!(this, ");"); + } + StatementKind::Nop => wln!(this, "Nop;"), + } + } + match &block.terminator { + Some(terminator) => match terminator { + Terminator::Goto { target } => { + wln!(this, "goto 'bb{};", u32::from(target.into_raw())) + } + Terminator::SwitchInt { discr, targets } => { + w!(this, "switch "); + this.operand(discr); + w!(this, " "); + this.with_block(|this| { + for (c, b) in targets.iter() { + wln!(this, "{c} => {},", this.basic_block_id(b)); + } + wln!(this, "_ => {},", this.basic_block_id(targets.otherwise())); + }); + } + Terminator::Call { func, args, destination, target, .. } => { + w!(this, "Call "); + this.with_block(|this| { + w!(this, "func: "); + this.operand(func); + wln!(this, ","); + w!(this, "args: ["); + this.operand_list(args); + wln!(this, "],"); + w!(this, "destination: "); + this.place(destination); + wln!(this, ","); + w!(this, "target: "); + match target { + Some(t) => w!(this, "{}", this.basic_block_id(*t)), + None => w!(this, ""), + } + wln!(this, ","); + }); + } + _ => wln!(this, "{:?};", terminator), + }, + None => wln!(this, ";"), + } + }) + } + } + + fn place(&mut self, p: &Place) { + fn f(this: &mut MirPrettyCtx<'_>, local: LocalId, projections: &[PlaceElem]) { + let Some((last, head)) = projections.split_last() else { + // no projection + w!(this, "{}", this.local_name(local)); + return; + }; + match last { + ProjectionElem::Deref => { + w!(this, "(*"); + f(this, local, head); + w!(this, ")"); + } + ProjectionElem::Field(field) => { + let variant_data = field.parent.variant_data(this.db.upcast()); + let name = &variant_data.fields()[field.local_id].name; + match field.parent { + hir_def::VariantId::EnumVariantId(e) => { + w!(this, "("); + f(this, local, head); + let variant_name = + &this.db.enum_data(e.parent).variants[e.local_id].name; + w!(this, " as {}).{}", variant_name, name); + } + hir_def::VariantId::StructId(_) | hir_def::VariantId::UnionId(_) => { + f(this, local, head); + w!(this, ".{name}"); + } + } + } + ProjectionElem::TupleField(x) => { + f(this, local, head); + w!(this, ".{}", x); + } + ProjectionElem::Index(l) => { + f(this, local, head); + w!(this, "[{}]", this.local_name(*l)); + } + x => { + f(this, local, head); + w!(this, ".{:?}", x); + } + } + } + f(self, p.local, &p.projection); + } + + fn operand(&mut self, r: &Operand) { + match r { + Operand::Copy(p) | Operand::Move(p) => { + // MIR at the time of writing doesn't have difference between move and copy, so we show them + // equally. Feel free to change it. + self.place(p); + } + Operand::Constant(c) => w!(self, "Const({})", c.display(self.db)), + } + } + + fn rvalue(&mut self, r: &Rvalue) { + match r { + Rvalue::Use(op) => self.operand(op), + Rvalue::Ref(r, p) => { + match r { + BorrowKind::Shared => w!(self, "&"), + BorrowKind::Shallow => w!(self, "&shallow "), + BorrowKind::Unique => w!(self, "&uniq "), + BorrowKind::Mut { .. } => w!(self, "&mut "), + } + self.place(p); + } + Rvalue::Aggregate(AggregateKind::Tuple(_), x) => { + w!(self, "("); + self.operand_list(x); + w!(self, ")"); + } + Rvalue::Aggregate(AggregateKind::Array(_), x) => { + w!(self, "["); + self.operand_list(x); + w!(self, "]"); + } + Rvalue::Aggregate(AggregateKind::Adt(_, _), x) => { + w!(self, "Adt("); + self.operand_list(x); + w!(self, ")"); + } + Rvalue::Aggregate(AggregateKind::Union(_, _), x) => { + w!(self, "Union("); + self.operand_list(x); + w!(self, ")"); + } + Rvalue::Len(p) => { + w!(self, "Len("); + self.place(p); + w!(self, ")"); + } + Rvalue::Cast(ck, op, ty) => { + w!(self, "Discriminant({ck:?}"); + self.operand(op); + w!(self, "{})", ty.display(self.db)); + } + Rvalue::CheckedBinaryOp(b, o1, o2) => { + self.operand(o1); + w!(self, " {b} "); + self.operand(o2); + } + Rvalue::UnaryOp(u, o) => { + let u = match u { + UnOp::Not => "!", + UnOp::Neg => "-", + }; + w!(self, "{u} "); + self.operand(o); + } + Rvalue::Discriminant(p) => { + w!(self, "Discriminant("); + self.place(p); + w!(self, ")"); + } + Rvalue::ShallowInitBox(op, _) => { + w!(self, "ShallowInitBox("); + self.operand(op); + w!(self, ")"); + } + Rvalue::CopyForDeref(p) => { + w!(self, "CopyForDeref("); + self.place(p); + w!(self, ")"); + } + } + } + + fn operand_list(&mut self, x: &[Operand]) { + let mut it = x.iter(); + if let Some(first) = it.next() { + self.operand(first); + for op in it { + w!(self, ", "); + self.operand(op); + } + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index ba5d9c24126..bcd63d9472a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -61,22 +61,27 @@ fn setup_tracing() -> Option { Some(tracing::subscriber::set_default(subscriber)) } +#[track_caller] fn check_types(ra_fixture: &str) { check_impl(ra_fixture, false, true, false) } +#[track_caller] fn check_types_source_code(ra_fixture: &str) { check_impl(ra_fixture, false, true, true) } +#[track_caller] fn check_no_mismatches(ra_fixture: &str) { check_impl(ra_fixture, true, false, false) } +#[track_caller] fn check(ra_fixture: &str) { check_impl(ra_fixture, false, false, false) } +#[track_caller] fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_source: bool) { let _tracing = setup_tracing(); let (db, files) = TestDB::with_many_files(ra_fixture); @@ -158,7 +163,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour } else { ty.display_test(&db).to_string() }; - assert_eq!(actual, expected); + assert_eq!(actual, expected, "type annotation differs at {:#?}", range.range); } } @@ -174,7 +179,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour } else { ty.display_test(&db).to_string() }; - assert_eq!(actual, expected); + assert_eq!(actual, expected, "type annotation differs at {:#?}", range.range); } if let Some(expected) = adjustments.remove(&range) { let adjustments = inference_result @@ -191,30 +196,11 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour } } - for (pat, mismatch) in inference_result.pat_type_mismatches() { - let node = match pat_node(&body_source_map, pat, &db) { - Some(value) => value, - None => continue, - }; - let range = node.as_ref().original_file_range(&db); - let actual = format!( - "expected {}, got {}", - mismatch.expected.display_test(&db), - mismatch.actual.display_test(&db) - ); - match mismatches.remove(&range) { - Some(annotation) => assert_eq!(actual, annotation), - None => format_to!(unexpected_type_mismatches, "{:?}: {}\n", range.range, actual), - } - } - for (expr, mismatch) in inference_result.expr_type_mismatches() { - let node = match body_source_map.expr_syntax(expr) { - Ok(sp) => { - let root = db.parse_or_expand(sp.file_id).unwrap(); - sp.map(|ptr| ptr.to_node(&root).syntax().clone()) - } - Err(SyntheticSyntax) => continue, - }; + for (expr_or_pat, mismatch) in inference_result.type_mismatches() { + let Some(node) = (match expr_or_pat { + hir_def::expr::ExprOrPatId::ExprId(expr) => expr_node(&body_source_map, expr, &db), + hir_def::expr::ExprOrPatId::PatId(pat) => pat_node(&body_source_map, pat, &db), + }) else { continue; }; let range = node.as_ref().original_file_range(&db); let actual = format!( "expected {}, got {}", diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs index 3e110abaf4b..b524922b6cf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs @@ -258,6 +258,7 @@ fn test() { #[test] fn coerce_autoderef_block() { + // FIXME: We should know mutability in overloaded deref check_no_mismatches( r#" //- minicore: deref @@ -267,7 +268,7 @@ fn takes_ref_str(x: &str) {} fn returns_string() -> String { loop {} } fn test() { takes_ref_str(&{ returns_string() }); - // ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(Not))), Borrow(Ref(Not)) + // ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(None))), Borrow(Ref(Not)) } "#, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs index f00fa972948..1876be303ad 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs @@ -73,3 +73,24 @@ fn test(x: bool) -> &'static str { "#, ); } + +#[test] +fn non_unit_block_expr_stmt_no_semi() { + check( + r#" +fn test(x: bool) { + if x { + "notok" + //^^^^^^^ expected (), got &str + } else { + "ok" + //^^^^ expected (), got &str + } + match x { true => true, false => 0 } + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), got bool + //^ expected bool, got i32 + () +} +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index 41c53701df6..e568e7013fa 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -1167,7 +1167,6 @@ fn test() { 123..167 '{ ...o(); }': () 133..134 's': &S 137..151 'unsafe { f() }': &S - 137..151 'unsafe { f() }': &S 146..147 'f': fn f() -> &S 146..149 'f()': &S 157..158 's': &S @@ -1253,6 +1252,7 @@ fn foo(a: &T) { #[test] fn autoderef_visibility_field() { + // FIXME: We should know mutability in overloaded deref check( r#" //- minicore: deref @@ -1274,7 +1274,7 @@ mod a { mod b { fn foo() { let x = super::a::Bar::new().0; - // ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Not))) + // ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(None))) // ^^^^^^^^^^^^^^^^^^^^^^ type: char } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs index 9333e269352..74bcab6caa9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs @@ -476,7 +476,7 @@ fn infer_adt_pattern() { 183..184 'x': usize 190..191 'x': usize 201..205 'E::B': E - 209..212 'foo': bool + 209..212 'foo': {unknown} 216..217 '1': usize 227..231 'E::B': E 235..237 '10': usize @@ -953,9 +953,9 @@ fn main() { 42..51 'true | ()': bool 49..51 '()': () 57..59 '{}': () - 68..80 '(() | true,)': ((),) + 68..80 '(() | true,)': (bool,) 69..71 '()': () - 69..78 '() | true': () + 69..78 '() | true': bool 74..78 'true': bool 74..78 'true': bool 84..86 '{}': () @@ -964,19 +964,15 @@ fn main() { 96..102 '_ | ()': bool 100..102 '()': () 108..110 '{}': () - 119..128 '(() | _,)': ((),) + 119..128 '(() | _,)': (bool,) 120..122 '()': () - 120..126 '() | _': () + 120..126 '() | _': bool 125..126 '_': bool 132..134 '{}': () 49..51: expected bool, got () - 68..80: expected (bool,), got ((),) 69..71: expected bool, got () - 69..78: expected bool, got () 100..102: expected bool, got () - 119..128: expected (bool,), got ((),) 120..122: expected bool, got () - 120..126: expected bool, got () "#]], ); } @@ -1092,3 +1088,19 @@ fn my_fn(foo: ...) {} "#, ); } + +#[test] +fn ref_pat_mutability() { + check( + r#" +fn foo() { + let &() = &(); + let &mut () = &mut (); + let &mut () = &(); + //^^^^^^^ expected &(), got &mut () + let &() = &mut (); + //^^^ expected &mut (), got &() +} +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index de6ae7fff8f..e6b4f13c8d1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -270,7 +270,7 @@ fn infer_std_crash_5() { 61..320 '{ ... }': () 75..79 'name': &{unknown} 82..166 'if doe... }': &{unknown} - 85..98 'doesnt_matter': bool + 85..98 'doesnt_matter': {unknown} 99..128 '{ ... }': &{unknown} 113..118 'first': &{unknown} 134..166 '{ ... }': &{unknown} @@ -279,7 +279,7 @@ fn infer_std_crash_5() { 181..188 'content': &{unknown} 191..313 'if ICE... }': &{unknown} 194..231 'ICE_RE..._VALUE': {unknown} - 194..247 'ICE_RE...&name)': bool + 194..247 'ICE_RE...&name)': {unknown} 241..246 '&name': &&{unknown} 242..246 'name': &{unknown} 248..276 '{ ... }': &{unknown} @@ -1015,9 +1015,9 @@ fn cfg_tail() { 20..31 '{ "first" }': () 22..29 '"first"': &str 72..190 '{ ...] 13 }': () - 78..88 '{ "fake" }': &str + 78..88 '{ "fake" }': () 80..86 '"fake"': &str - 93..103 '{ "fake" }': &str + 93..103 '{ "fake" }': () 95..101 '"fake"': &str 108..120 '{ "second" }': () 110..118 '"second"': &str @@ -1744,3 +1744,15 @@ fn foo(b: Bar) { "#, ); } + +#[test] +fn regression_14305() { + check_no_mismatches( + r#" +//- minicore: add +trait Tr {} +impl Tr for [u8; C] {} +const C: usize = 2 + 2; +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index 2e5787b701c..0e9c349afef 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -352,7 +352,6 @@ unsafe fn baz(u: MyUnion) { 71..89 'MyUnio...o: 0 }': MyUnion 86..87 '0': u32 95..113 'unsafe...(u); }': () - 95..113 'unsafe...(u); }': () 104..107 'baz': fn baz(MyUnion) 104..110 'baz(u)': () 108..109 'u': MyUnion @@ -360,7 +359,6 @@ unsafe fn baz(u: MyUnion) { 126..146 'MyUnio... 0.0 }': MyUnion 141..144 '0.0': f32 152..170 'unsafe...(u); }': () - 152..170 'unsafe...(u); }': () 161..164 'baz': fn baz(MyUnion) 161..167 'baz(u)': () 165..166 'u': MyUnion @@ -2077,22 +2075,17 @@ async fn main() { 16..193 '{ ...2 }; }': () 26..27 'x': i32 30..43 'unsafe { 92 }': i32 - 30..43 'unsafe { 92 }': i32 39..41 '92': i32 53..54 'y': impl Future - 57..85 'async ...wait }': () 57..85 'async ...wait }': impl Future - 65..77 'async { () }': () 65..77 'async { () }': impl Future 65..83 'async ....await': () 73..75 '()': () 95..96 'z': ControlFlow<(), ()> - 130..140 'try { () }': () 130..140 'try { () }': ControlFlow<(), ()> 136..138 '()': () 150..151 'w': i32 154..166 'const { 92 }': i32 - 154..166 'const { 92 }': i32 162..164 '92': i32 176..177 't': i32 180..190 ''a: { 92 }': i32 @@ -2122,7 +2115,6 @@ fn main() { 83..84 'f': F 89..91 '{}': () 103..231 '{ ... }); }': () - 109..161 'async ... }': Result<(), ()> 109..161 'async ... }': impl Future> 125..139 'return Err(())': ! 132..135 'Err': Err<(), ()>(()) -> Result<(), ()> @@ -2134,7 +2126,6 @@ fn main() { 167..171 'test': fn test<(), (), || -> impl Future>, impl Future>>(|| -> impl Future>) 167..228 'test(|... })': () 172..227 '|| asy... }': || -> impl Future> - 175..227 'async ... }': Result<(), ()> 175..227 'async ... }': impl Future> 191..205 'return Err(())': ! 198..201 'Err': Err<(), ()>(()) -> Result<(), ()> @@ -3283,3 +3274,18 @@ fn func() { "#]], ); } + +#[test] +fn issue_14275() { + // FIXME: evaluate const generic + check_types( + r#" +struct Foo; +fn main() { + const B: bool = false; + let foo = Foo::; + //^^^ Foo<_> +} +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs index 70d2d5efa6c..34d957e26ef 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -5,6 +5,7 @@ use std::iter; use base_db::CrateId; use chalk_ir::{cast::Cast, fold::Shift, BoundVar, DebruijnIndex}; +use either::Either; use hir_def::{ db::DefDatabase, generics::{ @@ -19,7 +20,6 @@ use hir_def::{ }; use hir_expand::name::Name; use intern::Interned; -use itertools::Either; use rustc_hash::FxHashSet; use smallvec::{smallvec, SmallVec}; @@ -315,7 +315,10 @@ fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option it.lookup(db).container, GenericDefId::ConstId(it) => it.lookup(db).container, GenericDefId::EnumVariantId(it) => return Some(it.parent.into()), - GenericDefId::AdtId(_) | GenericDefId::TraitId(_) | GenericDefId::ImplId(_) => return None, + GenericDefId::AdtId(_) + | GenericDefId::TraitId(_) + | GenericDefId::ImplId(_) + | GenericDefId::TraitAliasId(_) => return None, }; match container { diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index 54425d69b6b..db0b84ef088 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -14,7 +14,8 @@ use syntax::{ast, AstNode}; use crate::{ Adt, AssocItem, Const, ConstParam, Enum, Field, Function, GenericParam, Impl, LifetimeParam, - Macro, Module, ModuleDef, Static, Struct, Trait, TypeAlias, TypeParam, Union, Variant, + Macro, Module, ModuleDef, Static, Struct, Trait, TraitAlias, TypeAlias, TypeParam, Union, + Variant, }; pub trait HasAttrs { @@ -60,6 +61,7 @@ impl_has_attrs![ (Static, StaticId), (Const, ConstId), (Trait, TraitId), + (TraitAlias, TraitAliasId), (TypeAlias, TypeAliasId), (Macro, MacroId), (Function, FunctionId), @@ -134,6 +136,7 @@ fn resolve_doc_path( AttrDefId::StaticId(it) => it.resolver(db.upcast()), AttrDefId::ConstId(it) => it.resolver(db.upcast()), AttrDefId::TraitId(it) => it.resolver(db.upcast()), + AttrDefId::TraitAliasId(it) => it.resolver(db.upcast()), AttrDefId::TypeAliasId(it) => it.resolver(db.upcast()), AttrDefId::ImplId(it) => it.resolver(db.upcast()), AttrDefId::ExternBlockId(it) => it.resolver(db.upcast()), diff --git a/src/tools/rust-analyzer/crates/hir/src/db.rs b/src/tools/rust-analyzer/crates/hir/src/db.rs index e25d8678458..cd465739139 100644 --- a/src/tools/rust-analyzer/crates/hir/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir/src/db.rs @@ -5,8 +5,9 @@ //! But we need this for at least LRU caching at the query level. pub use hir_def::db::*; pub use hir_expand::db::{ - AstDatabase, AstDatabaseStorage, AstIdMapQuery, HygieneFrameQuery, InternMacroCallQuery, - MacroArgTextQuery, MacroDefQuery, MacroExpandQuery, ParseMacroExpansionQuery, + AstDatabase, AstDatabaseStorage, AstIdMapQuery, ExpandProcMacroQuery, HygieneFrameQuery, + InternMacroCallQuery, MacroArgTextQuery, MacroDefQuery, MacroExpandErrorQuery, + MacroExpandQuery, ParseMacroExpansionQuery, }; pub use hir_ty::db::*; diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 54d43fa8dc7..8f019a81b2d 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -10,7 +10,7 @@ use hir_def::path::ModPath; use hir_expand::{name::Name, HirFileId, InFile}; use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; -use crate::{AssocItem, Field, MacroKind, Type}; +use crate::{AssocItem, Field, Local, MacroKind, Type}; macro_rules! diagnostics { ($($diag:ident,)*) => { @@ -31,6 +31,7 @@ macro_rules! diagnostics { diagnostics![ BreakOutsideOfLoop, + ExpectedFunction, InactiveCode, IncorrectCase, InvalidDeriveTarget, @@ -40,6 +41,7 @@ diagnostics![ MissingFields, MissingMatchArms, MissingUnsafe, + NeedMut, NoSuchField, PrivateAssocItem, PrivateField, @@ -47,10 +49,13 @@ diagnostics![ TypeMismatch, UnimplementedBuiltinMacro, UnresolvedExternCrate, + UnresolvedField, UnresolvedImport, UnresolvedMacroCall, + UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro, + UnusedMut, ]; #[derive(Debug)] @@ -130,6 +135,28 @@ pub struct PrivateAssocItem { pub item: AssocItem, } +#[derive(Debug)] +pub struct ExpectedFunction { + pub call: InFile>, + pub found: Type, +} + +#[derive(Debug)] +pub struct UnresolvedField { + pub expr: InFile>, + pub receiver: Type, + pub name: Name, + pub method_with_same_name_exists: bool, +} + +#[derive(Debug)] +pub struct UnresolvedMethodCall { + pub expr: InFile>, + pub receiver: Type, + pub name: Name, + pub field_with_same_name: Option, +} + #[derive(Debug)] pub struct PrivateField { pub expr: InFile>, @@ -140,6 +167,7 @@ pub struct PrivateField { pub struct BreakOutsideOfLoop { pub expr: InFile>, pub is_break: bool, + pub bad_value_break: bool, } #[derive(Debug)] @@ -171,17 +199,26 @@ pub struct MismatchedArgCount { #[derive(Debug)] pub struct MissingMatchArms { - pub file: HirFileId, - pub match_expr: AstPtr, + pub scrutinee_expr: InFile>, pub uncovered_patterns: String, } #[derive(Debug)] pub struct TypeMismatch { - // FIXME: add mismatches in patterns as well - pub expr: InFile>, + pub expr_or_pat: Either>, InFile>>, pub expected: Type, pub actual: Type, } +#[derive(Debug)] +pub struct NeedMut { + pub local: Local, + pub span: InFile, +} + +#[derive(Debug)] +pub struct UnusedMut { + pub local: Local, +} + pub use hir_ty::diagnostics::IncorrectCase; diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 0d19420127f..5aae92efd19 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -17,15 +17,23 @@ use hir_ty::{ }; use crate::{ - Adt, Const, ConstParam, Enum, Field, Function, GenericParam, HasCrate, HasVisibility, - LifetimeParam, Macro, Module, Static, Struct, Trait, TyBuilder, Type, TypeAlias, - TypeOrConstParam, TypeParam, Union, Variant, + Adt, AsAssocItem, AssocItemContainer, Const, ConstParam, Enum, Field, Function, GenericParam, + HasCrate, HasVisibility, LifetimeParam, Macro, Module, Static, Struct, Trait, TraitAlias, + TyBuilder, Type, TypeAlias, TypeOrConstParam, TypeParam, Union, Variant, }; impl HirDisplay for Function { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - let data = f.db.function_data(self.id); - write_visibility(self.module(f.db).id, self.visibility(f.db), f)?; + let db = f.db; + let data = db.function_data(self.id); + let container = self.as_assoc_item(db).map(|it| it.container(db)); + let mut module = self.module(db); + if let Some(AssocItemContainer::Impl(_)) = container { + // Block-local impls are "hoisted" to the nearest (non-block) module. + module = module.nearest_non_block_module(db); + } + let module_id = module.id; + write_visibility(module_id, self.visibility(db), f)?; if data.has_default_kw() { f.write_str("default ")?; } @@ -35,7 +43,7 @@ impl HirDisplay for Function { if data.has_async_kw() { f.write_str("async ")?; } - if self.is_unsafe_to_call(f.db) { + if self.is_unsafe_to_call(db) { f.write_str("unsafe ")?; } if let Some(abi) = &data.abi { @@ -50,7 +58,7 @@ impl HirDisplay for Function { let write_self_param = |ty: &TypeRef, f: &mut HirFormatter<'_>| match ty { TypeRef::Path(p) if p.is_self_type() => f.write_str("self"), - TypeRef::Reference(inner, lifetime, mut_) if matches!(&**inner,TypeRef::Path(p) if p.is_self_type()) => + TypeRef::Reference(inner, lifetime, mut_) if matches!(&**inner, TypeRef::Path(p) if p.is_self_type()) => { f.write_char('&')?; if let Some(lifetime) = lifetime { @@ -442,8 +450,15 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(), impl HirDisplay for Const { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - write_visibility(self.module(f.db).id, self.visibility(f.db), f)?; - let data = f.db.const_data(self.id); + let db = f.db; + let container = self.as_assoc_item(db).map(|it| it.container(db)); + let mut module = self.module(db); + if let Some(AssocItemContainer::Impl(_)) = container { + // Block-local impls are "hoisted" to the nearest (non-block) module. + module = module.nearest_non_block_module(db); + } + write_visibility(module.id, self.visibility(db), f)?; + let data = db.const_data(self.id); f.write_str("const ")?; match &data.name { Some(name) => write!(f, "{name}: ")?, @@ -486,6 +501,22 @@ impl HirDisplay for Trait { } } +impl HirDisplay for TraitAlias { + fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { + write_visibility(self.module(f.db).id, self.visibility(f.db), f)?; + let data = f.db.trait_alias_data(self.id); + write!(f, "trait {}", data.name)?; + let def_id = GenericDefId::TraitAliasId(self.id); + write_generic_params(def_id, f)?; + f.write_str(" = ")?; + // FIXME: Currently we lower every bounds in a trait alias as a trait bound on `Self` i.e. + // `trait Foo = Bar` is stored and displayed as `trait Foo = where Self: Bar`, which might + // be less readable. + write_where_clause(def_id, f)?; + Ok(()) + } +} + impl HirDisplay for TypeAlias { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { write_visibility(self.module(f.db).id, self.visibility(f.db), f)?; diff --git a/src/tools/rust-analyzer/crates/hir/src/from_id.rs b/src/tools/rust-analyzer/crates/hir/src/from_id.rs index f825a72c0f5..aaaa7abf386 100644 --- a/src/tools/rust-analyzer/crates/hir/src/from_id.rs +++ b/src/tools/rust-analyzer/crates/hir/src/from_id.rs @@ -4,7 +4,7 @@ //! are splitting the hir. use hir_def::{ - expr::{LabelId, PatId}, + expr::{BindingId, LabelId}, AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId, GenericParamId, ModuleDefId, VariantId, }; @@ -37,6 +37,7 @@ from_id![ (hir_def::EnumId, crate::Enum), (hir_def::TypeAliasId, crate::TypeAlias), (hir_def::TraitId, crate::Trait), + (hir_def::TraitAliasId, crate::TraitAlias), (hir_def::StaticId, crate::Static), (hir_def::ConstId, crate::Const), (hir_def::FunctionId, crate::Function), @@ -110,6 +111,7 @@ impl From for ModuleDef { ModuleDefId::ConstId(it) => ModuleDef::Const(it.into()), ModuleDefId::StaticId(it) => ModuleDef::Static(it.into()), ModuleDefId::TraitId(it) => ModuleDef::Trait(it.into()), + ModuleDefId::TraitAliasId(it) => ModuleDef::TraitAlias(it.into()), ModuleDefId::TypeAliasId(it) => ModuleDef::TypeAlias(it.into()), ModuleDefId::BuiltinType(it) => ModuleDef::BuiltinType(it.into()), ModuleDefId::MacroId(it) => ModuleDef::Macro(it.into()), @@ -127,6 +129,7 @@ impl From for ModuleDefId { ModuleDef::Const(it) => ModuleDefId::ConstId(it.into()), ModuleDef::Static(it) => ModuleDefId::StaticId(it.into()), ModuleDef::Trait(it) => ModuleDefId::TraitId(it.into()), + ModuleDef::TraitAlias(it) => ModuleDefId::TraitAliasId(it.into()), ModuleDef::TypeAlias(it) => ModuleDefId::TypeAliasId(it.into()), ModuleDef::BuiltinType(it) => ModuleDefId::BuiltinType(it.into()), ModuleDef::Macro(it) => ModuleDefId::MacroId(it.into()), @@ -172,6 +175,7 @@ impl From for GenericDefId { GenericDef::Function(it) => GenericDefId::FunctionId(it.id), GenericDef::Adt(it) => GenericDefId::AdtId(it.into()), GenericDef::Trait(it) => GenericDefId::TraitId(it.id), + GenericDef::TraitAlias(it) => GenericDefId::TraitAliasId(it.id), GenericDef::TypeAlias(it) => GenericDefId::TypeAliasId(it.id), GenericDef::Impl(it) => GenericDefId::ImplId(it.id), GenericDef::Variant(it) => GenericDefId::EnumVariantId(it.into()), @@ -186,6 +190,7 @@ impl From for GenericDef { GenericDefId::FunctionId(it) => GenericDef::Function(it.into()), GenericDefId::AdtId(it) => GenericDef::Adt(it.into()), GenericDefId::TraitId(it) => GenericDef::Trait(it.into()), + GenericDefId::TraitAliasId(it) => GenericDef::TraitAlias(it.into()), GenericDefId::TypeAliasId(it) => GenericDef::TypeAlias(it.into()), GenericDefId::ImplId(it) => GenericDef::Impl(it.into()), GenericDefId::EnumVariantId(it) => GenericDef::Variant(it.into()), @@ -246,9 +251,9 @@ impl From for GenericDefId { } } -impl From<(DefWithBodyId, PatId)> for Local { - fn from((parent, pat_id): (DefWithBodyId, PatId)) -> Self { - Local { parent, pat_id } +impl From<(DefWithBodyId, BindingId)> for Local { + fn from((parent, binding_id): (DefWithBodyId, BindingId)) -> Self { + Local { parent, binding_id } } } diff --git a/src/tools/rust-analyzer/crates/hir/src/has_source.rs b/src/tools/rust-analyzer/crates/hir/src/has_source.rs index f8b01db3e32..9f6b5c0a9fc 100644 --- a/src/tools/rust-analyzer/crates/hir/src/has_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/has_source.rs @@ -10,8 +10,9 @@ use hir_expand::InFile; use syntax::ast; use crate::{ - db::HirDatabase, Adt, Const, Enum, Field, FieldSource, Function, Impl, LifetimeParam, Macro, - Module, Static, Struct, Trait, TypeAlias, TypeOrConstParam, Union, Variant, + db::HirDatabase, Adt, Const, Enum, Field, FieldSource, Function, Impl, LifetimeParam, + LocalSource, Macro, Module, Static, Struct, Trait, TraitAlias, TypeAlias, TypeOrConstParam, + Union, Variant, }; pub trait HasSource { @@ -122,6 +123,12 @@ impl HasSource for Trait { Some(self.id.lookup(db.upcast()).source(db.upcast())) } } +impl HasSource for TraitAlias { + type Ast = ast::TraitAlias; + fn source(self, db: &dyn HirDatabase) -> Option> { + Some(self.id.lookup(db.upcast()).source(db.upcast())) + } +} impl HasSource for TypeAlias { type Ast = ast::TypeAlias; fn source(self, db: &dyn HirDatabase) -> Option> { @@ -158,7 +165,7 @@ impl HasSource for Impl { } impl HasSource for TypeOrConstParam { - type Ast = Either; + type Ast = Either; fn source(self, db: &dyn HirDatabase) -> Option> { let child_source = self.id.parent.child_source(db.upcast()); Some(child_source.map(|it| it[self.id.local_id].clone())) @@ -172,3 +179,11 @@ impl HasSource for LifetimeParam { Some(child_source.map(|it| it[self.id.local_id].clone())) } } + +impl HasSource for LocalSource { + type Ast = Either; + + fn source(self, _: &dyn HirDatabase) -> Option> { + Some(self.source) + } +} diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 2cb4ed2c335..25c07a2fbd3 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -41,34 +41,34 @@ use either::Either; use hir_def::{ adt::VariantData, body::{BodyDiagnostic, SyntheticSyntax}, - expr::{BindingAnnotation, ExprOrPatId, LabelId, Pat, PatId}, - generics::{TypeOrConstParamData, TypeParamProvenance}, + expr::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat}, + generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance}, item_tree::ItemTreeNode, lang_item::{LangItem, LangItemTarget}, layout::{Layout, LayoutError, ReprOptions}, - nameres::{self, diagnostics::DefDiagnostic}, + nameres::{self, diagnostics::DefDiagnostic, ModuleOrigin}, per_ns::PerNs, resolver::{HasResolver, Resolver}, src::HasSource as _, - type_ref::ConstScalar, AdtId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, LifetimeParamId, LocalEnumVariantId, LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId, - TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, + TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, }; use hir_expand::{name::name, MacroCallKind}; use hir_ty::{ all_super_traits, autoderef, - consteval::{unknown_const_as_generic, ComputedExpr, ConstEvalError, ConstExt}, + consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt}, diagnostics::BodyValidationDiagnostic, + display::HexifiedConst, layout::layout_of_ty, method_resolution::{self, TyFingerprint}, + mir::{self, interpret_mir}, primitive::UintTy, traits::FnTrait, AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, - ConcreteConst, ConstValue, GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, - Substitution, TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, - WhereClause, + GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution, + TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, WhereClause, }; use itertools::Itertools; use nameres::diagnostics::DefDiagnosticKind; @@ -77,7 +77,7 @@ use rustc_hash::FxHashSet; use stdx::{impl_from, never}; use syntax::{ ast::{self, HasAttrs as _, HasDocComments, HasName}, - AstNode, AstPtr, SmolStr, SyntaxNodePtr, TextRange, T, + AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, TextRange, T, }; use crate::db::{DefDatabase, HirDatabase}; @@ -85,12 +85,12 @@ use crate::db::{DefDatabase, HirDatabase}; pub use crate::{ attrs::{HasAttrs, Namespace}, diagnostics::{ - AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase, InvalidDeriveTarget, - MacroError, MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms, - MissingUnsafe, NoSuchField, PrivateAssocItem, PrivateField, + AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncorrectCase, + InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount, MissingFields, + MissingMatchArms, MissingUnsafe, NeedMut, NoSuchField, PrivateAssocItem, PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch, UnimplementedBuiltinMacro, - UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, UnresolvedModule, - UnresolvedProcMacro, + UnresolvedExternCrate, UnresolvedField, UnresolvedImport, UnresolvedMacroCall, + UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro, UnusedMut, }, has_source::HasSource, semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits}, @@ -130,6 +130,7 @@ pub use { }, hir_ty::{ display::{HirDisplay, HirDisplayError, HirWrite}, + mir::MirEvalError, PointerCast, Safety, }, }; @@ -272,6 +273,7 @@ pub enum ModuleDef { Const(Const), Static(Static), Trait(Trait), + TraitAlias(TraitAlias), TypeAlias(TypeAlias), BuiltinType(BuiltinType), Macro(Macro), @@ -284,6 +286,7 @@ impl_from!( Const, Static, Trait, + TraitAlias, TypeAlias, BuiltinType, Macro @@ -310,6 +313,7 @@ impl ModuleDef { ModuleDef::Const(it) => Some(it.module(db)), ModuleDef::Static(it) => Some(it.module(db)), ModuleDef::Trait(it) => Some(it.module(db)), + ModuleDef::TraitAlias(it) => Some(it.module(db)), ModuleDef::TypeAlias(it) => Some(it.module(db)), ModuleDef::Macro(it) => Some(it.module(db)), ModuleDef::BuiltinType(_) => None, @@ -338,6 +342,7 @@ impl ModuleDef { ModuleDef::Const(it) => it.name(db)?, ModuleDef::Adt(it) => it.name(db), ModuleDef::Trait(it) => it.name(db), + ModuleDef::TraitAlias(it) => it.name(db), ModuleDef::Function(it) => it.name(db), ModuleDef::Variant(it) => it.name(db), ModuleDef::TypeAlias(it) => it.name(db), @@ -356,6 +361,7 @@ impl ModuleDef { Adt::Union(it) => it.id.into(), }, ModuleDef::Trait(it) => it.id.into(), + ModuleDef::TraitAlias(it) => it.id.into(), ModuleDef::Function(it) => it.id.into(), ModuleDef::TypeAlias(it) => it.id.into(), ModuleDef::Module(it) => it.id.into(), @@ -398,6 +404,7 @@ impl ModuleDef { ModuleDef::Module(_) | ModuleDef::Adt(_) | ModuleDef::Trait(_) + | ModuleDef::TraitAlias(_) | ModuleDef::TypeAlias(_) | ModuleDef::Macro(_) | ModuleDef::BuiltinType(_) => None, @@ -413,6 +420,7 @@ impl ModuleDef { ModuleDef::Const(it) => it.attrs(db), ModuleDef::Static(it) => it.attrs(db), ModuleDef::Trait(it) => it.attrs(db), + ModuleDef::TraitAlias(it) => it.attrs(db), ModuleDef::TypeAlias(it) => it.attrs(db), ModuleDef::Macro(it) => it.attrs(db), ModuleDef::BuiltinType(_) => return None, @@ -429,6 +437,7 @@ impl HasVisibility for ModuleDef { ModuleDef::Const(it) => it.visibility(db), ModuleDef::Static(it) => it.visibility(db), ModuleDef::Trait(it) => it.visibility(db), + ModuleDef::TraitAlias(it) => it.visibility(db), ModuleDef::TypeAlias(it) => it.visibility(db), ModuleDef::Variant(it) => it.visibility(db), ModuleDef::Macro(it) => it.visibility(db), @@ -488,6 +497,20 @@ impl Module { Some(Module { id: def_map.module_id(parent_id) }) } + /// Finds nearest non-block ancestor `Module` (`self` included). + pub fn nearest_non_block_module(self, db: &dyn HirDatabase) -> Module { + let mut id = self.id; + loop { + let def_map = id.def_map(db.upcast()); + let origin = def_map[id.local_id].origin; + if matches!(origin, ModuleOrigin::BlockExpr { .. }) { + id = id.containing_module(db.upcast()).expect("block without parent module") + } else { + return Module { id }; + } + } + } + pub fn path_to_root(self, db: &dyn HirDatabase) -> Vec { let mut res = vec![self]; let mut curr = self; @@ -1092,8 +1115,8 @@ impl Variant { self.source(db)?.value.expr() } - pub fn eval(self, db: &dyn HirDatabase) -> Result { - db.const_eval_variant(self.into()) + pub fn eval(self, db: &dyn HirDatabase) -> Result { + db.const_eval_discriminant(self.into()) } } @@ -1170,6 +1193,25 @@ impl Adt { } } + /// Returns the lifetime of the DataType + pub fn lifetime(&self, db: &dyn HirDatabase) -> Option { + let resolver = match self { + Adt::Struct(s) => s.id.resolver(db.upcast()), + Adt::Union(u) => u.id.resolver(db.upcast()), + Adt::Enum(e) => e.id.resolver(db.upcast()), + }; + resolver + .generic_params() + .and_then(|gp| { + (&gp.lifetimes) + .iter() + // there should only be a single lifetime + // but `Arena` requires to use an iterator + .nth(0) + }) + .map(|arena| arena.1.clone()) + } + pub fn as_enum(&self) -> Option { if let Self::Enum(v) = self { Some(*v) @@ -1285,6 +1327,15 @@ impl DefWithBody { body.pretty_print(db.upcast(), self.id()) } + /// A textual representation of the MIR of this def's body for debugging purposes. + pub fn debug_mir(self, db: &dyn HirDatabase) -> String { + let body = db.mir_body(self.id()); + match body { + Ok(body) => body.pretty_print(db), + Err(e) => format!("error:\n{e:?}"), + } + } + pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec) { let krate = self.module(db).id.krate(); @@ -1334,42 +1385,35 @@ impl DefWithBody { let infer = db.infer(self.into()); let source_map = Lazy::new(|| db.body_with_source_map(self.into()).1); + let expr_syntax = |expr| source_map.expr_syntax(expr).expect("unexpected synthetic"); for d in &infer.diagnostics { match d { - hir_ty::InferenceDiagnostic::NoSuchField { expr } => { - let field = source_map.field_syntax(*expr); + &hir_ty::InferenceDiagnostic::NoSuchField { expr } => { + let field = source_map.field_syntax(expr); acc.push(NoSuchField { field }.into()) } - &hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break } => { - let expr = source_map - .expr_syntax(expr) - .expect("break outside of loop in synthetic syntax"); - acc.push(BreakOutsideOfLoop { expr, is_break }.into()) + &hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { + expr, + is_break, + bad_value_break, + } => { + let expr = expr_syntax(expr); + acc.push(BreakOutsideOfLoop { expr, is_break, bad_value_break }.into()) } - hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => { - match source_map.expr_syntax(*call_expr) { - Ok(source_ptr) => acc.push( - MismatchedArgCount { - call_expr: source_ptr, - expected: *expected, - found: *found, - } + &hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => { + acc.push( + MismatchedArgCount { call_expr: expr_syntax(call_expr), expected, found } .into(), - ), - Err(SyntheticSyntax) => (), - } + ) } &hir_ty::InferenceDiagnostic::PrivateField { expr, field } => { - let expr = source_map.expr_syntax(expr).expect("unexpected synthetic"); + let expr = expr_syntax(expr); let field = field.into(); acc.push(PrivateField { expr, field }.into()) } &hir_ty::InferenceDiagnostic::PrivateAssocItem { id, item } => { let expr_or_pat = match id { - ExprOrPatId::ExprId(expr) => source_map - .expr_syntax(expr) - .expect("unexpected synthetic") - .map(Either::Left), + ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(Either::Left), ExprOrPatId::PatId(pat) => source_map .pat_syntax(pat) .expect("unexpected synthetic") @@ -1378,16 +1422,76 @@ impl DefWithBody { let item = item.into(); acc.push(PrivateAssocItem { expr_or_pat, item }.into()) } + hir_ty::InferenceDiagnostic::ExpectedFunction { call_expr, found } => { + let call_expr = expr_syntax(*call_expr); + + acc.push( + ExpectedFunction { + call: call_expr, + found: Type::new(db, DefWithBodyId::from(self), found.clone()), + } + .into(), + ) + } + hir_ty::InferenceDiagnostic::UnresolvedField { + expr, + receiver, + name, + method_with_same_name_exists, + } => { + let expr = expr_syntax(*expr); + + acc.push( + UnresolvedField { + expr, + name: name.clone(), + receiver: Type::new(db, DefWithBodyId::from(self), receiver.clone()), + method_with_same_name_exists: *method_with_same_name_exists, + } + .into(), + ) + } + hir_ty::InferenceDiagnostic::UnresolvedMethodCall { + expr, + receiver, + name, + field_with_same_name, + } => { + let expr = expr_syntax(*expr); + + acc.push( + UnresolvedMethodCall { + expr, + name: name.clone(), + receiver: Type::new(db, DefWithBodyId::from(self), receiver.clone()), + field_with_same_name: field_with_same_name + .clone() + .map(|ty| Type::new(db, DefWithBodyId::from(self), ty)), + } + .into(), + ) + } } } - for (expr, mismatch) in infer.expr_type_mismatches() { - let expr = match source_map.expr_syntax(expr) { - Ok(expr) => expr, - Err(SyntheticSyntax) => continue, + for (pat_or_expr, mismatch) in infer.type_mismatches() { + let expr_or_pat = match pat_or_expr { + ExprOrPatId::ExprId(expr) => source_map.expr_syntax(expr).map(Either::Left), + // FIXME: Re-enable these once we have less false positives + ExprOrPatId::PatId(_pat) => continue, + #[allow(unreachable_patterns)] + ExprOrPatId::PatId(pat) => source_map.pat_syntax(pat).map(Either::Right), }; + let expr_or_pat = match expr_or_pat { + Ok(Either::Left(expr)) => Either::Left(expr), + Ok(Either::Right(InFile { file_id, value: Either::Left(pat) })) => { + Either::Right(InFile { file_id, value: pat }) + } + Ok(Either::Right(_)) | Err(SyntheticSyntax) => continue, + }; + acc.push( TypeMismatch { - expr, + expr_or_pat, expected: Type::new(db, DefWithBodyId::from(self), mismatch.expected.clone()), actual: Type::new(db, DefWithBodyId::from(self), mismatch.actual.clone()), } @@ -1405,6 +1509,41 @@ impl DefWithBody { } } + let hir_body = db.body(self.into()); + + if let Ok(borrowck_result) = db.borrowck(self.into()) { + let mir_body = &borrowck_result.mir_body; + let mol = &borrowck_result.mutability_of_locals; + for (binding_id, _) in hir_body.bindings.iter() { + let need_mut = &mol[mir_body.binding_locals[binding_id]]; + let local = Local { parent: self.into(), binding_id }; + match (need_mut, local.is_mut(db)) { + (mir::MutabilityReason::Mut { .. }, true) + | (mir::MutabilityReason::Not, false) => (), + (mir::MutabilityReason::Mut { spans }, false) => { + for span in spans { + let span: InFile = match span { + mir::MirSpan::ExprId(e) => match source_map.expr_syntax(*e) { + Ok(s) => s.map(|x| x.into()), + Err(_) => continue, + }, + mir::MirSpan::PatId(p) => match source_map.pat_syntax(*p) { + Ok(s) => s.map(|x| match x { + Either::Left(e) => e.into(), + Either::Right(e) => e.into(), + }), + Err(_) => continue, + }, + mir::MirSpan::Unknown => continue, + }; + acc.push(NeedMut { local, span }.into()); + } + } + (mir::MutabilityReason::Not, true) => acc.push(UnusedMut { local }.into()), + } + } + } + for diagnostic in BodyValidationDiagnostic::collect(db, self.into()) { match diagnostic { BodyValidationDiagnostic::RecordMissingFields { @@ -1489,11 +1628,13 @@ impl DefWithBody { if let ast::Expr::MatchExpr(match_expr) = &source_ptr.value.to_node(&root) { - if let Some(match_expr) = match_expr.expr() { + if let Some(scrut_expr) = match_expr.expr() { acc.push( MissingMatchArms { - file: source_ptr.file_id, - match_expr: AstPtr::new(&match_expr), + scrutinee_expr: InFile::new( + source_ptr.file_id, + AstPtr::new(&scrut_expr), + ), uncovered_patterns, } .into(), @@ -1582,6 +1723,10 @@ impl Function { .collect() } + pub fn num_params(self, db: &dyn HirDatabase) -> usize { + db.function_data(self.id).params.len() + } + pub fn method_params(self, db: &dyn HirDatabase) -> Option> { if self.self_param(db).is_none() { return None; @@ -1639,6 +1784,14 @@ impl Function { let def_map = db.crate_def_map(loc.krate(db).into()); def_map.fn_as_proc_macro(self.id).map(|id| Macro { id: id.into() }) } + + pub fn eval(self, db: &dyn HirDatabase) -> Result<(), MirEvalError> { + let body = db + .mir_body(self.id.into()) + .map_err(|e| MirEvalError::MirLowerError(self.id.into(), e))?; + interpret_mir(db, &body, false)?; + Ok(()) + } } // Note: logically, this belongs to `hir_ty`, but we are not using it there yet. @@ -1679,8 +1832,8 @@ impl Param { let parent = DefWithBodyId::FunctionId(self.func.into()); let body = db.body(parent); let pat_id = body.params[self.idx]; - if let Pat::Bind { .. } = &body[pat_id] { - Some(Local { parent, pat_id: body.params[self.idx] }) + if let Pat::Bind { id, .. } = &body[pat_id] { + Some(Local { parent, binding_id: *id }) } else { None } @@ -1781,8 +1934,18 @@ impl Const { Type::new_with_resolver_inner(db, &resolver, ty) } - pub fn eval(self, db: &dyn HirDatabase) -> Result { - db.const_eval(self.id) + pub fn render_eval(self, db: &dyn HirDatabase) -> Result { + let c = db.const_eval(self.id)?; + let r = format!("{}", HexifiedConst(c).display(db)); + // We want to see things like `` and `` as they are probably bug in our + // implementation, but there is no need to show things like `` or `` to + // the user. + if r.contains("not-supported>") { + return Err(ConstEvalError::MirEvalError(MirEvalError::NotSupported( + "rendering complex constants".to_string(), + ))); + } + return Ok(r); } } @@ -1893,6 +2056,27 @@ impl HasVisibility for Trait { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct TraitAlias { + pub(crate) id: TraitAliasId, +} + +impl TraitAlias { + pub fn module(self, db: &dyn HirDatabase) -> Module { + Module { id: self.id.lookup(db.upcast()).container } + } + + pub fn name(self, db: &dyn HirDatabase) -> Name { + db.trait_alias_data(self.id).name.clone() + } +} + +impl HasVisibility for TraitAlias { + fn visibility(&self, db: &dyn HirDatabase) -> Visibility { + db.trait_alias_data(self.id).visibility.resolve(db.upcast(), &self.id.resolver(db.upcast())) + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct TypeAlias { pub(crate) id: TypeAliasId, @@ -2265,6 +2449,7 @@ pub enum GenericDef { Function(Function), Adt(Adt), Trait(Trait), + TraitAlias(TraitAlias), TypeAlias(TypeAlias), Impl(Impl), // enum variants cannot have generics themselves, but their parent enums @@ -2277,6 +2462,7 @@ impl_from!( Function, Adt(Struct, Enum, Union), Trait, + TraitAlias, TypeAlias, Impl, Variant, @@ -2317,20 +2503,53 @@ impl GenericDef { } /// A single local definition. -/// -/// If the definition of this is part of a "MultiLocal", that is a local that has multiple declarations due to or-patterns -/// then this only references a single one of those. -/// To retrieve the other locals you should use [`Local::associated_locals`] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct Local { pub(crate) parent: DefWithBodyId, - pub(crate) pat_id: PatId, + pub(crate) binding_id: BindingId, +} + +pub struct LocalSource { + pub local: Local, + pub source: InFile>, +} + +impl LocalSource { + pub fn as_ident_pat(&self) -> Option<&ast::IdentPat> { + match &self.source.value { + Either::Left(x) => Some(x), + Either::Right(_) => None, + } + } + + pub fn into_ident_pat(self) -> Option { + match self.source.value { + Either::Left(x) => Some(x), + Either::Right(_) => None, + } + } + + pub fn original_file(&self, db: &dyn HirDatabase) -> FileId { + self.source.file_id.original_file(db.upcast()) + } + + pub fn name(&self) -> Option { + self.source.value.name() + } + + pub fn syntax(&self) -> &SyntaxNode { + self.source.value.syntax() + } + + pub fn syntax_ptr(self) -> InFile { + self.source.map(|x| SyntaxNodePtr::new(x.syntax())) + } } impl Local { pub fn is_param(self, db: &dyn HirDatabase) -> bool { - let src = self.source(db); - match src.value { + let src = self.primary_source(db); + match src.source.value { Either::Left(pat) => pat .syntax() .ancestors() @@ -2350,13 +2569,7 @@ impl Local { pub fn name(self, db: &dyn HirDatabase) -> Name { let body = db.body(self.parent); - match &body[self.pat_id] { - Pat::Bind { name, .. } => name.clone(), - _ => { - stdx::never!("hir::Local is missing a name!"); - Name::missing() - } - } + body[self.binding_id].name.clone() } pub fn is_self(self, db: &dyn HirDatabase) -> bool { @@ -2365,15 +2578,12 @@ impl Local { pub fn is_mut(self, db: &dyn HirDatabase) -> bool { let body = db.body(self.parent); - matches!(&body[self.pat_id], Pat::Bind { mode: BindingAnnotation::Mutable, .. }) + body[self.binding_id].mode == BindingAnnotation::Mutable } pub fn is_ref(self, db: &dyn HirDatabase) -> bool { let body = db.body(self.parent); - matches!( - &body[self.pat_id], - Pat::Bind { mode: BindingAnnotation::Ref | BindingAnnotation::RefMut, .. } - ) + matches!(body[self.binding_id].mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) } pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody { @@ -2387,34 +2597,33 @@ impl Local { pub fn ty(self, db: &dyn HirDatabase) -> Type { let def = self.parent; let infer = db.infer(def); - let ty = infer[self.pat_id].clone(); + let ty = infer[self.binding_id].clone(); Type::new(db, def, ty) } - pub fn associated_locals(self, db: &dyn HirDatabase) -> Box<[Local]> { - let body = db.body(self.parent); - body.ident_patterns_for(&self.pat_id) + /// All definitions for this local. Example: `let (a$0, _) | (_, a$0) = x;` + pub fn sources(self, db: &dyn HirDatabase) -> Vec { + let (body, source_map) = db.body_with_source_map(self.parent); + body[self.binding_id] + .definitions .iter() - .map(|&pat_id| Local { parent: self.parent, pat_id }) + .map(|&definition| { + let src = source_map.pat_syntax(definition).unwrap(); // Hmm... + let root = src.file_syntax(db.upcast()); + src.map(|ast| match ast { + // Suspicious unwrap + Either::Left(it) => Either::Left(it.cast().unwrap().to_node(&root)), + Either::Right(it) => Either::Right(it.to_node(&root)), + }) + }) + .map(|source| LocalSource { local: self, source }) .collect() } - /// If this local is part of a multi-local, retrieve the representative local. - /// That is the local that references are being resolved to. - pub fn representative(self, db: &dyn HirDatabase) -> Local { - let body = db.body(self.parent); - Local { pat_id: body.pattern_representative(self.pat_id), ..self } - } - - pub fn source(self, db: &dyn HirDatabase) -> InFile> { - let (_body, source_map) = db.body_with_source_map(self.parent); - let src = source_map.pat_syntax(self.pat_id).unwrap(); // Hmm... - let root = src.file_syntax(db.upcast()); - src.map(|ast| match ast { - // Suspicious unwrap - Either::Left(it) => Either::Left(it.cast().unwrap().to_node(&root)), - Either::Right(it) => Either::Right(it.to_node(&root)), - }) + /// The leftmost definition for this local. Example: `let (a$0, _) | (_, a) = x;` + pub fn primary_source(self, db: &dyn HirDatabase) -> LocalSource { + let all_sources = self.sources(db); + all_sources.into_iter().next().unwrap() } } @@ -3190,6 +3399,14 @@ impl Type { matches!(self.ty.kind(Interner), TyKind::Raw(..)) } + pub fn remove_raw_ptr(&self) -> Option { + if let TyKind::Raw(_, ty) = self.ty.kind(Interner) { + Some(self.derived(ty.clone())) + } else { + None + } + } + pub fn contains_unknown(&self) -> bool { // FIXME: When we get rid of `ConstScalar::Unknown`, we can just look at precomputed // `TypeFlags` in `TyData`. @@ -3260,12 +3477,7 @@ impl Type { pub fn as_array(&self, _db: &dyn HirDatabase) -> Option<(Type, usize)> { if let TyKind::Array(ty, len) = &self.ty.kind(Interner) { - match len.data(Interner).value { - ConstValue::Concrete(ConcreteConst { interned: ConstScalar::UInt(len) }) => { - Some((self.derived(ty.clone()), len as usize)) - } - _ => None, - } + try_const_usize(len).map(|x| (self.derived(ty.clone()), x as usize)) } else { None } @@ -3321,6 +3533,24 @@ impl Type { } } + /// Iterates its type arguments + /// + /// It iterates the actual type arguments when concrete types are used + /// and otherwise the generic names. + /// It does not include `const` arguments. + /// + /// For code, such as: + /// ```text + /// struct Foo + /// + /// impl Foo + /// ``` + /// + /// It iterates: + /// ```text + /// - "String" + /// - "U" + /// ``` pub fn type_arguments(&self) -> impl Iterator + '_ { self.ty .strip_references() @@ -3331,12 +3561,62 @@ impl Type { .map(move |ty| self.derived(ty)) } - pub fn iterate_method_candidates( + /// Iterates its type and const arguments + /// + /// It iterates the actual type and const arguments when concrete types + /// are used and otherwise the generic names. + /// + /// For code, such as: + /// ```text + /// struct Foo + /// + /// impl Foo + /// ``` + /// + /// It iterates: + /// ```text + /// - "String" + /// - "U" + /// - "12" + /// ``` + pub fn type_and_const_arguments<'a>( + &'a self, + db: &'a dyn HirDatabase, + ) -> impl Iterator + 'a { + self.ty + .strip_references() + .as_adt() + .into_iter() + .flat_map(|(_, substs)| substs.iter(Interner)) + .filter_map(|arg| { + // arg can be either a `Ty` or `constant` + if let Some(ty) = arg.ty(Interner) { + Some(SmolStr::new(ty.display(db).to_string())) + } else if let Some(const_) = arg.constant(Interner) { + Some(SmolStr::new_inline(&const_.display(db).to_string())) + } else { + None + } + }) + } + + /// Combines lifetime indicators, type and constant parameters into a single `Iterator` + pub fn generic_parameters<'a>( + &'a self, + db: &'a dyn HirDatabase, + ) -> impl Iterator + 'a { + // iterate the lifetime + self.as_adt() + .and_then(|a| a.lifetime(db).and_then(|lt| Some((<.name).to_smol_str()))) + .into_iter() + // add the type and const paramaters + .chain(self.type_and_const_arguments(db)) + } + + pub fn iterate_method_candidates_with_traits( &self, db: &dyn HirDatabase, scope: &SemanticsScope<'_>, - // FIXME this can be retrieved from `scope`, except autoimport uses this - // to specify a different set, so the method needs to be split traits_in_scope: &FxHashSet, with_local_impls: Option, name: Option<&Name>, @@ -3364,6 +3644,24 @@ impl Type { slot } + pub fn iterate_method_candidates( + &self, + db: &dyn HirDatabase, + scope: &SemanticsScope<'_>, + with_local_impls: Option, + name: Option<&Name>, + callback: impl FnMut(Function) -> Option, + ) -> Option { + self.iterate_method_candidates_with_traits( + db, + scope, + &scope.visible_traits().0, + with_local_impls, + name, + callback, + ) + } + fn iterate_method_candidates_dyn( &self, db: &dyn HirDatabase, @@ -3632,11 +3930,13 @@ impl Type { } } +// FIXME: Document this #[derive(Debug)] pub struct Callable { ty: Type, sig: CallableSig, callee: Callee, + /// Whether this is a method that was called with method call syntax. pub(crate) is_bound_method: bool, } @@ -3670,14 +3970,14 @@ impl Callable { Other => CallableKind::Other, } } - pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option { + pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<(ast::SelfParam, Type)> { let func = match self.callee { Callee::Def(CallableDefId::FunctionId(it)) if self.is_bound_method => it, _ => return None, }; let src = func.lookup(db.upcast()).source(db.upcast()); let param_list = src.value.param_list()?; - param_list.self_param() + Some((param_list.self_param()?, self.ty.derived(self.sig.params()[0].clone()))) } pub fn n_params(&self) -> usize { self.sig.params().len() - if self.is_bound_method { 1 } else { 0 } @@ -3936,6 +4236,12 @@ impl HasCrate for Trait { } } +impl HasCrate for TraitAlias { + fn krate(&self, db: &dyn HirDatabase) -> Crate { + self.module(db).krate() + } +} + impl HasCrate for Static { fn krate(&self, db: &dyn HirDatabase) -> Crate { self.module(db).krate() diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 486b7ee62ed..8bd905d0113 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -12,7 +12,7 @@ use hir_def::{ macro_id_to_def_id, resolver::{self, HasResolver, Resolver, TypeNs}, type_ref::Mutability, - AsMacroCall, DefWithBodyId, FunctionId, MacroId, TraitId, VariantId, + AsMacroCall, DefWithBodyId, FieldId, FunctionId, MacroId, TraitId, VariantId, }; use hir_expand::{ db::AstDatabase, @@ -68,7 +68,8 @@ impl PathResolution { | ModuleDef::Function(_) | ModuleDef::Module(_) | ModuleDef::Static(_) - | ModuleDef::Trait(_), + | ModuleDef::Trait(_) + | ModuleDef::TraitAlias(_), ) => None, PathResolution::Def(ModuleDef::TypeAlias(alias)) => { Some(TypeNs::TypeAliasId((*alias).into())) @@ -365,6 +366,16 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.resolve_method_call(call).map(Function::from) } + /// Attempts to resolve this call expression as a method call falling back to resolving it as a field. + pub fn resolve_method_call_field_fallback( + &self, + call: &ast::MethodCallExpr, + ) -> Option> { + self.imp + .resolve_method_call_fallback(call) + .map(|it| it.map_left(Function::from).map_right(Field::from)) + } + pub fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option { self.imp.resolve_await_to_poll(await_expr).map(Function::from) } @@ -527,8 +538,8 @@ impl<'db> SemanticsImpl<'db> { } fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Attr) -> Option { - let src = self.wrap_node_infile(attr.clone()); let adt = attr.syntax().parent().and_then(ast::Adt::cast)?; + let src = self.wrap_node_infile(attr.clone()); let call_id = self.with_ctx(|ctx| { ctx.attr_to_derive_macro_call(src.with_value(&adt), src).map(|(_, it, _)| it) })?; @@ -1092,7 +1103,10 @@ impl<'db> SemanticsImpl<'db> { let kind = match adjust.kind { hir_ty::Adjust::NeverToAny => Adjust::NeverToAny, hir_ty::Adjust::Deref(Some(hir_ty::OverloadedDeref(m))) => { - Adjust::Deref(Some(OverloadedDeref(mutability(m)))) + // FIXME: Should we handle unknown mutability better? + Adjust::Deref(Some(OverloadedDeref( + m.map(mutability).unwrap_or(Mutability::Shared), + ))) } hir_ty::Adjust::Deref(None) => Adjust::Deref(None), hir_ty::Adjust::Borrow(hir_ty::AutoBorrow::RawPtr(m)) => { @@ -1145,6 +1159,13 @@ impl<'db> SemanticsImpl<'db> { self.analyze(call.syntax())?.resolve_method_call(self.db, call) } + fn resolve_method_call_fallback( + &self, + call: &ast::MethodCallExpr, + ) -> Option> { + self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call) + } + fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option { self.analyze(await_expr.syntax())?.resolve_await_to_poll(self.db, await_expr) } @@ -1330,6 +1351,7 @@ impl<'db> SemanticsImpl<'db> { }) } ChildContainer::TraitId(it) => it.resolver(self.db.upcast()), + ChildContainer::TraitAliasId(it) => it.resolver(self.db.upcast()), ChildContainer::ImplId(it) => it.resolver(self.db.upcast()), ChildContainer::ModuleId(it) => it.resolver(self.db.upcast()), ChildContainer::EnumId(it) => it.resolver(self.db.upcast()), @@ -1556,6 +1578,7 @@ to_def_impls![ (crate::Enum, ast::Enum, enum_to_def), (crate::Union, ast::Union, union_to_def), (crate::Trait, ast::Trait, trait_to_def), + (crate::TraitAlias, ast::TraitAlias, trait_alias_to_def), (crate::Impl, ast::Impl, impl_to_def), (crate::TypeAlias, ast::TypeAlias, type_alias_to_def), (crate::Const, ast::Const, const_to_def), @@ -1634,8 +1657,8 @@ impl<'a> SemanticsScope<'a> { resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()), resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()), resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(id.into()), - resolver::ScopeDef::Local(pat_id) => match self.resolver.body_owner() { - Some(parent) => ScopeDef::Local(Local { parent, pat_id }), + resolver::ScopeDef::Local(binding_id) => match self.resolver.body_owner() { + Some(parent) => ScopeDef::Local(Local { parent, binding_id }), None => continue, }, resolver::ScopeDef::Label(label_id) => match self.resolver.body_owner() { @@ -1673,6 +1696,7 @@ impl<'a> SemanticsScope<'a> { } } +#[derive(Debug)] pub struct VisibleTraits(pub FxHashSet); impl ops::Deref for VisibleTraits { diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs index 2b5bfda1d43..f6f8c9a250f 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs @@ -89,16 +89,16 @@ use base_db::FileId; use hir_def::{ child_by_source::ChildBySource, dyn_map::DynMap, - expr::{LabelId, PatId}, + expr::{BindingId, LabelId}, keys::{self, Key}, AdtId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, LifetimeParamId, MacroId, ModuleId, StaticId, StructId, - TraitId, TypeAliasId, TypeParamId, UnionId, VariantId, + TraitAliasId, TraitId, TypeAliasId, TypeParamId, UnionId, VariantId, }; use hir_expand::{attrs::AttrId, name::AsName, HirFileId, MacroCallId}; use rustc_hash::FxHashMap; use smallvec::SmallVec; -use stdx::impl_from; +use stdx::{impl_from, never}; use syntax::{ ast::{self, HasName}, AstNode, SyntaxNode, @@ -159,6 +159,12 @@ impl SourceToDefCtx<'_, '_> { pub(super) fn trait_to_def(&mut self, src: InFile) -> Option { self.to_def(src, keys::TRAIT) } + pub(super) fn trait_alias_to_def( + &mut self, + src: InFile, + ) -> Option { + self.to_def(src, keys::TRAIT_ALIAS) + } pub(super) fn impl_to_def(&mut self, src: InFile) -> Option { self.to_def(src, keys::IMPL) } @@ -210,14 +216,14 @@ impl SourceToDefCtx<'_, '_> { pub(super) fn bind_pat_to_def( &mut self, src: InFile, - ) -> Option<(DefWithBodyId, PatId)> { + ) -> Option<(DefWithBodyId, BindingId)> { let container = self.find_pat_or_label_container(src.syntax())?; let (body, source_map) = self.db.body_with_source_map(container); let src = src.map(ast::Pat::from); let pat_id = source_map.node_pat(src.as_ref())?; // the pattern could resolve to a constant, verify that that is not the case - if let crate::Pat::Bind { .. } = body[pat_id] { - Some((container, pat_id)) + if let crate::Pat::Bind { id, .. } = body[pat_id] { + Some((container, id)) } else { None } @@ -225,11 +231,16 @@ impl SourceToDefCtx<'_, '_> { pub(super) fn self_param_to_def( &mut self, src: InFile, - ) -> Option<(DefWithBodyId, PatId)> { + ) -> Option<(DefWithBodyId, BindingId)> { let container = self.find_pat_or_label_container(src.syntax())?; - let (_body, source_map) = self.db.body_with_source_map(container); + let (body, source_map) = self.db.body_with_source_map(container); let pat_id = source_map.node_self_param(src.as_ref())?; - Some((container, pat_id)) + if let crate::Pat::Bind { id, .. } = body[pat_id] { + Some((container, id)) + } else { + never!(); + None + } } pub(super) fn label_to_def( &mut self, @@ -353,6 +364,9 @@ impl SourceToDefCtx<'_, '_> { match item { ast::Item::Module(it) => self.module_to_def(container.with_value(it))?.into(), ast::Item::Trait(it) => self.trait_to_def(container.with_value(it))?.into(), + ast::Item::TraitAlias(it) => { + self.trait_alias_to_def(container.with_value(it))?.into() + } ast::Item::Impl(it) => self.impl_to_def(container.with_value(it))?.into(), ast::Item::Enum(it) => self.enum_to_def(container.with_value(it))?.into(), ast::Item::TypeAlias(it) => { @@ -400,6 +414,9 @@ impl SourceToDefCtx<'_, '_> { ast::Item::Struct(it) => self.struct_to_def(InFile::new(file_id, it))?.into(), ast::Item::Enum(it) => self.enum_to_def(InFile::new(file_id, it))?.into(), ast::Item::Trait(it) => self.trait_to_def(InFile::new(file_id, it))?.into(), + ast::Item::TraitAlias(it) => { + self.trait_alias_to_def(InFile::new(file_id, it))?.into() + } ast::Item::TypeAlias(it) => { self.type_alias_to_def(InFile::new(file_id, it))?.into() } @@ -435,6 +452,7 @@ pub(crate) enum ChildContainer { DefWithBodyId(DefWithBodyId), ModuleId(ModuleId), TraitId(TraitId), + TraitAliasId(TraitAliasId), ImplId(ImplId), EnumId(EnumId), VariantId(VariantId), @@ -447,6 +465,7 @@ impl_from! { DefWithBodyId, ModuleId, TraitId, + TraitAliasId, ImplId, EnumId, VariantId, @@ -462,6 +481,7 @@ impl ChildContainer { ChildContainer::DefWithBodyId(it) => it.child_by_source(db, file_id), ChildContainer::ModuleId(it) => it.child_by_source(db, file_id), ChildContainer::TraitId(it) => it.child_by_source(db, file_id), + ChildContainer::TraitAliasId(_) => DynMap::default(), ChildContainer::ImplId(it) => it.child_by_source(db, file_id), ChildContainer::EnumId(it) => it.child_by_source(db, file_id), ChildContainer::VariantId(it) => it.child_by_source(db, file_id), diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 3b39e9fa919..133fa810d66 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -10,6 +10,7 @@ use std::{ sync::Arc, }; +use either::Either; use hir_def::{ body::{ self, @@ -51,7 +52,7 @@ use syntax::{ use crate::{ db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr, BuiltinType, Callable, Const, DeriveHelper, Field, Function, Local, Macro, ModuleDef, Static, - Struct, ToolModule, Trait, Type, TypeAlias, Variant, + Struct, ToolModule, Trait, TraitAlias, Type, TypeAlias, Variant, }; /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of @@ -266,6 +267,21 @@ impl SourceAnalyzer { Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs)) } + pub(crate) fn resolve_method_call_fallback( + &self, + db: &dyn HirDatabase, + call: &ast::MethodCallExpr, + ) -> Option> { + let expr_id = self.expr_id(db, &call.clone().into())?; + let inference_result = self.infer.as_ref()?; + match inference_result.method_resolution(expr_id) { + Some((f_in_trait, substs)) => { + Some(Either::Left(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs))) + } + None => inference_result.field_resolution(expr_id).map(Either::Right), + } + } + pub(crate) fn resolve_await_to_poll( &self, db: &dyn HirDatabase, @@ -406,8 +422,8 @@ impl SourceAnalyzer { // Shorthand syntax, resolve to the local let path = ModPath::from_segments(PathKind::Plain, once(local_name.clone())); match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) { - Some(ValueNs::LocalBinding(pat_id)) => { - Some(Local { pat_id, parent: self.resolver.body_owner()? }) + Some(ValueNs::LocalBinding(binding_id)) => { + Some(Local { binding_id, parent: self.resolver.body_owner()? }) } _ => None, } @@ -791,7 +807,7 @@ impl SourceAnalyzer { || Arc::new(hir_ty::TraitEnvironment::empty(krate)), |d| db.trait_environment(d), ); - method_resolution::lookup_impl_method(db, env, func, substs) + method_resolution::lookup_impl_method(db, env, func, substs).0 } fn resolve_impl_const_or_trait_def( @@ -809,7 +825,7 @@ impl SourceAnalyzer { || Arc::new(hir_ty::TraitEnvironment::empty(krate)), |d| db.trait_environment(d), ); - method_resolution::lookup_impl_const(db, env, const_id, subs) + method_resolution::lookup_impl_const(db, env, const_id, subs).0 } fn lang_trait_fn( @@ -943,17 +959,17 @@ fn resolve_hir_path_( res.map(|ty_ns| (ty_ns, path.segments().first())) } None => { - let (ty, remaining) = + let (ty, remaining_idx) = resolver.resolve_path_in_type_ns(db.upcast(), path.mod_path())?; - match remaining { - Some(remaining) if remaining > 1 => { - if remaining + 1 == path.segments().len() { + match remaining_idx { + Some(remaining_idx) => { + if remaining_idx + 1 == path.segments().len() { Some((ty, path.segments().last())) } else { None } } - _ => Some((ty, path.segments().get(1))), + None => Some((ty, None)), } } }?; @@ -978,6 +994,7 @@ fn resolve_hir_path_( TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), TypeNs::BuiltinType(it) => PathResolution::Def(BuiltinType::from(it).into()), TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), + TypeNs::TraitAliasId(it) => PathResolution::Def(TraitAlias::from(it).into()), }; match unresolved { Some(unresolved) => resolver @@ -1001,8 +1018,8 @@ fn resolve_hir_path_( let values = || { resolver.resolve_path_in_value_ns_fully(db.upcast(), path.mod_path()).and_then(|val| { let res = match val { - ValueNs::LocalBinding(pat_id) => { - let var = Local { parent: body_owner?, pat_id }; + ValueNs::LocalBinding(binding_id) => { + let var = Local { parent: body_owner?, binding_id }; PathResolution::Local(var) } ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()), @@ -1065,6 +1082,7 @@ fn resolve_hir_path_qualifier( TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), TypeNs::BuiltinType(it) => PathResolution::Def(BuiltinType::from(it).into()), TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), + TypeNs::TraitAliasId(it) => PathResolution::Def(TraitAlias::from(it).into()), }) .or_else(|| { resolver diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index fd78decda4e..a9afa1c6f45 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -68,6 +68,7 @@ pub enum FileSymbolKind { Static, Struct, Trait, + TraitAlias, TypeAlias, Union, } @@ -153,6 +154,9 @@ impl<'a> SymbolCollector<'a> { self.push_decl(id, FileSymbolKind::Trait); self.collect_from_trait(id); } + ModuleDefId::TraitAliasId(id) => { + self.push_decl(id, FileSymbolKind::TraitAlias); + } ModuleDefId::TypeAliasId(id) => { self.push_decl_assoc(id, FileSymbolKind::TypeAlias); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs index 0057f439f1a..785ae3d09c6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs @@ -211,10 +211,8 @@ fn main() { check_assist_not_applicable( add_explicit_type, r#" -//- minicore: option - fn main() { - let $0l = [0.0; Some(2).unwrap()]; + let $0l = [0.0; unresolved_function(5)]; } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs index f32ef2d59d8..9e1d9a702a1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs @@ -157,19 +157,12 @@ fn is_ref_and_impls_iter_method( let iter_trait = FamousDefs(sema, krate).core_iter_Iterator()?; let has_wanted_method = ty - .iterate_method_candidates( - sema.db, - &scope, - &scope.visible_traits().0, - None, - Some(&wanted_method), - |func| { - if func.ret_type(sema.db).impls_trait(sema.db, iter_trait, &[]) { - return Some(()); - } - None - }, - ) + .iterate_method_candidates(sema.db, &scope, None, Some(&wanted_method), |func| { + if func.ret_type(sema.db).impls_trait(sema.db, iter_trait, &[]) { + return Some(()); + } + None + }) .is_some(); if !has_wanted_method { return None; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs index 65c2479e9f2..7f2c01772ba 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs @@ -1,6 +1,6 @@ use ide_db::defs::{Definition, NameRefClass}; use syntax::{ - ast::{self, HasName}, + ast::{self, HasName, Name}, ted, AstNode, SyntaxNode, }; @@ -48,7 +48,7 @@ pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<' other => format!("{{ {other} }}"), }; let extracting_arm_pat = extracting_arm.pat()?; - let extracted_variable = find_extracted_variable(ctx, &extracting_arm)?; + let extracted_variable_positions = find_extracted_variable(ctx, &extracting_arm)?; acc.add( AssistId("convert_match_to_let_else", AssistKind::RefactorRewrite), @@ -56,7 +56,7 @@ pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<' let_stmt.syntax().text_range(), |builder| { let extracting_arm_pat = - rename_variable(&extracting_arm_pat, extracted_variable, binding); + rename_variable(&extracting_arm_pat, &extracted_variable_positions, binding); builder.replace( let_stmt.syntax().text_range(), format!("let {extracting_arm_pat} = {initializer_expr} else {diverging_arm_expr};"), @@ -95,14 +95,15 @@ fn find_arms( } // Given an extracting arm, find the extracted variable. -fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Option { +fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Option> { match arm.expr()? { ast::Expr::PathExpr(path) => { let name_ref = path.syntax().descendants().find_map(ast::NameRef::cast)?; match NameRefClass::classify(&ctx.sema, &name_ref)? { NameRefClass::Definition(Definition::Local(local)) => { - let source = local.source(ctx.db()).value.left()?; - Some(source.name()?) + let source = + local.sources(ctx.db()).into_iter().map(|x| x.into_ident_pat()?.name()); + source.collect() } _ => None, } @@ -115,27 +116,34 @@ fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Opti } // Rename `extracted` with `binding` in `pat`. -fn rename_variable(pat: &ast::Pat, extracted: ast::Name, binding: ast::Pat) -> SyntaxNode { +fn rename_variable(pat: &ast::Pat, extracted: &[Name], binding: ast::Pat) -> SyntaxNode { let syntax = pat.syntax().clone_for_update(); - let extracted_syntax = syntax.covering_element(extracted.syntax().text_range()); + let extracted = extracted + .iter() + .map(|e| syntax.covering_element(e.syntax().text_range())) + .collect::>(); + for extracted_syntax in extracted { + // If `extracted` variable is a record field, we should rename it to `binding`, + // otherwise we just need to replace `extracted` with `binding`. - // If `extracted` variable is a record field, we should rename it to `binding`, - // otherwise we just need to replace `extracted` with `binding`. - - if let Some(record_pat_field) = extracted_syntax.ancestors().find_map(ast::RecordPatField::cast) - { - if let Some(name_ref) = record_pat_field.field_name() { - ted::replace( - record_pat_field.syntax(), - ast::make::record_pat_field(ast::make::name_ref(&name_ref.text()), binding) + if let Some(record_pat_field) = + extracted_syntax.ancestors().find_map(ast::RecordPatField::cast) + { + if let Some(name_ref) = record_pat_field.field_name() { + ted::replace( + record_pat_field.syntax(), + ast::make::record_pat_field( + ast::make::name_ref(&name_ref.text()), + binding.clone(), + ) .syntax() .clone_for_update(), - ); + ); + } + } else { + ted::replace(extracted_syntax, binding.clone().syntax().clone_for_update()); } - } else { - ted::replace(extracted_syntax, binding.syntax().clone_for_update()); } - syntax } @@ -162,6 +170,39 @@ fn foo(opt: Option<()>) { ); } + #[test] + fn or_pattern_multiple_binding() { + check_assist( + convert_match_to_let_else, + r#" +//- minicore: option +enum Foo { + A(u32), + B(u32), + C(String), +} + +fn foo(opt: Option) -> Result { + let va$0lue = match opt { + Some(Foo::A(it) | Foo::B(it)) => it, + _ => return Err(()), + }; +} + "#, + r#" +enum Foo { + A(u32), + B(u32), + C(String), +} + +fn foo(opt: Option) -> Result { + let Some(Foo::A(value) | Foo::B(value)) = opt else { return Err(()) }; +} + "#, + ); + } + #[test] fn should_not_be_applicable_if_extracting_arm_is_not_an_identity_expr() { cov_mark::check_count!(extracting_arm_is_not_an_identity_expr, 2); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index e04a1dabb2c..0b90c9ba34f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -3,7 +3,8 @@ use std::iter; use ast::make; use either::Either; use hir::{ - HasSource, HirDisplay, InFile, Local, ModuleDef, PathResolution, Semantics, TypeInfo, TypeParam, + HasSource, HirDisplay, InFile, Local, LocalSource, ModuleDef, PathResolution, Semantics, + TypeInfo, TypeParam, }; use ide_db::{ defs::{Definition, NameRefClass}, @@ -710,7 +711,7 @@ impl FunctionBody { ) => local_ref, _ => return, }; - let InFile { file_id, value } = local_ref.source(sema.db); + let InFile { file_id, value } = local_ref.primary_source(sema.db).source; // locals defined inside macros are not relevant to us if !file_id.is_macro() { match value { @@ -972,11 +973,11 @@ impl FunctionBody { locals: impl Iterator, ) -> Vec { locals - .map(|local| (local, local.source(ctx.db()))) + .map(|local| (local, local.primary_source(ctx.db()))) .filter(|(_, src)| is_defined_outside_of_body(ctx, self, src)) - .filter_map(|(local, src)| match src.value { - Either::Left(src) => Some((local, src)), - Either::Right(_) => { + .filter_map(|(local, src)| match src.into_ident_pat() { + Some(src) => Some((local, src)), + None => { stdx::never!(false, "Local::is_self returned false, but source is SelfParam"); None } @@ -1238,17 +1239,9 @@ fn local_outlives_body( fn is_defined_outside_of_body( ctx: &AssistContext<'_>, body: &FunctionBody, - src: &hir::InFile>, + src: &LocalSource, ) -> bool { - src.file_id.original_file(ctx.db()) == ctx.file_id() - && !body.contains_node(either_syntax(&src.value)) -} - -fn either_syntax(value: &Either) -> &SyntaxNode { - match value { - Either::Left(pat) => pat.syntax(), - Either::Right(it) => it.syntax(), - } + src.original_file(ctx.db()) == ctx.file_id() && !body.contains_node(src.syntax()) } /// find where to put extracted function definition diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index a738deffb95..16356141288 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -287,7 +287,7 @@ fn foo() { extract_variable, r" fn foo() { - $0{ let x = 0; x }$0 + $0{ let x = 0; x }$0; something_else(); }", r" diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs index d9e00435ecf..4c61678eab4 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs @@ -192,6 +192,10 @@ fn target_data_for_def( target_name = Some(t.name(db)); offset_target_and_file_id(db, t)? } + hir::ModuleDef::TraitAlias(t) => { + target_name = Some(t.name(db)); + offset_target_and_file_id(db, t)? + } hir::ModuleDef::TypeAlias(t) => { target_name = Some(t.name(db)); offset_target_and_file_id(db, t)? diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs index 45b27a63ce2..eef037f9875 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs @@ -5,6 +5,7 @@ use ide_db::{ base_db::FileId, defs::{Definition, NameRefClass}, famous_defs::FamousDefs, + helpers::is_editable_crate, path_transform::PathTransform, FxHashMap, FxHashSet, RootDatabase, SnippetCap, }; @@ -65,6 +66,13 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let fn_name = &*name_ref.text(); let TargetInfo { target_module, adt_name, target, file, insert_offset } = fn_target_info(ctx, path, &call, fn_name)?; + + if let Some(m) = target_module { + if !is_editable_crate(m.krate(), ctx.db()) { + return None; + } + } + let function_builder = FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target)?; let text_range = call.syntax().text_range(); let label = format!("Generate {} function", function_builder.fn_name); @@ -141,12 +149,11 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let receiver_ty = ctx.sema.type_of_expr(&call.receiver()?)?.original().strip_references(); let adt = receiver_ty.as_adt()?; - let current_module = ctx.sema.scope(call.syntax())?.module(); let target_module = adt.module(ctx.sema.db); - - if current_module.krate() != target_module.krate() { + if !is_editable_crate(target_module.krate(), ctx.db()) { return None; } + let (impl_, file) = get_adt_source(ctx, &adt, fn_name.text().as_str())?; let (target, insert_offset) = get_method_target(ctx, &impl_, &adt)?; @@ -253,7 +260,7 @@ struct FunctionBuilder { params: ast::ParamList, ret_type: Option, should_focus_return_type: bool, - needs_pub: bool, + visibility: Visibility, is_async: bool, } @@ -264,12 +271,14 @@ impl FunctionBuilder { ctx: &AssistContext<'_>, call: &ast::CallExpr, fn_name: &str, - target_module: Option, + target_module: Option, target: GeneratedFunctionTarget, ) -> Option { - let needs_pub = target_module.is_some(); let target_module = target_module.or_else(|| ctx.sema.scope(target.syntax()).map(|it| it.module()))?; + + let current_module = ctx.sema.scope(call.syntax())?.module(); + let visibility = calculate_necessary_visibility(current_module, target_module, ctx); let fn_name = make::name(fn_name); let mut necessary_generic_params = FxHashSet::default(); let params = fn_args( @@ -300,7 +309,7 @@ impl FunctionBuilder { params, ret_type, should_focus_return_type, - needs_pub, + visibility, is_async, }) } @@ -313,8 +322,9 @@ impl FunctionBuilder { target_module: Module, target: GeneratedFunctionTarget, ) -> Option { - let needs_pub = - !module_is_descendant(&ctx.sema.scope(call.syntax())?.module(), &target_module, ctx); + let current_module = ctx.sema.scope(call.syntax())?.module(); + let visibility = calculate_necessary_visibility(current_module, target_module, ctx); + let fn_name = make::name(&name.text()); let mut necessary_generic_params = FxHashSet::default(); necessary_generic_params.extend(receiver_ty.generic_params(ctx.db())); @@ -346,7 +356,7 @@ impl FunctionBuilder { params, ret_type, should_focus_return_type, - needs_pub, + visibility, is_async, }) } @@ -354,7 +364,11 @@ impl FunctionBuilder { fn render(self, is_method: bool) -> FunctionTemplate { let placeholder_expr = make::ext::expr_todo(); let fn_body = make::block_expr(vec![], Some(placeholder_expr)); - let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None }; + let visibility = match self.visibility { + Visibility::None => None, + Visibility::Crate => Some(make::visibility_pub_crate()), + Visibility::Pub => Some(make::visibility_pub()), + }; let mut fn_def = make::fn_( visibility, self.fn_name, @@ -527,7 +541,7 @@ impl GeneratedFunctionTarget { /// Computes parameter list for the generated function. fn fn_args( ctx: &AssistContext<'_>, - target_module: hir::Module, + target_module: Module, call: ast::CallableExpr, necessary_generic_params: &mut FxHashSet, ) -> Option { @@ -957,13 +971,13 @@ fn fn_arg_name(sema: &Semantics<'_, RootDatabase>, arg_expr: &ast::Expr) -> Stri fn fn_arg_type( ctx: &AssistContext<'_>, - target_module: hir::Module, + target_module: Module, fn_arg: &ast::Expr, generic_params: &mut FxHashSet, ) -> String { fn maybe_displayed_type( ctx: &AssistContext<'_>, - target_module: hir::Module, + target_module: Module, fn_arg: &ast::Expr, generic_params: &mut FxHashSet, ) -> Option { @@ -1048,16 +1062,29 @@ fn next_space_for_fn_in_impl(impl_: &ast::Impl) -> Option) -> bool { - if module == ans { - return true; +#[derive(Clone, Copy)] +enum Visibility { + None, + Crate, + Pub, +} + +fn calculate_necessary_visibility( + current_module: Module, + target_module: Module, + ctx: &AssistContext<'_>, +) -> Visibility { + let db = ctx.db(); + let current_module = current_module.nearest_non_block_module(db); + let target_module = target_module.nearest_non_block_module(db); + + if target_module.krate() != current_module.krate() { + Visibility::Pub + } else if current_module.path_to_root(db).contains(&target_module) { + Visibility::None + } else { + Visibility::Crate } - for c in ans.children(ctx.sema.db) { - if module_is_descendant(module, &c, ctx) { - return true; - } - } - false } // This is never intended to be used as a generic graph strucuture. If there's ever another need of @@ -2656,4 +2683,79 @@ fn main() { ", ) } + + #[test] + fn applicable_in_different_local_crate() { + check_assist( + generate_function, + r" +//- /lib.rs crate:lib new_source_root:local +fn dummy() {} +//- /main.rs crate:main deps:lib new_source_root:local +fn main() { + lib::foo$0(); +} +", + r" +fn dummy() {} + +pub fn foo() ${0:-> _} { + todo!() +} +", + ); + } + + #[test] + fn applicable_in_different_local_crate_method() { + check_assist( + generate_function, + r" +//- /lib.rs crate:lib new_source_root:local +pub struct S; +//- /main.rs crate:main deps:lib new_source_root:local +fn main() { + lib::S.foo$0(); +} +", + r" +pub struct S; +impl S { + pub fn foo(&self) ${0:-> _} { + todo!() + } +} +", + ); + } + + #[test] + fn not_applicable_in_different_library_crate() { + check_assist_not_applicable( + generate_function, + r" +//- /lib.rs crate:lib new_source_root:library +fn dummy() {} +//- /main.rs crate:main deps:lib new_source_root:local +fn main() { + lib::foo$0(); +} +", + ); + } + + #[test] + fn not_applicable_in_different_library_crate_method() { + check_assist_not_applicable( + generate_function, + r" +//- /lib.rs crate:lib new_source_root:library +pub struct S; +//- /main.rs crate:main deps:lib new_source_root:local +fn main() { + lib::S.foo$0(); +} +", + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs index 9ce525ca375..44291861960 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs @@ -95,14 +95,7 @@ fn get_impl_method( let scope = ctx.sema.scope(impl_.syntax())?; let ty = impl_def.self_ty(db); - ty.iterate_method_candidates( - db, - &scope, - &scope.visible_traits().0, - None, - Some(fn_name), - |func| Some(func), - ) + ty.iterate_method_candidates(db, &scope, None, Some(fn_name), |func| Some(func)) } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs index 8d311262a75..e30a3e942c4 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs @@ -1,5 +1,5 @@ use ide_db::{ - imports::import_assets::item_for_path_search, use_trivial_contructor::use_trivial_constructor, + imports::import_assets::item_for_path_search, use_trivial_constructor::use_trivial_constructor, }; use itertools::Itertools; use stdx::format_to; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs index ce44100e34b..e69d1a29677 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs @@ -1,4 +1,3 @@ -use either::Either; use hir::{PathResolution, Semantics}; use ide_db::{ base_db::FileId, @@ -205,12 +204,14 @@ fn inline_usage( return None; } - // FIXME: Handle multiple local definitions - let bind_pat = match local.source(sema.db).value { - Either::Left(ident) => ident, - _ => return None, + let sources = local.sources(sema.db); + let [source] = sources.as_slice() else { + // Not applicable with locals with multiple definitions (i.e. or patterns) + return None; }; + let bind_pat = source.as_ident_pat()?; + let let_stmt = ast::LetStmt::cast(bind_pat.syntax().parent()?)?; let UsageSearchResult { mut references } = Definition::Local(local).usages(sema).all(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs index 52dd670ec2a..1361cdf00cc 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs @@ -1,7 +1,7 @@ use itertools::Itertools; use syntax::{ - ast::{self, AstNode, AstToken}, - match_ast, NodeOrToken, SyntaxElement, TextRange, TextSize, T, + ast::{self, make, AstNode, AstToken}, + match_ast, ted, NodeOrToken, SyntaxElement, TextRange, TextSize, T, }; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -12,24 +12,28 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; // // ``` // fn main() { -// $0dbg!(92); +// let x = $0dbg!(42 * dbg!(4 + 2));$0 // } // ``` // -> // ``` // fn main() { -// 92; +// let x = 42 * (4 + 2); // } // ``` pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let macro_calls = if ctx.has_empty_selection() { - vec![ctx.find_node_at_offset::()?] + vec![ctx.find_node_at_offset::()?] } else { ctx.covering_element() .as_node()? .descendants() .filter(|node| ctx.selection_trimmed().contains_range(node.text_range())) + // When the selection exactly covers the macro call to be removed, `covering_element()` + // returns `ast::MacroCall` instead of its parent `ast::MacroExpr` that we want. So + // first try finding `ast::MacroCall`s and then retrieve their parent. .filter_map(ast::MacroCall::cast) + .filter_map(|it| it.syntax().parent().and_then(ast::MacroExpr::cast)) .collect() }; @@ -44,14 +48,25 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( "Remove dbg!()", ctx.selection_trimmed(), |builder| { - for (range, text) in replacements { - builder.replace(range, text); + for (range, expr) in replacements { + if let Some(expr) = expr { + builder.replace(range, expr.to_string()); + } else { + builder.delete(range); + } } }, ) } -fn compute_dbg_replacement(macro_call: ast::MacroCall) -> Option<(TextRange, String)> { +/// Returns `None` when either +/// - macro call is not `dbg!()` +/// - any node inside `dbg!()` could not be parsed as an expression +/// - (`macro_expr` has no parent - is that possible?) +/// +/// Returns `Some(_, None)` when the macro call should just be removed. +fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Option)> { + let macro_call = macro_expr.macro_call()?; let tt = macro_call.token_tree()?; let r_delim = NodeOrToken::Token(tt.right_delimiter_token()?); if macro_call.path()?.segment()?.name_ref()?.text() != "dbg" @@ -68,20 +83,19 @@ fn compute_dbg_replacement(macro_call: ast::MacroCall) -> Option<(TextRange, Str .map(|mut tokens| syntax::hacks::parse_expr_from_str(&tokens.join(""))) .collect::>>()?; - let macro_expr = ast::MacroExpr::cast(macro_call.syntax().parent()?)?; let parent = macro_expr.syntax().parent()?; Some(match &*input_expressions { // dbg!() [] => { match_ast! { match parent { - ast::StmtList(__) => { + ast::StmtList(_) => { let range = macro_expr.syntax().text_range(); let range = match whitespace_start(macro_expr.syntax().prev_sibling_or_token()) { Some(start) => range.cover_offset(start), None => range, }; - (range, String::new()) + (range, None) }, ast::ExprStmt(it) => { let range = it.syntax().text_range(); @@ -89,19 +103,23 @@ fn compute_dbg_replacement(macro_call: ast::MacroCall) -> Option<(TextRange, Str Some(start) => range.cover_offset(start), None => range, }; - (range, String::new()) + (range, None) }, - _ => (macro_call.syntax().text_range(), "()".to_owned()) + _ => (macro_call.syntax().text_range(), Some(make::expr_unit())), } } } // dbg!(expr0) [expr] => { + // dbg!(expr, &parent); let wrap = match ast::Expr::cast(parent) { Some(parent) => match (expr, parent) { (ast::Expr::CastExpr(_), ast::Expr::CastExpr(_)) => false, ( - ast::Expr::BoxExpr(_) | ast::Expr::PrefixExpr(_) | ast::Expr::RefExpr(_), + ast::Expr::BoxExpr(_) + | ast::Expr::PrefixExpr(_) + | ast::Expr::RefExpr(_) + | ast::Expr::MacroExpr(_), ast::Expr::AwaitExpr(_) | ast::Expr::CallExpr(_) | ast::Expr::CastExpr(_) @@ -112,7 +130,10 @@ fn compute_dbg_replacement(macro_call: ast::MacroCall) -> Option<(TextRange, Str | ast::Expr::TryExpr(_), ) => true, ( - ast::Expr::BinExpr(_) | ast::Expr::CastExpr(_) | ast::Expr::RangeExpr(_), + ast::Expr::BinExpr(_) + | ast::Expr::CastExpr(_) + | ast::Expr::RangeExpr(_) + | ast::Expr::MacroExpr(_), ast::Expr::AwaitExpr(_) | ast::Expr::BinExpr(_) | ast::Expr::CallExpr(_) @@ -129,16 +150,61 @@ fn compute_dbg_replacement(macro_call: ast::MacroCall) -> Option<(TextRange, Str }, None => false, }; - ( - macro_call.syntax().text_range(), - if wrap { format!("({expr})") } else { expr.to_string() }, - ) + let expr = replace_nested_dbgs(expr.clone()); + let expr = if wrap { make::expr_paren(expr) } else { expr.clone_subtree() }; + (macro_call.syntax().text_range(), Some(expr)) } // dbg!(expr0, expr1, ...) - exprs => (macro_call.syntax().text_range(), format!("({})", exprs.iter().format(", "))), + exprs => { + let exprs = exprs.iter().cloned().map(replace_nested_dbgs); + let expr = make::expr_tuple(exprs); + (macro_call.syntax().text_range(), Some(expr)) + } }) } +fn replace_nested_dbgs(expanded: ast::Expr) -> ast::Expr { + if let ast::Expr::MacroExpr(mac) = &expanded { + // Special-case when `expanded` itself is `dbg!()` since we cannot replace the whole tree + // with `ted`. It should be fairly rare as it means the user wrote `dbg!(dbg!(..))` but you + // never know how code ends up being! + let replaced = if let Some((_, expr_opt)) = compute_dbg_replacement(mac.clone()) { + match expr_opt { + Some(expr) => expr, + None => { + stdx::never!("dbg! inside dbg! should not be just removed"); + expanded + } + } + } else { + expanded + }; + + return replaced; + } + + let expanded = expanded.clone_for_update(); + + // We need to collect to avoid mutation during traversal. + let macro_exprs: Vec<_> = + expanded.syntax().descendants().filter_map(ast::MacroExpr::cast).collect(); + + for mac in macro_exprs { + let expr_opt = match compute_dbg_replacement(mac.clone()) { + Some((_, expr)) => expr, + None => continue, + }; + + if let Some(expr) = expr_opt { + ted::replace(mac.syntax(), expr.syntax().clone_for_update()); + } else { + ted::remove(mac.syntax()); + } + } + + expanded +} + fn whitespace_start(it: Option) -> Option { Some(it?.into_token().and_then(ast::Whitespace::cast)?.syntax().text_range().start()) } @@ -287,4 +353,32 @@ fn f() { check_assist_not_applicable(remove_dbg, r#"$0dbg$0!(0)"#); check_assist_not_applicable(remove_dbg, r#"$0dbg!(0$0)"#); } + + #[test] + fn test_nested_dbg() { + check( + r#"$0let x = dbg!(dbg!(dbg!(dbg!(0 + 1)) * 2) + dbg!(3));$0"#, + r#"let x = ((0 + 1) * 2) + 3;"#, + ); + check(r#"$0dbg!(10, dbg!(), dbg!(20, 30))$0"#, r#"(10, (), (20, 30))"#); + } + + #[test] + fn test_multiple_nested_dbg() { + check( + r#" +fn f() { + $0dbg!(); + let x = dbg!(dbg!(dbg!(0 + 1)) + 2) + dbg!(3); + dbg!(10, dbg!(), dbg!(20, 30));$0 +} +"#, + r#" +fn f() { + let x = ((0 + 1) + 2) + 3; + (10, (), (20, 30)); +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs index 457559656a4..5e31d38fbd6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs @@ -102,9 +102,11 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<' return None; } + let let_ = if pat_seen { " let" } else { "" }; + acc.add( AssistId("replace_if_let_with_match", AssistKind::RefactorRewrite), - "Replace if let with match", + format!("Replace if{let_} with match"), available_range, move |edit| { let match_expr = { @@ -210,8 +212,17 @@ fn make_else_arm( // ``` pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let match_expr: ast::MatchExpr = ctx.find_node_at_offset()?; + let match_arm_list = match_expr.match_arm_list()?; + let available_range = TextRange::new( + match_expr.syntax().text_range().start(), + match_arm_list.syntax().text_range().start(), + ); + let cursor_in_range = available_range.contains_range(ctx.selection_trimmed()); + if !cursor_in_range { + return None; + } - let mut arms = match_expr.match_arm_list()?.arms(); + let mut arms = match_arm_list.arms(); let (first_arm, second_arm) = (arms.next()?, arms.next()?); if arms.next().is_some() || first_arm.guard().is_some() || second_arm.guard().is_some() { return None; @@ -226,10 +237,20 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' )?; let scrutinee = match_expr.expr()?; + let let_ = match &if_let_pat { + ast::Pat::LiteralPat(p) + if p.literal() + .map(|it| it.token().kind()) + .map_or(false, |it| it == T![true] || it == T![false]) => + { + "" + } + _ => " let", + }; let target = match_expr.syntax().text_range(); acc.add( AssistId("replace_match_with_if_let", AssistKind::RefactorRewrite), - "Replace match with if let", + format!("Replace match with if{let_}"), target, move |edit| { fn make_block_expr(expr: ast::Expr) -> ast::BlockExpr { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs new file mode 100644 index 00000000000..a7e3ed793f1 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs @@ -0,0 +1,310 @@ +use ide_db::assists::{AssistId, AssistKind}; +use syntax::{ + ast::{self, make, Expr, HasArgList}, + AstNode, +}; + +use crate::{AssistContext, Assists}; + +// Assist: replace_with_lazy_method +// +// Replace `unwrap_or` with `unwrap_or_else` and `ok_or` with `ok_or_else`. +// +// ``` +// # //- minicore:option, fn +// fn foo() { +// let a = Some(1); +// a.unwra$0p_or(2); +// } +// ``` +// -> +// ``` +// fn foo() { +// let a = Some(1); +// a.unwrap_or_else(|| 2); +// } +// ``` +pub(crate) fn replace_with_lazy_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let call: ast::MethodCallExpr = ctx.find_node_at_offset()?; + let scope = ctx.sema.scope(call.syntax())?; + + let last_arg = call.arg_list()?.args().next()?; + let method_name = call.name_ref()?; + + let callable = ctx.sema.resolve_method_call_as_callable(&call)?; + let (_, receiver_ty) = callable.receiver_param(ctx.sema.db)?; + let n_params = callable.n_params() + 1; + + let method_name_lazy = format!( + "{method_name}{}", + if method_name.text().ends_with("or") { "_else" } else { "_with" } + ); + + receiver_ty.iterate_method_candidates_with_traits( + ctx.sema.db, + &scope, + &scope.visible_traits().0, + None, + None, + |func| { + let valid = func.name(ctx.sema.db).as_str() == Some(&*method_name_lazy) + && func.num_params(ctx.sema.db) == n_params + && { + let params = func.params_without_self(ctx.sema.db); + let last_p = params.first()?; + // FIXME: Check that this has the form of `() -> T` where T is the current type of the argument + last_p.ty().impls_fnonce(ctx.sema.db) + }; + valid.then_some(func) + }, + )?; + + acc.add( + AssistId("replace_with_lazy_method", AssistKind::RefactorRewrite), + format!("Replace {method_name} with {method_name_lazy}"), + call.syntax().text_range(), + |builder| { + builder.replace(method_name.syntax().text_range(), method_name_lazy); + let closured = into_closure(&last_arg); + builder.replace_ast(last_arg, closured); + }, + ) +} + +fn into_closure(param: &Expr) -> Expr { + (|| { + if let ast::Expr::CallExpr(call) = param { + if call.arg_list()?.args().count() == 0 { + Some(call.expr()?) + } else { + None + } + } else { + None + } + })() + .unwrap_or_else(|| make::expr_closure(None, param.clone())) +} + +// Assist: replace_with_eager_method +// +// Replace `unwrap_or_else` with `unwrap_or` and `ok_or_else` with `ok_or`. +// +// ``` +// # //- minicore:option, fn +// fn foo() { +// let a = Some(1); +// a.unwra$0p_or_else(|| 2); +// } +// ``` +// -> +// ``` +// fn foo() { +// let a = Some(1); +// a.unwrap_or(2); +// } +// ``` +pub(crate) fn replace_with_eager_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let call: ast::MethodCallExpr = ctx.find_node_at_offset()?; + let scope = ctx.sema.scope(call.syntax())?; + + let last_arg = call.arg_list()?.args().next()?; + let method_name = call.name_ref()?; + + let callable = ctx.sema.resolve_method_call_as_callable(&call)?; + let (_, receiver_ty) = callable.receiver_param(ctx.sema.db)?; + let n_params = callable.n_params() + 1; + let params = callable.params(ctx.sema.db); + + // FIXME: Check that the arg is of the form `() -> T` + if !params.first()?.1.impls_fnonce(ctx.sema.db) { + return None; + } + + let method_name_text = method_name.text(); + let method_name_eager = method_name_text + .strip_suffix("_else") + .or_else(|| method_name_text.strip_suffix("_with"))?; + + receiver_ty.iterate_method_candidates_with_traits( + ctx.sema.db, + &scope, + &scope.visible_traits().0, + None, + None, + |func| { + let valid = func.name(ctx.sema.db).as_str() == Some(&*method_name_eager) + && func.num_params(ctx.sema.db) == n_params; + valid.then_some(func) + }, + )?; + + acc.add( + AssistId("replace_with_eager_method", AssistKind::RefactorRewrite), + format!("Replace {method_name} with {method_name_eager}"), + call.syntax().text_range(), + |builder| { + builder.replace(method_name.syntax().text_range(), method_name_eager); + let called = into_call(&last_arg); + builder.replace_ast(last_arg, called); + }, + ) +} + +fn into_call(param: &Expr) -> Expr { + (|| { + if let ast::Expr::ClosureExpr(closure) = param { + if closure.param_list()?.params().count() == 0 { + Some(closure.body()?) + } else { + None + } + } else { + None + } + })() + .unwrap_or_else(|| make::expr_call(param.clone(), make::arg_list(Vec::new()))) +} + +#[cfg(test)] +mod tests { + use crate::tests::check_assist; + + use super::*; + + #[test] + fn replace_or_with_or_else_simple() { + check_assist( + replace_with_lazy_method, + r#" +//- minicore: option, fn +fn foo() { + let foo = Some(1); + return foo.unwrap_$0or(2); +} +"#, + r#" +fn foo() { + let foo = Some(1); + return foo.unwrap_or_else(|| 2); +} +"#, + ) + } + + #[test] + fn replace_or_with_or_else_call() { + check_assist( + replace_with_lazy_method, + r#" +//- minicore: option, fn +fn foo() { + let foo = Some(1); + return foo.unwrap_$0or(x()); +} +"#, + r#" +fn foo() { + let foo = Some(1); + return foo.unwrap_or_else(x); +} +"#, + ) + } + + #[test] + fn replace_or_with_or_else_block() { + check_assist( + replace_with_lazy_method, + r#" +//- minicore: option, fn +fn foo() { + let foo = Some(1); + return foo.unwrap_$0or({ + let mut x = bar(); + for i in 0..10 { + x += i; + } + x + }); +} +"#, + r#" +fn foo() { + let foo = Some(1); + return foo.unwrap_or_else(|| { + let mut x = bar(); + for i in 0..10 { + x += i; + } + x + }); +} +"#, + ) + } + + #[test] + fn replace_or_else_with_or_simple() { + check_assist( + replace_with_eager_method, + r#" +//- minicore: option, fn +fn foo() { + let foo = Some(1); + return foo.unwrap_$0or_else(|| 2); +} +"#, + r#" +fn foo() { + let foo = Some(1); + return foo.unwrap_or(2); +} +"#, + ) + } + + #[test] + fn replace_or_else_with_or_call() { + check_assist( + replace_with_eager_method, + r#" +//- minicore: option, fn +fn foo() { + let foo = Some(1); + return foo.unwrap_$0or_else(x); +} + +fn x() -> i32 { 0 } +"#, + r#" +fn foo() { + let foo = Some(1); + return foo.unwrap_or(x()); +} + +fn x() -> i32 { 0 } +"#, + ) + } + + #[test] + fn replace_or_else_with_or_map() { + check_assist( + replace_with_eager_method, + r#" +//- minicore: option, fn +fn foo() { + let foo = Some("foo"); + return foo.map$0_or_else(|| 42, |v| v.len()); +} +"#, + r#" +fn foo() { + let foo = Some("foo"); + return foo.map_or(42, |v| v.len()); +} +"#, + ) + } +} diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_or_with_or_else.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_or_with_or_else.rs deleted file mode 100644 index f0ed3c4fe6f..00000000000 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_or_with_or_else.rs +++ /dev/null @@ -1,364 +0,0 @@ -use ide_db::{ - assists::{AssistId, AssistKind}, - famous_defs::FamousDefs, -}; -use syntax::{ - ast::{self, make, Expr, HasArgList}, - AstNode, -}; - -use crate::{AssistContext, Assists}; - -// Assist: replace_or_with_or_else -// -// Replace `unwrap_or` with `unwrap_or_else` and `ok_or` with `ok_or_else`. -// -// ``` -// # //- minicore:option -// fn foo() { -// let a = Some(1); -// a.unwra$0p_or(2); -// } -// ``` -// -> -// ``` -// fn foo() { -// let a = Some(1); -// a.unwrap_or_else(|| 2); -// } -// ``` -pub(crate) fn replace_or_with_or_else(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let call: ast::MethodCallExpr = ctx.find_node_at_offset()?; - - let kind = is_option_or_result(call.receiver()?, ctx)?; - - let (name, arg_list) = (call.name_ref()?, call.arg_list()?); - - let mut map_or = false; - - let replace = match &*name.text() { - "unwrap_or" => "unwrap_or_else".to_string(), - "or" => "or_else".to_string(), - "ok_or" if kind == Kind::Option => "ok_or_else".to_string(), - "map_or" => { - map_or = true; - "map_or_else".to_string() - } - _ => return None, - }; - - let arg = match arg_list.args().collect::>().as_slice() { - [] => make::arg_list(Vec::new()), - [first] => { - let param = into_closure(first); - make::arg_list(vec![param]) - } - [first, second] if map_or => { - let param = into_closure(first); - make::arg_list(vec![param, second.clone()]) - } - _ => return None, - }; - - acc.add( - AssistId("replace_or_with_or_else", AssistKind::RefactorRewrite), - format!("Replace {name} with {replace}"), - call.syntax().text_range(), - |builder| { - builder.replace(name.syntax().text_range(), replace); - builder.replace_ast(arg_list, arg) - }, - ) -} - -fn into_closure(param: &Expr) -> Expr { - (|| { - if let ast::Expr::CallExpr(call) = param { - if call.arg_list()?.args().count() == 0 { - Some(call.expr()?) - } else { - None - } - } else { - None - } - })() - .unwrap_or_else(|| make::expr_closure(None, param.clone())) -} - -// Assist: replace_or_else_with_or -// -// Replace `unwrap_or_else` with `unwrap_or` and `ok_or_else` with `ok_or`. -// -// ``` -// # //- minicore:option -// fn foo() { -// let a = Some(1); -// a.unwra$0p_or_else(|| 2); -// } -// ``` -// -> -// ``` -// fn foo() { -// let a = Some(1); -// a.unwrap_or(2); -// } -// ``` -pub(crate) fn replace_or_else_with_or(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let call: ast::MethodCallExpr = ctx.find_node_at_offset()?; - - let kind = is_option_or_result(call.receiver()?, ctx)?; - - let (name, arg_list) = (call.name_ref()?, call.arg_list()?); - - let mut map_or = false; - let replace = match &*name.text() { - "unwrap_or_else" => "unwrap_or".to_string(), - "or_else" => "or".to_string(), - "ok_or_else" if kind == Kind::Option => "ok_or".to_string(), - "map_or_else" => { - map_or = true; - "map_or".to_string() - } - _ => return None, - }; - - let arg = match arg_list.args().collect::>().as_slice() { - [] => make::arg_list(Vec::new()), - [first] => { - let param = into_call(first); - make::arg_list(vec![param]) - } - [first, second] if map_or => { - let param = into_call(first); - make::arg_list(vec![param, second.clone()]) - } - _ => return None, - }; - - acc.add( - AssistId("replace_or_else_with_or", AssistKind::RefactorRewrite), - format!("Replace {name} with {replace}"), - call.syntax().text_range(), - |builder| { - builder.replace(name.syntax().text_range(), replace); - builder.replace_ast(arg_list, arg) - }, - ) -} - -fn into_call(param: &Expr) -> Expr { - (|| { - if let ast::Expr::ClosureExpr(closure) = param { - if closure.param_list()?.params().count() == 0 { - Some(closure.body()?) - } else { - None - } - } else { - None - } - })() - .unwrap_or_else(|| make::expr_call(param.clone(), make::arg_list(Vec::new()))) -} - -#[derive(PartialEq, Eq)] -enum Kind { - Option, - Result, -} - -fn is_option_or_result(receiver: Expr, ctx: &AssistContext<'_>) -> Option { - let ty = ctx.sema.type_of_expr(&receiver)?.adjusted().as_adt()?.as_enum()?; - let option_enum = - FamousDefs(&ctx.sema, ctx.sema.scope(receiver.syntax())?.krate()).core_option_Option(); - - if let Some(option_enum) = option_enum { - if ty == option_enum { - return Some(Kind::Option); - } - } - - let result_enum = - FamousDefs(&ctx.sema, ctx.sema.scope(receiver.syntax())?.krate()).core_result_Result(); - - if let Some(result_enum) = result_enum { - if ty == result_enum { - return Some(Kind::Result); - } - } - - None -} - -#[cfg(test)] -mod tests { - use crate::tests::{check_assist, check_assist_not_applicable}; - - use super::*; - - #[test] - fn replace_or_with_or_else_simple() { - check_assist( - replace_or_with_or_else, - r#" -//- minicore: option -fn foo() { - let foo = Some(1); - return foo.unwrap_$0or(2); -} -"#, - r#" -fn foo() { - let foo = Some(1); - return foo.unwrap_or_else(|| 2); -} -"#, - ) - } - - #[test] - fn replace_or_with_or_else_call() { - check_assist( - replace_or_with_or_else, - r#" -//- minicore: option -fn foo() { - let foo = Some(1); - return foo.unwrap_$0or(x()); -} -"#, - r#" -fn foo() { - let foo = Some(1); - return foo.unwrap_or_else(x); -} -"#, - ) - } - - #[test] - fn replace_or_with_or_else_block() { - check_assist( - replace_or_with_or_else, - r#" -//- minicore: option -fn foo() { - let foo = Some(1); - return foo.unwrap_$0or({ - let mut x = bar(); - for i in 0..10 { - x += i; - } - x - }); -} -"#, - r#" -fn foo() { - let foo = Some(1); - return foo.unwrap_or_else(|| { - let mut x = bar(); - for i in 0..10 { - x += i; - } - x - }); -} -"#, - ) - } - - #[test] - fn replace_or_else_with_or_simple() { - check_assist( - replace_or_else_with_or, - r#" -//- minicore: option -fn foo() { - let foo = Some(1); - return foo.unwrap_$0or_else(|| 2); -} -"#, - r#" -fn foo() { - let foo = Some(1); - return foo.unwrap_or(2); -} -"#, - ) - } - - #[test] - fn replace_or_else_with_or_call() { - check_assist( - replace_or_else_with_or, - r#" -//- minicore: option -fn foo() { - let foo = Some(1); - return foo.unwrap_$0or_else(x); -} -"#, - r#" -fn foo() { - let foo = Some(1); - return foo.unwrap_or(x()); -} -"#, - ) - } - - #[test] - fn replace_or_else_with_or_result() { - check_assist( - replace_or_else_with_or, - r#" -//- minicore: result -fn foo() { - let foo = Ok(1); - return foo.unwrap_$0or_else(x); -} -"#, - r#" -fn foo() { - let foo = Ok(1); - return foo.unwrap_or(x()); -} -"#, - ) - } - - #[test] - fn replace_or_else_with_or_map() { - check_assist( - replace_or_else_with_or, - r#" -//- minicore: result -fn foo() { - let foo = Ok("foo"); - return foo.map$0_or_else(|| 42, |v| v.len()); -} -"#, - r#" -fn foo() { - let foo = Ok("foo"); - return foo.map_or(42, |v| v.len()); -} -"#, - ) - } - - #[test] - fn replace_or_else_with_or_not_applicable() { - check_assist_not_applicable( - replace_or_else_with_or, - r#" -fn foo() { - let foo = Ok(1); - return foo.unwrap_$0or_else(x); -} -"#, - ) - } -} diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs index 276cf5f5dd0..4d489b62b5c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs @@ -188,7 +188,7 @@ mod handlers { mod replace_try_expr_with_match; mod replace_derive_with_manual_impl; mod replace_if_let_with_match; - mod replace_or_with_or_else; + mod replace_method_eager_lazy; mod replace_arith_op; mod introduce_named_generic; mod replace_let_with_if_let; @@ -297,8 +297,8 @@ mod handlers { replace_if_let_with_match::replace_if_let_with_match, replace_if_let_with_match::replace_match_with_if_let, replace_let_with_if_let::replace_let_with_if_let, - replace_or_with_or_else::replace_or_else_with_or, - replace_or_with_or_else::replace_or_with_or_else, + replace_method_eager_lazy::replace_with_eager_method, + replace_method_eager_lazy::replace_with_lazy_method, replace_turbofish_with_explicit_type::replace_turbofish_with_explicit_type, replace_qualified_name_with_use::replace_qualified_name_with_use, replace_arith_op::replace_arith_with_wrapping, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index 8a25e1f648a..e5a8d675a9e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -2006,12 +2006,12 @@ fn doctest_remove_dbg() { "remove_dbg", r#####" fn main() { - $0dbg!(92); + let x = $0dbg!(42 * dbg!(4 + 2));$0 } "#####, r#####" fn main() { - 92; + let x = 42 * (4 + 2); } "#####, ) @@ -2313,46 +2313,6 @@ fn handle(action: Action) { ) } -#[test] -fn doctest_replace_or_else_with_or() { - check_doc_test( - "replace_or_else_with_or", - r#####" -//- minicore:option -fn foo() { - let a = Some(1); - a.unwra$0p_or_else(|| 2); -} -"#####, - r#####" -fn foo() { - let a = Some(1); - a.unwrap_or(2); -} -"#####, - ) -} - -#[test] -fn doctest_replace_or_with_or_else() { - check_doc_test( - "replace_or_with_or_else", - r#####" -//- minicore:option -fn foo() { - let a = Some(1); - a.unwra$0p_or(2); -} -"#####, - r#####" -fn foo() { - let a = Some(1); - a.unwrap_or_else(|| 2); -} -"#####, - ) -} - #[test] fn doctest_replace_qualified_name_with_use() { check_doc_test( @@ -2427,6 +2387,46 @@ fn main() { ) } +#[test] +fn doctest_replace_with_eager_method() { + check_doc_test( + "replace_with_eager_method", + r#####" +//- minicore:option, fn +fn foo() { + let a = Some(1); + a.unwra$0p_or_else(|| 2); +} +"#####, + r#####" +fn foo() { + let a = Some(1); + a.unwrap_or(2); +} +"#####, + ) +} + +#[test] +fn doctest_replace_with_lazy_method() { + check_doc_test( + "replace_with_lazy_method", + r#####" +//- minicore:option, fn +fn foo() { + let a = Some(1); + a.unwra$0p_or(2); +} +"#####, + r#####" +fn foo() { + let a = Some(1); + a.unwrap_or_else(|| 2); +} +"#####, + ) +} + #[test] fn doctest_sort_items() { check_doc_test( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index 7c6e5e100f6..09ac57153ae 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -122,7 +122,7 @@ fn complete_methods( mut f: impl FnMut(hir::Function), ) { let mut seen_methods = FxHashSet::default(); - receiver.iterate_method_candidates( + receiver.iterate_method_candidates_with_traits( ctx.db, &ctx.scope, &ctx.traits_in_scope(), diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs index 364969af9c9..0979f6a6dfc 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs @@ -5,10 +5,7 @@ use ide_db::imports::{ insert_use::ImportScope, }; use itertools::Itertools; -use syntax::{ - ast::{self}, - AstNode, SyntaxNode, T, -}; +use syntax::{ast, AstNode, SyntaxNode, T}; use crate::{ context::{ diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs index 37849c251a4..69c05a76df4 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs @@ -33,7 +33,9 @@ pub(crate) fn complete_type_path( // Don't suggest attribute macros and derives. ScopeDef::ModuleDef(Macro(mac)) => mac.is_fn_like(ctx.db), // Type things are fine - ScopeDef::ModuleDef(BuiltinType(_) | Adt(_) | Module(_) | Trait(_) | TypeAlias(_)) + ScopeDef::ModuleDef( + BuiltinType(_) | Adt(_) | Module(_) | Trait(_) | TraitAlias(_) | TypeAlias(_), + ) | ScopeDef::AdtSelfType(_) | ScopeDef::Unknown | ScopeDef::GenericParam(TypeParam(_)) => true, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index ea54068b0f8..7dc29c3d5ac 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -6,13 +6,13 @@ mod tests; use std::iter; -use base_db::SourceDatabaseExt; use hir::{ HasAttrs, Local, Name, PathResolution, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo, }; use ide_db::{ base_db::{FilePosition, SourceDatabase}, famous_defs::FamousDefs, + helpers::is_editable_crate, FxHashMap, FxHashSet, RootDatabase, }; use syntax::{ @@ -387,8 +387,7 @@ pub(crate) struct CompletionContext<'a> { impl<'a> CompletionContext<'a> { /// The range of the identifier that is being completed. pub(crate) fn source_range(&self) -> TextRange { - // check kind of macro-expanded token, but use range of original token - let kind = self.token.kind(); + let kind = self.original_token.kind(); match kind { CHAR => { // assume we are completing a lifetime but the user has only typed the ' @@ -416,6 +415,7 @@ impl<'a> CompletionContext<'a> { hir::ModuleDef::Const(it) => self.is_visible(it), hir::ModuleDef::Static(it) => self.is_visible(it), hir::ModuleDef::Trait(it) => self.is_visible(it), + hir::ModuleDef::TraitAlias(it) => self.is_visible(it), hir::ModuleDef::TypeAlias(it) => self.is_visible(it), hir::ModuleDef::Macro(it) => self.is_visible(it), hir::ModuleDef::BuiltinType(_) => Visible::Yes, @@ -525,10 +525,11 @@ impl<'a> CompletionContext<'a> { return Visible::No; } // If the definition location is editable, also show private items - let root_file = defining_crate.root_file(self.db); - let source_root_id = self.db.file_source_root(root_file); - let is_editable = !self.db.source_root(source_root_id).is_library; - return if is_editable { Visible::Editable } else { Visible::No }; + return if is_editable_crate(defining_crate, self.db) { + Visible::Editable + } else { + Visible::No + }; } if self.is_doc_hidden(attrs, defining_crate) { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index 2f65491d85e..bb9fa7ccacc 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -288,7 +288,7 @@ impl_from!(SymbolKind for CompletionItemKind); impl CompletionItemKind { #[cfg(test)] - pub(crate) fn tag(&self) -> &'static str { + pub(crate) fn tag(self) -> &'static str { match self { CompletionItemKind::SymbolKind(kind) => match kind { SymbolKind::Attribute => "at", @@ -312,6 +312,7 @@ impl CompletionItemKind { SymbolKind::Struct => "st", SymbolKind::ToolModule => "tm", SymbolKind::Trait => "tt", + SymbolKind::TraitAlias => "tr", SymbolKind::TypeAlias => "ta", SymbolKind::TypeParam => "tp", SymbolKind::Union => "un", diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index d99ad5f9f04..c1f51aabb96 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -367,6 +367,9 @@ fn res_to_kind(resolution: ScopeDef) -> CompletionItemKind { ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::SymbolKind(SymbolKind::Const), ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::SymbolKind(SymbolKind::Static), ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::SymbolKind(SymbolKind::Trait), + ScopeDef::ModuleDef(TraitAlias(..)) => { + CompletionItemKind::SymbolKind(SymbolKind::TraitAlias) + } ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::SymbolKind(SymbolKind::TypeAlias), ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType, ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs index ffcad1185aa..44e88607633 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs @@ -264,6 +264,65 @@ macro_rules! foo { fn main() { foo!($0) } +"#, + ); + } + + #[test] + fn complete_missing_macro_arg() { + // Regression test for https://github.com/rust-lang/rust-analyzer/issues/14246 + check_edit( + "BAR", + r#" +macro_rules! foo { + ($val:ident, $val2: ident) => { + $val $val2 + }; +} + +const BAR: u32 = 9; +fn main() { + foo!(BAR, $0) +} +"#, + r#" +macro_rules! foo { + ($val:ident, $val2: ident) => { + $val $val2 + }; +} + +const BAR: u32 = 9; +fn main() { + foo!(BAR, BAR) +} +"#, + ); + check_edit( + "BAR", + r#" +macro_rules! foo { + ($val:ident, $val2: ident) => { + $val $val2 + }; +} + +const BAR: u32 = 9; +fn main() { + foo!($0) +} +"#, + r#" +macro_rules! foo { + ($val:ident, $val2: ident) => { + $val $val2 + }; +} + +const BAR: u32 = 9; +fn main() { + foo!(BAR) +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs index 4e60820dd6d..c97144b61b6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs @@ -857,9 +857,9 @@ mod lint { #[test] fn lint_feature() { check_edit( - "box_syntax", + "box_patterns", r#"#[feature(box_$0)] struct Test;"#, - r#"#[feature(box_syntax)] struct Test;"#, + r#"#[feature(box_patterns)] struct Test;"#, ) } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs index 2b6b60547b3..0da4e729a8d 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs @@ -96,6 +96,7 @@ pub fn generic_def_for_node( hir::PathResolution::Def(hir::ModuleDef::Adt(it)) => it.into(), hir::PathResolution::Def(hir::ModuleDef::Function(it)) => it.into(), hir::PathResolution::Def(hir::ModuleDef::Trait(it)) => it.into(), + hir::PathResolution::Def(hir::ModuleDef::TraitAlias(it)) => it.into(), hir::PathResolution::Def(hir::ModuleDef::TypeAlias(it)) => it.into(), hir::PathResolution::Def(hir::ModuleDef::Variant(it)) => it.into(), hir::PathResolution::Def(hir::ModuleDef::BuiltinType(_)) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs index b1ee9b58d5b..244e99fe2e2 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs @@ -73,68 +73,96 @@ impl RootDatabase { // AstDatabase hir::db::AstIdMapQuery + hir::db::ParseMacroExpansionQuery + hir::db::InternMacroCallQuery hir::db::MacroArgTextQuery hir::db::MacroDefQuery - hir::db::ParseMacroExpansionQuery hir::db::MacroExpandQuery + hir::db::ExpandProcMacroQuery + hir::db::MacroExpandErrorQuery hir::db::HygieneFrameQuery - hir::db::InternMacroCallQuery // DefDatabase hir::db::FileItemTreeQuery - hir::db::BlockDefMapQuery hir::db::CrateDefMapQueryQuery - hir::db::FieldsAttrsQuery - hir::db::VariantsAttrsQuery - hir::db::FieldsAttrsSourceMapQuery - hir::db::VariantsAttrsSourceMapQuery + hir::db::BlockDefMapQuery hir::db::StructDataQuery + hir::db::StructDataWithDiagnosticsQuery hir::db::UnionDataQuery + hir::db::UnionDataWithDiagnosticsQuery hir::db::EnumDataQuery + hir::db::EnumDataWithDiagnosticsQuery hir::db::ImplDataQuery + hir::db::ImplDataWithDiagnosticsQuery hir::db::TraitDataQuery + hir::db::TraitDataWithDiagnosticsQuery + hir::db::TraitAliasDataQuery hir::db::TypeAliasDataQuery hir::db::FunctionDataQuery hir::db::ConstDataQuery hir::db::StaticDataQuery + hir::db::Macro2DataQuery + hir::db::MacroRulesDataQuery + hir::db::ProcMacroDataQuery hir::db::BodyWithSourceMapQuery hir::db::BodyQuery hir::db::ExprScopesQuery hir::db::GenericParamsQuery + hir::db::VariantsAttrsQuery + hir::db::FieldsAttrsQuery + hir::db::VariantsAttrsSourceMapQuery + hir::db::FieldsAttrsSourceMapQuery hir::db::AttrsQuery hir::db::CrateLangItemsQuery hir::db::LangItemQuery hir::db::ImportMapQuery + hir::db::FieldVisibilitiesQuery + hir::db::FunctionVisibilityQuery + hir::db::ConstVisibilityQuery + hir::db::CrateSupportsNoStdQuery // HirDatabase hir::db::InferQueryQuery + hir::db::MirBodyQuery + hir::db::BorrowckQuery hir::db::TyQuery hir::db::ValueTyQuery hir::db::ImplSelfTyQuery + hir::db::ConstParamTyQuery + hir::db::ConstEvalQuery + hir::db::ConstEvalDiscriminantQuery hir::db::ImplTraitQuery hir::db::FieldTypesQuery + hir::db::LayoutOfAdtQuery + hir::db::TargetDataLayoutQuery hir::db::CallableItemSignatureQuery + hir::db::ReturnTypeImplTraitsQuery hir::db::GenericPredicatesForParamQuery hir::db::GenericPredicatesQuery + hir::db::TraitEnvironmentQuery hir::db::GenericDefaultsQuery hir::db::InherentImplsInCrateQuery - hir::db::TraitEnvironmentQuery + hir::db::InherentImplsInBlockQuery + hir::db::IncoherentInherentImplCratesQuery hir::db::TraitImplsInCrateQuery + hir::db::TraitImplsInBlockQuery hir::db::TraitImplsInDepsQuery - hir::db::AssociatedTyDataQuery + hir::db::InternCallableDefQuery + hir::db::InternLifetimeParamIdQuery + hir::db::InternImplTraitIdQuery + hir::db::InternTypeOrConstParamIdQuery + hir::db::InternClosureQuery + hir::db::InternGeneratorQuery hir::db::AssociatedTyDataQuery hir::db::TraitDatumQuery hir::db::StructDatumQuery hir::db::ImplDatumQuery hir::db::FnDefDatumQuery - hir::db::ReturnTypeImplTraitsQuery - hir::db::InternCallableDefQuery - hir::db::InternTypeOrConstParamIdQuery - hir::db::InternImplTraitIdQuery - hir::db::InternClosureQuery + hir::db::FnDefVarianceQuery + hir::db::AdtVarianceQuery hir::db::AssociatedTyValueQuery hir::db::TraitSolveQueryQuery - hir::db::InternTypeOrConstParamIdQuery + hir::db::ProgramClausesForChalkEnvQuery // SymbolsDatabase crate::symbol_index::ModuleSymbolsQuery @@ -153,8 +181,14 @@ impl RootDatabase { hir::db::InternConstQuery hir::db::InternStaticQuery hir::db::InternTraitQuery + hir::db::InternTraitAliasQuery hir::db::InternTypeAliasQuery hir::db::InternImplQuery + hir::db::InternExternBlockQuery + hir::db::InternBlockQuery + hir::db::InternMacro2Query + hir::db::InternProcMacroQuery + hir::db::InternMacroRulesQuery ]; acc.sort_by_key(|it| std::cmp::Reverse(it.1)); diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index ed7f04fd8e7..1322f5228e8 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -9,7 +9,8 @@ use arrayvec::ArrayVec; use hir::{ Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper, Field, Function, GenericParam, HasVisibility, Impl, ItemInNs, Label, Local, Macro, Module, ModuleDef, - Name, PathResolution, Semantics, Static, ToolModule, Trait, TypeAlias, Variant, Visibility, + Name, PathResolution, Semantics, Static, ToolModule, Trait, TraitAlias, TypeAlias, Variant, + Visibility, }; use stdx::impl_from; use syntax::{ @@ -31,6 +32,7 @@ pub enum Definition { Const(Const), Static(Static), Trait(Trait), + TraitAlias(TraitAlias), TypeAlias(TypeAlias), BuiltinType(BuiltinType), SelfType(Impl), @@ -64,6 +66,7 @@ impl Definition { Definition::Const(it) => it.module(db), Definition::Static(it) => it.module(db), Definition::Trait(it) => it.module(db), + Definition::TraitAlias(it) => it.module(db), Definition::TypeAlias(it) => it.module(db), Definition::Variant(it) => it.module(db), Definition::SelfType(it) => it.module(db), @@ -87,6 +90,7 @@ impl Definition { Definition::Const(it) => it.visibility(db), Definition::Static(it) => it.visibility(db), Definition::Trait(it) => it.visibility(db), + Definition::TraitAlias(it) => it.visibility(db), Definition::TypeAlias(it) => it.visibility(db), Definition::Variant(it) => it.visibility(db), Definition::BuiltinType(_) => Visibility::Public, @@ -113,6 +117,7 @@ impl Definition { Definition::Const(it) => it.name(db)?, Definition::Static(it) => it.name(db), Definition::Trait(it) => it.name(db), + Definition::TraitAlias(it) => it.name(db), Definition::TypeAlias(it) => it.name(db), Definition::BuiltinType(it) => it.name(), Definition::SelfType(_) => return None, @@ -300,6 +305,7 @@ impl NameClass { ast::Item::Module(it) => Definition::Module(sema.to_def(&it)?), ast::Item::Static(it) => Definition::Static(sema.to_def(&it)?), ast::Item::Trait(it) => Definition::Trait(sema.to_def(&it)?), + ast::Item::TraitAlias(it) => Definition::TraitAlias(sema.to_def(&it)?), ast::Item::TypeAlias(it) => Definition::TypeAlias(sema.to_def(&it)?), ast::Item::Enum(it) => Definition::Adt(hir::Adt::Enum(sema.to_def(&it)?)), ast::Item::Struct(it) => Definition::Adt(hir::Adt::Struct(sema.to_def(&it)?)), @@ -463,9 +469,12 @@ impl NameRefClass { match_ast! { match parent { ast::MethodCallExpr(method_call) => { - sema.resolve_method_call(&method_call) - .map(Definition::Function) - .map(NameRefClass::Definition) + sema.resolve_method_call_field_fallback(&method_call) + .map(|it| { + it.map_left(Definition::Function) + .map_right(Definition::Field) + .either(NameRefClass::Definition, NameRefClass::Definition) + }) }, ast::FieldExpr(field_expr) => { sema.resolve_field(&field_expr) @@ -542,7 +551,7 @@ impl NameRefClass { } impl_from!( - Field, Module, Function, Adt, Variant, Const, Static, Trait, TypeAlias, BuiltinType, Local, + Field, Module, Function, Adt, Variant, Const, Static, Trait, TraitAlias, TypeAlias, BuiltinType, Local, GenericParam, Label, Macro for Definition ); @@ -599,6 +608,7 @@ impl From for Definition { ModuleDef::Const(it) => Definition::Const(it), ModuleDef::Static(it) => Definition::Static(it), ModuleDef::Trait(it) => Definition::Trait(it), + ModuleDef::TraitAlias(it) => Definition::TraitAlias(it), ModuleDef::TypeAlias(it) => Definition::TypeAlias(it), ModuleDef::Macro(it) => Definition::Macro(it), ModuleDef::BuiltinType(it) => Definition::BuiltinType(it), @@ -616,6 +626,7 @@ impl From for Option { Definition::Const(it) => ModuleDef::Const(it), Definition::Static(it) => ModuleDef::Static(it), Definition::Trait(it) => ModuleDef::Trait(it), + Definition::TraitAlias(it) => ModuleDef::TraitAlias(it), Definition::TypeAlias(it) => ModuleDef::TypeAlias(it), Definition::BuiltinType(it) => ModuleDef::BuiltinType(it), _ => return None, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs index 6e56efe344d..8e3b1eef15b 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs @@ -2,8 +2,8 @@ use std::collections::VecDeque; -use base_db::FileId; -use hir::{ItemInNs, ModuleDef, Name, Semantics}; +use base_db::{FileId, SourceDatabaseExt}; +use hir::{Crate, ItemInNs, ModuleDef, Name, Semantics}; use syntax::{ ast::{self, make}, AstToken, SyntaxKind, SyntaxToken, TokenAtOffset, @@ -103,3 +103,9 @@ pub fn lint_eq_or_in_group(lint: &str, lint_is: &str) -> bool { false } } + +pub fn is_editable_crate(krate: Crate, db: &RootDatabase) -> bool { + let root_file = krate.root_file(db); + let source_root_id = db.file_source_root(root_file); + !db.source_root(source_root_id).is_library +} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index 994d48385a0..b26b0a9087e 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs @@ -528,7 +528,7 @@ fn trait_applicable_items( }, ) } else { - trait_candidate.receiver_ty.iterate_method_candidates( + trait_candidate.receiver_ty.iterate_method_candidates_with_traits( db, scope, &trait_candidates, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 156bbb634e4..ae120470047 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -22,7 +22,7 @@ pub mod source_change; pub mod symbol_index; pub mod traits; pub mod ty_filter; -pub mod use_trivial_contructor; +pub mod use_trivial_constructor; pub mod imports { pub mod import_assets; @@ -191,6 +191,7 @@ pub enum SymbolKind { Struct, ToolModule, Trait, + TraitAlias, TypeAlias, TypeParam, Union, @@ -221,6 +222,7 @@ impl From for SymbolKind { FileSymbolKind::Static => SymbolKind::Static, FileSymbolKind::Struct => SymbolKind::Struct, FileSymbolKind::Trait => SymbolKind::Trait, + FileSymbolKind::TraitAlias => SymbolKind::TraitAlias, FileSymbolKind::TypeAlias => SymbolKind::TypeAlias, FileSymbolKind::Union => SymbolKind::Union, } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs index 84d70b258ff..f710211c8cb 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs @@ -119,15 +119,9 @@ impl Definition { Definition::Const(it) => name_range(it, sema), Definition::Static(it) => name_range(it, sema), Definition::Trait(it) => name_range(it, sema), + Definition::TraitAlias(it) => name_range(it, sema), Definition::TypeAlias(it) => name_range(it, sema), - Definition::Local(local) => { - let src = local.source(sema.db); - let name = match &src.value { - Either::Left(bind_pat) => bind_pat.name()?, - Either::Right(_) => return None, - }; - src.with_value(name.syntax()).original_file_range_opt(sema.db) - } + Definition::Local(it) => name_range(it.primary_source(sema.db), sema), Definition::GenericParam(generic_param) => match generic_param { hir::GenericParam::LifetimeParam(lifetime_param) => { let src = lifetime_param.source(sema.db)?; @@ -301,13 +295,7 @@ fn rename_reference( source_change.insert_source_edit(file_id, edit); Ok(()) }; - match def { - Definition::Local(l) => l - .associated_locals(sema.db) - .iter() - .try_for_each(|&local| insert_def_edit(Definition::Local(local))), - def => insert_def_edit(def), - }?; + insert_def_edit(def)?; Ok(source_change) } @@ -470,59 +458,64 @@ fn source_edit_from_def( def: Definition, new_name: &str, ) -> Result<(FileId, TextEdit)> { - let FileRange { file_id, range } = def - .range_for_rename(sema) - .ok_or_else(|| format_err!("No identifier available to rename"))?; - let mut edit = TextEdit::builder(); if let Definition::Local(local) = def { - if let Either::Left(pat) = local.source(sema.db).value { - // special cases required for renaming fields/locals in Record patterns - if let Some(pat_field) = pat.syntax().parent().and_then(ast::RecordPatField::cast) { + let mut file_id = None; + for source in local.sources(sema.db) { + let source = source.source; + file_id = source.file_id.file_id(); + if let Either::Left(pat) = source.value { let name_range = pat.name().unwrap().syntax().text_range(); - if let Some(name_ref) = pat_field.name_ref() { - if new_name == name_ref.text() && pat.at_token().is_none() { - // Foo { field: ref mut local } -> Foo { ref mut field } - // ^^^^^^ delete this - // ^^^^^ replace this with `field` - cov_mark::hit!(test_rename_local_put_init_shorthand_pat); - edit.delete( - name_ref - .syntax() - .text_range() - .cover_offset(pat.syntax().text_range().start()), - ); - edit.replace(name_range, name_ref.text().to_string()); + // special cases required for renaming fields/locals in Record patterns + if let Some(pat_field) = pat.syntax().parent().and_then(ast::RecordPatField::cast) { + if let Some(name_ref) = pat_field.name_ref() { + if new_name == name_ref.text() && pat.at_token().is_none() { + // Foo { field: ref mut local } -> Foo { ref mut field } + // ^^^^^^ delete this + // ^^^^^ replace this with `field` + cov_mark::hit!(test_rename_local_put_init_shorthand_pat); + edit.delete( + name_ref + .syntax() + .text_range() + .cover_offset(pat.syntax().text_range().start()), + ); + edit.replace(name_range, name_ref.text().to_string()); + } else { + // Foo { field: ref mut local @ local 2} -> Foo { field: ref mut new_name @ local2 } + // Foo { field: ref mut local } -> Foo { field: ref mut new_name } + // ^^^^^ replace this with `new_name` + edit.replace(name_range, new_name.to_string()); + } } else { - // Foo { field: ref mut local @ local 2} -> Foo { field: ref mut new_name @ local2 } - // Foo { field: ref mut local } -> Foo { field: ref mut new_name } - // ^^^^^ replace this with `new_name` + // Foo { ref mut field } -> Foo { field: ref mut new_name } + // ^ insert `field: ` + // ^^^^^ replace this with `new_name` + edit.insert( + pat.syntax().text_range().start(), + format!("{}: ", pat_field.field_name().unwrap()), + ); edit.replace(name_range, new_name.to_string()); } } else { - // Foo { ref mut field } -> Foo { field: ref mut new_name } - // ^ insert `field: ` - // ^^^^^ replace this with `new_name` - edit.insert( - pat.syntax().text_range().start(), - format!("{}: ", pat_field.field_name().unwrap()), - ); edit.replace(name_range, new_name.to_string()); } } } + let Some(file_id) = file_id else { bail!("No file available to rename") }; + return Ok((file_id, edit.finish())); } - if edit.is_empty() { - let (range, new_name) = match def { - Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) - | Definition::Label(_) => ( - TextRange::new(range.start() + syntax::TextSize::from(1), range.end()), - new_name.strip_prefix('\'').unwrap_or(new_name).to_owned(), - ), - _ => (range, new_name.to_owned()), - }; - edit.replace(range, new_name); - } + let FileRange { file_id, range } = def + .range_for_rename(sema) + .ok_or_else(|| format_err!("No identifier available to rename"))?; + let (range, new_name) = match def { + Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_) => ( + TextRange::new(range.start() + syntax::TextSize::from(1), range.end()), + new_name.strip_prefix('\'').unwrap_or(new_name).to_owned(), + ), + _ => (range, new_name.to_owned()), + }; + edit.replace(range, new_name); Ok((file_id, edit.finish())) } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index c18a27f17d2..12f5e4e2a23 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -244,14 +244,14 @@ impl Definition { DefWithBody::Variant(v) => v.source(db).map(|src| src.syntax().cloned()), }; return match def { - Some(def) => SearchScope::file_range(def.as_ref().original_file_range(db)), + Some(def) => SearchScope::file_range(def.as_ref().original_file_range_full(db)), None => SearchScope::single_file(file_id), }; } if let Definition::SelfType(impl_) = self { return match impl_.source(db).map(|src| src.syntax().cloned()) { - Some(def) => SearchScope::file_range(def.as_ref().original_file_range(db)), + Some(def) => SearchScope::file_range(def.as_ref().original_file_range_full(db)), None => SearchScope::single_file(file_id), }; } @@ -261,13 +261,14 @@ impl Definition { hir::GenericDef::Function(it) => it.source(db).map(|src| src.syntax().cloned()), hir::GenericDef::Adt(it) => it.source(db).map(|src| src.syntax().cloned()), hir::GenericDef::Trait(it) => it.source(db).map(|src| src.syntax().cloned()), + hir::GenericDef::TraitAlias(it) => it.source(db).map(|src| src.syntax().cloned()), hir::GenericDef::TypeAlias(it) => it.source(db).map(|src| src.syntax().cloned()), hir::GenericDef::Impl(it) => it.source(db).map(|src| src.syntax().cloned()), hir::GenericDef::Variant(it) => it.source(db).map(|src| src.syntax().cloned()), hir::GenericDef::Const(it) => it.source(db).map(|src| src.syntax().cloned()), }; return match def { - Some(def) => SearchScope::file_range(def.as_ref().original_file_range(db)), + Some(def) => SearchScope::file_range(def.as_ref().original_file_range_full(db)), None => SearchScope::single_file(file_id), }; } @@ -318,10 +319,6 @@ impl Definition { sema, scope: None, include_self_kw_refs: None, - local_repr: match self { - Definition::Local(local) => Some(local.representative(sema.db)), - _ => None, - }, search_self_mod: false, } } @@ -336,9 +333,6 @@ pub struct FindUsages<'a> { assoc_item_container: Option, /// whether to search for the `Self` type of the definition include_self_kw_refs: Option, - /// the local representative for the local definition we are searching for - /// (this is required for finding all local declarations in a or-pattern) - local_repr: Option, /// whether to search for the `self` module search_self_mod: bool, } @@ -643,19 +637,6 @@ impl<'a> FindUsages<'a> { sink: &mut dyn FnMut(FileId, FileReference) -> bool, ) -> bool { match NameRefClass::classify(self.sema, name_ref) { - Some(NameRefClass::Definition(def @ Definition::Local(local))) - if matches!( - self.local_repr, Some(repr) if repr == local.representative(self.sema.db) - ) => - { - let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); - let reference = FileReference { - range, - name: ast::NameLike::NameRef(name_ref.clone()), - category: ReferenceCategory::new(&def, name_ref), - }; - sink(file_id, reference) - } Some(NameRefClass::Definition(def)) if self.def == def // is our def a trait assoc item? then we want to find all assoc items from trait impls of our trait @@ -700,14 +681,16 @@ impl<'a> FindUsages<'a> { } } Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { - let field = Definition::Field(field); let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); + + let field = Definition::Field(field); + let local = Definition::Local(local); let access = match self.def { Definition::Field(_) if field == self.def => { ReferenceCategory::new(&field, name_ref) } - Definition::Local(_) if matches!(self.local_repr, Some(repr) if repr == local.representative(self.sema.db)) => { - ReferenceCategory::new(&Definition::Local(local), name_ref) + Definition::Local(_) if local == self.def => { + ReferenceCategory::new(&local, name_ref) } _ => return false, }; @@ -751,21 +734,6 @@ impl<'a> FindUsages<'a> { }; sink(file_id, reference) } - Some(NameClass::Definition(def @ Definition::Local(local))) if def != self.def => { - if matches!( - self.local_repr, - Some(repr) if local.representative(self.sema.db) == repr - ) { - let FileRange { file_id, range } = self.sema.original_range(name.syntax()); - let reference = FileReference { - range, - name: ast::NameLike::Name(name.clone()), - category: None, - }; - return sink(file_id, reference); - } - false - } Some(NameClass::Definition(def)) if def != self.def => { match (&self.assoc_item_container, self.def) { // for type aliases we always want to reference the trait def and all the trait impl counterparts diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs index 8e338061df4..936354f2961 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs @@ -83,6 +83,14 @@ impl From> for SourceChange { } } +impl FromIterator<(FileId, TextEdit)> for SourceChange { + fn from_iter>(iter: T) -> Self { + let mut this = SourceChange::default(); + this.extend(iter); + this + } +} + pub struct SourceChangeBuilder { pub edit: TextEditBuilder, pub file_id: FileId, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_contructor.rs b/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs similarity index 100% rename from src/tools/rust-analyzer/crates/ide-db/src/use_trivial_contructor.rs rename to src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs index 10e637979f2..114face2dca 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs @@ -7,10 +7,15 @@ pub(crate) fn break_outside_of_loop( ctx: &DiagnosticsContext<'_>, d: &hir::BreakOutsideOfLoop, ) -> Diagnostic { - let construct = if d.is_break { "break" } else { "continue" }; + let message = if d.bad_value_break { + "can't break with a value in this position".to_owned() + } else { + let construct = if d.is_break { "break" } else { "continue" }; + format!("{construct} outside of loop") + }; Diagnostic::new( "break-outside-of-loop", - format!("{construct} outside of loop"), + message, ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, ) } @@ -132,6 +137,20 @@ fn foo() { //^^^^^^^^^^^ error: continue outside of loop } } +"#, + ); + } + + #[test] + fn value_break_in_for_loop() { + check_diagnostics( + r#" +fn test() { + for _ in [()] { + break 3; + // ^^^^^^^ error: can't break with a value in this position + } +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs new file mode 100644 index 00000000000..d2f27664d6f --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs @@ -0,0 +1,39 @@ +use hir::HirDisplay; + +use crate::{Diagnostic, DiagnosticsContext}; + +// Diagnostic: expected-function +// +// This diagnostic is triggered if a call is made on something that is not callable. +pub(crate) fn expected_function( + ctx: &DiagnosticsContext<'_>, + d: &hir::ExpectedFunction, +) -> Diagnostic { + Diagnostic::new( + "expected-function", + format!("expected function, found {}", d.found.display(ctx.sema.db)), + ctx.sema.diagnostics_display_range(d.call.clone().map(|it| it.into())).range, + ) + .experimental() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn smoke_test() { + check_diagnostics( + r#" +fn foo() { + let x = 3; + x(); + // ^^^ error: expected function, found i32 + ""(); + // ^^^^ error: expected function, found &str + foo(); +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs index 43af4d4f16a..14039087b3f 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -5,7 +5,7 @@ use hir::{ }; use ide_db::{ assists::Assist, famous_defs::FamousDefs, imports::import_assets::item_for_path_search, - source_change::SourceChange, use_trivial_contructor::use_trivial_constructor, FxHashMap, + source_change::SourceChange, use_trivial_constructor::use_trivial_constructor, FxHashMap, }; use stdx::format_to; use syntax::{ diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index c24430ce604..ac4463331f2 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs @@ -1,5 +1,3 @@ -use hir::InFile; - use crate::{Diagnostic, DiagnosticsContext}; // Diagnostic: missing-match-arm @@ -12,7 +10,7 @@ pub(crate) fn missing_match_arms( Diagnostic::new( "missing-match-arm", format!("missing match arm: {}", d.uncovered_patterns), - ctx.sema.diagnostics_display_range(InFile::new(d.file, d.match_expr.clone().into())).range, + ctx.sema.diagnostics_display_range(d.scrutinee_expr.clone().map(Into::into)).range, ) } @@ -1038,7 +1036,6 @@ fn main() { #[test] fn reference_patterns_in_fields() { cov_mark::check_count!(validate_match_bailed_out, 2); - check_diagnostics( r#" fn main() { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs new file mode 100644 index 00000000000..84189a5d560 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -0,0 +1,625 @@ +use ide_db::source_change::SourceChange; +use syntax::{AstNode, SyntaxKind, SyntaxNode, SyntaxToken, T}; +use text_edit::TextEdit; + +use crate::{fix, Diagnostic, DiagnosticsContext, Severity}; + +// Diagnostic: need-mut +// +// This diagnostic is triggered on mutating an immutable variable. +pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Diagnostic { + let fixes = (|| { + if d.local.is_ref(ctx.sema.db) { + // There is no simple way to add `mut` to `ref x` and `ref mut x` + return None; + } + let file_id = d.span.file_id.file_id()?; + let mut edit_builder = TextEdit::builder(); + let use_range = d.span.value.text_range(); + for source in d.local.sources(ctx.sema.db) { + let Some(ast) = source.name() else { continue }; + edit_builder.insert(ast.syntax().text_range().start(), "mut ".to_string()); + } + let edit = edit_builder.finish(); + Some(vec![fix( + "add_mut", + "Change it to be mutable", + SourceChange::from_text_edit(file_id, edit), + use_range, + )]) + })(); + Diagnostic::new( + "need-mut", + format!("cannot mutate immutable variable `{}`", d.local.name(ctx.sema.db)), + ctx.sema.diagnostics_display_range(d.span.clone()).range, + ) + .with_fixes(fixes) +} + +// Diagnostic: unused-mut +// +// This diagnostic is triggered when a mutable variable isn't actually mutated. +pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Diagnostic { + let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); + let fixes = (|| { + let file_id = ast.file_id.file_id()?; + let mut edit_builder = TextEdit::builder(); + let use_range = ast.value.text_range(); + for source in d.local.sources(ctx.sema.db) { + let ast = source.syntax(); + let Some(mut_token) = token(ast, T![mut]) else { continue }; + edit_builder.delete(mut_token.text_range()); + if let Some(token) = mut_token.next_token() { + if token.kind() == SyntaxKind::WHITESPACE { + edit_builder.delete(token.text_range()); + } + } + } + let edit = edit_builder.finish(); + Some(vec![fix( + "remove_mut", + "Remove unnecessary `mut`", + SourceChange::from_text_edit(file_id, edit), + use_range, + )]) + })(); + let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); + Diagnostic::new( + "unused-mut", + "variable does not need to be mutable", + ctx.sema.diagnostics_display_range(ast).range, + ) + .severity(Severity::WeakWarning) + .experimental() // Not supporting `#[allow(unused_mut)]` leads to false positive. + .with_fixes(fixes) +} + +pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option { + parent.children_with_tokens().filter_map(|it| it.into_token()).find(|it| it.kind() == kind) +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_diagnostics, check_fix}; + + #[test] + fn unused_mut_simple() { + check_diagnostics( + r#" +fn f(_: i32) {} +fn main() { + let mut x = 2; + //^^^^^ 💡 weak: variable does not need to be mutable + f(x); +} +"#, + ); + } + + #[test] + fn no_false_positive_simple() { + check_diagnostics( + r#" +fn f(_: i32) {} +fn main() { + let x = 2; + f(x); +} +"#, + ); + check_diagnostics( + r#" +fn f(_: i32) {} +fn main() { + let mut x = 2; + x = 5; + f(x); +} +"#, + ); + } + + #[test] + fn multiple_errors_for_single_variable() { + check_diagnostics( + r#" +fn f(_: i32) {} +fn main() { + let x = 2; + x = 10; + //^^^^^^ 💡 error: cannot mutate immutable variable `x` + x = 5; + //^^^^^ 💡 error: cannot mutate immutable variable `x` + &mut x; + //^^^^^^ 💡 error: cannot mutate immutable variable `x` + f(x); +} +"#, + ); + } + + #[test] + fn unused_mut_fix() { + check_fix( + r#" +fn f(_: i32) {} +fn main() { + let mu$0t x = 2; + f(x); +} +"#, + r#" +fn f(_: i32) {} +fn main() { + let x = 2; + f(x); +} +"#, + ); + check_fix( + r#" +fn f(_: i32) {} +fn main() { + let ((mu$0t x, _) | (_, mut x)) = (2, 3); + f(x); +} +"#, + r#" +fn f(_: i32) {} +fn main() { + let ((x, _) | (_, x)) = (2, 3); + f(x); +} +"#, + ); + } + + #[test] + fn need_mut_fix() { + check_fix( + r#" +fn f(_: i32) {} +fn main() { + let x = 2; + x$0 = 5; + f(x); +} +"#, + r#" +fn f(_: i32) {} +fn main() { + let mut x = 2; + x = 5; + f(x); +} +"#, + ); + check_fix( + r#" +fn f(_: i32) {} +fn main() { + let ((x, _) | (_, x)) = (2, 3); + x =$0 4; + f(x); +} +"#, + r#" +fn f(_: i32) {} +fn main() { + let ((mut x, _) | (_, mut x)) = (2, 3); + x = 4; + f(x); +} +"#, + ); + + check_fix( + r#" +struct Foo(i32); + +impl Foo { + fn foo(self) { + self = Fo$0o(5); + } +} +"#, + r#" +struct Foo(i32); + +impl Foo { + fn foo(mut self) { + self = Foo(5); + } +} +"#, + ); + } + + #[test] + fn need_mut_fix_not_applicable_on_ref() { + check_diagnostics( + r#" +fn main() { + let ref x = 2; + x = &5; + //^^^^^^ error: cannot mutate immutable variable `x` +} +"#, + ); + check_diagnostics( + r#" +fn main() { + let ref mut x = 2; + x = &mut 5; + //^^^^^^^^^^ error: cannot mutate immutable variable `x` +} +"#, + ); + } + + #[test] + fn field_mutate() { + check_diagnostics( + r#" +fn f(_: i32) {} +fn main() { + let mut x = (2, 7); + //^^^^^ 💡 weak: variable does not need to be mutable + f(x.1); +} +"#, + ); + check_diagnostics( + r#" +fn f(_: i32) {} +fn main() { + let mut x = (2, 7); + x.0 = 5; + f(x.1); +} +"#, + ); + check_diagnostics( + r#" +fn f(_: i32) {} +fn main() { + let x = (2, 7); + x.0 = 5; + //^^^^^^^ 💡 error: cannot mutate immutable variable `x` + f(x.1); +} +"#, + ); + } + + #[test] + fn mutable_reference() { + check_diagnostics( + r#" +fn main() { + let mut x = &mut 2; + //^^^^^ 💡 weak: variable does not need to be mutable + *x = 5; +} +"#, + ); + check_diagnostics( + r#" +fn main() { + let x = 2; + &mut x; + //^^^^^^ 💡 error: cannot mutate immutable variable `x` +} +"#, + ); + check_diagnostics( + r#" +fn main() { + let x_own = 2; + let ref mut x_ref = x_own; + //^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x_own` +} +"#, + ); + check_diagnostics( + r#" +struct Foo; +impl Foo { + fn method(&mut self, x: i32) {} +} +fn main() { + let x = Foo; + x.method(2); + //^ 💡 error: cannot mutate immutable variable `x` +} +"#, + ); + } + + #[test] + fn regression_14310() { + check_diagnostics( + r#" + fn clone(mut i: &!) -> ! { + //^^^^^ 💡 weak: variable does not need to be mutable + *i + } + "#, + ); + } + + #[test] + fn match_bindings() { + check_diagnostics( + r#" +fn main() { + match (2, 3) { + (x, mut y) => { + //^^^^^ 💡 weak: variable does not need to be mutable + x = 7; + //^^^^^ 💡 error: cannot mutate immutable variable `x` + } + } +} +"#, + ); + } + + #[test] + fn mutation_in_dead_code() { + // This one is interesting. Dead code is not represented at all in the MIR, so + // there would be no mutablility error for locals in dead code. Rustc tries to + // not emit `unused_mut` in this case, but since it works without `mut`, and + // special casing it is not trivial, we emit it. + check_diagnostics( + r#" +fn main() { + return; + let mut x = 2; + //^^^^^ 💡 weak: variable does not need to be mutable + &mut x; +} +"#, + ); + check_diagnostics( + r#" +fn main() { + loop {} + let mut x = 2; + //^^^^^ 💡 weak: variable does not need to be mutable + &mut x; +} +"#, + ); + check_diagnostics( + r#" +enum X {} +fn g() -> X { + loop {} +} +fn f() -> ! { + loop {} +} +fn main(b: bool) { + if b { + f(); + } else { + g(); + } + let mut x = 2; + //^^^^^ 💡 weak: variable does not need to be mutable + &mut x; +} +"#, + ); + check_diagnostics( + r#" +fn main(b: bool) { + if b { + loop {} + } else { + return; + } + let mut x = 2; + //^^^^^ 💡 weak: variable does not need to be mutable + &mut x; +} +"#, + ); + } + + #[test] + fn initialization_is_not_mutation() { + check_diagnostics( + r#" +fn f(_: i32) {} +fn main() { + let mut x; + //^^^^^ 💡 weak: variable does not need to be mutable + x = 5; + f(x); +} +"#, + ); + check_diagnostics( + r#" +fn f(_: i32) {} +fn main(b: bool) { + let mut x; + //^^^^^ 💡 weak: variable does not need to be mutable + if b { + x = 1; + } else { + x = 3; + } + f(x); +} +"#, + ); + check_diagnostics( + r#" +fn f(_: i32) {} +fn main(b: bool) { + let x; + if b { + x = 1; + } + x = 3; + //^^^^^ 💡 error: cannot mutate immutable variable `x` + f(x); +} +"#, + ); + check_diagnostics( + r#" +fn f(_: i32) {} +fn main() { + let x; + loop { + x = 1; + //^^^^^ 💡 error: cannot mutate immutable variable `x` + f(x); + } +} +"#, + ); + check_diagnostics( + r#" +fn f(_: i32) {} +fn main() { + loop { + let mut x = 1; + //^^^^^ 💡 weak: variable does not need to be mutable + f(x); + if let mut y = 2 { + //^^^^^ 💡 weak: variable does not need to be mutable + f(y); + } + match 3 { + mut z => f(z), + //^^^^^ 💡 weak: variable does not need to be mutable + } + } +} +"#, + ); + } + + #[test] + fn function_arguments_are_initialized() { + check_diagnostics( + r#" +fn f(mut x: i32) { + //^^^^^ 💡 weak: variable does not need to be mutable +} +"#, + ); + check_diagnostics( + r#" +fn f(x: i32) { + x = 5; + //^^^^^ 💡 error: cannot mutate immutable variable `x` +} +"#, + ); + } + + #[test] + fn for_loop() { + check_diagnostics( + r#" +//- minicore: iterators +fn f(x: [(i32, u8); 10]) { + for (a, mut b) in x { + //^^^^^ 💡 weak: variable does not need to be mutable + a = 2; + //^^^^^ 💡 error: cannot mutate immutable variable `a` + } +} +"#, + ); + } + + #[test] + fn overloaded_deref() { + // FIXME: check for false negative + check_diagnostics( + r#" +//- minicore: deref_mut +use core::ops::{Deref, DerefMut}; + +struct Foo; +impl Deref for Foo { + type Target = i32; + fn deref(&self) -> &i32 { + &5 + } +} +impl DerefMut for Foo { + fn deref_mut(&mut self) -> &mut i32 { + &mut 5 + } +} +fn f() { + let x = Foo; + let y = &*x; + let x = Foo; + let mut x = Foo; + let y: &mut i32 = &mut x; +} +"#, + ); + } + + #[test] + fn or_pattern() { + check_diagnostics( + r#" +//- minicore: option +fn f(_: i32) {} +fn main() { + let ((Some(mut x), None) | (_, Some(mut x))) = (None, Some(7)); + //^^^^^ 💡 weak: variable does not need to be mutable + f(x); +} +"#, + ); + } + + #[test] + fn or_pattern_no_terminator() { + check_diagnostics( + r#" +enum Foo { + A, B, C, D +} + +use Foo::*; + +fn f(inp: (Foo, Foo, Foo, Foo)) { + let ((A, B, _, x) | (B, C | D, x, _)) = inp else { + return; + }; + x = B; + //^^^^^ 💡 error: cannot mutate immutable variable `x` +} +"#, + ); + } + + #[test] + fn respect_allow_unused_mut() { + // FIXME: respect + check_diagnostics( + r#" +fn f(_: i32) {} +fn main() { + #[allow(unused_mut)] + let mut x = 2; + //^^^^^ 💡 weak: variable does not need to be mutable + f(x); +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs index 0b3121c765d..67da5c7f27d 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs @@ -115,6 +115,44 @@ mod module { fn main(s: module::Struct) { s.method(); } +"#, + ); + } + + #[test] + fn can_see_through_top_level_anonymous_const() { + // regression test for #14046. + check_diagnostics( + r#" +struct S; +mod m { + const _: () = { + impl crate::S { + pub(crate) fn method(self) {} + pub(crate) const A: usize = 42; + } + }; + mod inner { + const _: () = { + impl crate::S { + pub(crate) fn method2(self) {} + pub(crate) const B: usize = 42; + pub(super) fn private(self) {} + pub(super) const PRIVATE: usize = 42; + } + }; + } +} +fn main() { + S.method(); + S::A; + S.method2(); + S::B; + S.private(); + //^^^^^^^^^^^ error: function `private` is private + S::PRIVATE; + //^^^^^^^^^^ error: const `PRIVATE` is private +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs index 9826e1c707e..a0c276cc332 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs @@ -55,7 +55,18 @@ fn fixes( #[cfg(test)] mod tests { - use crate::tests::{check_diagnostics, check_fix}; + use crate::{ + tests::{check_diagnostics_with_config, check_fix}, + DiagnosticsConfig, + }; + + #[track_caller] + pub(crate) fn check_diagnostics(ra_fixture: &str) { + let mut config = DiagnosticsConfig::test_sample(); + config.disabled.insert("inactive-code".to_string()); + config.disabled.insert("unresolved-method".to_string()); + check_diagnostics_with_config(config, ra_fixture) + } #[test] fn replace_filter_map_next_with_find_map2() { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs index 2adae165e4d..b57a13e53e6 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -1,8 +1,9 @@ -use hir::{db::AstDatabase, HirDisplay, Type}; +use either::Either; +use hir::{db::AstDatabase, HirDisplay, InFile, Type}; use ide_db::{famous_defs::FamousDefs, source_change::SourceChange}; use syntax::{ ast::{self, BlockExpr, ExprStmt}, - AstNode, + AstNode, AstPtr, }; use text_edit::TextEdit; @@ -10,19 +11,23 @@ use crate::{adjusted_display_range, fix, Assist, Diagnostic, DiagnosticsContext} // Diagnostic: type-mismatch // -// This diagnostic is triggered when the type of an expression does not match +// This diagnostic is triggered when the type of an expression or pattern does not match // the expected type. pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Diagnostic { - let display_range = adjusted_display_range::( - ctx, - d.expr.clone().map(|it| it.into()), - &|block| { - let r_curly_range = block.stmt_list()?.r_curly_token()?.text_range(); - cov_mark::hit!(type_mismatch_on_block); - Some(r_curly_range) - }, - ); - + let display_range = match &d.expr_or_pat { + Either::Left(expr) => adjusted_display_range::( + ctx, + expr.clone().map(|it| it.into()), + &|block| { + let r_curly_range = block.stmt_list()?.r_curly_token()?.text_range(); + cov_mark::hit!(type_mismatch_on_block); + Some(r_curly_range) + }, + ), + Either::Right(pat) => { + ctx.sema.diagnostics_display_range(pat.clone().map(|it| it.into())).range + } + }; let mut diag = Diagnostic::new( "type-mismatch", format!( @@ -42,10 +47,15 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Option> { let mut fixes = Vec::new(); - add_reference(ctx, d, &mut fixes); - add_missing_ok_or_some(ctx, d, &mut fixes); - remove_semicolon(ctx, d, &mut fixes); - str_ref_to_owned(ctx, d, &mut fixes); + match &d.expr_or_pat { + Either::Left(expr_ptr) => { + add_reference(ctx, d, expr_ptr, &mut fixes); + add_missing_ok_or_some(ctx, d, expr_ptr, &mut fixes); + remove_semicolon(ctx, d, expr_ptr, &mut fixes); + str_ref_to_owned(ctx, d, expr_ptr, &mut fixes); + } + Either::Right(_pat_ptr) => {} + } if fixes.is_empty() { None @@ -57,9 +67,10 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Option, d: &hir::TypeMismatch, + expr_ptr: &InFile>, acc: &mut Vec, ) -> Option<()> { - let range = ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range; + let range = ctx.sema.diagnostics_display_range(expr_ptr.clone().map(|it| it.into())).range; let (_, mutability) = d.expected.as_reference()?; let actual_with_ref = Type::reference(&d.actual, mutability); @@ -71,7 +82,7 @@ fn add_reference( let edit = TextEdit::insert(range.start(), ampersands); let source_change = - SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit); + SourceChange::from_text_edit(expr_ptr.file_id.original_file(ctx.sema.db), edit); acc.push(fix("add_reference_here", "Add reference here", source_change, range)); Some(()) } @@ -79,10 +90,11 @@ fn add_reference( fn add_missing_ok_or_some( ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch, + expr_ptr: &InFile>, acc: &mut Vec, ) -> Option<()> { - let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?; - let expr = d.expr.value.to_node(&root); + let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?; + let expr = expr_ptr.value.to_node(&root); let expr_range = expr.syntax().text_range(); let scope = ctx.sema.scope(expr.syntax())?; @@ -109,7 +121,7 @@ fn add_missing_ok_or_some( builder.insert(expr.syntax().text_range().start(), format!("{variant_name}(")); builder.insert(expr.syntax().text_range().end(), ")".to_string()); let source_change = - SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), builder.finish()); + SourceChange::from_text_edit(expr_ptr.file_id.original_file(ctx.sema.db), builder.finish()); let name = format!("Wrap in {variant_name}"); acc.push(fix("wrap_in_constructor", &name, source_change, expr_range)); Some(()) @@ -118,10 +130,11 @@ fn add_missing_ok_or_some( fn remove_semicolon( ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch, + expr_ptr: &InFile>, acc: &mut Vec, ) -> Option<()> { - let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?; - let expr = d.expr.value.to_node(&root); + let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?; + let expr = expr_ptr.value.to_node(&root); if !d.actual.is_unit() { return None; } @@ -136,7 +149,7 @@ fn remove_semicolon( let edit = TextEdit::delete(semicolon_range); let source_change = - SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit); + SourceChange::from_text_edit(expr_ptr.file_id.original_file(ctx.sema.db), edit); acc.push(fix("remove_semicolon", "Remove this semicolon", source_change, semicolon_range)); Some(()) @@ -145,24 +158,26 @@ fn remove_semicolon( fn str_ref_to_owned( ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch, + expr_ptr: &InFile>, acc: &mut Vec, ) -> Option<()> { let expected = d.expected.display(ctx.sema.db); let actual = d.actual.display(ctx.sema.db); + // FIXME do this properly if expected.to_string() != "String" || actual.to_string() != "&str" { return None; } - let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?; - let expr = d.expr.value.to_node(&root); + let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?; + let expr = expr_ptr.value.to_node(&root); let expr_range = expr.syntax().text_range(); let to_owned = format!(".to_owned()"); let edit = TextEdit::insert(expr.syntax().text_range().end(), to_owned); let source_change = - SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit); + SourceChange::from_text_edit(expr_ptr.file_id.original_file(ctx.sema.db), edit); acc.push(fix("str_ref_to_owned", "Add .to_owned() here", source_change, expr_range)); Some(()) @@ -592,6 +607,21 @@ fn f() -> i32 { let _ = x + y; } //^ error: expected i32, found () +"#, + ); + } + + #[test] + fn type_mismatch_pat_smoke_test() { + check_diagnostics( + r#" +fn f() { + let &() = &mut (); + match &() { + &9 => () + //^ error: expected (), found i32 + } +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs new file mode 100644 index 00000000000..7de03416e56 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs @@ -0,0 +1,148 @@ +use hir::{db::AstDatabase, HirDisplay, InFile}; +use ide_db::{ + assists::{Assist, AssistId, AssistKind}, + base_db::FileRange, + label::Label, + source_change::SourceChange, +}; +use syntax::{ast, AstNode, AstPtr}; +use text_edit::TextEdit; + +use crate::{Diagnostic, DiagnosticsContext}; + +// Diagnostic: unresolved-field +// +// This diagnostic is triggered if a field does not exist on a given type. +pub(crate) fn unresolved_field( + ctx: &DiagnosticsContext<'_>, + d: &hir::UnresolvedField, +) -> Diagnostic { + let method_suffix = if d.method_with_same_name_exists { + ", but a method with a similar name exists" + } else { + "" + }; + Diagnostic::new( + "unresolved-field", + format!( + "no field `{}` on type `{}`{method_suffix}", + d.name, + d.receiver.display(ctx.sema.db) + ), + ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, + ) + .with_fixes(fixes(ctx, d)) + .experimental() +} + +fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option> { + if d.method_with_same_name_exists { + method_fix(ctx, &d.expr) + } else { + // FIXME: add quickfix + + None + } +} + +// FIXME: We should fill out the call here, mvoe the cursor and trigger signature help +fn method_fix( + ctx: &DiagnosticsContext<'_>, + expr_ptr: &InFile>, +) -> Option> { + let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?; + let expr = expr_ptr.value.to_node(&root); + let FileRange { range, file_id } = ctx.sema.original_range_opt(expr.syntax())?; + Some(vec![Assist { + id: AssistId("expected-field-found-method-call-fix", AssistKind::QuickFix), + label: Label::new("Use parentheses to call the method".to_string()), + group: None, + target: range, + source_change: Some(SourceChange::from_text_edit( + file_id, + TextEdit::insert(range.end(), "()".to_owned()), + )), + trigger_signature_help: false, + }]) +} +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn smoke_test() { + check_diagnostics( + r#" +fn main() { + ().foo; + // ^^^^^^ error: no field `foo` on type `()` +} +"#, + ); + } + + #[test] + fn method_clash() { + check_diagnostics( + r#" +struct Foo; +impl Foo { + fn bar(&self) {} +} +fn foo() { + Foo.bar; + // ^^^^^^^ 💡 error: no field `bar` on type `Foo`, but a method with a similar name exists +} +"#, + ); + } + + #[test] + fn method_trait_() { + check_diagnostics( + r#" +struct Foo; +trait Bar { + fn bar(&self) {} +} +impl Bar for Foo {} +fn foo() { + Foo.bar; + // ^^^^^^^ 💡 error: no field `bar` on type `Foo`, but a method with a similar name exists +} +"#, + ); + } + + #[test] + fn method_trait_2() { + check_diagnostics( + r#" +struct Foo; +trait Bar { + fn bar(&self); +} +impl Bar for Foo { + fn bar(&self) {} +} +fn foo() { + Foo.bar; + // ^^^^^^^ 💡 error: no field `bar` on type `Foo`, but a method with a similar name exists +} +"#, + ); + } + + #[test] + fn no_diagnostic_on_unknown() { + check_diagnostics( + r#" +fn foo() { + x.foo; + (&x).foo; + (&((x,),),).foo; +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs new file mode 100644 index 00000000000..4b0e64cb896 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs @@ -0,0 +1,131 @@ +use hir::{db::AstDatabase, HirDisplay}; +use ide_db::{ + assists::{Assist, AssistId, AssistKind}, + base_db::FileRange, + label::Label, + source_change::SourceChange, +}; +use syntax::{ast, AstNode, TextRange}; +use text_edit::TextEdit; + +use crate::{Diagnostic, DiagnosticsContext}; + +// Diagnostic: unresolved-method +// +// This diagnostic is triggered if a method does not exist on a given type. +pub(crate) fn unresolved_method( + ctx: &DiagnosticsContext<'_>, + d: &hir::UnresolvedMethodCall, +) -> Diagnostic { + let field_suffix = if d.field_with_same_name.is_some() { + ", but a field with a similar name exists" + } else { + "" + }; + Diagnostic::new( + "unresolved-method", + format!( + "no method `{}` on type `{}`{field_suffix}", + d.name, + d.receiver.display(ctx.sema.db) + ), + ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, + ) + .with_fixes(fixes(ctx, d)) + .experimental() +} + +fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -> Option> { + if let Some(ty) = &d.field_with_same_name { + field_fix(ctx, d, ty) + } else { + // FIXME: add quickfix + None + } +} + +fn field_fix( + ctx: &DiagnosticsContext<'_>, + d: &hir::UnresolvedMethodCall, + ty: &hir::Type, +) -> Option> { + if !ty.impls_fnonce(ctx.sema.db) { + return None; + } + let expr_ptr = &d.expr; + let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?; + let expr = expr_ptr.value.to_node(&root); + let (file_id, range) = match expr { + ast::Expr::MethodCallExpr(mcall) => { + let FileRange { range, file_id } = + ctx.sema.original_range_opt(mcall.receiver()?.syntax())?; + let FileRange { range: range2, file_id: file_id2 } = + ctx.sema.original_range_opt(mcall.name_ref()?.syntax())?; + if file_id != file_id2 { + return None; + } + (file_id, TextRange::new(range.start(), range2.end())) + } + _ => return None, + }; + Some(vec![Assist { + id: AssistId("expected-method-found-field-fix", AssistKind::QuickFix), + label: Label::new("Use parentheses to call the value of the field".to_string()), + group: None, + target: range, + source_change: Some(SourceChange::from_iter([ + (file_id, TextEdit::insert(range.start(), "(".to_owned())), + (file_id, TextEdit::insert(range.end(), ")".to_owned())), + ])), + trigger_signature_help: false, + }]) +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_diagnostics, check_fix}; + + #[test] + fn smoke_test() { + check_diagnostics( + r#" +fn main() { + ().foo(); + // ^^^^^^^^ error: no method `foo` on type `()` +} +"#, + ); + } + + #[test] + fn field() { + check_diagnostics( + r#" +struct Foo { bar: i32 } +fn foo() { + Foo { bar: i32 }.bar(); + // ^^^^^^^^^^^^^^^^^^^^^^ error: no method `bar` on type `Foo`, but a field with a similar name exists +} +"#, + ); + } + + #[test] + fn callable_field() { + check_fix( + r#" +//- minicore: fn +struct Foo { bar: fn() } +fn foo() { + Foo { bar: foo }.b$0ar(); +} +"#, + r#" +struct Foo { bar: fn() } +fn foo() { + (Foo { bar: foo }.bar)(); +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 64ba08ac883..f6c9b79c30c 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -27,6 +27,7 @@ mod handlers { pub(crate) mod break_outside_of_loop; + pub(crate) mod expected_function; pub(crate) mod inactive_code; pub(crate) mod incorrect_case; pub(crate) mod invalid_derive_target; @@ -36,6 +37,7 @@ mod handlers { pub(crate) mod missing_fields; pub(crate) mod missing_match_arms; pub(crate) mod missing_unsafe; + pub(crate) mod mutability_errors; pub(crate) mod no_such_field; pub(crate) mod private_assoc_item; pub(crate) mod private_field; @@ -43,6 +45,8 @@ mod handlers { pub(crate) mod type_mismatch; pub(crate) mod unimplemented_builtin_macro; pub(crate) mod unresolved_extern_crate; + pub(crate) mod unresolved_field; + pub(crate) mod unresolved_method; pub(crate) mod unresolved_import; pub(crate) mod unresolved_macro_call; pub(crate) mod unresolved_module; @@ -248,6 +252,7 @@ pub fn diagnostics( #[rustfmt::skip] let d = match diag { AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d), + AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d), AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d), AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d), AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d), @@ -267,7 +272,10 @@ pub fn diagnostics( AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d), AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d, config.proc_macros_enabled, config.proc_attr_macros_enabled), AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d), - + AnyDiagnostic::UnresolvedField(d) => handlers::unresolved_field::unresolved_field(&ctx, &d), + AnyDiagnostic::UnresolvedMethodCall(d) => handlers::unresolved_method::unresolved_method(&ctx, &d), + AnyDiagnostic::NeedMut(d) => handlers::mutability_errors::need_mut(&ctx, &d), + AnyDiagnostic::UnusedMut(d) => handlers::mutability_errors::unused_mut(&ctx, &d), AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) { Some(it) => it, None => continue, diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs index 57b5ab6abda..a8e88369088 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs @@ -561,7 +561,7 @@ impl<'db, 'sema> Matcher<'db, 'sema> { .sema .resolve_method_call_as_callable(code) .and_then(|callable| callable.receiver_param(self.sema.db)) - .map(|self_param| self_param.kind()) + .map(|(self_param, _)| self_param.kind()) .unwrap_or(ast::SelfParamKind::Owned); } } diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index b4a7f2b918a..fae25f31023 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -107,7 +107,18 @@ pub(crate) fn remove_links(markdown: &str) -> String { out } -/// Retrieve a link to documentation for the given symbol. +// Feature: Open Docs +// +// Retrieve a link to documentation for the given symbol. +// +// The simplest way to use this feature is via the context menu. Right-click on +// the selected item. The context menu opens. Select **Open Docs**. +// +// |=== +// | Editor | Action Name +// +// | VS Code | **rust-analyzer: Open Docs** +// |=== pub(crate) fn external_docs( db: &RootDatabase, position: &FilePosition, @@ -181,6 +192,7 @@ pub(crate) fn resolve_doc_path_for_def( Definition::Const(it) => it.resolve_doc_path(db, link, ns), Definition::Static(it) => it.resolve_doc_path(db, link, ns), Definition::Trait(it) => it.resolve_doc_path(db, link, ns), + Definition::TraitAlias(it) => it.resolve_doc_path(db, link, ns), Definition::TypeAlias(it) => it.resolve_doc_path(db, link, ns), Definition::Macro(it) => it.resolve_doc_path(db, link, ns), Definition::Field(it) => it.resolve_doc_path(db, link, ns), @@ -493,6 +505,7 @@ fn filename_and_frag_for_def( None => String::from("index.html"), }, Definition::Trait(t) => format!("trait.{}.html", t.name(db)), + Definition::TraitAlias(t) => format!("traitalias.{}.html", t.name(db)), Definition::TypeAlias(t) => format!("type.{}.html", t.name(db)), Definition::BuiltinType(t) => format!("primitive.{}.html", t.name()), Definition::Function(f) => format!("fn.{}.html", f.name(db)), diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs index b23763dce86..a32ac35496a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs +++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs @@ -149,6 +149,7 @@ fn structure_node(node: &SyntaxNode) -> Option { ast::Enum(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Enum)), ast::Variant(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Variant)), ast::Trait(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Trait)), + ast::TraitAlias(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::TraitAlias)), ast::Module(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Module)), ast::TypeAlias(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::TypeAlias)), ast::RecordField(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::Field)), @@ -262,6 +263,8 @@ enum E { X, Y(i32) } type T = (); static S: i32 = 92; const C: i32 = 92; +trait Tr {} +trait Alias = Tr; impl E {} @@ -457,11 +460,33 @@ fn g() {} ), deprecated: false, }, + StructureNode { + parent: None, + label: "Tr", + navigation_range: 239..241, + node_range: 233..244, + kind: SymbolKind( + Trait, + ), + detail: None, + deprecated: false, + }, + StructureNode { + parent: None, + label: "Alias", + navigation_range: 251..256, + node_range: 245..262, + kind: SymbolKind( + TraitAlias, + ), + detail: None, + deprecated: false, + }, StructureNode { parent: None, label: "impl E", - navigation_range: 239..240, - node_range: 234..243, + navigation_range: 269..270, + node_range: 264..273, kind: SymbolKind( Impl, ), @@ -471,8 +496,8 @@ fn g() {} StructureNode { parent: None, label: "impl fmt::Debug for E", - navigation_range: 265..266, - node_range: 245..269, + navigation_range: 295..296, + node_range: 275..299, kind: SymbolKind( Impl, ), @@ -482,8 +507,8 @@ fn g() {} StructureNode { parent: None, label: "mc", - navigation_range: 284..286, - node_range: 271..303, + navigation_range: 314..316, + node_range: 301..333, kind: SymbolKind( Macro, ), @@ -493,8 +518,8 @@ fn g() {} StructureNode { parent: None, label: "mcexp", - navigation_range: 334..339, - node_range: 305..356, + navigation_range: 364..369, + node_range: 335..386, kind: SymbolKind( Macro, ), @@ -504,8 +529,8 @@ fn g() {} StructureNode { parent: None, label: "mcexp", - navigation_range: 387..392, - node_range: 358..409, + navigation_range: 417..422, + node_range: 388..439, kind: SymbolKind( Macro, ), @@ -515,8 +540,8 @@ fn g() {} StructureNode { parent: None, label: "obsolete", - navigation_range: 428..436, - node_range: 411..441, + navigation_range: 458..466, + node_range: 441..471, kind: SymbolKind( Function, ), @@ -528,8 +553,8 @@ fn g() {} StructureNode { parent: None, label: "very_obsolete", - navigation_range: 481..494, - node_range: 443..499, + navigation_range: 511..524, + node_range: 473..529, kind: SymbolKind( Function, ), @@ -541,8 +566,8 @@ fn g() {} StructureNode { parent: None, label: "Some region name", - navigation_range: 501..528, - node_range: 501..528, + navigation_range: 531..558, + node_range: 531..558, kind: Region, detail: None, deprecated: false, @@ -550,8 +575,8 @@ fn g() {} StructureNode { parent: None, label: "m", - navigation_range: 568..569, - node_range: 543..606, + navigation_range: 598..599, + node_range: 573..636, kind: SymbolKind( Module, ), @@ -560,22 +585,22 @@ fn g() {} }, StructureNode { parent: Some( - 20, + 22, ), label: "dontpanic", - navigation_range: 543..563, - node_range: 543..563, + navigation_range: 573..593, + node_range: 573..593, kind: Region, detail: None, deprecated: false, }, StructureNode { parent: Some( - 20, + 22, ), label: "f", - navigation_range: 575..576, - node_range: 572..581, + navigation_range: 605..606, + node_range: 602..611, kind: SymbolKind( Function, ), @@ -586,11 +611,11 @@ fn g() {} }, StructureNode { parent: Some( - 20, + 22, ), label: "g", - navigation_range: 598..599, - node_range: 582..604, + navigation_range: 628..629, + node_range: 612..634, kind: SymbolKind( Function, ), diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index 93019527f44..cf0819a2524 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -764,6 +764,13 @@ trait Foo$0 { } "#, ); + check( + r#" +trait Foo$0 = ; + //^^^ +"#, + ); + check( r#" mod bar$0 { } @@ -1065,6 +1072,23 @@ fn f() -> impl Sub {} ); } + #[test] + fn goto_def_for_module_declaration_in_path_if_types_and_values_same_name() { + check( + r#" +mod bar { + pub struct Foo {} + //^^^ + pub fn Foo() {} +} + +fn baz() { + let _foo_enum: bar::Foo$0 = bar::Foo {}; +} + "#, + ) + } + #[test] fn unknown_assoc_ty() { check_unresolved( @@ -1406,7 +1430,6 @@ include!("included.rs$0"); ); } - #[cfg(test)] mod goto_impl_of_trait_fn { use super::check; #[test] diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs index c889eb930f3..d88ffd25c40 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -14,7 +14,7 @@ use syntax::{ SyntaxNode, SyntaxToken, TextRange, T, }; -use crate::{references, NavigationTarget, TryToNav}; +use crate::{navigation_target::ToNav, references, NavigationTarget, TryToNav}; #[derive(PartialEq, Eq, Hash)] pub struct HighlightedRange { @@ -98,32 +98,39 @@ fn highlight_references( category: access, }); let mut res = FxHashSet::default(); - - let mut def_to_hl_range = |def| { - let hl_range = match def { - Definition::Module(module) => { - Some(NavigationTarget::from_module_to_decl(sema.db, module)) - } - def => def.try_to_nav(sema.db), - } - .filter(|decl| decl.file_id == file_id) - .and_then(|decl| decl.focus_range) - .map(|range| { - let category = - references::decl_mutability(&def, node, range).then_some(ReferenceCategory::Write); - HighlightedRange { range, category } - }); - if let Some(hl_range) = hl_range { - res.insert(hl_range); - } - }; for &def in &defs { match def { - Definition::Local(local) => local - .associated_locals(sema.db) - .iter() - .for_each(|&local| def_to_hl_range(Definition::Local(local))), - def => def_to_hl_range(def), + Definition::Local(local) => { + let category = local.is_mut(sema.db).then_some(ReferenceCategory::Write); + local + .sources(sema.db) + .into_iter() + .map(|x| x.to_nav(sema.db)) + .filter(|decl| decl.file_id == file_id) + .filter_map(|decl| decl.focus_range) + .map(|range| HighlightedRange { range, category }) + .for_each(|x| { + res.insert(x); + }); + } + def => { + let hl_range = match def { + Definition::Module(module) => { + Some(NavigationTarget::from_module_to_decl(sema.db, module)) + } + def => def.try_to_nav(sema.db), + } + .filter(|decl| decl.file_id == file_id) + .and_then(|decl| decl.focus_range) + .map(|range| { + let category = references::decl_mutability(&def, node, range) + .then_some(ReferenceCategory::Write); + HighlightedRange { range, category } + }); + if let Some(hl_range) = hl_range { + res.insert(hl_range); + } + } } } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 5f2c61f5b5f..64b2221bdea 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -30,6 +30,7 @@ pub struct HoverConfig { pub documentation: bool, pub keywords: bool, pub format: HoverDocFormat, + pub interpret_tests: bool, } #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 22611cfb892..da725ce502b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -3,7 +3,8 @@ use std::fmt::Display; use either::Either; use hir::{ - Adt, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo, + db::DefDatabase, Adt, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, + MirEvalError, Semantics, TypeInfo, }; use ide_db::{ base_db::SourceDatabase, @@ -402,7 +403,20 @@ pub(super) fn definition( )) }), Definition::Module(it) => label_and_docs(db, it), - Definition::Function(it) => label_and_docs(db, it), + Definition::Function(it) => label_and_layout_info_and_docs(db, it, |_| { + if !config.interpret_tests { + return None; + } + match it.eval(db) { + Ok(()) => Some("pass".into()), + Err(MirEvalError::Panic) => Some("fail".into()), + Err(MirEvalError::MirLowerError(f, e)) => { + let name = &db.function_data(f).name; + Some(format!("error: fail to lower {name} due {e:?}")) + } + Err(e) => Some(format!("error: {e:?}")), + } + }), Definition::Adt(it) => label_and_layout_info_and_docs(db, it, |&it| { let layout = it.layout(db).ok()?; Some(format!("size = {}, align = {}", layout.size.bytes(), layout.align.abi.bytes())) @@ -410,7 +424,7 @@ pub(super) fn definition( Definition::Variant(it) => label_value_and_docs(db, it, |&it| { if !it.parent_enum(db).is_data_carrying(db) { match it.eval(db) { - Ok(x) => Some(format!("{x}")), + Ok(x) => Some(if x >= 10 { format!("{x} ({x:#X})") } else { format!("{x}") }), Err(_) => it.value(db).map(|x| format!("{x:?}")), } } else { @@ -418,9 +432,9 @@ pub(super) fn definition( } }), Definition::Const(it) => label_value_and_docs(db, it, |it| { - let body = it.eval(db); + let body = it.render_eval(db); match body { - Ok(x) => Some(format!("{x}")), + Ok(x) => Some(x), Err(_) => { let source = it.source(db)?; let mut body = source.value.body()?.syntax().clone(); @@ -440,6 +454,7 @@ pub(super) fn definition( Some(body.to_string()) }), Definition::Trait(it) => label_and_docs(db, it), + Definition::TraitAlias(it) => label_and_docs(db, it), Definition::TypeAlias(it) => label_and_docs(db, it), Definition::BuiltinType(it) => { return famous_defs @@ -620,8 +635,8 @@ fn local(db: &RootDatabase, it: hir::Local) -> Option { let ty = it.ty(db); let ty = ty.display_truncated(db, None); let is_mut = if it.is_mut(db) { "mut " } else { "" }; - let desc = match it.source(db).value { - Either::Left(ident) => { + let desc = match it.primary_source(db).into_ident_pat() { + Some(ident) => { let name = it.name(db); let let_kw = if ident .syntax() @@ -634,7 +649,7 @@ fn local(db: &RootDatabase, it: hir::Local) -> Option { }; format!("{let_kw}{is_mut}{name}: {ty}") } - Either::Right(_) => format!("{is_mut}self: {ty}"), + None => format!("{is_mut}self: {ty}"), }; markup(None, desc, None) } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index bd7ce2f1d0d..57bf0f9ad5f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -4,16 +4,19 @@ use syntax::TextRange; use crate::{fixture, HoverConfig, HoverDocFormat}; +const HOVER_BASE_CONFIG: HoverConfig = HoverConfig { + links_in_hover: false, + documentation: true, + format: HoverDocFormat::Markdown, + keywords: true, + interpret_tests: false, +}; + fn check_hover_no_result(ra_fixture: &str) { let (analysis, position) = fixture::position(ra_fixture); let hover = analysis .hover( - &HoverConfig { - links_in_hover: true, - documentation: true, - keywords: true, - format: HoverDocFormat::Markdown, - }, + &HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, ) .unwrap(); @@ -25,12 +28,7 @@ fn check(ra_fixture: &str, expect: Expect) { let (analysis, position) = fixture::position(ra_fixture); let hover = analysis .hover( - &HoverConfig { - links_in_hover: true, - documentation: true, - keywords: true, - format: HoverDocFormat::Markdown, - }, + &HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, ) .unwrap() @@ -47,12 +45,7 @@ fn check_hover_no_links(ra_fixture: &str, expect: Expect) { let (analysis, position) = fixture::position(ra_fixture); let hover = analysis .hover( - &HoverConfig { - links_in_hover: false, - documentation: true, - keywords: true, - format: HoverDocFormat::Markdown, - }, + &HOVER_BASE_CONFIG, FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, ) .unwrap() @@ -71,9 +64,8 @@ fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) { .hover( &HoverConfig { links_in_hover: true, - documentation: true, - keywords: true, format: HoverDocFormat::PlainText, + ..HOVER_BASE_CONFIG }, FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, ) @@ -91,12 +83,7 @@ fn check_actions(ra_fixture: &str, expect: Expect) { let (analysis, file_id, position) = fixture::range_or_position(ra_fixture); let hover = analysis .hover( - &HoverConfig { - links_in_hover: true, - documentation: true, - keywords: true, - format: HoverDocFormat::Markdown, - }, + &HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, FileRange { file_id, range: position.range_or_empty() }, ) .unwrap() @@ -106,34 +93,13 @@ fn check_actions(ra_fixture: &str, expect: Expect) { fn check_hover_range(ra_fixture: &str, expect: Expect) { let (analysis, range) = fixture::range(ra_fixture); - let hover = analysis - .hover( - &HoverConfig { - links_in_hover: false, - documentation: true, - keywords: true, - format: HoverDocFormat::Markdown, - }, - range, - ) - .unwrap() - .unwrap(); + let hover = analysis.hover(&HOVER_BASE_CONFIG, range).unwrap().unwrap(); expect.assert_eq(hover.info.markup.as_str()) } fn check_hover_range_no_results(ra_fixture: &str) { let (analysis, range) = fixture::range(ra_fixture); - let hover = analysis - .hover( - &HoverConfig { - links_in_hover: false, - documentation: true, - keywords: true, - format: HoverDocFormat::Markdown, - }, - range, - ) - .unwrap(); + let hover = analysis.hover(&HOVER_BASE_CONFIG, range).unwrap(); assert!(hover.is_none()); } @@ -490,7 +456,6 @@ fn hover_field_offset() { // Hovering over the field when instantiating check( r#" -//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128 struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 } "#, expect![[r#" @@ -512,7 +477,6 @@ fn hover_shows_struct_field_info() { // Hovering over the field when instantiating check( r#" -//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128 struct Foo { field_a: u32 } fn main() { @@ -535,7 +499,6 @@ fn main() { // Hovering over the field in the definition check( r#" -//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128 struct Foo { field_a$0: u32 } fn main() { @@ -610,6 +573,27 @@ const foo$0: u32 = { ); } +#[test] +fn hover_eval_complex_constants() { + check( + r#" + struct X { f1: (), f2: i32 } + const foo$0: (i8, X, i64) = (1, X { f2: 5 - 1, f1: () }, 1 - 2); + "#, + expect![[r#" + *foo* + + ```rust + test + ``` + + ```rust + const foo: (i8, X, i64) = (1, X { f1: (), f2: 4 }, -1) + ``` + "#]], + ); +} + #[test] fn hover_default_generic_types() { check( @@ -1467,8 +1451,6 @@ fn my() {} fn test_hover_struct_doc_comment() { check( r#" -//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128 - /// This is an example /// multiline doc /// @@ -1527,7 +1509,7 @@ fn foo() { let bar = Ba$0r; } ``` ```rust - struct Bar + struct Bar // size = 0, align = 1 ``` --- @@ -1556,7 +1538,7 @@ fn foo() { let bar = Ba$0r; } ``` ```rust - struct Bar + struct Bar // size = 0, align = 1 ``` --- @@ -1584,7 +1566,7 @@ pub struct B$0ar ``` ```rust - pub struct Bar + pub struct Bar // size = 0, align = 1 ``` --- @@ -1611,7 +1593,7 @@ pub struct B$0ar ``` ```rust - pub struct Bar + pub struct Bar // size = 0, align = 1 ``` --- @@ -2913,8 +2895,6 @@ fn main() { let foo_test = name_with_dashes::wrapper::Thing::new$0(); } fn hover_field_pat_shorthand_ref_match_ergonomics() { check( r#" -//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128 - struct S { f: i32, } @@ -3506,8 +3486,8 @@ impl Foo {} } #[test] -fn hover_const_eval_variant() { - // show hex for <10 +fn hover_const_eval_discriminant() { + // Don't show hex for <10 check( r#" #[repr(u8)] @@ -3532,7 +3512,7 @@ enum E { This is a doc "#]], ); - // show hex for >10 + // Show hex for >10 check( r#" #[repr(u8)] @@ -3656,7 +3636,7 @@ trait T { } impl T for i32 { const AA: A = A { - i: 2 + i: 2 + 3 } } fn main() { @@ -3671,9 +3651,7 @@ fn main() { ``` ```rust - const AA: A = A { - i: 2 - } + const AA: A = A { i: 5 } ``` "#]], ); @@ -3792,7 +3770,6 @@ const FOO$0: usize = 1 << 3; This is a doc "#]], ); - // show hex for >10 check( r#" /// This is a doc @@ -3850,7 +3827,7 @@ const FOO$0: i32 = 2 - 3; ``` ```rust - const FOO: i32 = -1 + const FOO: i32 = -1 (0xFFFFFFFF) ``` --- @@ -4011,6 +3988,28 @@ const FOO$0: f32 = 1f32; This is a doc "#]], ); + // Don't show `` in const hover + check( + r#" +/// This is a doc +const FOO$0: &i32 = &2; +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: &i32 = &2 + ``` + + --- + + This is a doc + "#]], + ); //show f64 typecasted from float check( r#" @@ -4354,8 +4353,6 @@ fn main() { fn hover_intra_doc_links() { check( r#" -//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128 - pub mod theitem { /// This is the item. Cool! pub struct TheItem; @@ -4496,7 +4493,7 @@ trait A where fn string_shadowed_with_inner_items() { check( r#" -//- /main.rs crate:main deps:alloc target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128 +//- /main.rs crate:main deps:alloc /// Custom `String` type. struct String; @@ -5191,7 +5188,7 @@ foo_macro!( ``` ```rust - pub struct Foo + pub struct Foo // size = 0, align = 1 ``` --- @@ -5205,8 +5202,6 @@ foo_macro!( fn hover_intra_in_attr() { check( r#" -//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128 - #[doc = "Doc comment for [`Foo$0`]"] pub struct Foo(i32); "#, @@ -5295,7 +5290,7 @@ pub struct Type; ``` ```rust - const KONST: dep::Type = $crate::Type + const KONST: dep::Type = Type ``` "#]], ); @@ -5327,8 +5322,6 @@ enum Enum { fn hover_record_variant_field() { check( r#" -//- /main.rs target_data_layout:e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128 - enum Enum { RecordV { field$0: u32 } } @@ -5647,3 +5640,204 @@ fn main() { "#]], ); } + +#[test] +fn assoc_fn_in_block_local_impl() { + check( + r#" +struct S; +mod m { + const _: () = { + impl crate::S { + pub(crate) fn foo() {} + } + }; +} +fn test() { + S::foo$0(); +} +"#, + expect![[r#" + *foo* + + ```rust + test::S + ``` + + ```rust + pub(crate) fn foo() + ``` + "#]], + ); + + check( + r#" +struct S; +mod m { + const _: () = { + const _: () = { + impl crate::S { + pub(crate) fn foo() {} + } + }; + }; +} +fn test() { + S::foo$0(); +} +"#, + expect![[r#" + *foo* + + ```rust + test::S + ``` + + ```rust + pub(crate) fn foo() + ``` + "#]], + ); + + check( + r#" +struct S; +mod m { + mod inner { + const _: () = { + impl crate::S { + pub(super) fn foo() {} + } + }; + } + + fn test() { + crate::S::foo$0(); + } +} +"#, + expect![[r#" + *foo* + + ```rust + test::S + ``` + + ```rust + pub(super) fn foo() + ``` + "#]], + ); +} + +#[test] +fn assoc_const_in_block_local_impl() { + check( + r#" +struct S; +mod m { + const _: () = { + impl crate::S { + pub(crate) const A: () = (); + } + }; +} +fn test() { + S::A$0; +} +"#, + expect![[r#" + *A* + + ```rust + test + ``` + + ```rust + pub(crate) const A: () = () + ``` + "#]], + ); + + check( + r#" +struct S; +mod m { + const _: () = { + const _: () = { + impl crate::S { + pub(crate) const A: () = (); + } + }; + }; +} +fn test() { + S::A$0; +} +"#, + expect![[r#" + *A* + + ```rust + test + ``` + + ```rust + pub(crate) const A: () = () + ``` + "#]], + ); + + check( + r#" +struct S; +mod m { + mod inner { + const _: () = { + impl crate::S { + pub(super) const A: () = (); + } + }; + } + + fn test() { + crate::S::A$0; + } +} +"#, + expect![[r#" + *A* + + ```rust + test + ``` + + ```rust + pub(super) const A: () = () + ``` + "#]], + ); +} + +#[test] +fn field_as_method_call_fallback() { + check( + r#" +struct S { f: u32 } +fn test() { + S { f: 0 }.f$0(); +} +"#, + expect![[r#" + *f* + + ```rust + test::S + ``` + + ```rust + f: u32 // size = 4, align = 4, offset = 0 + ``` + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs index 188eb7f977b..729780fa0c9 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs @@ -606,14 +606,13 @@ fn a() { } #[test] - fn bug() { + fn let_stmt_explicit_ty() { check_with_config( InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG }, r#" fn main() { - // These should be identical, but they are not... - let () = return; + //^^^^^^ let (): () = return; //^^^^^^ } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index 4af7f9bdb73..6a50927333d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -176,15 +176,12 @@ fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &hir mod tests { // This module also contains tests for super::closure_ret - use expect_test::expect; use syntax::{TextRange, TextSize}; use test_utils::extract_annotations; use crate::{fixture, inlay_hints::InlayHintsConfig}; - use crate::inlay_hints::tests::{ - check, check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG, - }; + use crate::inlay_hints::tests::{check, check_with_config, DISABLED_CONFIG, TEST_CONFIG}; use crate::ClosureReturnTypeHints; #[track_caller] @@ -278,8 +275,7 @@ fn main() { #[test] fn iterator_hint_regression_issue_12674() { // Ensure we don't crash while solving the projection type of iterators. - check_expect( - InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG }, + let (analysis, file_id) = fixture::file( r#" //- minicore: iterators struct S(T); @@ -302,107 +298,18 @@ impl<'a, T> Iterator for SliceIter<'a, T> { fn main(a: SliceIter<'_, Container>) { a - .filter_map(|c| Some(c.elements.iter().filter_map(|v| Some(v)))) - .map(|e| e); + .filter_map(|c| Some(c.elements.iter().filter_map(|v| Some(v)))) + .map(|e| e); } - "#, - expect![[r#" - [ - InlayHint { - range: 484..554, - kind: Chaining, - label: [ - "impl ", - InlayHintLabelPart { - text: "Iterator", - linked_location: Some( - FileRange { - file_id: FileId( - 1, - ), - range: 2611..2619, - }, - ), - tooltip: "", - }, - "<", - InlayHintLabelPart { - text: "Item", - linked_location: Some( - FileRange { - file_id: FileId( - 1, - ), - range: 2643..2647, - }, - ), - tooltip: "", - }, - " = impl ", - InlayHintLabelPart { - text: "Iterator", - linked_location: Some( - FileRange { - file_id: FileId( - 1, - ), - range: 2611..2619, - }, - ), - tooltip: "", - }, - "<", - InlayHintLabelPart { - text: "Item", - linked_location: Some( - FileRange { - file_id: FileId( - 1, - ), - range: 2643..2647, - }, - ), - tooltip: "", - }, - " = &&str>>", - ], - }, - InlayHint { - range: 484..485, - kind: Chaining, - label: [ - "", - InlayHintLabelPart { - text: "SliceIter", - linked_location: Some( - FileRange { - file_id: FileId( - 0, - ), - range: 289..298, - }, - ), - tooltip: "", - }, - "<", - InlayHintLabelPart { - text: "Container", - linked_location: Some( - FileRange { - file_id: FileId( - 0, - ), - range: 238..247, - }, - ), - tooltip: "", - }, - ">", - ], - }, - ] - "#]], +"#, ); + analysis + .inlay_hints( + &InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG }, + file_id, + None, + ) + .unwrap(); } #[test] diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs index 0c54f084c19..0a7513e465a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs @@ -435,7 +435,7 @@ fn main() { file_id: FileId( 1, ), - range: 2611..2619, + range: 3386..3394, }, ), tooltip: "", @@ -448,7 +448,7 @@ fn main() { file_id: FileId( 1, ), - range: 2643..2647, + range: 3418..3422, }, ), tooltip: "", @@ -468,7 +468,7 @@ fn main() { file_id: FileId( 1, ), - range: 2611..2619, + range: 3386..3394, }, ), tooltip: "", @@ -481,7 +481,7 @@ fn main() { file_id: FileId( 1, ), - range: 2643..2647, + range: 3418..3422, }, ), tooltip: "", @@ -501,7 +501,7 @@ fn main() { file_id: FileId( 1, ), - range: 2611..2619, + range: 3386..3394, }, ), tooltip: "", @@ -514,7 +514,7 @@ fn main() { file_id: FileId( 1, ), - range: 2643..2647, + range: 3418..3422, }, ), tooltip: "", diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs index 5dd51ad11f4..67eaa553ada 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs @@ -59,8 +59,14 @@ fn variant_hints( }, kind: InlayKind::Discriminant, label: InlayHintLabel::simple( - match &d { - Ok(v) => format!("{}", v), + match d { + Ok(x) => { + if x >= 10 { + format!("{x} ({x:#X})") + } else { + format!("{x}") + } + } Err(_) => "?".into(), }, Some(InlayTooltip::String(match &d { diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index f2b535bdc7e..078b66dd395 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -55,6 +55,7 @@ mod syntax_tree; mod typing; mod view_crate_graph; mod view_hir; +mod view_mir; mod view_item_tree; mod shuffle_crate_graph; @@ -308,6 +309,10 @@ impl Analysis { self.with_db(|db| view_hir::view_hir(db, position)) } + pub fn view_mir(&self, position: FilePosition) -> Cancellable { + self.with_db(|db| view_mir::view_mir(db, position)) + } + pub fn view_item_tree(&self, file_id: FileId) -> Cancellable { self.with_db(|db| view_item_tree::view_item_tree(db, file_id)) } diff --git a/src/tools/rust-analyzer/crates/ide/src/markup.rs b/src/tools/rust-analyzer/crates/ide/src/markup.rs index de9fef61a78..411eb695fbf 100644 --- a/src/tools/rust-analyzer/crates/ide/src/markup.rs +++ b/src/tools/rust-analyzer/crates/ide/src/markup.rs @@ -32,7 +32,7 @@ impl Markup { pub fn as_str(&self) -> &str { self.text.as_str() } - pub fn fenced_block(contents: &impl fmt::Display) -> Markup { + pub fn fenced_block(contents: impl fmt::Display) -> Markup { format!("```rust\n{contents}\n```").into() } } diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs index af5e96d2381..349e79ecfdd 100644 --- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs +++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs @@ -208,6 +208,9 @@ pub(crate) fn def_to_moniker( Definition::Trait(trait_) => { MonikerDescriptor { name: trait_.name(db), desc: MonikerDescriptorKind::Type } } + Definition::TraitAlias(ta) => { + MonikerDescriptor { name: ta.name(db), desc: MonikerDescriptorKind::Type } + } Definition::TypeAlias(ta) => { MonikerDescriptor { name: ta.name(db), desc: MonikerDescriptorKind::TypeParameter } } diff --git a/src/tools/rust-analyzer/crates/ide/src/move_item.rs b/src/tools/rust-analyzer/crates/ide/src/move_item.rs index ffc4bdd7da3..b955ea99f0c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/move_item.rs +++ b/src/tools/rust-analyzer/crates/ide/src/move_item.rs @@ -73,6 +73,7 @@ fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) - SyntaxKind::MACRO_CALL, SyntaxKind::TYPE_ALIAS, SyntaxKind::TRAIT, + SyntaxKind::TRAIT_ALIAS, SyntaxKind::IMPL, SyntaxKind::MACRO_DEF, SyntaxKind::STRUCT, diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index 3aa799d43a8..6aae82f9816 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -5,7 +5,7 @@ use std::fmt; use either::Either; use hir::{ symbols::FileSymbol, AssocItem, Documentation, FieldSource, HasAttrs, HasSource, HirDisplay, - InFile, ModuleSource, Semantics, + InFile, LocalSource, ModuleSource, Semantics, }; use ide_db::{ base_db::{FileId, FileRange}, @@ -192,6 +192,7 @@ impl TryToNav for Definition { Definition::Const(it) => it.try_to_nav(db), Definition::Static(it) => it.try_to_nav(db), Definition::Trait(it) => it.try_to_nav(db), + Definition::TraitAlias(it) => it.try_to_nav(db), Definition::TypeAlias(it) => it.try_to_nav(db), Definition::BuiltinType(_) => None, Definition::ToolModule(_) => None, @@ -212,6 +213,7 @@ impl TryToNav for hir::ModuleDef { hir::ModuleDef::Const(it) => it.try_to_nav(db), hir::ModuleDef::Static(it) => it.try_to_nav(db), hir::ModuleDef::Trait(it) => it.try_to_nav(db), + hir::ModuleDef::TraitAlias(it) => it.try_to_nav(db), hir::ModuleDef::TypeAlias(it) => it.try_to_nav(db), hir::ModuleDef::Macro(it) => it.try_to_nav(db), hir::ModuleDef::BuiltinType(_) => None, @@ -249,6 +251,9 @@ impl ToNavFromAst for hir::TypeAlias { impl ToNavFromAst for hir::Trait { const KIND: SymbolKind = SymbolKind::Trait; } +impl ToNavFromAst for hir::TraitAlias { + const KIND: SymbolKind = SymbolKind::TraitAlias; +} impl TryToNav for D where @@ -382,9 +387,11 @@ impl TryToNav for hir::GenericParam { } } -impl ToNav for hir::Local { +impl ToNav for LocalSource { fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { - let InFile { file_id, value } = self.source(db); + let InFile { file_id, value } = &self.source; + let file_id = *file_id; + let local = self.local; let (node, name) = match &value { Either::Left(bind_pat) => (bind_pat.syntax(), bind_pat.name()), Either::Right(it) => (it.syntax(), it.name()), @@ -393,10 +400,10 @@ impl ToNav for hir::Local { let FileRange { file_id, range: full_range } = InFile::new(file_id, node).original_file_range(db); - let name = self.name(db).to_smol_str(); - let kind = if self.is_self(db) { + let name = local.name(db).to_smol_str(); + let kind = if local.is_self(db) { SymbolKind::SelfParam - } else if self.is_param(db) { + } else if local.is_param(db) { SymbolKind::ValueParam } else { SymbolKind::Local @@ -414,6 +421,12 @@ impl ToNav for hir::Local { } } +impl ToNav for hir::Local { + fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { + self.primary_source(db).to_nav(db) + } +} + impl ToNav for hir::Label { fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { let InFile { file_id, value } = self.source(db); @@ -544,6 +557,7 @@ pub(crate) fn description_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> ast::Struct(it) => sema.to_def(&it).map(|it| it.display(db).to_string()), ast::Enum(it) => sema.to_def(&it).map(|it| it.display(db).to_string()), ast::Trait(it) => sema.to_def(&it).map(|it| it.display(db).to_string()), + ast::TraitAlias(it) => sema.to_def(&it).map(|it| it.display(db).to_string()), ast::Module(it) => sema.to_def(&it).map(|it| it.display(db).to_string()), ast::TypeAlias(it) => sema.to_def(&it).map(|it| it.display(db).to_string()), ast::Const(it) => sema.to_def(&it).map(|it| it.display(db).to_string()), diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index cabbc287279..3684c1033f3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -1355,6 +1355,38 @@ impl Foo { ); } + #[test] + fn test_trait_alias() { + check( + r#" +trait Foo {} +trait Bar$0 = Foo where Self: ; +fn foo(_: impl Bar, _: &dyn Bar) {} +"#, + expect![[r#" + Bar TraitAlias FileId(0) 13..42 19..22 + + FileId(0) 53..56 + FileId(0) 66..69 + FileId(0) 79..82 + "#]], + ); + } + + #[test] + fn test_trait_alias_self() { + check( + r#" +trait Foo = where Self$0: ; +"#, + expect![[r#" + Self TypeParam FileId(0) 6..9 6..9 + + FileId(0) 18..22 + "#]], + ); + } + #[test] fn test_attr_differs_from_fn_with_same_name() { check( diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index c0237e1edd0..e10c4638102 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -353,6 +353,11 @@ mod tests { fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) { let ra_fixture_after = &trim_indent(ra_fixture_after); let (analysis, position) = fixture::position(ra_fixture_before); + if !ra_fixture_after.starts_with("error: ") { + if let Err(err) = analysis.prepare_rename(position).unwrap() { + panic!("Prepare rename to '{new_name}' was failed: {err}") + } + } let rename_result = analysis .rename(position, new_name) .unwrap_or_else(|err| panic!("Rename to '{new_name}' was cancelled: {err}")); @@ -1709,6 +1714,23 @@ fn foo(bar: i32) -> Foo { ); } + #[test] + fn test_rename_local_simple() { + check( + "i", + r#" +fn foo(bar$0: i32) -> i32 { + bar +} +"#, + r#" +fn foo(i: i32) -> i32 { + i +} +"#, + ); + } + #[test] fn test_rename_local_put_init_shorthand() { cov_mark::check!(test_rename_local_put_init_shorthand); diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index 5b35262aabe..8a8a9151c42 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -2,7 +2,7 @@ use std::fmt; use ast::HasName; use cfg::CfgExpr; -use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics}; +use hir::{AsAssocItem, HasAttrs, HasSource, Semantics}; use ide_assists::utils::test_related_attribute; use ide_db::{ base_db::{FilePosition, FileRange}, @@ -195,14 +195,13 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec { // // Provides a sneak peek of all tests where the current item is used. // -// The simplest way to use this feature is via the context menu: -// - Right-click on the selected item. The context menu opens. -// - Select **Peek related tests** +// The simplest way to use this feature is via the context menu. Right-click on +// the selected item. The context menu opens. Select **Peek Related Tests**. // // |=== // | Editor | Action Name // -// | VS Code | **rust-analyzer: Peek related tests** +// | VS Code | **rust-analyzer: Peek Related Tests** // |=== pub(crate) fn related_tests( db: &RootDatabase, @@ -371,9 +370,9 @@ pub(crate) fn runnable_impl( let nav = def.try_to_nav(sema.db)?; let ty = def.self_ty(sema.db); let adt_name = ty.as_adt()?.name(sema.db); - let mut ty_args = ty.type_arguments().peekable(); + let mut ty_args = ty.generic_parameters(sema.db).peekable(); let params = if ty_args.peek().is_some() { - format!("<{}>", ty_args.format_with(",", |ty, cb| cb(&ty.display(sema.db)))) + format!("<{}>", ty_args.format_with(",", |ty, cb| cb(&ty))) } else { String::new() }; @@ -417,6 +416,7 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option { Definition::Const(it) => it.attrs(db), Definition::Static(it) => it.attrs(db), Definition::Trait(it) => it.attrs(db), + Definition::TraitAlias(it) => it.attrs(db), Definition::TypeAlias(it) => it.attrs(db), Definition::Macro(it) => it.attrs(db), Definition::SelfType(it) => it.attrs(db), @@ -437,14 +437,10 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option { let ty = imp.self_ty(db); if let Some(adt) = ty.as_adt() { let name = adt.name(db); - let mut ty_args = ty.type_arguments().peekable(); + let mut ty_args = ty.generic_parameters(db).peekable(); format_to!(path, "{}", name); if ty_args.peek().is_some() { - format_to!( - path, - "<{}>", - ty_args.format_with(",", |ty, cb| cb(&ty.display(db))) - ); + format_to!(path, "<{}>", ty_args.format_with(",", |ty, cb| cb(&ty))); } format_to!(path, "::{}", def_name); path.retain(|c| c != ' '); @@ -1000,6 +996,221 @@ impl Data { ); } + #[test] + fn test_runnables_doc_test_in_impl_with_lifetime() { + check( + r#" +//- /lib.rs +$0 +fn main() {} + +struct Data<'a>; +impl Data<'a> { + /// ``` + /// let x = 5; + /// ``` + fn foo() {} +} +"#, + &[Bin, DocTest], + expect![[r#" + [ + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 1..13, + focus_range: 4..8, + name: "main", + kind: Function, + }, + kind: Bin, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 52..106, + name: "foo", + }, + kind: DocTest { + test_id: Path( + "Data<'a>::foo", + ), + }, + cfg: None, + }, + ] + "#]], + ); + } + + #[test] + fn test_runnables_doc_test_in_impl_with_lifetime_and_types() { + check( + r#" +//- /lib.rs +$0 +fn main() {} + +struct Data<'a, T, U>; +impl Data<'a, T, U> { + /// ``` + /// let x = 5; + /// ``` + fn foo() {} +} +"#, + &[Bin, DocTest], + expect![[r#" + [ + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 1..13, + focus_range: 4..8, + name: "main", + kind: Function, + }, + kind: Bin, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 70..124, + name: "foo", + }, + kind: DocTest { + test_id: Path( + "Data<'a,T,U>::foo", + ), + }, + cfg: None, + }, + ] + "#]], + ); + } + + #[test] + fn test_runnables_doc_test_in_impl_with_const() { + check( + r#" +//- /lib.rs +$0 +fn main() {} + +struct Data; +impl Data { + /// ``` + /// let x = 5; + /// ``` + fn foo() {} +} +"#, + &[Bin, DocTest], + expect![[r#" + [ + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 1..13, + focus_range: 4..8, + name: "main", + kind: Function, + }, + kind: Bin, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 79..133, + name: "foo", + }, + kind: DocTest { + test_id: Path( + "Data::foo", + ), + }, + cfg: None, + }, + ] + "#]], + ); + } + + #[test] + fn test_runnables_doc_test_in_impl_with_lifetime_types_and_const() { + check( + r#" +//- /lib.rs +$0 +fn main() {} + +struct Data<'a, T, const N: usize>; +impl<'a, T, const N: usize> Data<'a, T, N> { + /// ``` + /// let x = 5; + /// ``` + fn foo() {} +} +"#, + &[Bin, DocTest], + expect![[r#" + [ + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 1..13, + focus_range: 4..8, + name: "main", + kind: Function, + }, + kind: Bin, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 100..154, + name: "foo", + }, + kind: DocTest { + test_id: Path( + "Data<'a,T,N>::foo", + ), + }, + cfg: None, + }, + ] + "#]], + ); + } #[test] fn test_runnables_module() { check( @@ -2062,6 +2273,59 @@ mod tests { ); } + #[test] + fn test_runnables_doc_test_in_impl_with_lifetime_type_const_value() { + check( + r#" +//- /lib.rs +$0 +fn main() {} + +struct Data<'a, A, const B: usize, C, const D: u32>; +impl Data<'a, A, 12, C, D> { + /// ``` + /// ``` + fn foo() {} +} +"#, + &[Bin, DocTest], + expect![[r#" + [ + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 1..13, + focus_range: 4..8, + name: "main", + kind: Function, + }, + kind: Bin, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 121..156, + name: "foo", + }, + kind: DocTest { + test_id: Path( + "Data<'a,A,12,C,D>::foo", + ), + }, + cfg: None, + }, + ] + "#]], + ); + } + #[test] fn doc_test_type_params() { check( diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index f70ca55a508..2c08c457b33 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -172,7 +172,7 @@ fn signature_help_for_call( res.signature.push('('); { - if let Some(self_param) = callable.receiver_param(db) { + if let Some((self_param, _)) = callable.receiver_param(db) { format_to!(res.signature, "{}", self_param) } let mut buf = String::new(); @@ -252,6 +252,10 @@ fn signature_help_for_generics( res.doc = it.docs(db).map(|it| it.into()); format_to!(res.signature, "trait {}", it.name(db)); } + hir::GenericDef::TraitAlias(it) => { + res.doc = it.docs(db).map(|it| it.into()); + format_to!(res.signature, "trait {}", it.name(db)); + } hir::GenericDef::TypeAlias(it) => { res.doc = it.docs(db).map(|it| it.into()); format_to!(res.signature, "type {}", it.name(db)); diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index 3f7f6885f61..c97691b14a5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -139,6 +139,7 @@ impl StaticIndex<'_> { documentation: true, keywords: true, format: crate::HoverDocFormat::Markdown, + interpret_tests: false, }; let tokens = tokens.filter(|token| { matches!( diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs index 892e6a9bb0a..2111baad74d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs @@ -217,7 +217,9 @@ fn highlight_name_ref( // to anything when used. // We can fix this for derive attributes since derive helpers are recorded, but not for // general attributes. - None if name_ref.syntax().ancestors().any(|it| it.kind() == ATTR) => { + None if name_ref.syntax().ancestors().any(|it| it.kind() == ATTR) + && !sema.hir_file_for(name_ref.syntax()).is_derive_attr_pseudo_expansion(sema.db) => + { return HlTag::Symbol(SymbolKind::Attribute).into(); } None => return HlTag::UnresolvedReference.into(), @@ -410,6 +412,7 @@ fn highlight_def( h } Definition::Trait(_) => Highlight::new(HlTag::Symbol(SymbolKind::Trait)), + Definition::TraitAlias(_) => Highlight::new(HlTag::Symbol(SymbolKind::TraitAlias)), Definition::TypeAlias(type_) => { let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias)); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index 9139528c7ed..3c4cfc78152 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs @@ -274,6 +274,7 @@ fn module_def_to_hl_tag(def: Definition) -> HlTag { Definition::Const(_) => SymbolKind::Const, Definition::Static(_) => SymbolKind::Static, Definition::Trait(_) => SymbolKind::Trait, + Definition::TraitAlias(_) => SymbolKind::TraitAlias, Definition::TypeAlias(_) => SymbolKind::TypeAlias, Definition::BuiltinType(_) => return HlTag::BuiltinType, Definition::Macro(_) => SymbolKind::Macro, diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs index 3949f1189bd..a81c4ee0cbd 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs @@ -150,6 +150,7 @@ impl HlTag { SymbolKind::Struct => "struct", SymbolKind::ToolModule => "tool_module", SymbolKind::Trait => "trait", + SymbolKind::TraitAlias => "trait_alias", SymbolKind::TypeAlias => "type_alias", SymbolKind::TypeParam => "type_param", SymbolKind::Union => "union", diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html index 1a439881476..567ab8ccc11 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html @@ -53,6 +53,6 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd // This is another normal comment /// This is another doc comment // This is another normal comment -#[derive(Copy)] +#[derive(Copy, Unresolved)] // The reason for these being here is to test AttrIds struct Foo; \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs index fc9b5d3ba4c..ac9bd8e39dc 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs @@ -34,7 +34,7 @@ fn attributes() { // This is another normal comment /// This is another doc comment // This is another normal comment -#[derive(Copy)] +#[derive(Copy, Unresolved)] // The reason for these being here is to test AttrIds struct Foo; "#, diff --git a/src/tools/rust-analyzer/crates/ide/src/view_mir.rs b/src/tools/rust-analyzer/crates/ide/src/view_mir.rs new file mode 100644 index 00000000000..a36aba58bc0 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/view_mir.rs @@ -0,0 +1,29 @@ +use hir::{DefWithBody, Semantics}; +use ide_db::base_db::FilePosition; +use ide_db::RootDatabase; +use syntax::{algo::find_node_at_offset, ast, AstNode}; + +// Feature: View Mir +// +// |=== +// | Editor | Action Name +// +// | VS Code | **rust-analyzer: View Mir** +// |=== +pub(crate) fn view_mir(db: &RootDatabase, position: FilePosition) -> String { + body_mir(db, position).unwrap_or_else(|| "Not inside a function body".to_string()) +} + +fn body_mir(db: &RootDatabase, position: FilePosition) -> Option { + let sema = Semantics::new(db); + let source_file = sema.parse(position.file_id); + + let item = find_node_at_offset::(source_file.syntax(), position.offset)?; + let def: DefWithBody = match item { + ast::Item::Fn(it) => sema.to_def(&it)?.into(), + ast::Item::Const(it) => sema.to_def(&it)?.into(), + ast::Item::Static(it) => sema.to_def(&it)?.into(), + _ => return None, + }; + Some(def.debug_mir(db)) +} diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar.rs b/src/tools/rust-analyzer/crates/parser/src/grammar.rs index 15ec9e167e0..15435a26cea 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar.rs @@ -198,6 +198,10 @@ impl BlockLike { fn is_block(self) -> bool { self == BlockLike::Block } + + fn is_blocklike(kind: SyntaxKind) -> bool { + matches!(kind, BLOCK_EXPR | IF_EXPR | WHILE_EXPR | FOR_EXPR | LOOP_EXPR | MATCH_EXPR) + } } const VISIBILITY_FIRST: TokenSet = TokenSet::new(&[T![pub], T![crate]]); diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs index 4ecaa6e6a85..c13a1943792 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs @@ -43,7 +43,7 @@ pub(super) fn meta(p: &mut Parser<'_>) { match p.current() { T![=] => { p.bump(T![=]); - if !expressions::expr(p) { + if expressions::expr(p).is_none() { p.error("expected expression"); } } diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs index 4b080102a2c..a884d8b6ec8 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs @@ -16,9 +16,9 @@ pub(super) enum Semicolon { const EXPR_FIRST: TokenSet = LHS_FIRST; -pub(super) fn expr(p: &mut Parser<'_>) -> bool { +pub(super) fn expr(p: &mut Parser<'_>) -> Option { let r = Restrictions { forbid_structs: false, prefer_stmt: false }; - expr_bp(p, None, r, 1).is_some() + expr_bp(p, None, r, 1).map(|(m, _)| m) } pub(super) fn expr_stmt( @@ -120,16 +120,27 @@ pub(super) fn stmt(p: &mut Parser<'_>, semicolon: Semicolon) { // fn f() { let x: i32; } types::ascription(p); } + + let mut expr_after_eq: Option = None; if p.eat(T![=]) { // test let_stmt_init // fn f() { let x = 92; } - expressions::expr(p); + expr_after_eq = expressions::expr(p); } if p.at(T![else]) { + // test_err let_else_right_curly_brace + // fn func() { let Some(_) = {Some(1)} else { panic!("h") };} + if let Some(expr) = expr_after_eq { + if BlockLike::is_blocklike(expr.kind()) { + p.error( + "right curly brace `}` before `else` in a `let...else` statement not allowed", + ) + } + } + // test let_else // fn f() { let Some(x) = opt else { return }; } - let m = p.start(); p.bump(T![else]); block_expr(p); @@ -578,7 +589,14 @@ fn arg_list(p: &mut Parser<'_>) { // fn main() { // foo(#[attr] 92) // } - delimited(p, T!['('], T![')'], T![,], EXPR_FIRST.union(ATTRIBUTE_FIRST), expr); + delimited( + p, + T!['('], + T![')'], + T![,], + EXPR_FIRST.union(ATTRIBUTE_FIRST), + |p: &mut Parser<'_>| expr(p).is_some(), + ); m.complete(p, ARG_LIST); } diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs index efc2603835e..d051dd2682f 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs @@ -163,10 +163,8 @@ pub(super) fn atom_expr( return None; } }; - let blocklike = match done.kind() { - IF_EXPR | WHILE_EXPR | FOR_EXPR | LOOP_EXPR | MATCH_EXPR | BLOCK_EXPR => BlockLike::Block, - _ => BlockLike::NotBlock, - }; + let blocklike = + if BlockLike::is_blocklike(done.kind()) { BlockLike::Block } else { BlockLike::NotBlock }; Some((done, blocklike)) } @@ -188,7 +186,7 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker { // test tuple_attrs // const A: (i64, i64) = (1, #[cfg(test)] 2); - if !expr(p) { + if expr(p).is_none() { break; } @@ -221,7 +219,7 @@ fn array_expr(p: &mut Parser<'_>) -> CompletedMarker { // test array_attrs // const A: &[i64] = &[1, #[cfg(test)] 2]; - if !expr(p) { + if expr(p).is_none() { break; } diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs index c982e2d564c..a8a1ccb15e6 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs @@ -20,7 +20,7 @@ pub(super) fn trait_(p: &mut Parser<'_>, m: Marker) { // trait Z = where Self: T; generic_params::opt_where_clause(p); p.expect(T![;]); - m.complete(p, TRAIT); + m.complete(p, TRAIT_ALIAS); return; } diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs index 1064ae9970c..26490aa97e0 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs @@ -77,6 +77,9 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) { // type X = ::Output; // fn foo() { ::default(); } if first && p.eat(T![<]) { + // test_err angled_path_without_qual + // type X = <()>; + // type Y = ; types::type_(p); if p.eat(T![as]) { if is_use_path_start(p) { @@ -86,6 +89,9 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) { } } p.expect(T![>]); + if !p.at(T![::]) { + p.error("expected `::`"); + } } else { let empty = if first { p.eat(T![::]); diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs index 52b3fc23d59..cd87b304a2f 100644 --- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs +++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs @@ -135,6 +135,7 @@ pub enum SyntaxKind { STATIC, CONST, TRAIT, + TRAIT_ALIAS, IMPL, TYPE_ALIAS, MACRO_CALL, diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0049_let_else_right_curly_brace_for.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0049_let_else_right_curly_brace_for.rast new file mode 100644 index 00000000000..026fecf4c9d --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0049_let_else_right_curly_brace_for.rast @@ -0,0 +1,58 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "f" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + LET_STMT + LET_KW "let" + WHITESPACE " " + WILDCARD_PAT + UNDERSCORE "_" + WHITESPACE " " + EQ "=" + WHITESPACE " " + FOR_EXPR + FOR_KW "for" + WHITESPACE " " + WILDCARD_PAT + UNDERSCORE "_" + WHITESPACE " " + IN_KW "in" + WHITESPACE " " + RANGE_EXPR + LITERAL + INT_NUMBER "0" + DOT2 ".." + LITERAL + INT_NUMBER "10" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + R_CURLY "}" + WHITESPACE " " + LET_ELSE + ELSE_KW "else" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + RETURN_EXPR + RETURN_KW "return" + WHITESPACE "\n " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" +error 43: right curly brace `}` before `else` in a `let...else` statement not allowed diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0049_let_else_right_curly_brace_for.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0049_let_else_right_curly_brace_for.rs new file mode 100644 index 00000000000..d410274198d --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0049_let_else_right_curly_brace_for.rs @@ -0,0 +1,6 @@ +fn f() { + let _ = for _ in 0..10 { + } else { + return + }; +} \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0050_let_else_right_curly_brace_loop.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0050_let_else_right_curly_brace_loop.rast new file mode 100644 index 00000000000..10232195413 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0050_let_else_right_curly_brace_loop.rast @@ -0,0 +1,46 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "f" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + LET_STMT + LET_KW "let" + WHITESPACE " " + WILDCARD_PAT + UNDERSCORE "_" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LOOP_EXPR + LOOP_KW "loop" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + R_CURLY "}" + WHITESPACE " " + LET_ELSE + ELSE_KW "else" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + RETURN_EXPR + RETURN_KW "return" + WHITESPACE "\n " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" +error 33: right curly brace `}` before `else` in a `let...else` statement not allowed diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0050_let_else_right_curly_brace_loop.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0050_let_else_right_curly_brace_loop.rs new file mode 100644 index 00000000000..28b892869a1 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0050_let_else_right_curly_brace_loop.rs @@ -0,0 +1,6 @@ +fn f() { + let _ = loop { + } else { + return + }; +} \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0051_let_else_right_curly_brace_match.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0051_let_else_right_curly_brace_match.rast new file mode 100644 index 00000000000..6e1181246c0 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0051_let_else_right_curly_brace_match.rast @@ -0,0 +1,85 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "f" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + LET_STMT + LET_KW "let" + WHITESPACE " " + WILDCARD_PAT + UNDERSCORE "_" + WHITESPACE " " + EQ "=" + WHITESPACE " " + MATCH_EXPR + MATCH_KW "match" + WHITESPACE " " + CALL_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "Some" + ARG_LIST + L_PAREN "(" + LITERAL + INT_NUMBER "1" + R_PAREN ")" + WHITESPACE " " + MATCH_ARM_LIST + L_CURLY "{" + WHITESPACE "\n " + MATCH_ARM + TUPLE_STRUCT_PAT + PATH + PATH_SEGMENT + NAME_REF + IDENT "Some" + L_PAREN "(" + WILDCARD_PAT + UNDERSCORE "_" + R_PAREN ")" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + LITERAL + INT_NUMBER "1" + COMMA "," + WHITESPACE "\n " + MATCH_ARM + IDENT_PAT + NAME + IDENT "None" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + LITERAL + INT_NUMBER "2" + COMMA "," + WHITESPACE "\n " + R_CURLY "}" + WHITESPACE " " + LET_ELSE + ELSE_KW "else" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + RETURN_EXPR + RETURN_KW "return" + WHITESPACE "\n " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" +error 83: right curly brace `}` before `else` in a `let...else` statement not allowed diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0051_let_else_right_curly_brace_match.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0051_let_else_right_curly_brace_match.rs new file mode 100644 index 00000000000..902d70daed5 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0051_let_else_right_curly_brace_match.rs @@ -0,0 +1,8 @@ +fn f() { + let _ = match Some(1) { + Some(_) => 1, + None => 2, + } else { + return + }; +} \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0052_let_else_right_curly_brace_while.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0052_let_else_right_curly_brace_while.rast new file mode 100644 index 00000000000..298d47d5394 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0052_let_else_right_curly_brace_while.rast @@ -0,0 +1,49 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "f" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + LET_STMT + LET_KW "let" + WHITESPACE " " + WILDCARD_PAT + UNDERSCORE "_" + WHITESPACE " " + EQ "=" + WHITESPACE " " + WHILE_EXPR + WHILE_KW "while" + WHITESPACE " " + LITERAL + TRUE_KW "true" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + R_CURLY "}" + WHITESPACE " " + LET_ELSE + ELSE_KW "else" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + RETURN_EXPR + RETURN_KW "return" + WHITESPACE "\n " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" +error 39: right curly brace `}` before `else` in a `let...else` statement not allowed diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0052_let_else_right_curly_brace_while.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0052_let_else_right_curly_brace_while.rs new file mode 100644 index 00000000000..a52343d8e6f --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0052_let_else_right_curly_brace_while.rs @@ -0,0 +1,6 @@ +fn f() { + let _ = while true { + } else { + return + }; +} \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0053_let_else_right_curly_brace_if.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0053_let_else_right_curly_brace_if.rast new file mode 100644 index 00000000000..c0a4b0400d8 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0053_let_else_right_curly_brace_if.rast @@ -0,0 +1,57 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "f" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + LET_STMT + LET_KW "let" + WHITESPACE " " + WILDCARD_PAT + UNDERSCORE "_" + WHITESPACE " " + EQ "=" + WHITESPACE " " + IF_EXPR + IF_KW "if" + WHITESPACE " " + LITERAL + TRUE_KW "true" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + R_CURLY "}" + WHITESPACE " " + ELSE_KW "else" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + R_CURLY "}" + WHITESPACE " " + LET_ELSE + ELSE_KW "else" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + RETURN_EXPR + RETURN_KW "return" + WHITESPACE "\n " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" +error 49: right curly brace `}` before `else` in a `let...else` statement not allowed diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0053_let_else_right_curly_brace_if.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0053_let_else_right_curly_brace_if.rs new file mode 100644 index 00000000000..9a87aecbd41 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0053_let_else_right_curly_brace_if.rs @@ -0,0 +1,7 @@ +fn f() { + let _ = if true { + } else { + } else { + return + }; +} \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0016_angled_path_without_qual.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0016_angled_path_without_qual.rast new file mode 100644 index 00000000000..0529e9750e7 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0016_angled_path_without_qual.rast @@ -0,0 +1,49 @@ +SOURCE_FILE + TYPE_ALIAS + TYPE_KW "type" + WHITESPACE " " + NAME + IDENT "X" + WHITESPACE " " + EQ "=" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + L_ANGLE "<" + TUPLE_TYPE + L_PAREN "(" + R_PAREN ")" + R_ANGLE ">" + SEMICOLON ";" + WHITESPACE "\n" + TYPE_ALIAS + TYPE_KW "type" + WHITESPACE " " + NAME + IDENT "Y" + WHITESPACE " " + EQ "=" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + L_ANGLE "<" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "A" + WHITESPACE " " + AS_KW "as" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "B" + R_ANGLE ">" + SEMICOLON ";" + WHITESPACE "\n" +error 13: expected `::` +error 32: expected `::` diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0016_angled_path_without_qual.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0016_angled_path_without_qual.rs new file mode 100644 index 00000000000..802d6cc14a4 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0016_angled_path_without_qual.rs @@ -0,0 +1,2 @@ +type X = <()>; +type Y = ; diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rast new file mode 100644 index 00000000000..6ec580212b4 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rast @@ -0,0 +1,69 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "func" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE " " + LET_STMT + LET_KW "let" + WHITESPACE " " + TUPLE_STRUCT_PAT + PATH + PATH_SEGMENT + NAME_REF + IDENT "Some" + L_PAREN "(" + WILDCARD_PAT + UNDERSCORE "_" + R_PAREN ")" + WHITESPACE " " + EQ "=" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + CALL_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "Some" + ARG_LIST + L_PAREN "(" + LITERAL + INT_NUMBER "1" + R_PAREN ")" + R_CURLY "}" + WHITESPACE " " + LET_ELSE + ELSE_KW "else" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE " " + MACRO_EXPR + MACRO_CALL + PATH + PATH_SEGMENT + NAME_REF + IDENT "panic" + BANG "!" + TOKEN_TREE + L_PAREN "(" + STRING "\"h\"" + R_PAREN ")" + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + R_CURLY "}" + WHITESPACE "\n" +error 35: right curly brace `}` before `else` in a `let...else` statement not allowed diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rs new file mode 100644 index 00000000000..30d52fea3b2 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0017_let_else_right_curly_brace.rs @@ -0,0 +1 @@ +fn func() { let Some(_) = {Some(1)} else { panic!("h") };} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0151_trait_alias.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0151_trait_alias.rast index 2ef66484ae4..c45f8708980 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0151_trait_alias.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0151_trait_alias.rast @@ -1,5 +1,5 @@ SOURCE_FILE - TRAIT + TRAIT_ALIAS TRAIT_KW "trait" WHITESPACE " " NAME diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0177_trait_alias_where_clause.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0177_trait_alias_where_clause.rast index 4443d9d1426..8f678247731 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0177_trait_alias_where_clause.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0177_trait_alias_where_clause.rast @@ -1,5 +1,5 @@ SOURCE_FILE - TRAIT + TRAIT_ALIAS TRAIT_KW "trait" WHITESPACE " " NAME @@ -50,7 +50,7 @@ SOURCE_FILE IDENT "Copy" SEMICOLON ";" WHITESPACE "\n" - TRAIT + TRAIT_ALIAS TRAIT_KW "trait" WHITESPACE " " NAME diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs index 6550cf27e99..6df1273edd6 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs @@ -15,13 +15,13 @@ use std::{ use cargo_metadata::{camino::Utf8Path, Message}; use la_arena::ArenaMap; -use paths::AbsPathBuf; +use paths::{AbsPath, AbsPathBuf}; use rustc_hash::FxHashMap; use semver::Version; use serde::Deserialize; use crate::{ - cfg_flag::CfgFlag, CargoConfig, CargoFeatures, CargoWorkspace, InvocationLocation, + cfg_flag::CfgFlag, utf8_stdout, CargoConfig, CargoFeatures, CargoWorkspace, InvocationLocation, InvocationStrategy, Package, }; @@ -67,6 +67,7 @@ impl WorkspaceBuildScripts { let mut cmd = Command::new(toolchain::cargo()); cmd.args(["check", "--quiet", "--workspace", "--message-format=json"]); + cmd.args(&config.extra_args); // --all-targets includes tests, benches and examples in addition to the // default lib and bins. This is an independent concept from the --target @@ -250,7 +251,7 @@ impl WorkspaceBuildScripts { if tracing::enabled!(tracing::Level::INFO) { for package in workspace.packages() { - let package_build_data = &mut outputs[package]; + let package_build_data = &outputs[package]; if !package_build_data.is_unchanged() { tracing::info!( "{}: {:?}", @@ -378,6 +379,83 @@ impl WorkspaceBuildScripts { pub(crate) fn get_output(&self, idx: Package) -> Option<&BuildScriptOutput> { self.outputs.get(idx) } + + pub(crate) fn rustc_crates( + rustc: &CargoWorkspace, + current_dir: &AbsPath, + extra_env: &FxHashMap, + ) -> Self { + let mut bs = WorkspaceBuildScripts::default(); + for p in rustc.packages() { + bs.outputs.insert(p, BuildScriptOutput::default()); + } + let res = (|| { + let target_libdir = (|| { + let mut cargo_config = Command::new(toolchain::cargo()); + cargo_config.envs(extra_env); + cargo_config + .current_dir(current_dir) + .args(["rustc", "-Z", "unstable-options", "--print", "target-libdir"]) + .env("RUSTC_BOOTSTRAP", "1"); + if let Ok(it) = utf8_stdout(cargo_config) { + return Ok(it); + } + let mut cmd = Command::new(toolchain::rustc()); + cmd.envs(extra_env); + cmd.args(["--print", "target-libdir"]); + utf8_stdout(cmd) + })()?; + + let target_libdir = AbsPathBuf::try_from(PathBuf::from(target_libdir)) + .map_err(|_| anyhow::format_err!("target-libdir was not an absolute path"))?; + tracing::info!("Loading rustc proc-macro paths from {}", target_libdir.display()); + + let proc_macro_dylibs: Vec<(String, AbsPathBuf)> = std::fs::read_dir(target_libdir)? + .filter_map(|entry| { + let dir_entry = entry.ok()?; + if dir_entry.file_type().ok()?.is_file() { + let path = dir_entry.path(); + tracing::info!("p{:?}", path); + let extension = path.extension()?; + if extension == std::env::consts::DLL_EXTENSION { + let name = path.file_stem()?.to_str()?.split_once('-')?.0.to_owned(); + let path = AbsPathBuf::try_from(path).ok()?; + return Some((name, path)); + } + } + None + }) + .collect(); + for p in rustc.packages() { + let package = &rustc[p]; + if package.targets.iter().any(|&it| rustc[it].is_proc_macro) { + if let Some((_, path)) = + proc_macro_dylibs.iter().find(|(name, _)| *name == package.name) + { + bs.outputs[p].proc_macro_dylib_path = Some(path.clone()); + } + } + } + + if tracing::enabled!(tracing::Level::INFO) { + for package in rustc.packages() { + let package_build_data = &bs.outputs[package]; + if !package_build_data.is_unchanged() { + tracing::info!( + "{}: {:?}", + rustc[package].manifest.parent().display(), + package_build_data, + ); + } + } + } + Ok(()) + })(); + if let Err::<_, anyhow::Error>(e) = res { + bs.error = Some(e.to_string()); + } + bs + } } // FIXME: Find a better way to know if it is a dylib. diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs index fdc7859eb90..732adc50b50 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs @@ -105,6 +105,8 @@ pub struct CargoConfig { pub wrap_rustc_in_build_scripts: bool, /// The command to run instead of `cargo check` for building build scripts. pub run_build_script_command: Option>, + /// Extra args to pass to the cargo command. + pub extra_args: Vec, /// Extra env vars to set when invoking the cargo command pub extra_env: FxHashMap, pub invocation_strategy: InvocationStrategy, diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs index 328d2fbcf31..74e41eda763 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs @@ -88,23 +88,17 @@ impl Sysroot { } pub fn discover_with_src_override( - dir: &AbsPath, + current_dir: &AbsPath, extra_env: &FxHashMap, src: AbsPathBuf, ) -> Result { - tracing::debug!("discovering sysroot for {}", dir.display()); - let sysroot_dir = discover_sysroot_dir(dir, extra_env)?; + tracing::debug!("discovering sysroot for {}", current_dir.display()); + let sysroot_dir = discover_sysroot_dir(current_dir, extra_env)?; Ok(Sysroot::load(sysroot_dir, src)) } - pub fn discover_rustc( - cargo_toml: &ManifestPath, - extra_env: &FxHashMap, - ) -> Option { - tracing::debug!("discovering rustc source for {}", cargo_toml.display()); - let current_dir = cargo_toml.parent(); - let sysroot_dir = discover_sysroot_dir(current_dir, extra_env).ok()?; - get_rustc_src(&sysroot_dir) + pub fn discover_rustc(&self) -> Option { + get_rustc_src(&self.root) } pub fn with_sysroot_dir(sysroot_dir: AbsPathBuf) -> Result { @@ -282,4 +276,7 @@ unwind std_detect test"; -const PROC_MACRO_DEPS: &str = "std"; +// core is required for our builtin derives to work in the proc_macro lib currently +const PROC_MACRO_DEPS: &str = " +std +core"; diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs index 9e9691d11e8..749eee531ee 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs @@ -1547,6 +1547,15 @@ fn rust_project_hello_world_project_model() { ), prelude: true, }, + Dependency { + crate_id: CrateId( + 1, + ), + name: CrateName( + "core", + ), + prelude: true, + }, ], proc_macro: Err( "no proc macro loaded for sysroot crate", diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index 2a11f1e8eb8..faa6816fdc2 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -70,7 +70,7 @@ pub enum ProjectWorkspace { cargo: CargoWorkspace, build_scripts: WorkspaceBuildScripts, sysroot: Option, - rustc: Option, + rustc: Option<(CargoWorkspace, WorkspaceBuildScripts)>, /// Holds cfg flags for the current target. We get those by running /// `rustc --print cfg`. /// @@ -116,7 +116,7 @@ impl fmt::Debug for ProjectWorkspace { .field("sysroot", &sysroot.is_some()) .field( "n_rustc_compiler_crates", - &rustc.as_ref().map_or(0, |rc| rc.packages().len()), + &rustc.as_ref().map_or(0, |(rc, _)| rc.packages().len()), ) .field("n_rustc_cfg", &rustc_cfg.len()) .field("n_cfg_overrides", &cfg_overrides.len()) @@ -237,37 +237,45 @@ impl ProjectWorkspace { }; if let Some(sysroot) = &sysroot { - tracing::info!(src_root = %sysroot.src_root().display(), root = %sysroot.root().display(), "Using sysroot"); + tracing::info!(workspace = %cargo_toml.display(), src_root = %sysroot.src_root().display(), root = %sysroot.root().display(), "Using sysroot"); } let rustc_dir = match &config.rustc_source { Some(RustcSource::Path(path)) => ManifestPath::try_from(path.clone()).ok(), Some(RustcSource::Discover) => { - Sysroot::discover_rustc(&cargo_toml, &config.extra_env) + sysroot.as_ref().and_then(Sysroot::discover_rustc) } None => None, }; - if let Some(rustc_dir) = &rustc_dir { - tracing::info!(rustc_dir = %rustc_dir.display(), "Using rustc source"); - } let rustc = match rustc_dir { - Some(rustc_dir) => match CargoWorkspace::fetch_metadata( - &rustc_dir, - cargo_toml.parent(), - config, - progress, - ) { - Ok(meta) => Some(CargoWorkspace::new(meta)), - Err(e) => { - tracing::error!( - %e, - "Failed to read Cargo metadata from rustc source at {}", - rustc_dir.display() - ); - None + Some(rustc_dir) => { + tracing::info!(workspace = %cargo_toml.display(), rustc_dir = %rustc_dir.display(), "Using rustc source"); + match CargoWorkspace::fetch_metadata( + &rustc_dir, + cargo_toml.parent(), + config, + progress, + ) { + Ok(meta) => { + let workspace = CargoWorkspace::new(meta); + let buildscripts = WorkspaceBuildScripts::rustc_crates( + &workspace, + cargo_toml.parent(), + &config.extra_env, + ); + Some((workspace, buildscripts)) + } + Err(e) => { + tracing::error!( + %e, + "Failed to read Cargo metadata from rustc source at {}", + rustc_dir.display() + ); + None + } } - }, + } None => None, }; @@ -531,7 +539,7 @@ impl ProjectWorkspace { PackageRoot { is_local, include, exclude } }) .chain(mk_sysroot(sysroot.as_ref(), Some(cargo.workspace_root()))) - .chain(rustc.iter().flat_map(|rustc| { + .chain(rustc.iter().flat_map(|(rustc, _)| { rustc.packages().map(move |krate| PackageRoot { is_local: false, include: vec![rustc[krate].manifest.parent().to_path_buf()], @@ -559,7 +567,7 @@ impl ProjectWorkspace { sysroot_package_len + project.n_crates() } ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => { - let rustc_package_len = rustc.as_ref().map_or(0, |it| it.packages().len()); + let rustc_package_len = rustc.as_ref().map_or(0, |(it, _)| it.packages().len()); let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.crates().len()); cargo.packages().len() + sysroot_package_len + rustc_package_len } @@ -778,7 +786,7 @@ fn project_json_to_crate_graph( fn cargo_to_crate_graph( load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult, load: &mut dyn FnMut(&AbsPath) -> Option, - rustc: &Option, + rustc: &Option<(CargoWorkspace, WorkspaceBuildScripts)>, cargo: &CargoWorkspace, sysroot: Option<&Sysroot>, rustc_cfg: Vec, @@ -924,7 +932,7 @@ fn cargo_to_crate_graph( if has_private { // If the user provided a path to rustc sources, we add all the rustc_private crates // and create dependencies on them for the crates which opt-in to that - if let Some(rustc_workspace) = rustc { + if let Some((rustc_workspace, build_scripts)) = rustc { handle_rustc_crates( &mut crate_graph, &mut pkg_to_lib_crate, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/default_12483297303756020505_0.profraw b/src/tools/rust-analyzer/crates/rust-analyzer/default_12483297303756020505_0.profraw deleted file mode 100644 index e49d7c14492..00000000000 Binary files a/src/tools/rust-analyzer/crates/rust-analyzer/default_12483297303756020505_0.profraw and /dev/null differ diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 93297faa664..e8c10927d62 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -12,7 +12,7 @@ use hir::{ }; use hir_def::{ body::{BodySourceMap, SyntheticSyntax}, - expr::ExprId, + expr::{ExprId, PatId}, FunctionId, }; use hir_ty::{Interner, TyExt, TypeFlags}; @@ -222,7 +222,11 @@ impl flags::AnalysisStats { let mut num_exprs = 0; let mut num_exprs_unknown = 0; let mut num_exprs_partially_unknown = 0; - let mut num_type_mismatches = 0; + let mut num_expr_type_mismatches = 0; + let mut num_pats = 0; + let mut num_pats_unknown = 0; + let mut num_pats_partially_unknown = 0; + let mut num_pat_type_mismatches = 0; let analysis = host.analysis(); for f in funcs.iter().copied() { let name = f.name(db); @@ -255,6 +259,8 @@ impl flags::AnalysisStats { let f_id = FunctionId::from(f); let (body, sm) = db.body_with_source_map(f_id.into()); let inference_result = db.infer(f_id.into()); + + // region:expressions let (previous_exprs, previous_unknown, previous_partially_unknown) = (num_exprs, num_exprs_unknown, num_exprs_partially_unknown); for (expr_id, _) in body.exprs.iter() { @@ -307,12 +313,12 @@ impl flags::AnalysisStats { if unknown_or_partial && self.output == Some(OutputFormat::Csv) { println!( r#"{},type,"{}""#, - location_csv(db, &analysis, vfs, &sm, expr_id), + location_csv_expr(db, &analysis, vfs, &sm, expr_id), ty.display(db) ); } if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) { - num_type_mismatches += 1; + num_expr_type_mismatches += 1; if verbosity.is_verbose() { if let Some((path, start, end)) = expr_syntax_range(db, &analysis, vfs, &sm, expr_id) @@ -339,7 +345,7 @@ impl flags::AnalysisStats { if self.output == Some(OutputFormat::Csv) { println!( r#"{},mismatch,"{}","{}""#, - location_csv(db, &analysis, vfs, &sm, expr_id), + location_csv_expr(db, &analysis, vfs, &sm, expr_id), mismatch.expected.display(db), mismatch.actual.display(db) ); @@ -355,6 +361,109 @@ impl flags::AnalysisStats { num_exprs_partially_unknown - previous_partially_unknown )); } + // endregion:expressions + + // region:patterns + let (previous_pats, previous_unknown, previous_partially_unknown) = + (num_pats, num_pats_unknown, num_pats_partially_unknown); + for (pat_id, _) in body.pats.iter() { + let ty = &inference_result[pat_id]; + num_pats += 1; + let unknown_or_partial = if ty.is_unknown() { + num_pats_unknown += 1; + if verbosity.is_spammy() { + if let Some((path, start, end)) = + pat_syntax_range(db, &analysis, vfs, &sm, pat_id) + { + bar.println(format!( + "{} {}:{}-{}:{}: Unknown type", + path, + start.line + 1, + start.col, + end.line + 1, + end.col, + )); + } else { + bar.println(format!("{name}: Unknown type",)); + } + } + true + } else { + let is_partially_unknown = + ty.data(Interner).flags.contains(TypeFlags::HAS_ERROR); + if is_partially_unknown { + num_pats_partially_unknown += 1; + } + is_partially_unknown + }; + if self.only.is_some() && verbosity.is_spammy() { + // in super-verbose mode for just one function, we print every single pattern + if let Some((_, start, end)) = pat_syntax_range(db, &analysis, vfs, &sm, pat_id) + { + bar.println(format!( + "{}:{}-{}:{}: {}", + start.line + 1, + start.col, + end.line + 1, + end.col, + ty.display(db) + )); + } else { + bar.println(format!("unknown location: {}", ty.display(db))); + } + } + if unknown_or_partial && self.output == Some(OutputFormat::Csv) { + println!( + r#"{},type,"{}""#, + location_csv_pat(db, &analysis, vfs, &sm, pat_id), + ty.display(db) + ); + } + if let Some(mismatch) = inference_result.type_mismatch_for_pat(pat_id) { + num_pat_type_mismatches += 1; + if verbosity.is_verbose() { + if let Some((path, start, end)) = + pat_syntax_range(db, &analysis, vfs, &sm, pat_id) + { + bar.println(format!( + "{} {}:{}-{}:{}: Expected {}, got {}", + path, + start.line + 1, + start.col, + end.line + 1, + end.col, + mismatch.expected.display(db), + mismatch.actual.display(db) + )); + } else { + bar.println(format!( + "{}: Expected {}, got {}", + name, + mismatch.expected.display(db), + mismatch.actual.display(db) + )); + } + } + if self.output == Some(OutputFormat::Csv) { + println!( + r#"{},mismatch,"{}","{}""#, + location_csv_pat(db, &analysis, vfs, &sm, pat_id), + mismatch.expected.display(db), + mismatch.actual.display(db) + ); + } + } + } + if verbosity.is_spammy() { + bar.println(format!( + "In {}: {} pats, {} unknown, {} partial", + full_name, + num_pats - previous_pats, + num_pats_unknown - previous_unknown, + num_pats_partially_unknown - previous_partially_unknown + )); + } + // endregion:patterns bar.inc(1); } @@ -366,10 +475,21 @@ impl flags::AnalysisStats { percentage(num_exprs_unknown, num_exprs), num_exprs_partially_unknown, percentage(num_exprs_partially_unknown, num_exprs), - num_type_mismatches + num_expr_type_mismatches + ); + eprintln!( + " pats: {}, ??ty: {} ({}%), ?ty: {} ({}%), !ty: {}", + num_pats, + num_pats_unknown, + percentage(num_pats_unknown, num_pats), + num_pats_partially_unknown, + percentage(num_pats_partially_unknown, num_pats), + num_pat_type_mismatches ); report_metric("unknown type", num_exprs_unknown, "#"); - report_metric("type mismatches", num_type_mismatches, "#"); + report_metric("type mismatches", num_expr_type_mismatches, "#"); + report_metric("pattern unknown type", num_pats_unknown, "#"); + report_metric("pattern type mismatches", num_pat_type_mismatches, "#"); eprintln!("{:<20} {}", "Inference:", inference_sw.elapsed()); } @@ -379,7 +499,7 @@ impl flags::AnalysisStats { } } -fn location_csv( +fn location_csv_expr( db: &RootDatabase, analysis: &Analysis, vfs: &Vfs, @@ -401,6 +521,30 @@ fn location_csv( format!("{path},{}:{},{}:{}", start.line + 1, start.col, end.line + 1, end.col) } +fn location_csv_pat( + db: &RootDatabase, + analysis: &Analysis, + vfs: &Vfs, + sm: &BodySourceMap, + pat_id: PatId, +) -> String { + let src = match sm.pat_syntax(pat_id) { + Ok(s) => s, + Err(SyntheticSyntax) => return "synthetic,,".to_string(), + }; + let root = db.parse_or_expand(src.file_id).unwrap(); + let node = src.map(|e| { + e.either(|it| it.to_node(&root).syntax().clone(), |it| it.to_node(&root).syntax().clone()) + }); + let original_range = node.as_ref().original_file_range(db); + let path = vfs.file_path(original_range.file_id); + let line_index = analysis.file_line_index(original_range.file_id).unwrap(); + let text_range = original_range.range; + let (start, end) = + (line_index.line_col(text_range.start()), line_index.line_col(text_range.end())); + format!("{path},{}:{},{}:{}", start.line + 1, start.col, end.line + 1, end.col) +} + fn expr_syntax_range( db: &RootDatabase, analysis: &Analysis, @@ -423,6 +567,33 @@ fn expr_syntax_range( None } } +fn pat_syntax_range( + db: &RootDatabase, + analysis: &Analysis, + vfs: &Vfs, + sm: &BodySourceMap, + pat_id: PatId, +) -> Option<(VfsPath, LineCol, LineCol)> { + let src = sm.pat_syntax(pat_id); + if let Ok(src) = src { + let root = db.parse_or_expand(src.file_id).unwrap(); + let node = src.map(|e| { + e.either( + |it| it.to_node(&root).syntax().clone(), + |it| it.to_node(&root).syntax().clone(), + ) + }); + let original_range = node.as_ref().original_file_range(db); + let path = vfs.file_path(original_range.file_id); + let line_index = analysis.file_line_index(original_range.file_id).unwrap(); + let text_range = original_range.range; + let (start, end) = + (line_index.line_col(text_range.start()), line_index.line_col(text_range.end())); + Some((path, start, end)) + } else { + None + } +} fn shuffle(rng: &mut Rand32, slice: &mut [T]) { for i in 0..slice.len() { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs index ff821be53d8..0721d486ef1 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs @@ -1,6 +1,7 @@ //! Analyze all modules in a project for diagnostics. Exits with a non-zero //! status code if any errors are found. +use project_model::{CargoConfig, RustcSource}; use rustc_hash::FxHashSet; use hir::{db::HirDatabase, Crate, Module}; @@ -14,7 +15,8 @@ use crate::cli::{ impl flags::Diagnostics { pub fn run(self) -> anyhow::Result<()> { - let cargo_config = Default::default(); + let mut cargo_config = CargoConfig::default(); + cargo_config.sysroot = Some(RustcSource::Discover); let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check: !self.disable_build_scripts, with_proc_macro_server: ProcMacroServerChoice::Sysroot, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs index 3fc1aa4eaeb..9b5451496c6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs @@ -13,7 +13,7 @@ use ide_db::LineIndexDatabase; use ide_db::base_db::salsa::{self, ParallelDatabase}; use ide_db::line_index::WideEncoding; use lsp_types::{self, lsif}; -use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace}; +use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustcSource}; use vfs::{AbsPathBuf, Vfs}; use crate::cli::load_cargo::ProcMacroServerChoice; @@ -289,7 +289,8 @@ impl flags::Lsif { pub fn run(self) -> Result<()> { eprintln!("Generating LSIF started..."); let now = Instant::now(); - let cargo_config = CargoConfig::default(); + let mut cargo_config = CargoConfig::default(); + cargo_config.sysroot = Some(RustcSource::Discover); let no_progress = &|_| (); let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check: true, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs index 9a04fbea774..df5c26cf77a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs @@ -15,7 +15,7 @@ use ide::{ TokenStaticData, }; use ide_db::LineIndexDatabase; -use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace}; +use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustcSource}; use scip::types as scip_types; use std::env; @@ -29,7 +29,8 @@ impl flags::Scip { pub fn run(self) -> Result<()> { eprintln!("Generating SCIP start..."); let now = Instant::now(); - let cargo_config = CargoConfig::default(); + let mut cargo_config = CargoConfig::default(); + cargo_config.sysroot = Some(RustcSource::Discover); let no_progress = &|s| (eprintln!("rust-analyzer: Loading {s}")); let load_cargo_config = LoadCargoConfig { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs index 3552f840a1b..35a874f8920 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs @@ -1,7 +1,7 @@ //! Applies structured search replace rules from the command line. use ide_ssr::MatchFinder; -use project_model::CargoConfig; +use project_model::{CargoConfig, RustcSource}; use crate::cli::{ flags, @@ -12,7 +12,8 @@ use crate::cli::{ impl flags::Ssr { pub fn run(self) -> Result<()> { use ide_db::base_db::SourceDatabaseExt; - let cargo_config = CargoConfig::default(); + let mut cargo_config = CargoConfig::default(); + cargo_config.sysroot = Some(RustcSource::Discover); let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check: true, with_proc_macro_server: ProcMacroServerChoice::Sysroot, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index f609a50a05f..75233dbb2ab 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -101,6 +101,8 @@ config_data! { /// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to /// avoid checking unnecessary things. cargo_buildScripts_useRustcWrapper: bool = "true", + /// Extra arguments that are passed to every cargo invocation. + cargo_extraArgs: Vec = "[]", /// Extra environment variables that will be set when running cargo, rustc /// or other commands within the workspace. Useful for setting RUSTFLAGS. cargo_extraEnv: FxHashMap = "{}", @@ -366,6 +368,8 @@ config_data! { inlayHints_typeHints_hideClosureInitialization: bool = "false", /// Whether to hide inlay type hints for constructors. inlayHints_typeHints_hideNamedConstructor: bool = "false", + /// Enables the experimental support for interpreting tests. + interpret_tests: bool = "false", /// Join lines merges consecutive declaration and initialization of an assignment. joinLines_joinAssignments: bool = "true", @@ -456,7 +460,10 @@ config_data! { /// Additional arguments to `rustfmt`. rustfmt_extraArgs: Vec = "[]", /// Advanced option, fully override the command rust-analyzer uses for - /// formatting. + /// formatting. This should be the equivalent of `rustfmt` here, and + /// not that of `cargo fmt`. The file contents will be passed on the + /// standard input and the formatted result will be read from the + /// standard output. rustfmt_overrideCommand: Option> = "null", /// Enables the use of rustfmt's unstable range formatting command for the /// `textDocument/rangeFormatting` request. The rustfmt option is unstable and only @@ -849,27 +856,27 @@ impl Config { } pub fn linked_projects(&self) -> Vec { match self.data.linkedProjects.as_slice() { - [] => match self.discovered_projects.as_ref() { - Some(discovered_projects) => { - let exclude_dirs: Vec<_> = self - .data - .files_excludeDirs + [] => { + match self.discovered_projects.as_ref() { + Some(discovered_projects) => { + let exclude_dirs: Vec<_> = self + .data + .files_excludeDirs + .iter() + .map(|p| self.root_path.join(p)) + .collect(); + discovered_projects .iter() - .map(|p| self.root_path.join(p)) - .collect(); - discovered_projects - .iter() - .filter(|p| { - let (ProjectManifest::ProjectJson(path) - | ProjectManifest::CargoToml(path)) = p; + .filter(|(ProjectManifest::ProjectJson(path) | ProjectManifest::CargoToml(path))| { !exclude_dirs.iter().any(|p| path.starts_with(p)) }) .cloned() .map(LinkedProject::from) .collect() + } + None => Vec::new(), } - None => Vec::new(), - }, + } linked_projects => linked_projects .iter() .filter_map(|linked_project| match linked_project { @@ -1050,10 +1057,20 @@ impl Config { } } + pub fn extra_args(&self) -> &Vec { + &self.data.cargo_extraArgs + } + pub fn extra_env(&self) -> &FxHashMap { &self.data.cargo_extraEnv } + pub fn check_extra_args(&self) -> Vec { + let mut extra_args = self.extra_args().clone(); + extra_args.extend_from_slice(&self.data.check_extraArgs); + extra_args + } + pub fn check_extra_env(&self) -> FxHashMap { let mut extra_env = self.data.cargo_extraEnv.clone(); extra_env.extend(self.data.check_extraEnv.clone()); @@ -1152,6 +1169,7 @@ impl Config { InvocationLocation::Workspace => project_model::InvocationLocation::Workspace, }, run_build_script_command: self.data.cargo_buildScripts_overrideCommand.clone(), + extra_args: self.data.cargo_extraArgs.clone(), extra_env: self.data.cargo_extraEnv.clone(), } } @@ -1222,7 +1240,7 @@ impl Config { CargoFeaturesDef::All => vec![], CargoFeaturesDef::Selected(it) => it, }, - extra_args: self.data.check_extraArgs.clone(), + extra_args: self.check_extra_args(), extra_env: self.check_extra_env(), ansi_color_output: self.color_diagnostic_output(), }, @@ -1441,6 +1459,7 @@ impl Config { } }, keywords: self.data.hover_documentation_keywords_enable, + interpret_tests: self.data.interpret_tests, } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs index 4e08bd0a724..32ac9a42dec 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs @@ -134,6 +134,16 @@ pub(crate) fn handle_view_hir( Ok(res) } +pub(crate) fn handle_view_mir( + snap: GlobalStateSnapshot, + params: lsp_types::TextDocumentPositionParams, +) -> Result { + let _p = profile::span("handle_view_mir"); + let position = from_proto::file_position(&snap, params)?; + let res = snap.analysis.view_mir(position)?; + Ok(res) +} + pub(crate) fn handle_view_file_text( snap: GlobalStateSnapshot, params: lsp_types::TextDocumentIdentifier, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs index e33589cc536..c7b513db981 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs @@ -74,6 +74,14 @@ impl Request for ViewHir { const METHOD: &'static str = "rust-analyzer/viewHir"; } +pub enum ViewMir {} + +impl Request for ViewMir { + type Params = lsp_types::TextDocumentPositionParams; + type Result = String; + const METHOD: &'static str = "rust-analyzer/viewMir"; +} + pub enum ViewFileText {} impl Request for ViewFileText { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index d1e38b33c7d..dd0804b4398 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -111,12 +111,7 @@ impl fmt::Debug for Event { impl GlobalState { fn run(mut self, inbox: Receiver) -> Result<()> { - if self.config.linked_projects().is_empty() - && self.config.detached_files().is_empty() - && self.config.notifications().cargo_toml_not_found - { - self.show_and_log_error("rust-analyzer failed to discover workspace".to_string(), None); - }; + self.update_status_or_notify(); if self.config.did_save_text_document_dynamic_registration() { let save_registration_options = lsp_types::TextDocumentSaveRegistrationOptions { @@ -323,17 +318,6 @@ impl GlobalState { if let Some(diagnostic_changes) = self.diagnostics.take_changes() { for file_id in diagnostic_changes { - let db = self.analysis_host.raw_database(); - let source_root = db.file_source_root(file_id); - if db.source_root(source_root).is_library { - // Only publish diagnostics for files in the workspace, not from crates.io deps - // or the sysroot. - // While theoretically these should never have errors, we have quite a few false - // positives particularly in the stdlib, and those diagnostics would stay around - // forever if we emitted them here. - continue; - } - let uri = file_id_to_url(&self.vfs.read().0, file_id); let mut diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect::>(); @@ -405,18 +389,7 @@ impl GlobalState { }); } - let status = self.current_status(); - if self.last_reported_status.as_ref() != Some(&status) { - self.last_reported_status = Some(status.clone()); - - if let (lsp_ext::Health::Error, Some(message)) = (status.health, &status.message) { - self.show_message(lsp_types::MessageType::ERROR, message.clone()); - } - - if self.config.server_status_notification() { - self.send_notification::(status); - } - } + self.update_status_or_notify(); let loop_duration = loop_start.elapsed(); if loop_duration > Duration::from_millis(100) && was_quiescent { @@ -426,6 +399,20 @@ impl GlobalState { Ok(()) } + fn update_status_or_notify(&mut self) { + let status = self.current_status(); + if self.last_reported_status.as_ref() != Some(&status) { + self.last_reported_status = Some(status.clone()); + + if self.config.server_status_notification() { + self.send_notification::(status); + } else if let (lsp_ext::Health::Error, Some(message)) = (status.health, &status.message) + { + self.show_and_log_error(message.clone(), None); + } + } + } + fn handle_task(&mut self, prime_caches_progress: &mut Vec, task: Task) { match task { Task::Response(response) => self.respond(response), @@ -456,6 +443,9 @@ impl GlobalState { ProjectWorkspaceProgress::Report(msg) => (Progress::Report, Some(msg)), ProjectWorkspaceProgress::End(workspaces) => { self.fetch_workspaces_queue.op_completed(Some(workspaces)); + if let Err(e) = self.fetch_workspace_error() { + tracing::error!("FetchWorkspaceError:\n{e}"); + } let old = Arc::clone(&self.workspaces); self.switch_workspaces("fetched workspace".to_string()); @@ -477,6 +467,9 @@ impl GlobalState { BuildDataProgress::Report(msg) => (Some(Progress::Report), Some(msg)), BuildDataProgress::End(build_data_result) => { self.fetch_build_data_queue.op_completed(build_data_result); + if let Err(e) = self.fetch_build_data_error() { + tracing::error!("FetchBuildDataError:\n{e}"); + } self.switch_workspaces("fetched build data".to_string()); @@ -509,6 +502,7 @@ impl GlobalState { self.vfs_progress_n_total = n_total; self.vfs_progress_n_done = n_done; + // if n_total != 0 { let state = if n_done == 0 { Progress::Begin } else if n_done < n_total { @@ -523,7 +517,8 @@ impl GlobalState { Some(format!("{n_done}/{n_total}")), Some(Progress::fraction(n_done, n_total)), None, - ) + ); + // } } } } @@ -565,7 +560,10 @@ impl GlobalState { flycheck::Progress::DidCheckCrate(target) => (Progress::Report, Some(target)), flycheck::Progress::DidCancel => (Progress::End, None), flycheck::Progress::DidFailToRestart(err) => { - self.show_and_log_error("cargo check failed".to_string(), Some(err)); + self.show_and_log_error( + "cargo check failed to start".to_string(), + Some(err), + ); return; } flycheck::Progress::DidFinish(result) => { @@ -634,6 +632,7 @@ impl GlobalState { .on::(handlers::handle_analyzer_status) .on::(handlers::handle_syntax_tree) .on::(handlers::handle_view_hir) + .on::(handlers::handle_view_mir) .on::(handlers::handle_view_file_text) .on::(handlers::handle_view_crate_graph) .on::(handlers::handle_view_item_tree) @@ -971,10 +970,20 @@ impl GlobalState { } fn update_diagnostics(&mut self) { + let db = self.analysis_host.raw_database(); let subscriptions = self .mem_docs .iter() .map(|path| self.vfs.read().0.file_id(path).unwrap()) + .filter(|&file_id| { + let source_root = db.file_source_root(file_id); + // Only publish diagnostics for files in the workspace, not from crates.io deps + // or the sysroot. + // While theoretically these should never have errors, we have quite a few false + // positives particularly in the stdlib, and those diagnostics would stay around + // forever if we emitted them here. + !db.source_root(source_root).is_library + }) .collect::>(); tracing::trace!("updating notifications for {:?}", subscriptions); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index abce0d73782..28d37f5685a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -12,17 +12,21 @@ //! correct. Instead, we try to provide a best-effort service. Even if the //! project is currently loading and we don't have a full project model, we //! still want to respond to various requests. -use std::{mem, sync::Arc}; +use std::{collections::hash_map::Entry, mem, sync::Arc}; use flycheck::{FlycheckConfig, FlycheckHandle}; use hir::db::DefDatabase; use ide::Change; -use ide_db::base_db::{ - CrateGraph, Env, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, - ProcMacroLoadResult, SourceRoot, VfsPath, +use ide_db::{ + base_db::{ + CrateGraph, Env, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, + ProcMacroLoadResult, SourceRoot, VfsPath, + }, + FxHashMap, }; +use itertools::Itertools; use proc_macro_api::{MacroDylib, ProcMacroServer}; -use project_model::{ProjectWorkspace, WorkspaceBuildScripts}; +use project_model::{PackageRoot, ProjectWorkspace, WorkspaceBuildScripts}; use syntax::SmolStr; use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind}; @@ -52,7 +56,8 @@ pub(crate) enum BuildDataProgress { impl GlobalState { pub(crate) fn is_quiescent(&self) -> bool { - !(self.fetch_workspaces_queue.op_in_progress() + !(self.last_reported_status.is_none() + || self.fetch_workspaces_queue.op_in_progress() || self.fetch_build_data_queue.op_in_progress() || self.vfs_progress_config_version < self.vfs_config_version || self.vfs_progress_n_done < self.vfs_progress_n_total) @@ -104,9 +109,9 @@ impl GlobalState { status.message = Some("Workspace reload required".to_string()) } - if let Err(error) = self.fetch_workspace_error() { + if let Err(_) = self.fetch_workspace_error() { status.health = lsp_ext::Health::Error; - status.message = Some(error) + status.message = Some("Failed to load workspaces".to_string()) } if self.config.linked_projects().is_empty() @@ -114,8 +119,9 @@ impl GlobalState { && self.config.notifications().cargo_toml_not_found { status.health = lsp_ext::Health::Warning; - status.message = Some("Workspace reload required".to_string()) + status.message = Some("Failed to discover workspace".to_string()) } + status } @@ -197,8 +203,7 @@ impl GlobalState { let _p = profile::span("GlobalState::switch_workspaces"); tracing::info!(%cause, "will switch workspaces"); - if let Err(error_message) = self.fetch_workspace_error() { - self.show_and_log_error(error_message, None); + if let Err(_) = self.fetch_workspace_error() { if !self.workspaces.is_empty() { // It only makes sense to switch to a partially broken workspace // if we don't have any workspace at all yet. @@ -206,10 +211,6 @@ impl GlobalState { } } - if let Err(error) = self.fetch_build_data_error() { - self.show_and_log_error("failed to run build scripts".to_string(), Some(error)); - } - let Some(workspaces) = self.fetch_workspaces_queue.last_op_result() else { return; }; let workspaces = workspaces.iter().filter_map(|res| res.as_ref().ok().cloned()).collect::>(); @@ -388,7 +389,7 @@ impl GlobalState { tracing::info!("did switch workspaces"); } - fn fetch_workspace_error(&self) -> Result<(), String> { + pub(super) fn fetch_workspace_error(&self) -> Result<(), String> { let mut buf = String::new(); let Some(last_op_result) = self.fetch_workspaces_queue.last_op_result() else { return Ok(()) }; @@ -409,7 +410,7 @@ impl GlobalState { Err(buf) } - fn fetch_build_data_error(&self) -> Result<(), String> { + pub(super) fn fetch_build_data_error(&self) -> Result<(), String> { let mut buf = String::new(); for ws in &self.fetch_build_data_queue.last_op_result().1 { @@ -494,7 +495,69 @@ impl ProjectFolders { let mut fsc = FileSetConfig::builder(); let mut local_filesets = vec![]; - for root in workspaces.iter().flat_map(|ws| ws.to_roots()) { + // Dedup source roots + // Depending on the project setup, we can have duplicated source roots, or for example in + // the case of the rustc workspace, we can end up with two source roots that are almost the + // same but not quite, like: + // PackageRoot { is_local: false, include: [AbsPathBuf(".../rust/src/tools/miri/cargo-miri")], exclude: [] } + // PackageRoot { + // is_local: true, + // include: [AbsPathBuf(".../rust/src/tools/miri/cargo-miri"), AbsPathBuf(".../rust/build/x86_64-pc-windows-msvc/stage0-tools/x86_64-pc-windows-msvc/release/build/cargo-miri-85801cd3d2d1dae4/out")], + // exclude: [AbsPathBuf(".../rust/src/tools/miri/cargo-miri/.git"), AbsPathBuf(".../rust/src/tools/miri/cargo-miri/target")] + // } + // + // The first one comes from the explicit rustc workspace which points to the rustc workspace itself + // The second comes from the rustc workspace that we load as the actual project workspace + // These `is_local` differing in this kind of way gives us problems, especially when trying to filter diagnostics as we don't report diagnostics for external libraries. + // So we need to deduplicate these, usually it would be enough to deduplicate by `include`, but as the rustc example shows here that doesn't work, + // so we need to also coalesce the includes if they overlap. + + let mut roots: Vec<_> = workspaces + .iter() + .flat_map(|ws| ws.to_roots()) + .update(|root| root.include.sort()) + .sorted_by(|a, b| a.include.cmp(&b.include)) + .collect(); + + // map that tracks indices of overlapping roots + let mut overlap_map = FxHashMap::<_, Vec<_>>::default(); + let mut done = false; + + while !mem::replace(&mut done, true) { + // maps include paths to indices of the corresponding root + let mut include_to_idx = FxHashMap::default(); + // Find and note down the indices of overlapping roots + for (idx, root) in roots.iter().enumerate().filter(|(_, it)| !it.include.is_empty()) { + for include in &root.include { + match include_to_idx.entry(include) { + Entry::Occupied(e) => { + overlap_map.entry(*e.get()).or_default().push(idx); + } + Entry::Vacant(e) => { + e.insert(idx); + } + } + } + } + for (k, v) in overlap_map.drain() { + done = false; + for v in v { + let r = mem::replace( + &mut roots[v], + PackageRoot { is_local: false, include: vec![], exclude: vec![] }, + ); + roots[k].is_local |= r.is_local; + roots[k].include.extend(r.include); + roots[k].exclude.extend(r.exclude); + } + roots[k].include.sort(); + roots[k].exclude.sort(); + roots[k].include.dedup(); + roots[k].exclude.dedup(); + } + } + + for root in roots.into_iter().filter(|it| !it.include.is_empty()) { let file_set_roots: Vec = root.include.iter().cloned().map(VfsPath::from).collect(); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs index 92029dc1de7..7d97b69f8ea 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs @@ -50,7 +50,7 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind { SymbolKind::Struct => lsp_types::SymbolKind::STRUCT, SymbolKind::Enum => lsp_types::SymbolKind::ENUM, SymbolKind::Variant => lsp_types::SymbolKind::ENUM_MEMBER, - SymbolKind::Trait => lsp_types::SymbolKind::INTERFACE, + SymbolKind::Trait | SymbolKind::TraitAlias => lsp_types::SymbolKind::INTERFACE, SymbolKind::Macro | SymbolKind::BuiltinAttr | SymbolKind::Attribute @@ -135,6 +135,7 @@ pub(crate) fn completion_item_kind( SymbolKind::Static => lsp_types::CompletionItemKind::VALUE, SymbolKind::Struct => lsp_types::CompletionItemKind::STRUCT, SymbolKind::Trait => lsp_types::CompletionItemKind::INTERFACE, + SymbolKind::TraitAlias => lsp_types::CompletionItemKind::INTERFACE, SymbolKind::TypeAlias => lsp_types::CompletionItemKind::STRUCT, SymbolKind::TypeParam => lsp_types::CompletionItemKind::TYPE_PARAMETER, SymbolKind::Union => lsp_types::CompletionItemKind::STRUCT, @@ -656,6 +657,7 @@ fn semantic_token_type_and_modifiers( SymbolKind::Union => semantic_tokens::UNION, SymbolKind::TypeAlias => semantic_tokens::TYPE_ALIAS, SymbolKind::Trait => semantic_tokens::INTERFACE, + SymbolKind::TraitAlias => semantic_tokens::INTERFACE, SymbolKind::Macro => semantic_tokens::MACRO, SymbolKind::BuiltinAttr => semantic_tokens::BUILTIN_ATTRIBUTE, SymbolKind::ToolModule => semantic_tokens::TOOL_MODULE, diff --git a/src/tools/rust-analyzer/crates/syntax/Cargo.toml b/src/tools/rust-analyzer/crates/syntax/Cargo.toml index 8fc493a23f5..305cf2d394b 100644 --- a/src/tools/rust-analyzer/crates/syntax/Cargo.toml +++ b/src/tools/rust-analyzer/crates/syntax/Cargo.toml @@ -14,6 +14,7 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" +either = "1.7.0" itertools = "0.10.5" rowan = "0.15.10" rustc_lexer = { version = "727.0.0", package = "rustc-ap-rustc_lexer" } diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 36ad5fddfd0..548b5ba8b8b 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -97,6 +97,7 @@ Item = | Static | Struct | Trait +| TraitAlias | TypeAlias | Union | Use @@ -240,10 +241,11 @@ Trait = Attr* Visibility? 'unsafe'? 'auto'? 'trait' Name GenericParamList? - ( - (':' TypeBoundList?)? WhereClause? AssocItemList - | '=' TypeBoundList? WhereClause? ';' - ) + (':' TypeBoundList?)? WhereClause? AssocItemList + +TraitAlias = + Attr* Visibility? + 'trait' Name GenericParamList? '=' TypeBoundList? WhereClause? ';' AssocItemList = '{' Attr* AssocItem* '}' diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast.rs b/src/tools/rust-analyzer/crates/syntax/src/ast.rs index 385a4e0a3ce..1e691befff6 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast.rs @@ -13,7 +13,7 @@ pub mod prec; use std::marker::PhantomData; -use itertools::Either; +use either::Either; use crate::{ syntax_node::{SyntaxNode, SyntaxNodeChildren, SyntaxToken}, @@ -25,7 +25,8 @@ pub use self::{ generated::{nodes::*, tokens::*}, node_ext::{ AttrKind, FieldKind, Macro, NameLike, NameOrNameRef, PathSegmentKind, SelfParamKind, - SlicePatComponents, StructKind, TypeBoundKind, TypeOrConstParam, VisibilityKind, + SlicePatComponents, StructKind, TraitOrAlias, TypeBoundKind, TypeOrConstParam, + VisibilityKind, }, operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp}, token_ext::{CommentKind, CommentPlacement, CommentShape, IsString, QuoteOffsets, Radix}, @@ -128,6 +129,13 @@ where } } +impl HasAttrs for Either +where + L: HasAttrs, + R: HasAttrs, +{ +} + mod support { use super::{AstChildren, AstNode, SyntaxKind, SyntaxNode, SyntaxToken}; diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index 642a3bfc35d..fe324845360 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -407,7 +407,21 @@ impl Trait { pub fn auto_token(&self) -> Option { support::token(&self.syntax, T![auto]) } pub fn trait_token(&self) -> Option { support::token(&self.syntax, T![trait]) } pub fn assoc_item_list(&self) -> Option { support::child(&self.syntax) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct TraitAlias { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasAttrs for TraitAlias {} +impl ast::HasName for TraitAlias {} +impl ast::HasVisibility for TraitAlias {} +impl ast::HasGenericParams for TraitAlias {} +impl ast::HasDocComments for TraitAlias {} +impl TraitAlias { + pub fn trait_token(&self) -> Option { support::token(&self.syntax, T![trait]) } pub fn eq_token(&self) -> Option { support::token(&self.syntax, T![=]) } + pub fn type_bound_list(&self) -> Option { support::child(&self.syntax) } pub fn semicolon_token(&self) -> Option { support::token(&self.syntax, T![;]) } } @@ -1573,6 +1587,7 @@ pub enum Item { Static(Static), Struct(Struct), Trait(Trait), + TraitAlias(TraitAlias), TypeAlias(TypeAlias), Union(Union), Use(Use), @@ -2058,6 +2073,17 @@ impl AstNode for Trait { } fn syntax(&self) -> &SyntaxNode { &self.syntax } } +impl AstNode for TraitAlias { + fn can_cast(kind: SyntaxKind) -> bool { kind == TRAIT_ALIAS } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} impl AstNode for TypeAlias { fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_ALIAS } fn cast(syntax: SyntaxNode) -> Option { @@ -3570,6 +3596,9 @@ impl From for Item { impl From for Item { fn from(node: Trait) -> Item { Item::Trait(node) } } +impl From for Item { + fn from(node: TraitAlias) -> Item { Item::TraitAlias(node) } +} impl From for Item { fn from(node: TypeAlias) -> Item { Item::TypeAlias(node) } } @@ -3596,6 +3625,7 @@ impl AstNode for Item { | STATIC | STRUCT | TRAIT + | TRAIT_ALIAS | TYPE_ALIAS | UNION | USE @@ -3616,6 +3646,7 @@ impl AstNode for Item { STATIC => Item::Static(Static { syntax }), STRUCT => Item::Struct(Struct { syntax }), TRAIT => Item::Trait(Trait { syntax }), + TRAIT_ALIAS => Item::TraitAlias(TraitAlias { syntax }), TYPE_ALIAS => Item::TypeAlias(TypeAlias { syntax }), UNION => Item::Union(Union { syntax }), USE => Item::Use(Use { syntax }), @@ -3638,6 +3669,7 @@ impl AstNode for Item { Item::Static(it) => &it.syntax, Item::Struct(it) => &it.syntax, Item::Trait(it) => &it.syntax, + Item::TraitAlias(it) => &it.syntax, Item::TypeAlias(it) => &it.syntax, Item::Union(it) => &it.syntax, Item::Use(it) => &it.syntax, @@ -3950,6 +3982,7 @@ impl AstNode for AnyHasAttrs { | STATIC | STRUCT | TRAIT + | TRAIT_ALIAS | TYPE_ALIAS | UNION | USE @@ -4035,6 +4068,7 @@ impl AstNode for AnyHasDocComments { | STATIC | STRUCT | TRAIT + | TRAIT_ALIAS | TYPE_ALIAS | UNION | USE @@ -4056,7 +4090,7 @@ impl AnyHasGenericParams { } impl AstNode for AnyHasGenericParams { fn can_cast(kind: SyntaxKind) -> bool { - matches!(kind, ENUM | FN | IMPL | STRUCT | TRAIT | TYPE_ALIAS | UNION) + matches!(kind, ENUM | FN | IMPL | STRUCT | TRAIT | TRAIT_ALIAS | TYPE_ALIAS | UNION) } fn cast(syntax: SyntaxNode) -> Option { Self::can_cast(syntax.kind()).then_some(AnyHasGenericParams { syntax }) @@ -4108,6 +4142,7 @@ impl AstNode for AnyHasName { | STATIC | STRUCT | TRAIT + | TRAIT_ALIAS | TYPE_ALIAS | UNION | RENAME @@ -4163,6 +4198,7 @@ impl AstNode for AnyHasVisibility { | STATIC | STRUCT | TRAIT + | TRAIT_ALIAS | TYPE_ALIAS | UNION | USE @@ -4391,6 +4427,11 @@ impl std::fmt::Display for Trait { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for TraitAlias { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for TypeAlias { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs index fe82aa90722..15bd5ab3c72 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs @@ -680,6 +680,81 @@ impl TypeOrConstParam { } } +impl AstNode for TypeOrConstParam { + fn can_cast(kind: SyntaxKind) -> bool + where + Self: Sized, + { + matches!(kind, SyntaxKind::TYPE_PARAM | SyntaxKind::CONST_PARAM) + } + + fn cast(syntax: SyntaxNode) -> Option + where + Self: Sized, + { + let res = match syntax.kind() { + SyntaxKind::TYPE_PARAM => TypeOrConstParam::Type(ast::TypeParam { syntax }), + SyntaxKind::CONST_PARAM => TypeOrConstParam::Const(ast::ConstParam { syntax }), + _ => return None, + }; + Some(res) + } + + fn syntax(&self) -> &SyntaxNode { + match self { + TypeOrConstParam::Type(it) => it.syntax(), + TypeOrConstParam::Const(it) => it.syntax(), + } + } +} + +impl HasAttrs for TypeOrConstParam {} + +#[derive(Debug, Clone)] +pub enum TraitOrAlias { + Trait(ast::Trait), + TraitAlias(ast::TraitAlias), +} + +impl TraitOrAlias { + pub fn name(&self) -> Option { + match self { + TraitOrAlias::Trait(x) => x.name(), + TraitOrAlias::TraitAlias(x) => x.name(), + } + } +} + +impl AstNode for TraitOrAlias { + fn can_cast(kind: SyntaxKind) -> bool + where + Self: Sized, + { + matches!(kind, SyntaxKind::TRAIT | SyntaxKind::TRAIT_ALIAS) + } + + fn cast(syntax: SyntaxNode) -> Option + where + Self: Sized, + { + let res = match syntax.kind() { + SyntaxKind::TRAIT => TraitOrAlias::Trait(ast::Trait { syntax }), + SyntaxKind::TRAIT_ALIAS => TraitOrAlias::TraitAlias(ast::TraitAlias { syntax }), + _ => return None, + }; + Some(res) + } + + fn syntax(&self) -> &SyntaxNode { + match self { + TraitOrAlias::Trait(it) => it.syntax(), + TraitOrAlias::TraitAlias(it) => it.syntax(), + } + } +} + +impl HasAttrs for TraitOrAlias {} + pub enum VisibilityKind { In(ast::Path), PubCrate, diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs index aa2b7ed5c8b..3e43df2d0d5 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs @@ -1,7 +1,7 @@ //! Various traits that are implemented by ast nodes. //! //! The implementations are usually trivial, and live in generated.rs -use itertools::Either; +use either::Either; use crate::{ ast::{self, support, AstChildren, AstNode, AstToken}, @@ -134,3 +134,5 @@ impl Iterator for AttrDocCommentIter { }) } } + +impl HasName for Either {} diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs b/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs index 3ff6e03006b..ccce71966ff 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs @@ -86,6 +86,7 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc { "STATIC", "CONST", "TRAIT", + "TRAIT_ALIAS", "IMPL", "TYPE_ALIAS", "MACRO_CALL", diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs index 03aa2c451e8..e954b58251f 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs @@ -783,6 +783,7 @@ fn extract_struct_traits(ast: &mut AstSrc) { "Enum", "Variant", "Trait", + "TraitAlias", "Module", "Static", "Const", diff --git a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs index d1afd0039aa..cd1235fa6dc 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs @@ -180,7 +180,9 @@ impl Fixture { let mut cfg_key_values = Vec::new(); let mut env = FxHashMap::default(); let mut introduce_new_source_root = None; - let mut target_data_layout = None; + let mut target_data_layout = Some( + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128".to_string(), + ); for component in components[1..].iter() { let (key, value) = component.split_once(':').unwrap_or_else(|| panic!("invalid meta line: {meta:?}")); diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 3b033e1aae5..93ff76a040c 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -510,6 +510,7 @@ pub mod fmt { pub mod slice { #[lang = "slice"] impl [T] { + #[lang = "slice_len_fn"] pub fn len(&self) -> usize { loop {} } @@ -533,6 +534,40 @@ pub mod option { None => panic!("called `Option::unwrap()` on a `None` value"), } } + + pub fn and(self, optb: Option) -> Option { + loop {} + } + pub fn unwrap_or(self, default: T) -> T { + loop {} + } + // region:fn + pub fn and_then(self, f: F) -> Option + where + F: FnOnce(T) -> Option, + { + loop {} + } + pub fn unwrap_or_else(self, f: F) -> T + where + F: FnOnce() -> T, + { + loop {} + } + pub fn map_or(self, default: U, f: F) -> U + where + F: FnOnce(T) -> U, + { + loop {} + } + pub fn map_or_else(self, default: D, f: F) -> U + where + D: FnOnce() -> U, + F: FnOnce(T) -> U, + { + loop {} + } + // endregion:fn } } // endregion:option @@ -727,6 +762,20 @@ pub mod iter { self } } + pub struct IntoIter([T; N]); + impl IntoIterator for [T; N] { + type Item = T; + type IntoIter = IntoIter; + fn into_iter(self) -> I { + IntoIter(self) + } + } + impl Iterator for IntoIter { + type Item = T; + fn next(&mut self) -> Option { + loop {} + } + } } pub use self::collect::IntoIterator; } diff --git a/src/tools/rust-analyzer/crates/toolchain/src/lib.rs b/src/tools/rust-analyzer/crates/toolchain/src/lib.rs index 67bdad2aadd..729f84a8150 100644 --- a/src/tools/rust-analyzer/crates/toolchain/src/lib.rs +++ b/src/tools/rust-analyzer/crates/toolchain/src/lib.rs @@ -31,8 +31,9 @@ fn get_path_for_executable(executable_name: &'static str) -> PathBuf { // example: for cargo, this checks $CARGO environment variable; for rustc, $RUSTC; etc // 2) `` // example: for cargo, this tries just `cargo`, which will succeed if `cargo` is on the $PATH - // 3) `~/.cargo/bin/` - // example: for cargo, this tries ~/.cargo/bin/cargo + // 3) `$CARGO_HOME/bin/` + // where $CARGO_HOME defaults to ~/.cargo (see https://doc.rust-lang.org/cargo/guide/cargo-home.html) + // example: for cargo, this tries $CARGO_HOME/bin/cargo, or ~/.cargo/bin/cargo if $CARGO_HOME is unset. // It seems that this is a reasonable place to try for cargo, rustc, and rustup let env_var = executable_name.to_ascii_uppercase(); if let Some(path) = env::var_os(env_var) { @@ -43,8 +44,7 @@ fn get_path_for_executable(executable_name: &'static str) -> PathBuf { return executable_name.into(); } - if let Some(mut path) = home::home_dir() { - path.push(".cargo"); + if let Some(mut path) = get_cargo_home() { path.push("bin"); path.push(executable_name); if let Some(path) = probe(path) { @@ -60,6 +60,19 @@ fn lookup_in_path(exec: &str) -> bool { env::split_paths(&paths).map(|path| path.join(exec)).find_map(probe).is_some() } +fn get_cargo_home() -> Option { + if let Some(path) = env::var_os("CARGO_HOME") { + return Some(path.into()); + } + + if let Some(mut path) = home::home_dir() { + path.push(".cargo"); + return Some(path); + } + + None +} + fn probe(path: PathBuf) -> Option { let with_extension = match env::consts::EXE_EXTENSION { "" => None, diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md index c3623a5cc46..de142203208 100644 --- a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md +++ b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@ $DIR/parse-error.rs:39:31 - | -LL | asm!("{}", options(), const foo); - | --------- ^^^^^^^^^ argument - | | - | previous options - error: expected string literal - --> $DIR/parse-error.rs:42:30 + --> $DIR/parse-error.rs:41:30 | LL | asm!("", clobber_abi(foo)); | ^^^ not a string literal error: expected one of `)` or `,`, found `foo` - --> $DIR/parse-error.rs:44:34 + --> $DIR/parse-error.rs:43:34 | LL | asm!("", clobber_abi("C" foo)); | ^^^ expected one of `)` or `,` error: expected string literal - --> $DIR/parse-error.rs:46:35 + --> $DIR/parse-error.rs:45:35 | LL | asm!("", clobber_abi("C", foo)); | ^^^ not a string literal -error: arguments are not allowed after clobber_abi - --> $DIR/parse-error.rs:48:38 - | -LL | asm!("{}", clobber_abi("C"), const foo); - | ---------------- ^^^^^^^^^ argument - | | - | clobber_abi - -error: clobber_abi is not allowed after options - --> $DIR/parse-error.rs:51:29 - | -LL | asm!("", options(), clobber_abi("C")); - | --------- ^^^^^^^^^^^^^^^^ - | | - | options - -error: clobber_abi is not allowed after options - --> $DIR/parse-error.rs:53:31 - | -LL | asm!("{}", options(), clobber_abi("C"), const foo); - | --------- ^^^^^^^^^^^^^^^^ - | | - | options - error: duplicate argument named `a` - --> $DIR/parse-error.rs:55:36 + --> $DIR/parse-error.rs:52:36 | LL | asm!("{a}", a = const foo, a = const bar); | ------------- ^^^^^^^^^^^^^ duplicate argument @@ -141,7 +109,7 @@ LL | asm!("{a}", a = const foo, a = const bar); | previously here error: argument never used - --> $DIR/parse-error.rs:55:36 + --> $DIR/parse-error.rs:52:36 | LL | asm!("{a}", a = const foo, a = const bar); | ^^^^^^^^^^^^^ argument never used @@ -149,29 +117,13 @@ LL | asm!("{a}", a = const foo, a = const bar); = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` error: explicit register arguments cannot have names - --> $DIR/parse-error.rs:60:18 + --> $DIR/parse-error.rs:57:18 | LL | asm!("", a = in("x0") foo); | ^^^^^^^^^^^^^^^^ -error: named arguments cannot follow explicit register arguments - --> $DIR/parse-error.rs:62:35 - | -LL | asm!("{a}", in("x0") foo, a = const bar); - | ------------ ^^^^^^^^^^^^^ named argument - | | - | explicit register argument - -error: named arguments cannot follow explicit register arguments - --> $DIR/parse-error.rs:65:35 - | -LL | asm!("{a}", in("x0") foo, a = const bar); - | ------------ ^^^^^^^^^^^^^ named argument - | | - | explicit register argument - error: positional arguments cannot follow named arguments or explicit register arguments - --> $DIR/parse-error.rs:68:35 + --> $DIR/parse-error.rs:63:35 | LL | asm!("{1}", in("x0") foo, const bar); | ------------ ^^^^^^^^^ positional argument @@ -179,19 +131,19 @@ LL | asm!("{1}", in("x0") foo, const bar); | explicit register argument error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `""` - --> $DIR/parse-error.rs:71:29 + --> $DIR/parse-error.rs:66:29 | LL | asm!("", options(), ""); | ^^ expected one of 9 possible tokens error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `"{}"` - --> $DIR/parse-error.rs:73:33 + --> $DIR/parse-error.rs:68:33 | LL | asm!("{}", in(reg) foo, "{}", out(reg) foo); | ^^^^ expected one of 9 possible tokens error: asm template must be a string literal - --> $DIR/parse-error.rs:75:14 + --> $DIR/parse-error.rs:70:14 | LL | asm!(format!("{{{}}}", 0), in(reg) foo); | ^^^^^^^^^^^^^^^^^^^^ @@ -199,7 +151,7 @@ LL | asm!(format!("{{{}}}", 0), in(reg) foo); = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: asm template must be a string literal - --> $DIR/parse-error.rs:77:21 + --> $DIR/parse-error.rs:72:21 | LL | asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar); | ^^^^^^^^^^^^^^^^^^^^ @@ -207,135 +159,115 @@ LL | asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar); = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: _ cannot be used for input operands - --> $DIR/parse-error.rs:79:28 + --> $DIR/parse-error.rs:74:28 | LL | asm!("{}", in(reg) _); | ^ error: _ cannot be used for input operands - --> $DIR/parse-error.rs:81:31 + --> $DIR/parse-error.rs:76:31 | LL | asm!("{}", inout(reg) _); | ^ error: _ cannot be used for input operands - --> $DIR/parse-error.rs:83:35 + --> $DIR/parse-error.rs:78:35 | LL | asm!("{}", inlateout(reg) _); | ^ error: requires at least a template string argument - --> $DIR/parse-error.rs:90:1 + --> $DIR/parse-error.rs:85:1 | LL | global_asm!(); | ^^^^^^^^^^^^^ error: asm template must be a string literal - --> $DIR/parse-error.rs:92:13 + --> $DIR/parse-error.rs:87:13 | LL | global_asm!(FOO); | ^^^ error: expected token: `,` - --> $DIR/parse-error.rs:94:18 + --> $DIR/parse-error.rs:89:18 | LL | global_asm!("{}" FOO); | ^^^ expected `,` error: expected operand, options, or additional template string - --> $DIR/parse-error.rs:96:19 + --> $DIR/parse-error.rs:91:19 | LL | global_asm!("{}", FOO); | ^^^ expected operand, options, or additional template string error: expected expression, found end of macro arguments - --> $DIR/parse-error.rs:98:24 + --> $DIR/parse-error.rs:93:24 | LL | global_asm!("{}", const); | ^ expected expression error: expected one of `,`, `.`, `?`, or an operator, found `FOO` - --> $DIR/parse-error.rs:100:30 + --> $DIR/parse-error.rs:95:30 | LL | global_asm!("{}", const(reg) FOO); | ^^^ expected one of `,`, `.`, `?`, or an operator error: expected one of `)`, `att_syntax`, or `raw`, found `FOO` - --> $DIR/parse-error.rs:102:25 + --> $DIR/parse-error.rs:97:25 | LL | global_asm!("", options(FOO)); | ^^^ expected one of `)`, `att_syntax`, or `raw` error: expected one of `)`, `att_syntax`, or `raw`, found `nomem` - --> $DIR/parse-error.rs:104:25 + --> $DIR/parse-error.rs:99:25 | LL | global_asm!("", options(nomem FOO)); | ^^^^^ expected one of `)`, `att_syntax`, or `raw` error: expected one of `)`, `att_syntax`, or `raw`, found `nomem` - --> $DIR/parse-error.rs:106:25 + --> $DIR/parse-error.rs:101:25 | LL | global_asm!("", options(nomem, FOO)); | ^^^^^ expected one of `)`, `att_syntax`, or `raw` -error: arguments are not allowed after options - --> $DIR/parse-error.rs:108:30 - | -LL | global_asm!("{}", options(), const FOO); - | --------- ^^^^^^^^^ argument - | | - | previous options - error: expected string literal - --> $DIR/parse-error.rs:110:29 + --> $DIR/parse-error.rs:104:29 | LL | global_asm!("", clobber_abi(FOO)); | ^^^ not a string literal error: expected one of `)` or `,`, found `FOO` - --> $DIR/parse-error.rs:112:33 + --> $DIR/parse-error.rs:106:33 | LL | global_asm!("", clobber_abi("C" FOO)); | ^^^ expected one of `)` or `,` error: expected string literal - --> $DIR/parse-error.rs:114:34 + --> $DIR/parse-error.rs:108:34 | LL | global_asm!("", clobber_abi("C", FOO)); | ^^^ not a string literal -error: arguments are not allowed after clobber_abi - --> $DIR/parse-error.rs:116:37 - | -LL | global_asm!("{}", clobber_abi("C"), const FOO); - | ---------------- ^^^^^^^^^ argument - | | - | clobber_abi - error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:116:19 + --> $DIR/parse-error.rs:110:19 | LL | global_asm!("{}", clobber_abi("C"), const FOO); | ^^^^^^^^^^^^^^^^ -error: clobber_abi is not allowed after options - --> $DIR/parse-error.rs:119:28 +error: `clobber_abi` cannot be used with `global_asm!` + --> $DIR/parse-error.rs:112:28 | LL | global_asm!("", options(), clobber_abi("C")); - | --------- ^^^^^^^^^^^^^^^^ - | | - | options + | ^^^^^^^^^^^^^^^^ -error: clobber_abi is not allowed after options - --> $DIR/parse-error.rs:121:30 +error: `clobber_abi` cannot be used with `global_asm!` + --> $DIR/parse-error.rs:114:30 | LL | global_asm!("{}", options(), clobber_abi("C"), const FOO); - | --------- ^^^^^^^^^^^^^^^^ - | | - | options + | ^^^^^^^^^^^^^^^^ error: duplicate argument named `a` - --> $DIR/parse-error.rs:123:35 + --> $DIR/parse-error.rs:116:35 | LL | global_asm!("{a}", a = const FOO, a = const BAR); | ------------- ^^^^^^^^^^^^^ duplicate argument @@ -343,7 +275,7 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR); | previously here error: argument never used - --> $DIR/parse-error.rs:123:35 + --> $DIR/parse-error.rs:116:35 | LL | global_asm!("{a}", a = const FOO, a = const BAR); | ^^^^^^^^^^^^^ argument never used @@ -351,19 +283,19 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR); = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `""` - --> $DIR/parse-error.rs:126:28 + --> $DIR/parse-error.rs:119:28 | LL | global_asm!("", options(), ""); | ^^ expected one of `clobber_abi`, `const`, `options`, or `sym` error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `"{}"` - --> $DIR/parse-error.rs:128:30 + --> $DIR/parse-error.rs:121:30 | LL | global_asm!("{}", const FOO, "{}", const FOO); | ^^^^ expected one of `clobber_abi`, `const`, `options`, or `sym` error: asm template must be a string literal - --> $DIR/parse-error.rs:130:13 + --> $DIR/parse-error.rs:123:13 | LL | global_asm!(format!("{{{}}}", 0), const FOO); | ^^^^^^^^^^^^^^^^^^^^ @@ -371,7 +303,7 @@ LL | global_asm!(format!("{{{}}}", 0), const FOO); = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: asm template must be a string literal - --> $DIR/parse-error.rs:132:20 + --> $DIR/parse-error.rs:125:20 | LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR); | ^^^^^^^^^^^^^^^^^^^^ @@ -388,7 +320,7 @@ LL | asm!("{}", options(), const foo); | ^^^ non-constant value error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:48:44 + --> $DIR/parse-error.rs:47:44 | LL | let mut foo = 0; | ----------- help: consider using `const` instead of `let`: `const foo` @@ -397,7 +329,16 @@ LL | asm!("{}", clobber_abi("C"), const foo); | ^^^ non-constant value error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:55:31 + --> $DIR/parse-error.rs:50:55 + | +LL | let mut foo = 0; + | ----------- help: consider using `const` instead of `let`: `const foo` +... +LL | asm!("{}", options(), clobber_abi("C"), const foo); + | ^^^ non-constant value + +error[E0435]: attempt to use a non-constant value in a constant + --> $DIR/parse-error.rs:52:31 | LL | let mut foo = 0; | ----------- help: consider using `const` instead of `let`: `const foo` @@ -406,7 +347,7 @@ LL | asm!("{a}", a = const foo, a = const bar); | ^^^ non-constant value error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:55:46 + --> $DIR/parse-error.rs:52:46 | LL | let mut bar = 0; | ----------- help: consider using `const` instead of `let`: `const bar` @@ -415,7 +356,7 @@ LL | asm!("{a}", a = const foo, a = const bar); | ^^^ non-constant value error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:62:45 + --> $DIR/parse-error.rs:59:45 | LL | let mut bar = 0; | ----------- help: consider using `const` instead of `let`: `const bar` @@ -424,7 +365,7 @@ LL | asm!("{a}", in("x0") foo, a = const bar); | ^^^ non-constant value error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:65:45 + --> $DIR/parse-error.rs:61:45 | LL | let mut bar = 0; | ----------- help: consider using `const` instead of `let`: `const bar` @@ -433,7 +374,7 @@ LL | asm!("{a}", in("x0") foo, a = const bar); | ^^^ non-constant value error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:68:41 + --> $DIR/parse-error.rs:63:41 | LL | let mut bar = 0; | ----------- help: consider using `const` instead of `let`: `const bar` @@ -441,6 +382,6 @@ LL | let mut bar = 0; LL | asm!("{1}", in("x0") foo, const bar); | ^^^ non-constant value -error: aborting due to 64 previous errors +error: aborting due to 57 previous errors For more information about this error, try `rustc --explain E0435`. diff --git a/tests/ui/asm/bad-template.aarch64_mirunsafeck.stderr b/tests/ui/asm/bad-template.aarch64_mirunsafeck.stderr index bb6a222b22e..b16f9a06c2a 100644 --- a/tests/ui/asm/bad-template.aarch64_mirunsafeck.stderr +++ b/tests/ui/asm/bad-template.aarch64_mirunsafeck.stderr @@ -81,6 +81,11 @@ note: explicit register arguments cannot be used in the asm template | LL | asm!("{}", in("x0") foo); | ^^^^^^^^^^^^ +help: use the register name directly in the assembly code + --> $DIR/bad-template.rs:48:20 + | +LL | asm!("{}", in("x0") foo); + | ^^^^^^^^^^^^ error: asm template modifier must be a single character --> $DIR/bad-template.rs:50:17 diff --git a/tests/ui/asm/bad-template.aarch64_thirunsafeck.stderr b/tests/ui/asm/bad-template.aarch64_thirunsafeck.stderr index bb6a222b22e..b16f9a06c2a 100644 --- a/tests/ui/asm/bad-template.aarch64_thirunsafeck.stderr +++ b/tests/ui/asm/bad-template.aarch64_thirunsafeck.stderr @@ -81,6 +81,11 @@ note: explicit register arguments cannot be used in the asm template | LL | asm!("{}", in("x0") foo); | ^^^^^^^^^^^^ +help: use the register name directly in the assembly code + --> $DIR/bad-template.rs:48:20 + | +LL | asm!("{}", in("x0") foo); + | ^^^^^^^^^^^^ error: asm template modifier must be a single character --> $DIR/bad-template.rs:50:17 diff --git a/tests/ui/asm/bad-template.x86_64_mirunsafeck.stderr b/tests/ui/asm/bad-template.x86_64_mirunsafeck.stderr index 903b5e959f3..41ac37c33c2 100644 --- a/tests/ui/asm/bad-template.x86_64_mirunsafeck.stderr +++ b/tests/ui/asm/bad-template.x86_64_mirunsafeck.stderr @@ -81,6 +81,11 @@ note: explicit register arguments cannot be used in the asm template | LL | asm!("{}", in("eax") foo); | ^^^^^^^^^^^^^ +help: use the register name directly in the assembly code + --> $DIR/bad-template.rs:45:20 + | +LL | asm!("{}", in("eax") foo); + | ^^^^^^^^^^^^^ error: asm template modifier must be a single character --> $DIR/bad-template.rs:50:17 diff --git a/tests/ui/asm/bad-template.x86_64_thirunsafeck.stderr b/tests/ui/asm/bad-template.x86_64_thirunsafeck.stderr index 903b5e959f3..41ac37c33c2 100644 --- a/tests/ui/asm/bad-template.x86_64_thirunsafeck.stderr +++ b/tests/ui/asm/bad-template.x86_64_thirunsafeck.stderr @@ -81,6 +81,11 @@ note: explicit register arguments cannot be used in the asm template | LL | asm!("{}", in("eax") foo); | ^^^^^^^^^^^^^ +help: use the register name directly in the assembly code + --> $DIR/bad-template.rs:45:20 + | +LL | asm!("{}", in("eax") foo); + | ^^^^^^^^^^^^^ error: asm template modifier must be a single character --> $DIR/bad-template.rs:50:17 diff --git a/tests/ui/asm/x86_64/issue-89875.rs b/tests/ui/asm/x86_64/issue-89875.rs index 669fd7e7e46..39c64564022 100644 --- a/tests/ui/asm/x86_64/issue-89875.rs +++ b/tests/ui/asm/x86_64/issue-89875.rs @@ -7,7 +7,7 @@ use std::arch::asm; #[target_feature(enable = "avx")] -fn main() { +fn foo() { unsafe { asm!( "/* {} */", @@ -15,3 +15,5 @@ fn main() { ); } } + +fn main() {} diff --git a/tests/ui/asm/x86_64/parse-error.rs b/tests/ui/asm/x86_64/parse-error.rs index 9aeb6b2853f..2e714d464ae 100644 --- a/tests/ui/asm/x86_64/parse-error.rs +++ b/tests/ui/asm/x86_64/parse-error.rs @@ -37,8 +37,7 @@ fn main() { asm!("", options(nomem, foo)); //~^ ERROR expected one of asm!("{}", options(), const foo); - //~^ ERROR arguments are not allowed after options - //~^^ ERROR attempt to use a non-constant value in a constant + //~^ ERROR attempt to use a non-constant value in a constant asm!("", clobber_abi()); //~^ ERROR at least one abi must be provided asm!("", clobber_abi(foo)); @@ -48,12 +47,10 @@ fn main() { asm!("", clobber_abi("C", foo)); //~^ ERROR expected string literal asm!("{}", clobber_abi("C"), const foo); - //~^ ERROR arguments are not allowed after clobber_abi - //~^^ ERROR attempt to use a non-constant value in a constant + //~^ ERROR attempt to use a non-constant value in a constant asm!("", options(), clobber_abi("C")); - //~^ ERROR clobber_abi is not allowed after options asm!("{}", options(), clobber_abi("C"), const foo); - //~^ ERROR clobber_abi is not allowed after options + //~^ ERROR attempt to use a non-constant value in a constant asm!("{a}", a = const foo, a = const bar); //~^ ERROR duplicate argument named `a` //~^^ ERROR argument never used @@ -62,11 +59,9 @@ fn main() { asm!("", a = in("eax") foo); //~^ ERROR explicit register arguments cannot have names asm!("{a}", in("eax") foo, a = const bar); - //~^ ERROR named arguments cannot follow explicit register arguments - //~^^ ERROR attempt to use a non-constant value in a constant + //~^ ERROR attempt to use a non-constant value in a constant asm!("{a}", in("eax") foo, a = const bar); - //~^ ERROR named arguments cannot follow explicit register arguments - //~^^ ERROR attempt to use a non-constant value in a constant + //~^ ERROR attempt to use a non-constant value in a constant asm!("{1}", in("eax") foo, const bar); //~^ ERROR positional arguments cannot follow named arguments or explicit register arguments //~^^ ERROR attempt to use a non-constant value in a constant @@ -108,7 +103,6 @@ global_asm!("", options(nomem FOO)); global_asm!("", options(nomem, FOO)); //~^ ERROR expected one of global_asm!("{}", options(), const FOO); -//~^ ERROR arguments are not allowed after options global_asm!("", clobber_abi(FOO)); //~^ ERROR expected string literal global_asm!("", clobber_abi("C" FOO)); @@ -116,12 +110,11 @@ global_asm!("", clobber_abi("C" FOO)); global_asm!("", clobber_abi("C", FOO)); //~^ ERROR expected string literal global_asm!("{}", clobber_abi("C"), const FOO); -//~^ ERROR arguments are not allowed after clobber_abi -//~^^ ERROR `clobber_abi` cannot be used with `global_asm!` +//~^ ERROR `clobber_abi` cannot be used with `global_asm!` global_asm!("", options(), clobber_abi("C")); -//~^ ERROR clobber_abi is not allowed after options +//~^ ERROR `clobber_abi` cannot be used with `global_asm!` global_asm!("{}", options(), clobber_abi("C"), const FOO); -//~^ ERROR clobber_abi is not allowed after options +//~^ ERROR `clobber_abi` cannot be used with `global_asm!` global_asm!("", clobber_abi("C"), clobber_abi("C")); //~^ ERROR `clobber_abi` cannot be used with `global_asm!` global_asm!("{a}", a = const FOO, a = const BAR); diff --git a/tests/ui/asm/x86_64/parse-error.stderr b/tests/ui/asm/x86_64/parse-error.stderr index 57702c37b7c..0c9d6f71529 100644 --- a/tests/ui/asm/x86_64/parse-error.stderr +++ b/tests/ui/asm/x86_64/parse-error.stderr @@ -82,64 +82,32 @@ error: expected one of `)`, `att_syntax`, `may_unwind`, `nomem`, `noreturn`, `no LL | asm!("", options(nomem, foo)); | ^^^ expected one of 10 possible tokens -error: arguments are not allowed after options - --> $DIR/parse-error.rs:39:31 - | -LL | asm!("{}", options(), const foo); - | --------- ^^^^^^^^^ argument - | | - | previous options - error: at least one abi must be provided as an argument to `clobber_abi` - --> $DIR/parse-error.rs:42:30 + --> $DIR/parse-error.rs:41:30 | LL | asm!("", clobber_abi()); | ^ error: expected string literal - --> $DIR/parse-error.rs:44:30 + --> $DIR/parse-error.rs:43:30 | LL | asm!("", clobber_abi(foo)); | ^^^ not a string literal error: expected one of `)` or `,`, found `foo` - --> $DIR/parse-error.rs:46:34 + --> $DIR/parse-error.rs:45:34 | LL | asm!("", clobber_abi("C" foo)); | ^^^ expected one of `)` or `,` error: expected string literal - --> $DIR/parse-error.rs:48:35 + --> $DIR/parse-error.rs:47:35 | LL | asm!("", clobber_abi("C", foo)); | ^^^ not a string literal -error: arguments are not allowed after clobber_abi - --> $DIR/parse-error.rs:50:38 - | -LL | asm!("{}", clobber_abi("C"), const foo); - | ---------------- ^^^^^^^^^ argument - | | - | clobber_abi - -error: clobber_abi is not allowed after options - --> $DIR/parse-error.rs:53:29 - | -LL | asm!("", options(), clobber_abi("C")); - | --------- ^^^^^^^^^^^^^^^^ - | | - | options - -error: clobber_abi is not allowed after options - --> $DIR/parse-error.rs:55:31 - | -LL | asm!("{}", options(), clobber_abi("C"), const foo); - | --------- ^^^^^^^^^^^^^^^^ - | | - | options - error: duplicate argument named `a` - --> $DIR/parse-error.rs:57:36 + --> $DIR/parse-error.rs:54:36 | LL | asm!("{a}", a = const foo, a = const bar); | ------------- ^^^^^^^^^^^^^ duplicate argument @@ -147,7 +115,7 @@ LL | asm!("{a}", a = const foo, a = const bar); | previously here error: argument never used - --> $DIR/parse-error.rs:57:36 + --> $DIR/parse-error.rs:54:36 | LL | asm!("{a}", a = const foo, a = const bar); | ^^^^^^^^^^^^^ argument never used @@ -155,29 +123,13 @@ LL | asm!("{a}", a = const foo, a = const bar); = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` error: explicit register arguments cannot have names - --> $DIR/parse-error.rs:62:18 + --> $DIR/parse-error.rs:59:18 | LL | asm!("", a = in("eax") foo); | ^^^^^^^^^^^^^^^^^ -error: named arguments cannot follow explicit register arguments - --> $DIR/parse-error.rs:64:36 - | -LL | asm!("{a}", in("eax") foo, a = const bar); - | ------------- ^^^^^^^^^^^^^ named argument - | | - | explicit register argument - -error: named arguments cannot follow explicit register arguments - --> $DIR/parse-error.rs:67:36 - | -LL | asm!("{a}", in("eax") foo, a = const bar); - | ------------- ^^^^^^^^^^^^^ named argument - | | - | explicit register argument - error: positional arguments cannot follow named arguments or explicit register arguments - --> $DIR/parse-error.rs:70:36 + --> $DIR/parse-error.rs:65:36 | LL | asm!("{1}", in("eax") foo, const bar); | ------------- ^^^^^^^^^ positional argument @@ -185,19 +137,19 @@ LL | asm!("{1}", in("eax") foo, const bar); | explicit register argument error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `""` - --> $DIR/parse-error.rs:73:29 + --> $DIR/parse-error.rs:68:29 | LL | asm!("", options(), ""); | ^^ expected one of 9 possible tokens error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `"{}"` - --> $DIR/parse-error.rs:75:33 + --> $DIR/parse-error.rs:70:33 | LL | asm!("{}", in(reg) foo, "{}", out(reg) foo); | ^^^^ expected one of 9 possible tokens error: asm template must be a string literal - --> $DIR/parse-error.rs:77:14 + --> $DIR/parse-error.rs:72:14 | LL | asm!(format!("{{{}}}", 0), in(reg) foo); | ^^^^^^^^^^^^^^^^^^^^ @@ -205,7 +157,7 @@ LL | asm!(format!("{{{}}}", 0), in(reg) foo); = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: asm template must be a string literal - --> $DIR/parse-error.rs:79:21 + --> $DIR/parse-error.rs:74:21 | LL | asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar); | ^^^^^^^^^^^^^^^^^^^^ @@ -213,141 +165,121 @@ LL | asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar); = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: _ cannot be used for input operands - --> $DIR/parse-error.rs:81:28 + --> $DIR/parse-error.rs:76:28 | LL | asm!("{}", in(reg) _); | ^ error: _ cannot be used for input operands - --> $DIR/parse-error.rs:83:31 + --> $DIR/parse-error.rs:78:31 | LL | asm!("{}", inout(reg) _); | ^ error: _ cannot be used for input operands - --> $DIR/parse-error.rs:85:35 + --> $DIR/parse-error.rs:80:35 | LL | asm!("{}", inlateout(reg) _); | ^ error: requires at least a template string argument - --> $DIR/parse-error.rs:92:1 + --> $DIR/parse-error.rs:87:1 | LL | global_asm!(); | ^^^^^^^^^^^^^ error: asm template must be a string literal - --> $DIR/parse-error.rs:94:13 + --> $DIR/parse-error.rs:89:13 | LL | global_asm!(FOO); | ^^^ error: expected token: `,` - --> $DIR/parse-error.rs:96:18 + --> $DIR/parse-error.rs:91:18 | LL | global_asm!("{}" FOO); | ^^^ expected `,` error: expected operand, options, or additional template string - --> $DIR/parse-error.rs:98:19 + --> $DIR/parse-error.rs:93:19 | LL | global_asm!("{}", FOO); | ^^^ expected operand, options, or additional template string error: expected expression, found end of macro arguments - --> $DIR/parse-error.rs:100:24 + --> $DIR/parse-error.rs:95:24 | LL | global_asm!("{}", const); | ^ expected expression error: expected one of `,`, `.`, `?`, or an operator, found `FOO` - --> $DIR/parse-error.rs:102:30 + --> $DIR/parse-error.rs:97:30 | LL | global_asm!("{}", const(reg) FOO); | ^^^ expected one of `,`, `.`, `?`, or an operator error: expected one of `)`, `att_syntax`, or `raw`, found `FOO` - --> $DIR/parse-error.rs:104:25 + --> $DIR/parse-error.rs:99:25 | LL | global_asm!("", options(FOO)); | ^^^ expected one of `)`, `att_syntax`, or `raw` error: expected one of `)`, `att_syntax`, or `raw`, found `nomem` - --> $DIR/parse-error.rs:106:25 + --> $DIR/parse-error.rs:101:25 | LL | global_asm!("", options(nomem FOO)); | ^^^^^ expected one of `)`, `att_syntax`, or `raw` error: expected one of `)`, `att_syntax`, or `raw`, found `nomem` - --> $DIR/parse-error.rs:108:25 + --> $DIR/parse-error.rs:103:25 | LL | global_asm!("", options(nomem, FOO)); | ^^^^^ expected one of `)`, `att_syntax`, or `raw` -error: arguments are not allowed after options - --> $DIR/parse-error.rs:110:30 - | -LL | global_asm!("{}", options(), const FOO); - | --------- ^^^^^^^^^ argument - | | - | previous options - error: expected string literal - --> $DIR/parse-error.rs:112:29 + --> $DIR/parse-error.rs:106:29 | LL | global_asm!("", clobber_abi(FOO)); | ^^^ not a string literal error: expected one of `)` or `,`, found `FOO` - --> $DIR/parse-error.rs:114:33 + --> $DIR/parse-error.rs:108:33 | LL | global_asm!("", clobber_abi("C" FOO)); | ^^^ expected one of `)` or `,` error: expected string literal - --> $DIR/parse-error.rs:116:34 + --> $DIR/parse-error.rs:110:34 | LL | global_asm!("", clobber_abi("C", FOO)); | ^^^ not a string literal -error: arguments are not allowed after clobber_abi - --> $DIR/parse-error.rs:118:37 - | -LL | global_asm!("{}", clobber_abi("C"), const FOO); - | ---------------- ^^^^^^^^^ argument - | | - | clobber_abi - error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:118:19 + --> $DIR/parse-error.rs:112:19 | LL | global_asm!("{}", clobber_abi("C"), const FOO); | ^^^^^^^^^^^^^^^^ -error: clobber_abi is not allowed after options - --> $DIR/parse-error.rs:121:28 +error: `clobber_abi` cannot be used with `global_asm!` + --> $DIR/parse-error.rs:114:28 | LL | global_asm!("", options(), clobber_abi("C")); - | --------- ^^^^^^^^^^^^^^^^ - | | - | options - -error: clobber_abi is not allowed after options - --> $DIR/parse-error.rs:123:30 - | -LL | global_asm!("{}", options(), clobber_abi("C"), const FOO); - | --------- ^^^^^^^^^^^^^^^^ - | | - | options + | ^^^^^^^^^^^^^^^^ error: `clobber_abi` cannot be used with `global_asm!` - --> $DIR/parse-error.rs:125:17 + --> $DIR/parse-error.rs:116:30 + | +LL | global_asm!("{}", options(), clobber_abi("C"), const FOO); + | ^^^^^^^^^^^^^^^^ + +error: `clobber_abi` cannot be used with `global_asm!` + --> $DIR/parse-error.rs:118:17 | LL | global_asm!("", clobber_abi("C"), clobber_abi("C")); | ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ error: duplicate argument named `a` - --> $DIR/parse-error.rs:127:35 + --> $DIR/parse-error.rs:120:35 | LL | global_asm!("{a}", a = const FOO, a = const BAR); | ------------- ^^^^^^^^^^^^^ duplicate argument @@ -355,7 +287,7 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR); | previously here error: argument never used - --> $DIR/parse-error.rs:127:35 + --> $DIR/parse-error.rs:120:35 | LL | global_asm!("{a}", a = const FOO, a = const BAR); | ^^^^^^^^^^^^^ argument never used @@ -363,19 +295,19 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR); = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `""` - --> $DIR/parse-error.rs:130:28 + --> $DIR/parse-error.rs:123:28 | LL | global_asm!("", options(), ""); | ^^ expected one of `clobber_abi`, `const`, `options`, or `sym` error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `"{}"` - --> $DIR/parse-error.rs:132:30 + --> $DIR/parse-error.rs:125:30 | LL | global_asm!("{}", const FOO, "{}", const FOO); | ^^^^ expected one of `clobber_abi`, `const`, `options`, or `sym` error: asm template must be a string literal - --> $DIR/parse-error.rs:134:13 + --> $DIR/parse-error.rs:127:13 | LL | global_asm!(format!("{{{}}}", 0), const FOO); | ^^^^^^^^^^^^^^^^^^^^ @@ -383,7 +315,7 @@ LL | global_asm!(format!("{{{}}}", 0), const FOO); = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: asm template must be a string literal - --> $DIR/parse-error.rs:136:20 + --> $DIR/parse-error.rs:129:20 | LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR); | ^^^^^^^^^^^^^^^^^^^^ @@ -400,7 +332,7 @@ LL | asm!("{}", options(), const foo); | ^^^ non-constant value error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:50:44 + --> $DIR/parse-error.rs:49:44 | LL | let mut foo = 0; | ----------- help: consider using `const` instead of `let`: `const foo` @@ -409,7 +341,16 @@ LL | asm!("{}", clobber_abi("C"), const foo); | ^^^ non-constant value error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:57:31 + --> $DIR/parse-error.rs:52:55 + | +LL | let mut foo = 0; + | ----------- help: consider using `const` instead of `let`: `const foo` +... +LL | asm!("{}", options(), clobber_abi("C"), const foo); + | ^^^ non-constant value + +error[E0435]: attempt to use a non-constant value in a constant + --> $DIR/parse-error.rs:54:31 | LL | let mut foo = 0; | ----------- help: consider using `const` instead of `let`: `const foo` @@ -418,7 +359,7 @@ LL | asm!("{a}", a = const foo, a = const bar); | ^^^ non-constant value error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:57:46 + --> $DIR/parse-error.rs:54:46 | LL | let mut bar = 0; | ----------- help: consider using `const` instead of `let`: `const bar` @@ -427,7 +368,7 @@ LL | asm!("{a}", a = const foo, a = const bar); | ^^^ non-constant value error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:64:46 + --> $DIR/parse-error.rs:61:46 | LL | let mut bar = 0; | ----------- help: consider using `const` instead of `let`: `const bar` @@ -436,7 +377,7 @@ LL | asm!("{a}", in("eax") foo, a = const bar); | ^^^ non-constant value error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:67:46 + --> $DIR/parse-error.rs:63:46 | LL | let mut bar = 0; | ----------- help: consider using `const` instead of `let`: `const bar` @@ -445,7 +386,7 @@ LL | asm!("{a}", in("eax") foo, a = const bar); | ^^^ non-constant value error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/parse-error.rs:70:42 + --> $DIR/parse-error.rs:65:42 | LL | let mut bar = 0; | ----------- help: consider using `const` instead of `let`: `const bar` @@ -453,6 +394,6 @@ LL | let mut bar = 0; LL | asm!("{1}", in("eax") foo, const bar); | ^^^ non-constant value -error: aborting due to 66 previous errors +error: aborting due to 59 previous errors For more information about this error, try `rustc --explain E0435`. diff --git a/tests/ui/async-await/generator-desc.stderr b/tests/ui/async-await/generator-desc.stderr index 51ac9d86bfb..042766f19ca 100644 --- a/tests/ui/async-await/generator-desc.stderr +++ b/tests/ui/async-await/generator-desc.stderr @@ -2,16 +2,18 @@ error[E0308]: mismatched types --> $DIR/generator-desc.rs:10:19 | LL | fun(async {}, async {}); - | -------- ^^^^^^^^ - | | | - | | expected `async` block, found a different `async` block - | | arguments to this function are incorrect - | the expected `async` block + | --- -------- ^^^^^^^^ expected `async` block, found a different `async` block + | | | + | | the expected `async` block + | arguments to this function are incorrect | = note: expected `async` block `[async block@$DIR/generator-desc.rs:10:9: 10:17]` found `async` block `[async block@$DIR/generator-desc.rs:10:19: 10:27]` note: function defined here - --> $SRC_DIR/core/src/future/mod.rs:LL:COL + --> $DIR/generator-desc.rs:8:4 + | +LL | fn fun>(f1: F, f2: F) {} + | ^^^ ----- error[E0308]: mismatched types --> $DIR/generator-desc.rs:12:16 diff --git a/tests/ui/async-await/large_moves.attribute.stderr b/tests/ui/async-await/large_moves.attribute.stderr index 0c5452475a6..94f61caa25d 100644 --- a/tests/ui/async-await/large_moves.attribute.stderr +++ b/tests/ui/async-await/large_moves.attribute.stderr @@ -1,22 +1,3 @@ -error: moving 10024 bytes - --> $DIR/large_moves.rs:13:13 - | -LL | let x = async { - | _____________^ -LL | | let y = [0; 9999]; -LL | | dbg!(y); -LL | | thing(&y).await; -LL | | dbg!(y); -LL | | }; - | |_____^ value moved from here - | - = note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]` -note: the lint level is defined here - --> $DIR/large_moves.rs:1:9 - | -LL | #![deny(large_assignments)] - | ^^^^^^^^^^^^^^^^^ - error: moving 10024 bytes --> $DIR/large_moves.rs:19:14 | @@ -24,6 +5,11 @@ LL | let z = (x, 42); | ^ value moved from here | = note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]` +note: the lint level is defined here + --> $DIR/large_moves.rs:1:9 + | +LL | #![deny(large_assignments)] + | ^^^^^^^^^^^^^^^^^ error: moving 10024 bytes --> $DIR/large_moves.rs:19:13 @@ -41,5 +27,5 @@ LL | let a = z.0; | = note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]` -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/async-await/large_moves.option.stderr b/tests/ui/async-await/large_moves.option.stderr index 0c5452475a6..94f61caa25d 100644 --- a/tests/ui/async-await/large_moves.option.stderr +++ b/tests/ui/async-await/large_moves.option.stderr @@ -1,22 +1,3 @@ -error: moving 10024 bytes - --> $DIR/large_moves.rs:13:13 - | -LL | let x = async { - | _____________^ -LL | | let y = [0; 9999]; -LL | | dbg!(y); -LL | | thing(&y).await; -LL | | dbg!(y); -LL | | }; - | |_____^ value moved from here - | - = note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]` -note: the lint level is defined here - --> $DIR/large_moves.rs:1:9 - | -LL | #![deny(large_assignments)] - | ^^^^^^^^^^^^^^^^^ - error: moving 10024 bytes --> $DIR/large_moves.rs:19:14 | @@ -24,6 +5,11 @@ LL | let z = (x, 42); | ^ value moved from here | = note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]` +note: the lint level is defined here + --> $DIR/large_moves.rs:1:9 + | +LL | #![deny(large_assignments)] + | ^^^^^^^^^^^^^^^^^ error: moving 10024 bytes --> $DIR/large_moves.rs:19:13 @@ -41,5 +27,5 @@ LL | let a = z.0; | = note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]` -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/async-await/large_moves.rs b/tests/ui/async-await/large_moves.rs index d43d0eec0ca..c8ed6bafe9c 100644 --- a/tests/ui/async-await/large_moves.rs +++ b/tests/ui/async-await/large_moves.rs @@ -10,7 +10,7 @@ // compile-flags: -Zmir-opt-level=0 fn main() { - let x = async { //~ ERROR large_assignments + let x = async { let y = [0; 9999]; dbg!(y); thing(&y).await; diff --git a/tests/ui/async-await/no-const-async.rs b/tests/ui/async-await/no-const-async.rs index cfb0ef1b33a..b3c59734e03 100644 --- a/tests/ui/async-await/no-const-async.rs +++ b/tests/ui/async-await/no-const-async.rs @@ -3,4 +3,3 @@ pub const async fn x() {} //~^ ERROR functions cannot be both `const` and `async` -//~| ERROR cycle detected diff --git a/tests/ui/async-await/no-const-async.stderr b/tests/ui/async-await/no-const-async.stderr index 71c228958f6..90ec646c8c0 100644 --- a/tests/ui/async-await/no-const-async.stderr +++ b/tests/ui/async-await/no-const-async.stderr @@ -7,36 +7,5 @@ LL | pub const async fn x() {} | | `async` because of this | `const` because of this -error[E0391]: cycle detected when computing type of `x::{opaque#0}` - --> $DIR/no-const-async.rs:4:24 - | -LL | pub const async fn x() {} - | ^ - | -note: ...which requires borrow-checking `x`... - --> $DIR/no-const-async.rs:4:1 - | -LL | pub const async fn x() {} - | ^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires processing MIR for `x`... - --> $DIR/no-const-async.rs:4:1 - | -LL | pub const async fn x() {} - | ^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires const checking `x`... - --> $DIR/no-const-async.rs:4:1 - | -LL | pub const async fn x() {} - | ^^^^^^^^^^^^^^^^^^^^^^ - = note: ...which requires computing whether `x::{opaque#0}` is freeze... - = note: ...which requires evaluating trait selection obligation `x::{opaque#0}: core::marker::Freeze`... - = note: ...which again requires computing type of `x::{opaque#0}`, completing the cycle -note: cycle used when checking item types in top-level module - --> $DIR/no-const-async.rs:4:1 - | -LL | pub const async fn x() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +error: aborting due to previous error -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/async-await/track-caller/async-block.rs b/tests/ui/async-await/track-caller/async-block.rs index 8e81387c34b..8ddd4ab1186 100644 --- a/tests/ui/async-await/track-caller/async-block.rs +++ b/tests/ui/async-await/track-caller/async-block.rs @@ -1,9 +1,9 @@ // edition:2021 -#![feature(closure_track_caller, stmt_expr_attributes)] +#![feature(stmt_expr_attributes)] fn main() { let _ = #[track_caller] async { - //~^ ERROR attribute should be applied to a function definition [E0739] + //~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] }; } diff --git a/tests/ui/async-await/track-caller/async-block.stderr b/tests/ui/async-await/track-caller/async-block.stderr index 407439921c0..21d1054d220 100644 --- a/tests/ui/async-await/track-caller/async-block.stderr +++ b/tests/ui/async-await/track-caller/async-block.stderr @@ -1,12 +1,12 @@ -error[E0739]: attribute should be applied to a function definition +error[E0658]: `#[track_caller]` on closures is currently unstable --> $DIR/async-block.rs:6:13 | -LL | let _ = #[track_caller] async { - | _____________^^^^^^^^^^^^^^^_- -LL | | -LL | | }; - | |_____- not a function definition +LL | let _ = #[track_caller] async { + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable error: aborting due to previous error -For more information about this error, try `rustc --explain E0739`. +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/async-await/track-caller/panic-track-caller.rs b/tests/ui/async-await/track-caller/panic-track-caller.rs index f45243b0ea6..65bb23e0b4b 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.rs +++ b/tests/ui/async-await/track-caller/panic-track-caller.rs @@ -79,6 +79,16 @@ async fn foo_closure() { c().await } +// Since compilation is expected to fail for this fn when using +// `nofeat`, we test that separately in `async-block.rs` +#[cfg(feat)] +async fn foo_block() { + let a = #[track_caller] async { + panic!(); + }; + a.await +} + fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 { let loc = Arc::new(Mutex::new(None)); @@ -110,4 +120,7 @@ fn main() { #[cfg(feat)] assert_eq!(panicked_at(|| block_on(foo_closure())), 79); + + #[cfg(feat)] + assert_eq!(panicked_at(|| block_on(foo_block())), 89); } diff --git a/tests/ui/chalkify/bugs/async.rs b/tests/ui/chalkify/bugs/async.rs index 3169e4781ee..a1ef4732b63 100644 --- a/tests/ui/chalkify/bugs/async.rs +++ b/tests/ui/chalkify/bugs/async.rs @@ -4,7 +4,7 @@ // compile-flags:-Z trait-solver=chalk // error-pattern:internal compiler error // failure-status:101 -// normalize-stderr-test "DefId([^)]*)" -> "..." +// normalize-stderr-test "DefId\([^)]*\)" -> "..." // normalize-stderr-test "\nerror: internal compiler error.*\n\n" -> "" // normalize-stderr-test "note:.*unexpectedly panicked.*\n\n" -> "" // normalize-stderr-test "note: we would appreciate a bug report.*\n\n" -> "" diff --git a/tests/ui/chalkify/bugs/async.stderr b/tests/ui/chalkify/bugs/async.stderr index 8043f1e5a05..9c559640b23 100644 --- a/tests/ui/chalkify/bugs/async.stderr +++ b/tests/ui/chalkify/bugs/async.stderr @@ -1,33 +1,3 @@ -error[E0277]: `[async fn body@$DIR/async.rs:23:29: 25:2]` is not a future - --> $DIR/async.rs:23:29 - | -LL | async fn foo(x: u32) -> u32 { - | _____________________________- -LL | | x -LL | | } - | | ^ - | | | - | |_`[async fn body@$DIR/async.rs:23:29: 25:2]` is not a future - | required by a bound introduced by this call - | - = help: the trait `Future` is not implemented for `[async fn body@$DIR/async.rs:23:29: 25:2]` - = note: [async fn body@$DIR/async.rs:23:29: 25:2] must be a future or must implement `IntoFuture` to be awaited -note: required by a bound in `identity_future` - --> $SRC_DIR/core/src/future/mod.rs:LL:COL - -error[E0277]: the size for values of type `<[async fn body@$DIR/async.rs:23:29: 25:2] as Future>::Output` cannot be known at compilation time - --> $DIR/async.rs:23:29 - | -LL | async fn foo(x: u32) -> u32 { - | _____________________________^ -LL | | x -LL | | } - | |_^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `<[async fn body@$DIR/async.rs:23:29: 25:2] as Future>::Output` -note: required by a bound in `identity_future` - --> $SRC_DIR/core/src/future/mod.rs:LL:COL - error[E0277]: `[async fn body@$DIR/async.rs:23:29: 25:2]` is not a future --> $DIR/async.rs:23:25 | @@ -37,7 +7,7 @@ LL | async fn foo(x: u32) -> u32 { = help: the trait `Future` is not implemented for `[async fn body@$DIR/async.rs:23:29: 25:2]` = note: [async fn body@$DIR/async.rs:23:29: 25:2] must be a future or must implement `IntoFuture` to be awaited -error: internal compiler error: projection clauses should be implied from elsewhere. obligation: `Obligation(predicate=Binder(ProjectionPredicate(AliasTy { substs: [[async fn body@$DIR/async.rs:23:29: 25:2]], def_id: ...) }, Term::Ty(u32)), []), depth=0)` +error: internal compiler error: projection clauses should be implied from elsewhere. obligation: `Obligation(predicate=Binder(ProjectionPredicate(AliasTy { substs: [[async fn body@$DIR/async.rs:23:29: 25:2]], def_id: ... }, Term::Ty(u32)), []), depth=0)` --> $DIR/async.rs:23:25 | LL | async fn foo(x: u32) -> u32 { @@ -53,6 +23,6 @@ LL | async fn foo(x: u32) -> u32 { #8 [check_mod_item_types] checking item types in top-level module #9 [analysis] running analysis passes on this crate end of query stack -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/check-cfg/my-awesome-platform.json b/tests/ui/check-cfg/my-awesome-platform.json new file mode 100644 index 00000000000..5e9ab8f1a2d --- /dev/null +++ b/tests/ui/check-cfg/my-awesome-platform.json @@ -0,0 +1,12 @@ +{ + "llvm-target": "x86_64-unknown-none-gnu", + "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", + "arch": "x86_64", + "target-endian": "little", + "target-pointer-width": "64", + "target-c-int-width": "32", + "os": "ericos", + "linker-flavor": "ld.lld", + "linker": "rust-lld", + "executables": true +} diff --git a/tests/ui/check-cfg/values-target-json.rs b/tests/ui/check-cfg/values-target-json.rs new file mode 100644 index 00000000000..2ef5a44592b --- /dev/null +++ b/tests/ui/check-cfg/values-target-json.rs @@ -0,0 +1,21 @@ +// This test checks that we don't lint values defined by a custom target (target json) +// +// check-pass +// needs-llvm-components: x86 +// compile-flags: --crate-type=lib --check-cfg=values() --target={{src-base}}/check-cfg/my-awesome-platform.json -Z unstable-options + +#![feature(lang_items, no_core, auto_traits)] +#![no_core] + +#[lang = "sized"] +trait Sized {} + +#[cfg(target_os = "linuz")] +//~^ WARNING unexpected `cfg` condition value +fn target_os_linux_misspell() {} + +#[cfg(target_os = "linux")] +fn target_os_linux() {} + +#[cfg(target_os = "ericos")] +fn target_os_ericos() {} diff --git a/tests/ui/check-cfg/values-target-json.stderr b/tests/ui/check-cfg/values-target-json.stderr new file mode 100644 index 00000000000..b58d2970773 --- /dev/null +++ b/tests/ui/check-cfg/values-target-json.stderr @@ -0,0 +1,13 @@ +warning: unexpected `cfg` condition value + --> $DIR/values-target-json.rs:13:7 + | +LL | #[cfg(target_os = "linuz")] + | ^^^^^^^^^^^^------- + | | + | help: did you mean: `"linux"` + | + = note: expected values for `target_os` are: aix, android, cuda, dragonfly, emscripten, ericos, espidf, freebsd, fuchsia, haiku, hermit, horizon, illumos, ios, l4re, linux, macos, netbsd, none, nto, openbsd, psp, redox, solaris, solid_asp3, tvos, uefi, unknown, vita, vxworks, wasi, watchos, windows, xous + = note: `#[warn(unexpected_cfgs)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/check-static-values-constraints.rs b/tests/ui/check-static-values-constraints.rs index f6a577d0d9c..005a7798895 100644 --- a/tests/ui/check-static-values-constraints.rs +++ b/tests/ui/check-static-values-constraints.rs @@ -1,7 +1,6 @@ // Verifies all possible restrictions for statics values. #![allow(warnings)] -#![feature(box_syntax)] use std::marker; @@ -19,7 +18,7 @@ enum SafeEnum { Variant1, Variant2(isize), Variant3(WithDtor), - Variant4(String) + Variant4(String), } // These should be ok @@ -29,42 +28,45 @@ static STATIC3: SafeEnum = SafeEnum::Variant3(WithDtor); enum UnsafeEnum { Variant5, - Variant6(isize) + Variant6(isize), } impl Drop for UnsafeEnum { fn drop(&mut self) {} } - static STATIC4: UnsafeEnum = UnsafeEnum::Variant5; static STATIC5: UnsafeEnum = UnsafeEnum::Variant6(0); - struct SafeStruct { field1: SafeEnum, field2: SafeEnum, } - // Struct fields are safe, hence this static should be safe -static STATIC6: SafeStruct = SafeStruct{field1: SafeEnum::Variant1, field2: SafeEnum::Variant2(0)}; +static STATIC6: SafeStruct = + SafeStruct { field1: SafeEnum::Variant1, field2: SafeEnum::Variant2(0) }; -static STATIC7: SafeStruct = SafeStruct{field1: SafeEnum::Variant1, - field2: SafeEnum::Variant3(WithDtor)}; +static STATIC7: SafeStruct = + SafeStruct { field1: SafeEnum::Variant1, field2: SafeEnum::Variant3(WithDtor) }; // Test variadic constructor for structs. The base struct should be examined // as well as every field present in the constructor. // This example shouldn't fail because all the fields are safe. -static STATIC8: SafeStruct = SafeStruct{field1: SafeEnum::Variant1, - ..SafeStruct{field1: SafeEnum::Variant1, - field2: SafeEnum::Variant1}}; +static STATIC8: SafeStruct = SafeStruct { + field1: SafeEnum::Variant1, + ..SafeStruct { field1: SafeEnum::Variant1, field2: SafeEnum::Variant1 } +}; // This example should fail because field1 in the base struct is not safe -static STATIC9: SafeStruct = SafeStruct{field1: SafeEnum::Variant1, - ..SafeStruct{field1: SafeEnum::Variant3(WithDtor), -//~^ ERROR destructor of - field2: SafeEnum::Variant1}}; +static STATIC9: SafeStruct = SafeStruct { + field1: SafeEnum::Variant1, + ..SafeStruct { + //~^ ERROR destructor of + field1: SafeEnum::Variant3(WithDtor), + field2: SafeEnum::Variant1, + } +}; struct UnsafeStruct; @@ -76,38 +78,45 @@ static STATIC10: UnsafeStruct = UnsafeStruct; struct MyOwned; -static STATIC11: Box = box MyOwned; +static STATIC11: Vec = vec![MyOwned]; //~^ ERROR allocations are not allowed in statics +//~^^ ERROR cannot call non-const static mut STATIC12: UnsafeStruct = UnsafeStruct; -static mut STATIC13: SafeStruct = SafeStruct{field1: SafeEnum::Variant1, - field2: SafeEnum::Variant3(WithDtor)}; +static mut STATIC13: SafeStruct = + SafeStruct { field1: SafeEnum::Variant1, field2: SafeEnum::Variant3(WithDtor) }; static mut STATIC14: SafeStruct = SafeStruct { field1: SafeEnum::Variant1, - field2: SafeEnum::Variant4("str".to_string()) -//~^ ERROR cannot call non-const fn + field2: SafeEnum::Variant4("str".to_string()), //~ ERROR cannot call non-const fn }; -static STATIC15: &'static [Box] = &[ - box MyOwned, //~ ERROR allocations are not allowed in statics - box MyOwned, //~ ERROR allocations are not allowed in statics +static STATIC15: &'static [Vec] = &[ + vec![MyOwned], //~ ERROR allocations are not allowed in statics + //~^ ERROR cannot call non-const + vec![MyOwned], //~ ERROR allocations are not allowed in statics + //~^ ERROR cannot call non-const ]; -static STATIC16: (&'static Box, &'static Box) = ( - &box MyOwned, //~ ERROR allocations are not allowed in statics - &box MyOwned, //~ ERROR allocations are not allowed in statics +static STATIC16: (&'static Vec, &'static Vec) = ( + &vec![MyOwned], //~ ERROR allocations are not allowed in statics + //~^ ERROR cannot call non-const + &vec![MyOwned], //~ ERROR allocations are not allowed in statics + //~^ ERROR cannot call non-const ); static mut STATIC17: SafeEnum = SafeEnum::Variant1; -static STATIC19: Box = - box 3; +static STATIC19: Vec = vec![3]; //~^ ERROR allocations are not allowed in statics +//~^^ ERROR cannot call non-const pub fn main() { - let y = { static x: Box = box 3; x }; - //~^ ERROR allocations are not allowed in statics - //~| ERROR cannot move out of static item + let y = { + static x: Vec = vec![3]; //~ ERROR allocations are not allowed in statics + //~^ ERROR cannot call non-const + x + //~^ ERROR cannot move out of static + }; } diff --git a/tests/ui/check-static-values-constraints.stderr b/tests/ui/check-static-values-constraints.stderr index 49056678448..064eb4b8a5c 100644 --- a/tests/ui/check-static-values-constraints.stderr +++ b/tests/ui/check-static-values-constraints.stderr @@ -1,24 +1,38 @@ error[E0493]: destructor of `SafeStruct` cannot be evaluated at compile-time - --> $DIR/check-static-values-constraints.rs:65:43 + --> $DIR/check-static-values-constraints.rs:64:7 | -LL | ..SafeStruct{field1: SafeEnum::Variant3(WithDtor), - | ___________________________________________^ +LL | ..SafeStruct { + | _______^ LL | | -LL | | field2: SafeEnum::Variant1}}; - | | ^- value is dropped here - | |________________________________________________________________________________| - | the destructor for this type cannot be evaluated in statics +LL | | field1: SafeEnum::Variant3(WithDtor), +LL | | field2: SafeEnum::Variant1, +LL | | } + | |_____^ the destructor for this type cannot be evaluated in statics +LL | }; + | - value is dropped here error[E0010]: allocations are not allowed in statics - --> $DIR/check-static-values-constraints.rs:79:33 + --> $DIR/check-static-values-constraints.rs:81:33 | -LL | static STATIC11: Box = box MyOwned; - | ^^^^^^^^^^^ allocation not allowed in statics +LL | static STATIC11: Vec = vec![MyOwned]; + | ^^^^^^^^^^^^^ allocation not allowed in statics + | + = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0015]: cannot call non-const fn `slice::::into_vec::` in statics + --> $DIR/check-static-values-constraints.rs:81:33 + | +LL | static STATIC11: Vec = vec![MyOwned]; + | ^^^^^^^^^^^^^ + | + = note: calls in statics are limited to constant functions, tuple structs and tuple variants + = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell + = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const fn `::to_string` in statics - --> $DIR/check-static-values-constraints.rs:89:38 + --> $DIR/check-static-values-constraints.rs:92:38 | -LL | field2: SafeEnum::Variant4("str".to_string()) +LL | field2: SafeEnum::Variant4("str".to_string()), | ^^^^^^^^^^^ | = note: calls in statics are limited to constant functions, tuple structs and tuple variants @@ -26,53 +40,125 @@ LL | field2: SafeEnum::Variant4("str".to_string()) = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell error[E0010]: allocations are not allowed in statics - --> $DIR/check-static-values-constraints.rs:94:5 + --> $DIR/check-static-values-constraints.rs:96:5 | -LL | box MyOwned, - | ^^^^^^^^^^^ allocation not allowed in statics +LL | vec![MyOwned], + | ^^^^^^^^^^^^^ allocation not allowed in statics + | + = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0015]: cannot call non-const fn `slice::::into_vec::` in statics + --> $DIR/check-static-values-constraints.rs:96:5 + | +LL | vec![MyOwned], + | ^^^^^^^^^^^^^ + | + = note: calls in statics are limited to constant functions, tuple structs and tuple variants + = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell + = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0010]: allocations are not allowed in statics - --> $DIR/check-static-values-constraints.rs:95:5 + --> $DIR/check-static-values-constraints.rs:98:5 | -LL | box MyOwned, - | ^^^^^^^^^^^ allocation not allowed in statics +LL | vec![MyOwned], + | ^^^^^^^^^^^^^ allocation not allowed in statics + | + = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0015]: cannot call non-const fn `slice::::into_vec::` in statics + --> $DIR/check-static-values-constraints.rs:98:5 + | +LL | vec![MyOwned], + | ^^^^^^^^^^^^^ + | + = note: calls in statics are limited to constant functions, tuple structs and tuple variants + = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell + = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0010]: allocations are not allowed in statics - --> $DIR/check-static-values-constraints.rs:99:6 + --> $DIR/check-static-values-constraints.rs:103:6 | -LL | &box MyOwned, - | ^^^^^^^^^^^ allocation not allowed in statics +LL | &vec![MyOwned], + | ^^^^^^^^^^^^^ allocation not allowed in statics + | + = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0015]: cannot call non-const fn `slice::::into_vec::` in statics + --> $DIR/check-static-values-constraints.rs:103:6 + | +LL | &vec![MyOwned], + | ^^^^^^^^^^^^^ + | + = note: calls in statics are limited to constant functions, tuple structs and tuple variants + = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell + = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0010]: allocations are not allowed in statics - --> $DIR/check-static-values-constraints.rs:100:6 + --> $DIR/check-static-values-constraints.rs:105:6 | -LL | &box MyOwned, - | ^^^^^^^^^^^ allocation not allowed in statics +LL | &vec![MyOwned], + | ^^^^^^^^^^^^^ allocation not allowed in statics + | + = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0015]: cannot call non-const fn `slice::::into_vec::` in statics + --> $DIR/check-static-values-constraints.rs:105:6 + | +LL | &vec![MyOwned], + | ^^^^^^^^^^^^^ + | + = note: calls in statics are limited to constant functions, tuple structs and tuple variants + = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell + = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0010]: allocations are not allowed in statics - --> $DIR/check-static-values-constraints.rs:106:5 + --> $DIR/check-static-values-constraints.rs:111:31 | -LL | box 3; - | ^^^^^ allocation not allowed in statics +LL | static STATIC19: Vec = vec![3]; + | ^^^^^^^ allocation not allowed in statics + | + = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0015]: cannot call non-const fn `slice::::into_vec::` in statics + --> $DIR/check-static-values-constraints.rs:111:31 + | +LL | static STATIC19: Vec = vec![3]; + | ^^^^^^^ + | + = note: calls in statics are limited to constant functions, tuple structs and tuple variants + = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell + = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0507]: cannot move out of static item `x` - --> $DIR/check-static-values-constraints.rs:110:45 + --> $DIR/check-static-values-constraints.rs:119:9 | -LL | let y = { static x: Box = box 3; x }; - | ^ move occurs because `x` has type `Box`, which does not implement the `Copy` trait +LL | x + | ^ move occurs because `x` has type `Vec`, which does not implement the `Copy` trait | help: consider borrowing here | -LL | let y = { static x: Box = box 3; &x }; - | + +LL | &x + | + error[E0010]: allocations are not allowed in statics - --> $DIR/check-static-values-constraints.rs:110:38 + --> $DIR/check-static-values-constraints.rs:117:32 | -LL | let y = { static x: Box = box 3; x }; - | ^^^^^ allocation not allowed in statics +LL | static x: Vec = vec![3]; + | ^^^^^^^ allocation not allowed in statics + | + = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 10 previous errors +error[E0015]: cannot call non-const fn `slice::::into_vec::` in statics + --> $DIR/check-static-values-constraints.rs:117:32 + | +LL | static x: Vec = vec![3]; + | ^^^^^^^ + | + = note: calls in statics are limited to constant functions, tuple structs and tuple variants + = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell + = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 17 previous errors Some errors have detailed explanations: E0010, E0015, E0493, E0507. For more information about an error, try `rustc --explain E0010`. diff --git a/tests/ui/coercion/coerce-expect-unsized-ascribed.rs b/tests/ui/coercion/coerce-expect-unsized-ascribed.rs index d7b11317ad5..43b0b375a98 100644 --- a/tests/ui/coercion/coerce-expect-unsized-ascribed.rs +++ b/tests/ui/coercion/coerce-expect-unsized-ascribed.rs @@ -1,18 +1,19 @@ // A version of coerce-expect-unsized that uses type ascription. // Doesn't work so far, but supposed to work eventually -#![feature(box_syntax, type_ascription)] +#![feature(type_ascription)] use std::fmt::Debug; pub fn main() { - let _ = type_ascribe!(box { [1, 2, 3] }, Box<[i32]>); //~ ERROR mismatched types - let _ = type_ascribe!(box if true { [1, 2, 3] } else { [1, 3, 4] }, Box<[i32]>); //~ ERROR mismatched types - let _ = type_ascribe!(box match true { true => [1, 2, 3], false => [1, 3, 4] }, Box<[i32]>); + let _ = type_ascribe!(Box::new({ [1, 2, 3] }), Box<[i32]>); //~ ERROR mismatched types + let _ = type_ascribe!(Box::new( if true { [1, 2, 3] } else { [1, 3, 4] }), Box<[i32]>); //~ ERROR mismatched types + let _ = type_ascribe!( + Box::new( match true { true => [1, 2, 3], false => [1, 3, 4] }), Box<[i32]>); //~^ ERROR mismatched types - let _ = type_ascribe!(box { |x| (x as u8) }, Box _>); //~ ERROR mismatched types - let _ = type_ascribe!(box if true { false } else { true }, Box); //~ ERROR mismatched types - let _ = type_ascribe!(box match true { true => 'a', false => 'b' }, Box); //~ ERROR mismatched types + let _ = type_ascribe!(Box::new( { |x| (x as u8) }), Box _>); //~ ERROR mismatched types + let _ = type_ascribe!(Box::new( if true { false } else { true }), Box); //~ ERROR mismatched types + let _ = type_ascribe!(Box::new( match true { true => 'a', false => 'b' }), Box); //~ ERROR mismatched types let _ = type_ascribe!(&{ [1, 2, 3] }, &[i32]); //~ ERROR mismatched types let _ = type_ascribe!(&if true { [1, 2, 3] } else { [1, 3, 4] }, &[i32]); //~ ERROR mismatched types @@ -27,6 +28,6 @@ pub fn main() { let _ = type_ascribe!(vec![ Box::new(|x| (x as u8)), - box |x| (x as i16 as u8), + Box::new(|x| (x as i16 as u8)), ], Vec _>>); } diff --git a/tests/ui/coercion/coerce-expect-unsized-ascribed.stderr b/tests/ui/coercion/coerce-expect-unsized-ascribed.stderr index f94422a9269..aa5ec6b5ae1 100644 --- a/tests/ui/coercion/coerce-expect-unsized-ascribed.stderr +++ b/tests/ui/coercion/coerce-expect-unsized-ascribed.stderr @@ -1,8 +1,8 @@ error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:9:27 | -LL | let _ = type_ascribe!(box { [1, 2, 3] }, Box<[i32]>); - | ^^^^^^^^^^^^^^^^^ expected `Box<[i32]>`, found `Box<[i32; 3]>` +LL | let _ = type_ascribe!(Box::new({ [1, 2, 3] }), Box<[i32]>); + | ^^^^^^^^^^^^^^^^^^^^^^^ expected `Box<[i32]>`, found `Box<[i32; 3]>` | = note: expected struct `Box<[i32]>` found struct `Box<[i32; 3]>` @@ -10,50 +10,50 @@ LL | let _ = type_ascribe!(box { [1, 2, 3] }, Box<[i32]>); error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:10:27 | -LL | let _ = type_ascribe!(box if true { [1, 2, 3] } else { [1, 3, 4] }, Box<[i32]>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Box<[i32]>`, found `Box<[i32; 3]>` +LL | let _ = type_ascribe!(Box::new( if true { [1, 2, 3] } else { [1, 3, 4] }), Box<[i32]>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Box<[i32]>`, found `Box<[i32; 3]>` | = note: expected struct `Box<[i32]>` found struct `Box<[i32; 3]>` error[E0308]: mismatched types - --> $DIR/coerce-expect-unsized-ascribed.rs:11:27 + --> $DIR/coerce-expect-unsized-ascribed.rs:12:9 | -LL | let _ = type_ascribe!(box match true { true => [1, 2, 3], false => [1, 3, 4] }, Box<[i32]>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Box<[i32]>`, found `Box<[i32; 3]>` +LL | Box::new( match true { true => [1, 2, 3], false => [1, 3, 4] }), Box<[i32]>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Box<[i32]>`, found `Box<[i32; 3]>` | = note: expected struct `Box<[i32]>` found struct `Box<[i32; 3]>` -error[E0308]: mismatched types - --> $DIR/coerce-expect-unsized-ascribed.rs:13:27 - | -LL | let _ = type_ascribe!(box { |x| (x as u8) }, Box _>); - | ^^^^^^^^^^^^^^^^^^^^^ expected `Box u8>`, found `Box<[closure@coerce-expect-unsized-ascribed.rs:13:33]>` - | - = note: expected struct `Box u8>` - found struct `Box<[closure@$DIR/coerce-expect-unsized-ascribed.rs:13:33: 13:36]>` - error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:14:27 | -LL | let _ = type_ascribe!(box if true { false } else { true }, Box); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Box`, found `Box` +LL | let _ = type_ascribe!(Box::new( { |x| (x as u8) }), Box _>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Box u8>`, found `Box<[closure@coerce-expect-unsized-ascribed.rs:14:39]>` + | + = note: expected struct `Box u8>` + found struct `Box<[closure@$DIR/coerce-expect-unsized-ascribed.rs:14:39: 14:42]>` + +error[E0308]: mismatched types + --> $DIR/coerce-expect-unsized-ascribed.rs:15:27 + | +LL | let _ = type_ascribe!(Box::new( if true { false } else { true }), Box); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Box`, found `Box` | = note: expected struct `Box` found struct `Box` error[E0308]: mismatched types - --> $DIR/coerce-expect-unsized-ascribed.rs:15:27 + --> $DIR/coerce-expect-unsized-ascribed.rs:16:27 | -LL | let _ = type_ascribe!(box match true { true => 'a', false => 'b' }, Box); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Box`, found `Box` +LL | let _ = type_ascribe!(Box::new( match true { true => 'a', false => 'b' }), Box); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Box`, found `Box` | = note: expected struct `Box` found struct `Box` error[E0308]: mismatched types - --> $DIR/coerce-expect-unsized-ascribed.rs:17:27 + --> $DIR/coerce-expect-unsized-ascribed.rs:18:27 | LL | let _ = type_ascribe!(&{ [1, 2, 3] }, &[i32]); | ^^^^^^^^^^^^^^ expected `&[i32]`, found `&[i32; 3]` @@ -62,7 +62,7 @@ LL | let _ = type_ascribe!(&{ [1, 2, 3] }, &[i32]); found reference `&[i32; 3]` error[E0308]: mismatched types - --> $DIR/coerce-expect-unsized-ascribed.rs:18:27 + --> $DIR/coerce-expect-unsized-ascribed.rs:19:27 | LL | let _ = type_ascribe!(&if true { [1, 2, 3] } else { [1, 3, 4] }, &[i32]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&[i32]`, found `&[i32; 3]` @@ -71,7 +71,7 @@ LL | let _ = type_ascribe!(&if true { [1, 2, 3] } else { [1, 3, 4] }, &[i32] found reference `&[i32; 3]` error[E0308]: mismatched types - --> $DIR/coerce-expect-unsized-ascribed.rs:19:27 + --> $DIR/coerce-expect-unsized-ascribed.rs:20:27 | LL | let _ = type_ascribe!(&match true { true => [1, 2, 3], false => [1, 3, 4] }, &[i32]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&[i32]`, found `&[i32; 3]` @@ -80,16 +80,16 @@ LL | let _ = type_ascribe!(&match true { true => [1, 2, 3], false => [1, 3, found reference `&[i32; 3]` error[E0308]: mismatched types - --> $DIR/coerce-expect-unsized-ascribed.rs:21:27 + --> $DIR/coerce-expect-unsized-ascribed.rs:22:27 | LL | let _ = type_ascribe!(&{ |x| (x as u8) }, &dyn Fn(i32) -> _); - | ^^^^^^^^^^^^^^^^^^ expected `&dyn Fn(i32) -> u8`, found `&[closure@coerce-expect-unsized-ascribed.rs:21:30]` + | ^^^^^^^^^^^^^^^^^^ expected `&dyn Fn(i32) -> u8`, found `&[closure@coerce-expect-unsized-ascribed.rs:22:30]` | = note: expected reference `&dyn Fn(i32) -> u8` - found reference `&[closure@$DIR/coerce-expect-unsized-ascribed.rs:21:30: 21:33]` + found reference `&[closure@$DIR/coerce-expect-unsized-ascribed.rs:22:30: 22:33]` error[E0308]: mismatched types - --> $DIR/coerce-expect-unsized-ascribed.rs:22:27 + --> $DIR/coerce-expect-unsized-ascribed.rs:23:27 | LL | let _ = type_ascribe!(&if true { false } else { true }, &dyn Debug); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&dyn Debug`, found `&bool` @@ -98,7 +98,7 @@ LL | let _ = type_ascribe!(&if true { false } else { true }, &dyn Debug); found reference `&bool` error[E0308]: mismatched types - --> $DIR/coerce-expect-unsized-ascribed.rs:23:27 + --> $DIR/coerce-expect-unsized-ascribed.rs:24:27 | LL | let _ = type_ascribe!(&match true { true => 'a', false => 'b' }, &dyn Debug); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&dyn Debug`, found `&char` @@ -107,7 +107,7 @@ LL | let _ = type_ascribe!(&match true { true => 'a', false => 'b' }, &dyn D found reference `&char` error[E0308]: mismatched types - --> $DIR/coerce-expect-unsized-ascribed.rs:25:27 + --> $DIR/coerce-expect-unsized-ascribed.rs:26:27 | LL | let _ = type_ascribe!(Box::new([1, 2, 3]), Box<[i32]>); | ^^^^^^^^^^^^^^^^^^^ expected `Box<[i32]>`, found `Box<[i32; 3]>` @@ -116,13 +116,13 @@ LL | let _ = type_ascribe!(Box::new([1, 2, 3]), Box<[i32]>); found struct `Box<[i32; 3]>` error[E0308]: mismatched types - --> $DIR/coerce-expect-unsized-ascribed.rs:26:27 + --> $DIR/coerce-expect-unsized-ascribed.rs:27:27 | LL | let _ = type_ascribe!(Box::new(|x| (x as u8)), Box _>); - | ^^^^^^^^^^^^^^^^^^^^^^^ expected `Box u8>`, found `Box<[closure@coerce-expect-unsized-ascribed.rs:26:36]>` + | ^^^^^^^^^^^^^^^^^^^^^^^ expected `Box u8>`, found `Box<[closure@coerce-expect-unsized-ascribed.rs:27:36]>` | = note: expected struct `Box u8>` - found struct `Box<[closure@$DIR/coerce-expect-unsized-ascribed.rs:26:36: 26:39]>` + found struct `Box<[closure@$DIR/coerce-expect-unsized-ascribed.rs:27:36: 27:39]>` error: aborting due to 14 previous errors diff --git a/tests/ui/const-generics/type_mismatch.rs b/tests/ui/const-generics/type_mismatch.rs index 4a7534e3713..daa13277be0 100644 --- a/tests/ui/const-generics/type_mismatch.rs +++ b/tests/ui/const-generics/type_mismatch.rs @@ -1,5 +1,6 @@ fn foo() -> [u8; N] { bar::() //~ ERROR mismatched types + //~^ ERROR the constant `N` is not of type `u8` } fn bar() -> [u8; N] {} diff --git a/tests/ui/const-generics/type_mismatch.stderr b/tests/ui/const-generics/type_mismatch.stderr index b28ae8f7e71..6d8955e411e 100644 --- a/tests/ui/const-generics/type_mismatch.stderr +++ b/tests/ui/const-generics/type_mismatch.stderr @@ -1,3 +1,15 @@ +error: the constant `N` is not of type `u8` + --> $DIR/type_mismatch.rs:2:5 + | +LL | bar::() + | ^^^^^^^^ + | +note: required by a bound in `bar` + --> $DIR/type_mismatch.rs:6:8 + | +LL | fn bar() -> [u8; N] {} + | ^^^^^^^^^^^ required by this bound in `bar` + error[E0308]: mismatched types --> $DIR/type_mismatch.rs:2:11 | @@ -5,7 +17,7 @@ LL | bar::() | ^ expected `u8`, found `usize` error[E0308]: mismatched types - --> $DIR/type_mismatch.rs:5:26 + --> $DIR/type_mismatch.rs:6:26 | LL | fn bar() -> [u8; N] {} | --- ^^^^^^^ expected `[u8; N]`, found `()` @@ -13,11 +25,11 @@ LL | fn bar() -> [u8; N] {} | implicitly returns `()` as its body has no tail or `return` expression error[E0308]: mismatched types - --> $DIR/type_mismatch.rs:5:31 + --> $DIR/type_mismatch.rs:6:31 | LL | fn bar() -> [u8; N] {} | ^ expected `usize`, found `u8` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/type_not_in_scope.rs b/tests/ui/const-generics/type_not_in_scope.rs index 5933701808b..917abaed15e 100644 --- a/tests/ui/const-generics/type_not_in_scope.rs +++ b/tests/ui/const-generics/type_not_in_scope.rs @@ -6,6 +6,5 @@ impl X { } fn getn() -> [u8; N] {} //~^ ERROR expected type, found built-in attribute `cfg_attr` -//~| ERROR mismatched types fn main() {} diff --git a/tests/ui/const-generics/type_not_in_scope.stderr b/tests/ui/const-generics/type_not_in_scope.stderr index 5f45550a627..5eb81ca0522 100644 --- a/tests/ui/const-generics/type_not_in_scope.stderr +++ b/tests/ui/const-generics/type_not_in_scope.stderr @@ -10,15 +10,7 @@ error[E0573]: expected type, found built-in attribute `cfg_attr` LL | fn getn() -> [u8; N] {} | ^^^^^^^^ not a type -error[E0308]: mismatched types - --> $DIR/type_not_in_scope.rs:7:33 - | -LL | fn getn() -> [u8; N] {} - | ---- ^^^^^^^ expected `[u8; N]`, found `()` - | | - | implicitly returns `()` as its body has no tail or `return` expression +error: aborting due to 2 previous errors -error: aborting due to 3 previous errors - -Some errors have detailed explanations: E0308, E0412, E0573. -For more information about an error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0412, E0573. +For more information about an error, try `rustc --explain E0412`. diff --git a/tests/ui/consts/const-err-late.stderr b/tests/ui/consts/const-err-late.stderr index cb0cab2444b..192b9ba204b 100644 --- a/tests/ui/consts/const-err-late.stderr +++ b/tests/ui/consts/const-err-late.stderr @@ -10,12 +10,6 @@ note: erroneous constant used LL | black_box((S::::FOO, S::::FOO)); | ^^^^^^^^^^^^^ -note: erroneous constant used - --> $DIR/const-err-late.rs:19:16 - | -LL | black_box((S::::FOO, S::::FOO)); - | ^^^^^^^^^^^^^ - error[E0080]: evaluation of `S::::FOO` failed --> $DIR/const-err-late.rs:13:21 | @@ -34,6 +28,12 @@ note: erroneous constant used LL | black_box((S::::FOO, S::::FOO)); | ^^^^^^^^^^^^^ +note: erroneous constant used + --> $DIR/const-err-late.rs:19:16 + | +LL | black_box((S::::FOO, S::::FOO)); + | ^^^^^^^^^^^^^ + error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/miri_unleashed/box.rs b/tests/ui/consts/miri_unleashed/box.rs index c2a260aa13c..39cddda2b80 100644 --- a/tests/ui/consts/miri_unleashed/box.rs +++ b/tests/ui/consts/miri_unleashed/box.rs @@ -1,12 +1,11 @@ // compile-flags: -Zunleash-the-miri-inside-of-you -#![feature(box_syntax)] use std::mem::ManuallyDrop; fn main() {} static TEST_BAD: &mut i32 = { - &mut *(box 0) + &mut *(Box::new(0)) //~^ ERROR could not evaluate static initializer - //~| NOTE calling non-const function `alloc::alloc::exchange_malloc` + //~| NOTE calling non-const function `Box::::new` }; diff --git a/tests/ui/consts/miri_unleashed/box.stderr b/tests/ui/consts/miri_unleashed/box.stderr index bc5d4a2576e..407f5d8cb11 100644 --- a/tests/ui/consts/miri_unleashed/box.stderr +++ b/tests/ui/consts/miri_unleashed/box.stderr @@ -1,31 +1,26 @@ error[E0080]: could not evaluate static initializer - --> $DIR/box.rs:9:11 + --> $DIR/box.rs:8:11 | -LL | &mut *(box 0) - | ^^^^^^^ calling non-const function `alloc::alloc::exchange_malloc` +LL | &mut *(Box::new(0)) + | ^^^^^^^^^^^^^ calling non-const function `Box::::new` warning: skipping const checks | help: skipping check that does not even have a feature gate - --> $DIR/box.rs:9:11 + --> $DIR/box.rs:8:11 | -LL | &mut *(box 0) - | ^^^^^^^ +LL | &mut *(Box::new(0)) + | ^^^^^^^^^^^^^ help: skipping check for `const_mut_refs` feature - --> $DIR/box.rs:9:16 + --> $DIR/box.rs:8:5 | -LL | &mut *(box 0) - | ^ -help: skipping check for `const_mut_refs` feature - --> $DIR/box.rs:9:5 - | -LL | &mut *(box 0) - | ^^^^^^^^^^^^^ +LL | &mut *(Box::new(0)) + | ^^^^^^^^^^^^^^^^^^^ help: skipping check that does not even have a feature gate - --> $DIR/box.rs:9:5 + --> $DIR/box.rs:8:5 | -LL | &mut *(box 0) - | ^^^^^^^^^^^^^ +LL | &mut *(Box::new(0)) + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error; 1 warning emitted diff --git a/tests/ui/dst/issue-90528-unsizing-suggestion-1.rs b/tests/ui/dst/issue-90528-unsizing-suggestion-1.rs new file mode 100644 index 00000000000..52863e22bb6 --- /dev/null +++ b/tests/ui/dst/issue-90528-unsizing-suggestion-1.rs @@ -0,0 +1,20 @@ +// Issue #90528: provide helpful suggestions when a trait bound is unsatisfied +// due to a missed unsizing coercion. +// +// This test exercises array literals and a trait implemented on immutable slices. + +trait Read {} + +impl Read for &[u8] {} + +fn wants_read(_: impl Read) {} + +fn main() { + wants_read([0u8]); + //~^ ERROR the trait bound `[u8; 1]: Read` is not satisfied + wants_read(&[0u8]); + //~^ ERROR the trait bound `&[u8; 1]: Read` is not satisfied + wants_read(&[0u8][..]); + wants_read(&mut [0u8]); + //~^ ERROR the trait bound `&mut [u8; 1]: Read` is not satisfied +} diff --git a/tests/ui/dst/issue-90528-unsizing-suggestion-1.stderr b/tests/ui/dst/issue-90528-unsizing-suggestion-1.stderr new file mode 100644 index 00000000000..27ef3fe97a5 --- /dev/null +++ b/tests/ui/dst/issue-90528-unsizing-suggestion-1.stderr @@ -0,0 +1,56 @@ +error[E0277]: the trait bound `[u8; 1]: Read` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-1.rs:13:16 + | +LL | wants_read([0u8]); + | ---------- ^^^^^ the trait `Read` is not implemented for `[u8; 1]` + | | + | required by a bound introduced by this call + | + = help: the trait `Read` is implemented for `&[u8]` +note: required by a bound in `wants_read` + --> $DIR/issue-90528-unsizing-suggestion-1.rs:10:23 + | +LL | fn wants_read(_: impl Read) {} + | ^^^^ required by this bound in `wants_read` +help: convert the array to a `&[u8]` slice instead + | +LL | wants_read(&[0u8][..]); + | + ++++ + +error[E0277]: the trait bound `&[u8; 1]: Read` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-1.rs:15:16 + | +LL | wants_read(&[0u8]); + | ---------- ^^^^^^ the trait `Read` is not implemented for `&[u8; 1]` + | | + | required by a bound introduced by this call + | + = help: the trait `Read` is implemented for `&[u8]` +note: required by a bound in `wants_read` + --> $DIR/issue-90528-unsizing-suggestion-1.rs:10:23 + | +LL | fn wants_read(_: impl Read) {} + | ^^^^ required by this bound in `wants_read` +help: convert the array to a `&[u8]` slice instead + | +LL | wants_read(&[0u8][..]); + | ++++ + +error[E0277]: the trait bound `&mut [u8; 1]: Read` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-1.rs:18:16 + | +LL | wants_read(&mut [0u8]); + | ---------- ^^^^^^^^^^ the trait `Read` is not implemented for `&mut [u8; 1]` + | | + | required by a bound introduced by this call + | + = help: the trait `Read` is implemented for `&[u8]` +note: required by a bound in `wants_read` + --> $DIR/issue-90528-unsizing-suggestion-1.rs:10:23 + | +LL | fn wants_read(_: impl Read) {} + | ^^^^ required by this bound in `wants_read` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/dst/issue-90528-unsizing-suggestion-2.rs b/tests/ui/dst/issue-90528-unsizing-suggestion-2.rs new file mode 100644 index 00000000000..f2762ad421b --- /dev/null +++ b/tests/ui/dst/issue-90528-unsizing-suggestion-2.rs @@ -0,0 +1,28 @@ +// Issue #90528: provide helpful suggestions when a trait bound is unsatisfied +// due to a missed unsizing coercion. +// +// This test exercises array variables and a trait implemented on immmutable slices. + +trait Read {} + +impl Read for &[u8] {} + +fn wants_read(_: impl Read) {} + +fn main() { + let x = [0u8]; + wants_read(x); + //~^ ERROR the trait bound `[u8; 1]: Read` is not satisfied + wants_read(&x); + //~^ ERROR the trait bound `&[u8; 1]: Read` is not satisfied + wants_read(&x[..]); + + let x = &[0u8]; + wants_read(x); + //~^ ERROR the trait bound `&[u8; 1]: Read` is not satisfied + wants_read(&x); + //~^ ERROR the trait bound `&&[u8; 1]: Read` is not satisfied + wants_read(*x); + //~^ ERROR the trait bound `[u8; 1]: Read` is not satisfied + wants_read(&x[..]); +} diff --git a/tests/ui/dst/issue-90528-unsizing-suggestion-2.stderr b/tests/ui/dst/issue-90528-unsizing-suggestion-2.stderr new file mode 100644 index 00000000000..ae0c4ca506a --- /dev/null +++ b/tests/ui/dst/issue-90528-unsizing-suggestion-2.stderr @@ -0,0 +1,94 @@ +error[E0277]: the trait bound `[u8; 1]: Read` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-2.rs:14:16 + | +LL | wants_read(x); + | ---------- ^ the trait `Read` is not implemented for `[u8; 1]` + | | + | required by a bound introduced by this call + | + = help: the trait `Read` is implemented for `&[u8]` +note: required by a bound in `wants_read` + --> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23 + | +LL | fn wants_read(_: impl Read) {} + | ^^^^ required by this bound in `wants_read` +help: convert the array to a `&[u8]` slice instead + | +LL | wants_read(&x[..]); + | + ++++ + +error[E0277]: the trait bound `&[u8; 1]: Read` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-2.rs:16:16 + | +LL | wants_read(&x); + | ---------- ^^ the trait `Read` is not implemented for `&[u8; 1]` + | | + | required by a bound introduced by this call + | + = help: the trait `Read` is implemented for `&[u8]` +note: required by a bound in `wants_read` + --> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23 + | +LL | fn wants_read(_: impl Read) {} + | ^^^^ required by this bound in `wants_read` +help: convert the array to a `&[u8]` slice instead + | +LL | wants_read(&x[..]); + | ++++ + +error[E0277]: the trait bound `&[u8; 1]: Read` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-2.rs:21:16 + | +LL | wants_read(x); + | ---------- ^ the trait `Read` is not implemented for `&[u8; 1]` + | | + | required by a bound introduced by this call + | + = help: the trait `Read` is implemented for `&[u8]` +note: required by a bound in `wants_read` + --> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23 + | +LL | fn wants_read(_: impl Read) {} + | ^^^^ required by this bound in `wants_read` +help: convert the array to a `&[u8]` slice instead + | +LL | wants_read(&x[..]); + | + ++++ + +error[E0277]: the trait bound `&&[u8; 1]: Read` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-2.rs:23:16 + | +LL | wants_read(&x); + | ---------- ^^ the trait `Read` is not implemented for `&&[u8; 1]` + | | + | required by a bound introduced by this call + | + = help: the trait `Read` is implemented for `&[u8]` +note: required by a bound in `wants_read` + --> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23 + | +LL | fn wants_read(_: impl Read) {} + | ^^^^ required by this bound in `wants_read` + +error[E0277]: the trait bound `[u8; 1]: Read` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-2.rs:25:16 + | +LL | wants_read(*x); + | ---------- ^^ the trait `Read` is not implemented for `[u8; 1]` + | | + | required by a bound introduced by this call + | + = help: the trait `Read` is implemented for `&[u8]` +note: required by a bound in `wants_read` + --> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23 + | +LL | fn wants_read(_: impl Read) {} + | ^^^^ required by this bound in `wants_read` +help: convert the array to a `&[u8]` slice instead + | +LL | wants_read(&*x[..]); + | + ++++ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/dst/issue-90528-unsizing-suggestion-3.rs b/tests/ui/dst/issue-90528-unsizing-suggestion-3.rs new file mode 100644 index 00000000000..218843d0500 --- /dev/null +++ b/tests/ui/dst/issue-90528-unsizing-suggestion-3.rs @@ -0,0 +1,22 @@ +// Issue #90528: provide helpful suggestions when a trait bound is unsatisfied +// due to a missed unsizing coercion. +// +// This test exercises array literals and a trait implemented on mutable slices. + +trait Write {} + +impl Write for &mut [u8] {} + +fn wants_write(_: impl Write) {} + +fn main() { + wants_write([0u8]); + //~^ ERROR the trait bound `[u8; 1]: Write` is not satisfied + wants_write(&mut [0u8]); + //~^ ERROR the trait bound `&mut [u8; 1]: Write` is not satisfied + wants_write(&mut [0u8][..]); + wants_write(&[0u8]); + //~^ ERROR the trait bound `&[u8; 1]: Write` is not satisfied + wants_write(&[0u8][..]); + //~^ ERROR the trait bound `&[u8]: Write` is not satisfied +} diff --git a/tests/ui/dst/issue-90528-unsizing-suggestion-3.stderr b/tests/ui/dst/issue-90528-unsizing-suggestion-3.stderr new file mode 100644 index 00000000000..774d5ba3c89 --- /dev/null +++ b/tests/ui/dst/issue-90528-unsizing-suggestion-3.stderr @@ -0,0 +1,75 @@ +error[E0277]: the trait bound `[u8; 1]: Write` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-3.rs:13:17 + | +LL | wants_write([0u8]); + | ----------- ^^^^^ the trait `Write` is not implemented for `[u8; 1]` + | | + | required by a bound introduced by this call + | + = help: the trait `Write` is implemented for `&mut [u8]` +note: required by a bound in `wants_write` + --> $DIR/issue-90528-unsizing-suggestion-3.rs:10:24 + | +LL | fn wants_write(_: impl Write) {} + | ^^^^^ required by this bound in `wants_write` +help: convert the array to a `&mut [u8]` slice instead + | +LL | wants_write(&mut [0u8][..]); + | ++++ ++++ + +error[E0277]: the trait bound `&mut [u8; 1]: Write` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-3.rs:15:17 + | +LL | wants_write(&mut [0u8]); + | ----------- ^^^^^^^^^^ the trait `Write` is not implemented for `&mut [u8; 1]` + | | + | required by a bound introduced by this call + | + = help: the trait `Write` is implemented for `&mut [u8]` +note: required by a bound in `wants_write` + --> $DIR/issue-90528-unsizing-suggestion-3.rs:10:24 + | +LL | fn wants_write(_: impl Write) {} + | ^^^^^ required by this bound in `wants_write` +help: convert the array to a `&mut [u8]` slice instead + | +LL | wants_write(&mut [0u8][..]); + | ++++ + +error[E0277]: the trait bound `&[u8; 1]: Write` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-3.rs:18:17 + | +LL | wants_write(&[0u8]); + | ----------- ^^^^^^ the trait `Write` is not implemented for `&[u8; 1]` + | | + | required by a bound introduced by this call + | + = help: the trait `Write` is implemented for `&mut [u8]` +note: required by a bound in `wants_write` + --> $DIR/issue-90528-unsizing-suggestion-3.rs:10:24 + | +LL | fn wants_write(_: impl Write) {} + | ^^^^^ required by this bound in `wants_write` + +error[E0277]: the trait bound `&[u8]: Write` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-3.rs:20:17 + | +LL | wants_write(&[0u8][..]); + | ----------- ^^^^^^^^^^ the trait `Write` is not implemented for `&[u8]` + | | + | required by a bound introduced by this call + | + = help: the trait `Write` is implemented for `&mut [u8]` +note: required by a bound in `wants_write` + --> $DIR/issue-90528-unsizing-suggestion-3.rs:10:24 + | +LL | fn wants_write(_: impl Write) {} + | ^^^^^ required by this bound in `wants_write` +help: consider changing this borrow's mutability + | +LL | wants_write(&mut [0u8][..]); + | ~~~~ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/dst/issue-90528-unsizing-suggestion-4.rs b/tests/ui/dst/issue-90528-unsizing-suggestion-4.rs new file mode 100644 index 00000000000..eae953c61ff --- /dev/null +++ b/tests/ui/dst/issue-90528-unsizing-suggestion-4.rs @@ -0,0 +1,26 @@ +// Issue #90528: provide helpful suggestions when a trait bound is unsatisfied +// due to a missed unsizing coercion. +// +// This test exercises array variables and a trait implemented on mutable slices. + +trait Write {} + +impl Write for &mut [u8] {} + +fn wants_write(_: impl Write) {} + +fn main() { + let mut x = [0u8]; + wants_write(x); + //~^ ERROR the trait bound `[u8; 1]: Write` is not satisfied + wants_write(&mut x); + //~^ ERROR the trait bound `&mut [u8; 1]: Write` is not satisfied + wants_write(&mut x[..]); + + let x = &mut [0u8]; + wants_write(x); + //~^ ERROR the trait bound `&mut [u8; 1]: Write` is not satisfied + wants_write(*x); + //~^ ERROR the trait bound `[u8; 1]: Write` is not satisfied + wants_write(&mut x[..]); +} diff --git a/tests/ui/dst/issue-90528-unsizing-suggestion-4.stderr b/tests/ui/dst/issue-90528-unsizing-suggestion-4.stderr new file mode 100644 index 00000000000..a4020ee0708 --- /dev/null +++ b/tests/ui/dst/issue-90528-unsizing-suggestion-4.stderr @@ -0,0 +1,79 @@ +error[E0277]: the trait bound `[u8; 1]: Write` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-4.rs:14:17 + | +LL | wants_write(x); + | ----------- ^ the trait `Write` is not implemented for `[u8; 1]` + | | + | required by a bound introduced by this call + | + = help: the trait `Write` is implemented for `&mut [u8]` +note: required by a bound in `wants_write` + --> $DIR/issue-90528-unsizing-suggestion-4.rs:10:24 + | +LL | fn wants_write(_: impl Write) {} + | ^^^^^ required by this bound in `wants_write` +help: convert the array to a `&mut [u8]` slice instead + | +LL | wants_write(&mut x[..]); + | ++++ ++++ + +error[E0277]: the trait bound `&mut [u8; 1]: Write` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-4.rs:16:17 + | +LL | wants_write(&mut x); + | ----------- ^^^^^^ the trait `Write` is not implemented for `&mut [u8; 1]` + | | + | required by a bound introduced by this call + | + = help: the trait `Write` is implemented for `&mut [u8]` +note: required by a bound in `wants_write` + --> $DIR/issue-90528-unsizing-suggestion-4.rs:10:24 + | +LL | fn wants_write(_: impl Write) {} + | ^^^^^ required by this bound in `wants_write` +help: convert the array to a `&mut [u8]` slice instead + | +LL | wants_write(&mut x[..]); + | ++++ + +error[E0277]: the trait bound `&mut [u8; 1]: Write` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-4.rs:21:17 + | +LL | wants_write(x); + | ----------- ^ the trait `Write` is not implemented for `&mut [u8; 1]` + | | + | required by a bound introduced by this call + | + = help: the trait `Write` is implemented for `&mut [u8]` +note: required by a bound in `wants_write` + --> $DIR/issue-90528-unsizing-suggestion-4.rs:10:24 + | +LL | fn wants_write(_: impl Write) {} + | ^^^^^ required by this bound in `wants_write` +help: convert the array to a `&mut [u8]` slice instead + | +LL | wants_write(&mut x[..]); + | ++++ ++++ + +error[E0277]: the trait bound `[u8; 1]: Write` is not satisfied + --> $DIR/issue-90528-unsizing-suggestion-4.rs:23:17 + | +LL | wants_write(*x); + | ----------- ^^ the trait `Write` is not implemented for `[u8; 1]` + | | + | required by a bound introduced by this call + | + = help: the trait `Write` is implemented for `&mut [u8]` +note: required by a bound in `wants_write` + --> $DIR/issue-90528-unsizing-suggestion-4.rs:10:24 + | +LL | fn wants_write(_: impl Write) {} + | ^^^^^ required by this bound in `wants_write` +help: convert the array to a `&mut [u8]` slice instead + | +LL | wants_write(&mut *x[..]); + | ++++ ++++ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/dyn-star/feature-gate-dyn_star.rs b/tests/ui/dyn-star/feature-gate-dyn_star.rs index 4756661cf41..41eed71cdc3 100644 --- a/tests/ui/dyn-star/feature-gate-dyn_star.rs +++ b/tests/ui/dyn-star/feature-gate-dyn_star.rs @@ -3,7 +3,7 @@ /// dyn* is not necessarily the final surface syntax (if we have one at all), /// but for now we will support it to aid in writing tests independently. pub fn dyn_star_parameter(_: &dyn* Send) { - //~^ dyn* trait objects are unstable + //~^ `dyn*` trait objects are experimental } fn main() {} diff --git a/tests/ui/dyn-star/feature-gate-dyn_star.stderr b/tests/ui/dyn-star/feature-gate-dyn_star.stderr index c3449b6278b..342e71c3a3a 100644 --- a/tests/ui/dyn-star/feature-gate-dyn_star.stderr +++ b/tests/ui/dyn-star/feature-gate-dyn_star.stderr @@ -1,8 +1,8 @@ -error[E0658]: dyn* trait objects are unstable +error[E0658]: `dyn*` trait objects are experimental --> $DIR/feature-gate-dyn_star.rs:5:31 | LL | pub fn dyn_star_parameter(_: &dyn* Send) { - | ^^^^^^^^^ + | ^^^^ | = note: see issue #102425 for more information = help: add `#![feature(dyn_star)]` to the crate attributes to enable diff --git a/tests/ui/dyn-star/gated-span.rs b/tests/ui/dyn-star/gated-span.rs new file mode 100644 index 00000000000..a747987bd24 --- /dev/null +++ b/tests/ui/dyn-star/gated-span.rs @@ -0,0 +1,8 @@ +macro_rules! t { + ($t:ty) => {} +} + +t!(dyn* Send); +//~^ ERROR `dyn*` trait objects are experimental + +fn main() {} diff --git a/tests/ui/dyn-star/gated-span.stderr b/tests/ui/dyn-star/gated-span.stderr new file mode 100644 index 00000000000..626b6cd1b7f --- /dev/null +++ b/tests/ui/dyn-star/gated-span.stderr @@ -0,0 +1,12 @@ +error[E0658]: `dyn*` trait objects are experimental + --> $DIR/gated-span.rs:5:4 + | +LL | t!(dyn* Send); + | ^^^^ + | + = note: see issue #102425 for more information + = help: add `#![feature(dyn_star)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/dyn-star/no-explicit-dyn-star-cast.rs b/tests/ui/dyn-star/no-explicit-dyn-star-cast.rs index 67240c8e8da..2d28f516ab5 100644 --- a/tests/ui/dyn-star/no-explicit-dyn-star-cast.rs +++ b/tests/ui/dyn-star/no-explicit-dyn-star-cast.rs @@ -4,8 +4,8 @@ fn make_dyn_star() { let i = 42usize; let dyn_i: dyn* Debug = i as dyn* Debug; //~^ ERROR casting `usize` as `dyn* Debug` is invalid - //~| ERROR dyn* trait objects are unstable - //~| ERROR dyn* trait objects are unstable + //~| ERROR `dyn*` trait objects are experimental + //~| ERROR `dyn*` trait objects are experimental } fn main() { diff --git a/tests/ui/dyn-star/no-explicit-dyn-star-cast.stderr b/tests/ui/dyn-star/no-explicit-dyn-star-cast.stderr index eb9c933055a..78af9c7a389 100644 --- a/tests/ui/dyn-star/no-explicit-dyn-star-cast.stderr +++ b/tests/ui/dyn-star/no-explicit-dyn-star-cast.stderr @@ -1,17 +1,17 @@ -error[E0658]: dyn* trait objects are unstable +error[E0658]: `dyn*` trait objects are experimental --> $DIR/no-explicit-dyn-star-cast.rs:5:16 | LL | let dyn_i: dyn* Debug = i as dyn* Debug; - | ^^^^^^^^^^ + | ^^^^ | = note: see issue #102425 for more information = help: add `#![feature(dyn_star)]` to the crate attributes to enable -error[E0658]: dyn* trait objects are unstable +error[E0658]: `dyn*` trait objects are experimental --> $DIR/no-explicit-dyn-star-cast.rs:5:34 | LL | let dyn_i: dyn* Debug = i as dyn* Debug; - | ^^^^^^^^^^ + | ^^^^ | = note: see issue #102425 for more information = help: add `#![feature(dyn_star)]` to the crate attributes to enable diff --git a/tests/ui/empty/empty-struct-tuple-pat.stderr b/tests/ui/empty/empty-struct-tuple-pat.stderr index 8d0f75d204c..45001c79753 100644 --- a/tests/ui/empty/empty-struct-tuple-pat.stderr +++ b/tests/ui/empty/empty-struct-tuple-pat.stderr @@ -46,8 +46,8 @@ LL | XEmpty5(), | help: use the tuple variant pattern syntax instead | -LL | XE::XEmpty5(/* fields */) => (), - | ~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | XE::XEmpty5() => (), + | ~~~~~~~~~~~~~ help: a unit variant with a similar name exists | LL | XE::XEmpty4 => (), diff --git a/tests/ui/error-codes/E0010-teach.rs b/tests/ui/error-codes/E0010-teach.rs index fc5dffb37cf..798fcda2a10 100644 --- a/tests/ui/error-codes/E0010-teach.rs +++ b/tests/ui/error-codes/E0010-teach.rs @@ -1,8 +1,7 @@ // compile-flags: -Z teach -#![feature(box_syntax)] #![allow(warnings)] -const CON : Box = box 0; //~ ERROR E0010 - +const CON: Vec = vec![1, 2, 3]; //~ ERROR E0010 +//~| ERROR cannot call non-const fn fn main() {} diff --git a/tests/ui/error-codes/E0010-teach.stderr b/tests/ui/error-codes/E0010-teach.stderr index 33de9fd685e..7634970f36e 100644 --- a/tests/ui/error-codes/E0010-teach.stderr +++ b/tests/ui/error-codes/E0010-teach.stderr @@ -1,11 +1,22 @@ error[E0010]: allocations are not allowed in constants - --> $DIR/E0010-teach.rs:6:24 + --> $DIR/E0010-teach.rs:5:23 | -LL | const CON : Box = box 0; - | ^^^^^ allocation not allowed in constants +LL | const CON: Vec = vec![1, 2, 3]; + | ^^^^^^^^^^^^^ allocation not allowed in constants | = note: The value of statics and constants must be known at compile time, and they live for the entire lifetime of a program. Creating a boxed value allocates memory on the heap at runtime, and therefore cannot be done at compile time. + = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to previous error +error[E0015]: cannot call non-const fn `slice::::into_vec::` in constants + --> $DIR/E0010-teach.rs:5:23 + | +LL | const CON: Vec = vec![1, 2, 3]; + | ^^^^^^^^^^^^^ + | + = note: calls in constants are limited to constant functions, tuple structs and tuple variants + = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) -For more information about this error, try `rustc --explain E0010`. +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0010, E0015. +For more information about an error, try `rustc --explain E0010`. diff --git a/tests/ui/error-codes/E0010.rs b/tests/ui/error-codes/E0010.rs index e62997640f4..11721efffcb 100644 --- a/tests/ui/error-codes/E0010.rs +++ b/tests/ui/error-codes/E0010.rs @@ -1,6 +1,5 @@ -#![feature(box_syntax)] #![allow(warnings)] -const CON : Box = box 0; //~ ERROR E0010 - +const CON: Vec = vec![1, 2, 3]; //~ ERROR E0010 +//~| ERROR cannot call non-const fn fn main() {} diff --git a/tests/ui/error-codes/E0010.stderr b/tests/ui/error-codes/E0010.stderr index 0042333b98a..08947222422 100644 --- a/tests/ui/error-codes/E0010.stderr +++ b/tests/ui/error-codes/E0010.stderr @@ -1,9 +1,21 @@ error[E0010]: allocations are not allowed in constants - --> $DIR/E0010.rs:4:24 + --> $DIR/E0010.rs:3:23 | -LL | const CON : Box = box 0; - | ^^^^^ allocation not allowed in constants +LL | const CON: Vec = vec![1, 2, 3]; + | ^^^^^^^^^^^^^ allocation not allowed in constants + | + = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to previous error +error[E0015]: cannot call non-const fn `slice::::into_vec::` in constants + --> $DIR/E0010.rs:3:23 + | +LL | const CON: Vec = vec![1, 2, 3]; + | ^^^^^^^^^^^^^ + | + = note: calls in constants are limited to constant functions, tuple structs and tuple variants + = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) -For more information about this error, try `rustc --explain E0010`. +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0010, E0015. +For more information about an error, try `rustc --explain E0010`. diff --git a/tests/ui/feature-gates/feature-gate-box-expr.rs b/tests/ui/feature-gates/feature-gate-box-expr.rs deleted file mode 100644 index 870253d2f05..00000000000 --- a/tests/ui/feature-gates/feature-gate-box-expr.rs +++ /dev/null @@ -1,14 +0,0 @@ -// gate-test-box_syntax - -// Check that `box EXPR` is feature-gated. -// -// See also feature-gate-placement-expr.rs -// -// (Note that the two tests are separated since the checks appear to -// be performed at distinct phases, with an abort_if_errors call -// separating them.) - -fn main() { - let x = box 'c'; //~ ERROR box expression syntax is experimental - println!("x: {}", x); -} diff --git a/tests/ui/feature-gates/feature-gate-box-expr.stderr b/tests/ui/feature-gates/feature-gate-box-expr.stderr deleted file mode 100644 index af864b25f14..00000000000 --- a/tests/ui/feature-gates/feature-gate-box-expr.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0658]: box expression syntax is experimental; you can call `Box::new` instead - --> $DIR/feature-gate-box-expr.rs:12:13 - | -LL | let x = box 'c'; - | ^^^^^^^ - | - = note: see issue #49733 for more information - = help: add `#![feature(box_syntax)]` to the crate attributes to enable - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-box_syntax.rs b/tests/ui/feature-gates/feature-gate-box_syntax.rs deleted file mode 100644 index 778660cc0b5..00000000000 --- a/tests/ui/feature-gates/feature-gate-box_syntax.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Test that the use of the box syntax is gated by `box_syntax` feature gate. - -fn main() { - let x = box 3; - //~^ ERROR box expression syntax is experimental; you can call `Box::new` instead -} diff --git a/tests/ui/feature-gates/feature-gate-box_syntax.stderr b/tests/ui/feature-gates/feature-gate-box_syntax.stderr deleted file mode 100644 index dcf8eeed7cf..00000000000 --- a/tests/ui/feature-gates/feature-gate-box_syntax.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0658]: box expression syntax is experimental; you can call `Box::new` instead - --> $DIR/feature-gate-box_syntax.rs:4:13 - | -LL | let x = box 3; - | ^^^^^ - | - = note: see issue #49733 for more information - = help: add `#![feature(box_syntax)]` to the crate attributes to enable - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-closure_track_caller.rs b/tests/ui/feature-gates/feature-gate-closure_track_caller.rs index a8d63a8145a..a4c91f3bc18 100644 --- a/tests/ui/feature-gates/feature-gate-closure_track_caller.rs +++ b/tests/ui/feature-gates/feature-gate-closure_track_caller.rs @@ -1,7 +1,9 @@ +// edition:2021 #![feature(stmt_expr_attributes)] #![feature(generators)] fn main() { let _closure = #[track_caller] || {}; //~ `#[track_caller]` on closures let _generator = #[track_caller] || { yield; }; //~ `#[track_caller]` on closures + let _future = #[track_caller] async {}; //~ `#[track_caller]` on closures } diff --git a/tests/ui/feature-gates/feature-gate-closure_track_caller.stderr b/tests/ui/feature-gates/feature-gate-closure_track_caller.stderr index ed63d74fe4d..cf2ea5fe1ca 100644 --- a/tests/ui/feature-gates/feature-gate-closure_track_caller.stderr +++ b/tests/ui/feature-gates/feature-gate-closure_track_caller.stderr @@ -1,5 +1,5 @@ error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/feature-gate-closure_track_caller.rs:5:20 + --> $DIR/feature-gate-closure_track_caller.rs:6:20 | LL | let _closure = #[track_caller] || {}; | ^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let _closure = #[track_caller] || {}; = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/feature-gate-closure_track_caller.rs:6:22 + --> $DIR/feature-gate-closure_track_caller.rs:7:22 | LL | let _generator = #[track_caller] || { yield; }; | ^^^^^^^^^^^^^^^ @@ -16,6 +16,15 @@ LL | let _generator = #[track_caller] || { yield; }; = note: see issue #87417 for more information = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable -error: aborting due to 2 previous errors +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/feature-gate-closure_track_caller.rs:8:19 + | +LL | let _future = #[track_caller] async {}; + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr index f647380ef9b..b1613f638d3 100644 --- a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr +++ b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr @@ -64,6 +64,12 @@ error[E0229]: associated type bindings are not allowed here | LL | impl FnOnce() for Foo1 { | ^^^^^^^^ associated type not allowed here + | +help: parenthesized trait syntax expands to `FnOnce<(), Output=()>` + --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:16:6 + | +LL | impl FnOnce() for Foo1 { + | ^^^^^^^^ error[E0658]: the precise format of `Fn`-family traits' type parameters is subject to change --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:23:6 diff --git a/tests/ui/fmt/auxiliary/format-string-proc-macro.rs b/tests/ui/fmt/auxiliary/format-string-proc-macro.rs index 1b7ef93f41d..0c39ade721f 100644 --- a/tests/ui/fmt/auxiliary/format-string-proc-macro.rs +++ b/tests/ui/fmt/auxiliary/format-string-proc-macro.rs @@ -28,25 +28,41 @@ pub fn err_with_input_span(input: TokenStream) -> TokenStream { TokenStream::from(TokenTree::Literal(lit)) } +fn build_format(args: impl Into) -> TokenStream { + TokenStream::from_iter([ + TokenTree::from(Ident::new("format", Span::call_site())), + TokenTree::from(Punct::new('!', Spacing::Alone)), + TokenTree::from(Group::new(Delimiter::Parenthesis, args.into())), + ]) +} #[proc_macro] pub fn respan_to_invalid_format_literal(input: TokenStream) -> TokenStream { let mut s = Literal::string("{"); s.set_span(input.into_iter().next().unwrap().span()); - TokenStream::from_iter([ - TokenTree::from(Ident::new("format", Span::call_site())), - TokenTree::from(Punct::new('!', Spacing::Alone)), - TokenTree::from(Group::new(Delimiter::Parenthesis, TokenTree::from(s).into())), - ]) + + build_format(TokenTree::from(s)) } #[proc_macro] pub fn capture_a_with_prepended_space_preserve_span(input: TokenStream) -> TokenStream { let mut s = Literal::string(" {a}"); s.set_span(input.into_iter().next().unwrap().span()); - TokenStream::from_iter([ - TokenTree::from(Ident::new("format", Span::call_site())), - TokenTree::from(Punct::new('!', Spacing::Alone)), - TokenTree::from(Group::new(Delimiter::Parenthesis, TokenTree::from(s).into())), - ]) + + build_format(TokenTree::from(s)) +} + +#[proc_macro] +pub fn format_args_captures(_: TokenStream) -> TokenStream { + r#"{ let x = 5; format!("{x}") }"#.parse().unwrap() +} + +#[proc_macro] +pub fn bad_format_args_captures(_: TokenStream) -> TokenStream { + r#"{ let x = 5; format!(concat!("{x}")) }"#.parse().unwrap() +} + +#[proc_macro] +pub fn identity_pm(input: TokenStream) -> TokenStream { + input } diff --git a/tests/ui/fmt/format-args-capture-first-literal-is-macro.rs b/tests/ui/fmt/format-args-capture-first-literal-is-macro.rs new file mode 100644 index 00000000000..bf5c0dcb54d --- /dev/null +++ b/tests/ui/fmt/format-args-capture-first-literal-is-macro.rs @@ -0,0 +1,21 @@ +// aux-build:format-string-proc-macro.rs + +#[macro_use] +extern crate format_string_proc_macro; + +macro_rules! identity_mbe { + ($tt:tt) => { + $tt + //~^ ERROR there is no argument named `a` + }; +} + +fn main() { + let a = 0; + + format!(identity_pm!("{a}")); + //~^ ERROR there is no argument named `a` + format!(identity_mbe!("{a}")); + format!(concat!("{a}")); + //~^ ERROR there is no argument named `a` +} diff --git a/tests/ui/fmt/format-args-capture-first-literal-is-macro.stderr b/tests/ui/fmt/format-args-capture-first-literal-is-macro.stderr new file mode 100644 index 00000000000..4cf3afad7b8 --- /dev/null +++ b/tests/ui/fmt/format-args-capture-first-literal-is-macro.stderr @@ -0,0 +1,30 @@ +error: there is no argument named `a` + --> $DIR/format-args-capture-first-literal-is-macro.rs:16:26 + | +LL | format!(identity_pm!("{a}")); + | ^^^^^ + | + = note: did you intend to capture a variable `a` from the surrounding scope? + = note: to avoid ambiguity, `format_args!` cannot capture variables when the format string is expanded from a macro + +error: there is no argument named `a` + --> $DIR/format-args-capture-first-literal-is-macro.rs:8:9 + | +LL | $tt + | ^^^ + | + = note: did you intend to capture a variable `a` from the surrounding scope? + = note: to avoid ambiguity, `format_args!` cannot capture variables when the format string is expanded from a macro + +error: there is no argument named `a` + --> $DIR/format-args-capture-first-literal-is-macro.rs:19:13 + | +LL | format!(concat!("{a}")); + | ^^^^^^^^^^^^^^ + | + = note: did you intend to capture a variable `a` from the surrounding scope? + = note: to avoid ambiguity, `format_args!` cannot capture variables when the format string is expanded from a macro + = note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors + diff --git a/tests/ui/fmt/format-args-capture-from-pm-first-arg-macro.rs b/tests/ui/fmt/format-args-capture-from-pm-first-arg-macro.rs new file mode 100644 index 00000000000..f67edf5e167 --- /dev/null +++ b/tests/ui/fmt/format-args-capture-from-pm-first-arg-macro.rs @@ -0,0 +1,8 @@ +// aux-build:format-string-proc-macro.rs + +extern crate format_string_proc_macro; + +fn main() { + format_string_proc_macro::bad_format_args_captures!(); + //~^ ERROR there is no argument named `x` +} diff --git a/tests/ui/fmt/format-args-capture-from-pm-first-arg-macro.stderr b/tests/ui/fmt/format-args-capture-from-pm-first-arg-macro.stderr new file mode 100644 index 00000000000..bb6a14d88b3 --- /dev/null +++ b/tests/ui/fmt/format-args-capture-from-pm-first-arg-macro.stderr @@ -0,0 +1,12 @@ +error: there is no argument named `x` + --> $DIR/format-args-capture-from-pm-first-arg-macro.rs:6:5 + | +LL | format_string_proc_macro::bad_format_args_captures!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: did you intend to capture a variable `x` from the surrounding scope? + = note: to avoid ambiguity, `format_args!` cannot capture variables when the format string is expanded from a macro + = note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + diff --git a/tests/ui/fmt/format-args-capture-issue-106408.rs b/tests/ui/fmt/format-args-capture-issue-106408.rs new file mode 100644 index 00000000000..0fd195416ee --- /dev/null +++ b/tests/ui/fmt/format-args-capture-issue-106408.rs @@ -0,0 +1,10 @@ +// check-pass +// aux-build:format-string-proc-macro.rs + +extern crate format_string_proc_macro; + +fn main() { + // While literal macros like `format_args!(concat!())` are not supposed to work with implicit + // captures, it should work if the whole invocation comes from a macro expansion (#106408). + format_string_proc_macro::format_args_captures!(); +} diff --git a/tests/ui/fmt/format-args-capture-macro-hygiene-pass.rs b/tests/ui/fmt/format-args-capture-macro-hygiene-pass.rs new file mode 100644 index 00000000000..7553fcc4e01 --- /dev/null +++ b/tests/ui/fmt/format-args-capture-macro-hygiene-pass.rs @@ -0,0 +1,16 @@ +// run-pass + +macro_rules! format_mbe { + ($tt:tt) => { + { + #[allow(unused_variables)] + let a = 123; + format!($tt) + } + }; +} + +fn main() { + let a = 0; + assert_eq!(format_mbe!("{a}"), "0"); +} diff --git a/tests/ui/fmt/respanned-literal-issue-106191.rs b/tests/ui/fmt/respanned-literal-issue-106191.rs index 5a18983a3fa..44642a10fc0 100644 --- a/tests/ui/fmt/respanned-literal-issue-106191.rs +++ b/tests/ui/fmt/respanned-literal-issue-106191.rs @@ -1,15 +1,10 @@ // aux-build:format-string-proc-macro.rs -// check-fail -// known-bug: #106191 -// unset-rustc-env:RUST_BACKTRACE -// had to be reverted -// error-pattern:unexpectedly panicked -// failure-status:101 -// dont-check-compiler-stderr extern crate format_string_proc_macro; fn main() { format_string_proc_macro::respan_to_invalid_format_literal!("¡"); + //~^ ERROR invalid format string: expected `'}'` but string was terminated format_args!(r#concat!("¡ {")); + //~^ ERROR invalid format string: expected `'}'` but string was terminated } diff --git a/tests/ui/fmt/respanned-literal-issue-106191.stderr b/tests/ui/fmt/respanned-literal-issue-106191.stderr index 16717f42253..73a3af65a38 100644 --- a/tests/ui/fmt/respanned-literal-issue-106191.stderr +++ b/tests/ui/fmt/respanned-literal-issue-106191.stderr @@ -1,2 +1,19 @@ - query stack during panic: -end of query stack +error: invalid format string: expected `'}'` but string was terminated + --> $DIR/respanned-literal-issue-106191.rs:6:65 + | +LL | format_string_proc_macro::respan_to_invalid_format_literal!("¡"); + | ^^^ expected `'}'` in format string + | + = note: if you intended to print `{`, you can escape it using `{{` + +error: invalid format string: expected `'}'` but string was terminated + --> $DIR/respanned-literal-issue-106191.rs:8:18 + | +LL | format_args!(r#concat!("¡ {")); + | ^^^^^^^^^^^^^^^^^^^^^^^ expected `'}'` in format string + | + = note: if you intended to print `{`, you can escape it using `{{` + = note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + diff --git a/tests/ui/fn/issue-39259.rs b/tests/ui/fn/issue-39259.rs new file mode 100644 index 00000000000..5872f1007b0 --- /dev/null +++ b/tests/ui/fn/issue-39259.rs @@ -0,0 +1,13 @@ +#![feature(fn_traits)] +#![feature(unboxed_closures)] + +struct S; + +impl Fn(u32) -> u32 for S { +//~^ ERROR associated type bindings are not allowed here [E0229] + fn call(&self) -> u32 { + 5 + } +} + +fn main() {} diff --git a/tests/ui/fn/issue-39259.stderr b/tests/ui/fn/issue-39259.stderr new file mode 100644 index 00000000000..b656b76bfe4 --- /dev/null +++ b/tests/ui/fn/issue-39259.stderr @@ -0,0 +1,15 @@ +error[E0229]: associated type bindings are not allowed here + --> $DIR/issue-39259.rs:6:17 + | +LL | impl Fn(u32) -> u32 for S { + | ^^^ associated type not allowed here + | +help: parenthesized trait syntax expands to `Fn<(u32,), Output=u32>` + --> $DIR/issue-39259.rs:6:6 + | +LL | impl Fn(u32) -> u32 for S { + | ^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0229`. diff --git a/tests/ui/generator/issue-105084.drop_tracking_mir.stderr b/tests/ui/generator/issue-105084.drop_tracking_mir.stderr index cfc0cf7cdd7..1cd4c4e4d6c 100644 --- a/tests/ui/generator/issue-105084.drop_tracking_mir.stderr +++ b/tests/ui/generator/issue-105084.drop_tracking_mir.stderr @@ -1,5 +1,5 @@ error[E0382]: borrow of moved value: `g` - --> $DIR/issue-105084.rs:44:14 + --> $DIR/issue-105084.rs:45:14 | LL | let mut g = || { | ----- move occurs because `g` has type `[generator@$DIR/issue-105084.rs:22:17: 22:19]`, which does not implement the `Copy` trait @@ -23,7 +23,7 @@ LL | let mut h = copy(g.clone()); | ++++++++ error[E0277]: the trait bound `Box<(i32, ())>: Copy` is not satisfied in `[generator@$DIR/issue-105084.rs:22:17: 22:19]` - --> $DIR/issue-105084.rs:38:17 + --> $DIR/issue-105084.rs:39:17 | LL | let mut g = || { | -- within this `[generator@$DIR/issue-105084.rs:22:17: 22:19]` @@ -32,13 +32,13 @@ LL | let mut h = copy(g); | ^^^^ within `[generator@$DIR/issue-105084.rs:22:17: 22:19]`, the trait `Copy` is not implemented for `Box<(i32, ())>` | note: generator does not implement `Copy` as this value is used across a yield - --> $DIR/issue-105084.rs:28:25 + --> $DIR/issue-105084.rs:29:22 | -LL | let t = box (5, yield); - | --------^^^^^- - | | | - | | yield occurs here, with `box (5, yield)` maybe used later - | has type `Box<(i32, ())>` which does not implement `Copy` +LL | Box::new((5, yield)); + | -------------^^^^^-- + | | | + | | yield occurs here, with `Box::new((5, yield))` maybe used later + | has type `Box<(i32, ())>` which does not implement `Copy` note: required by a bound in `copy` --> $DIR/issue-105084.rs:17:12 | diff --git a/tests/ui/generator/issue-105084.rs b/tests/ui/generator/issue-105084.rs index 7c9a97b40a5..ff9357b76a0 100644 --- a/tests/ui/generator/issue-105084.rs +++ b/tests/ui/generator/issue-105084.rs @@ -9,7 +9,7 @@ #![feature(generators)] #![feature(generator_clone)] #![feature(generator_trait)] -#![feature(box_syntax)] +#![feature(rustc_attrs, stmt_expr_attributes)] use std::ops::Generator; use std::pin::Pin; @@ -25,7 +25,8 @@ fn main() { // - create a Box that is ignored for trait computations; // - compute fields (and yields); // - assign to `t`. - let t = box (5, yield); + let t = #[rustc_box] + Box::new((5, yield)); drop(t); }; diff --git a/tests/ui/generator/yield-in-box.rs b/tests/ui/generator/yield-in-box.rs deleted file mode 100644 index dd6fa7c151a..00000000000 --- a/tests/ui/generator/yield-in-box.rs +++ /dev/null @@ -1,24 +0,0 @@ -// run-pass -// Test that box-statements with yields in them work. - -#![feature(generators, box_syntax, generator_trait)] -use std::pin::Pin; -use std::ops::Generator; -use std::ops::GeneratorState; - -fn main() { - let x = 0i32; - || { //~ WARN unused generator that must be used - let y = 2u32; - { - let _t = box (&x, yield 0, &y); - } - match box (&x, yield 0, &y) { - _t => {} - } - }; - - let mut g = |_| box yield; - assert_eq!(Pin::new(&mut g).resume(1), GeneratorState::Yielded(())); - assert_eq!(Pin::new(&mut g).resume(2), GeneratorState::Complete(box 2)); -} diff --git a/tests/ui/generator/yield-in-box.stderr b/tests/ui/generator/yield-in-box.stderr deleted file mode 100644 index 9d03ee00800..00000000000 --- a/tests/ui/generator/yield-in-box.stderr +++ /dev/null @@ -1,17 +0,0 @@ -warning: unused generator that must be used - --> $DIR/yield-in-box.rs:11:5 - | -LL | / || { -LL | | let y = 2u32; -LL | | { -LL | | let _t = box (&x, yield 0, &y); -... | -LL | | } -LL | | }; - | |_____^ - | - = note: generators are lazy and do nothing unless resumed - = note: `#[warn(unused_must_use)]` on by default - -warning: 1 warning emitted - diff --git a/tests/ui/higher-rank-trait-bounds/fn-ptr.classic.stderr b/tests/ui/higher-rank-trait-bounds/fn-ptr.classic.stderr new file mode 100644 index 00000000000..9af6bc45c7a --- /dev/null +++ b/tests/ui/higher-rank-trait-bounds/fn-ptr.classic.stderr @@ -0,0 +1,19 @@ +error[E0277]: expected a `Fn<(&'w (),)>` closure, found `fn(&'w ())` + --> $DIR/fn-ptr.rs:12:5 + | +LL | ice(); + | ^^^ expected an `Fn<(&'w (),)>` closure, found `fn(&'w ())` + | + = help: the trait `for<'w> Fn<(&'w (),)>` is not implemented for `fn(&'w ())` +note: required by a bound in `ice` + --> $DIR/fn-ptr.rs:7:25 + | +LL | fn ice() + | --- required by a bound in this function +LL | where +LL | for<'w> fn(&'w ()): Fn(&'w ()), + | ^^^^^^^^^^ required by this bound in `ice` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/higher-rank-trait-bounds/fn-ptr.rs b/tests/ui/higher-rank-trait-bounds/fn-ptr.rs new file mode 100644 index 00000000000..853160f9612 --- /dev/null +++ b/tests/ui/higher-rank-trait-bounds/fn-ptr.rs @@ -0,0 +1,14 @@ +// revisions: classic next +//[next] compile-flags: -Ztrait-solver=next +//[next] check-pass + +fn ice() +where + for<'w> fn(&'w ()): Fn(&'w ()), +{ +} + +fn main() { + ice(); + //[classic]~^ ERROR expected a `Fn<(&'w (),)>` closure, found `fn(&'w ())` +} diff --git a/tests/ui/impl-trait/in-trait/default-body-with-rpit.rs b/tests/ui/impl-trait/in-trait/default-body-with-rpit.rs index ad3cc7c2524..0558d95128f 100644 --- a/tests/ui/impl-trait/in-trait/default-body-with-rpit.rs +++ b/tests/ui/impl-trait/in-trait/default-body-with-rpit.rs @@ -1,5 +1,5 @@ -// check-pass // edition:2021 +// known-bug: #108304 #![feature(async_fn_in_trait, return_position_impl_trait_in_trait)] #![allow(incomplete_features)] diff --git a/tests/ui/impl-trait/in-trait/default-body-with-rpit.stderr b/tests/ui/impl-trait/in-trait/default-body-with-rpit.stderr new file mode 100644 index 00000000000..b5fc9d44d36 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/default-body-with-rpit.stderr @@ -0,0 +1,24 @@ +error: concrete type differs from previous defining opaque type use + --> $DIR/default-body-with-rpit.rs:11:9 + | +LL | "" + | ^^ expected `impl Debug`, got `&'static str` + | +note: previous use here + --> $DIR/default-body-with-rpit.rs:10:39 + | +LL | async fn baz(&self) -> impl Debug { + | _______________________________________^ +LL | | "" +LL | | } + | |_____^ + +error[E0720]: cannot resolve opaque type + --> $DIR/default-body-with-rpit.rs:10:28 + | +LL | async fn baz(&self) -> impl Debug { + | ^^^^^^^^^^ cannot resolve opaque type + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0720`. diff --git a/tests/ui/impl-trait/in-trait/new-lowering-strategy/simple-impl-trait.rs b/tests/ui/impl-trait/in-trait/new-lowering-strategy/simple-impl-trait.rs new file mode 100644 index 00000000000..ae09d20f6f5 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/new-lowering-strategy/simple-impl-trait.rs @@ -0,0 +1,17 @@ +// check-pass +// compile-flags: -Zlower-impl-trait-in-trait-to-assoc-ty + +#![feature(return_position_impl_trait_in_trait)] +#![allow(incomplete_features)] + +trait Foo { + fn foo() -> impl Sized; +} + +impl Foo for String { + fn foo() -> i32 { + 22 + } +} + +fn main() {} diff --git a/tests/ui/impl-trait/issues/issue-78722.rs b/tests/ui/impl-trait/issues/issue-78722.rs index 78233f300bd..7b5ab5f2298 100644 --- a/tests/ui/impl-trait/issues/issue-78722.rs +++ b/tests/ui/impl-trait/issues/issue-78722.rs @@ -12,7 +12,6 @@ struct Bug { } let f: F = async { 1 }; //~^ ERROR `async` blocks are not allowed in constants - //~| ERROR destructor of 1 }], } diff --git a/tests/ui/impl-trait/issues/issue-78722.stderr b/tests/ui/impl-trait/issues/issue-78722.stderr index c00df8087e8..05a2c135cf7 100644 --- a/tests/ui/impl-trait/issues/issue-78722.stderr +++ b/tests/ui/impl-trait/issues/issue-78722.stderr @@ -7,22 +7,13 @@ LL | let f: F = async { 1 }; = note: see issue #85368 for more information = help: add `#![feature(const_async_blocks)]` to the crate attributes to enable -error[E0493]: destructor of `F` cannot be evaluated at compile-time - --> $DIR/issue-78722.rs:13:13 - | -LL | let f: F = async { 1 }; - | ^ the destructor for this type cannot be evaluated in constants -... -LL | }], - | - value is dropped here - error[E0271]: expected `[async block@$DIR/issue-78722.rs:11:13: 11:21]` to be a future that resolves to `u8`, but it resolves to `()` --> $DIR/issue-78722.rs:9:30 | LL | fn concrete_use() -> F { | ^ expected `()`, found `u8` -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0271, E0493, E0658. +Some errors have detailed explanations: E0271, E0658. For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/issues/issue-3029.rs b/tests/ui/issues/issue-3029.rs index a5d30960a4c..43c8a0a23fb 100644 --- a/tests/ui/issues/issue-3029.rs +++ b/tests/ui/issues/issue-3029.rs @@ -2,9 +2,7 @@ // error-pattern:so long // ignore-emscripten no processes -#![allow(unused_allocation)] #![allow(unreachable_code)] -#![allow(unused_variables)] fn main() { let mut x = Vec::new(); diff --git a/tests/ui/issues/issue-66667-function-cmp-cycle.rs b/tests/ui/issues/issue-66667-function-cmp-cycle.rs index 7b025be11a0..b4f09fbbb04 100644 --- a/tests/ui/issues/issue-66667-function-cmp-cycle.rs +++ b/tests/ui/issues/issue-66667-function-cmp-cycle.rs @@ -1,16 +1,19 @@ fn first() { second == 1 //~ ERROR binary operation //~^ ERROR mismatched types + //~| ERROR mismatched types } fn second() { first == 1 //~ ERROR binary operation //~^ ERROR mismatched types + //~| ERROR mismatched types } fn bar() { bar == 1 //~ ERROR binary operation //~^ ERROR mismatched types + //~| ERROR mismatched types } fn main() {} diff --git a/tests/ui/issues/issue-66667-function-cmp-cycle.stderr b/tests/ui/issues/issue-66667-function-cmp-cycle.stderr index 887699ef5ce..d9a960ce197 100644 --- a/tests/ui/issues/issue-66667-function-cmp-cycle.stderr +++ b/tests/ui/issues/issue-66667-function-cmp-cycle.stderr @@ -15,8 +15,16 @@ LL | second == 1 = note: expected fn item `fn() {second}` found type `{integer}` +error[E0308]: mismatched types + --> $DIR/issue-66667-function-cmp-cycle.rs:2:5 + | +LL | fn first() { + | - help: try adding a return type: `-> bool` +LL | second == 1 + | ^^^^^^^^^^^ expected `()`, found `bool` + error[E0369]: binary operation `==` cannot be applied to type `fn() {first}` - --> $DIR/issue-66667-function-cmp-cycle.rs:7:11 + --> $DIR/issue-66667-function-cmp-cycle.rs:8:11 | LL | first == 1 | ----- ^^ - {integer} @@ -24,7 +32,7 @@ LL | first == 1 | fn() {first} error[E0308]: mismatched types - --> $DIR/issue-66667-function-cmp-cycle.rs:7:14 + --> $DIR/issue-66667-function-cmp-cycle.rs:8:14 | LL | first == 1 | ^ expected fn item, found integer @@ -32,8 +40,16 @@ LL | first == 1 = note: expected fn item `fn() {first}` found type `{integer}` +error[E0308]: mismatched types + --> $DIR/issue-66667-function-cmp-cycle.rs:8:5 + | +LL | fn second() { + | - help: try adding a return type: `-> bool` +LL | first == 1 + | ^^^^^^^^^^ expected `()`, found `bool` + error[E0369]: binary operation `==` cannot be applied to type `fn() {bar}` - --> $DIR/issue-66667-function-cmp-cycle.rs:12:9 + --> $DIR/issue-66667-function-cmp-cycle.rs:14:9 | LL | bar == 1 | --- ^^ - {integer} @@ -41,7 +57,7 @@ LL | bar == 1 | fn() {bar} error[E0308]: mismatched types - --> $DIR/issue-66667-function-cmp-cycle.rs:12:12 + --> $DIR/issue-66667-function-cmp-cycle.rs:14:12 | LL | bar == 1 | ^ expected fn item, found integer @@ -49,7 +65,15 @@ LL | bar == 1 = note: expected fn item `fn() {bar}` found type `{integer}` -error: aborting due to 6 previous errors +error[E0308]: mismatched types + --> $DIR/issue-66667-function-cmp-cycle.rs:14:5 + | +LL | fn bar() { + | - help: try adding a return type: `-> bool` +LL | bar == 1 + | ^^^^^^^^ expected `()`, found `bool` + +error: aborting due to 9 previous errors Some errors have detailed explanations: E0308, E0369. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/iterators/into-iter-on-arrays-lint.fixed b/tests/ui/iterators/into-iter-on-arrays-lint.fixed index 6e02a7024b9..5b91aaf9ea5 100644 --- a/tests/ui/iterators/into-iter-on-arrays-lint.fixed +++ b/tests/ui/iterators/into-iter-on-arrays-lint.fixed @@ -2,7 +2,7 @@ // run-rustfix // rustfix-only-machine-applicable -#[allow(unused_must_use)] +#[allow(unused_must_use, unused_allocation)] fn main() { let small = [1, 2]; let big = [0u8; 33]; diff --git a/tests/ui/iterators/into-iter-on-arrays-lint.rs b/tests/ui/iterators/into-iter-on-arrays-lint.rs index 582d5cadd06..25b0cef73d7 100644 --- a/tests/ui/iterators/into-iter-on-arrays-lint.rs +++ b/tests/ui/iterators/into-iter-on-arrays-lint.rs @@ -2,7 +2,7 @@ // run-rustfix // rustfix-only-machine-applicable -#[allow(unused_must_use)] +#[allow(unused_must_use, unused_allocation)] fn main() { let small = [1, 2]; let big = [0u8; 33]; diff --git a/tests/ui/lang-items/no_owned_box_lang_item.rs b/tests/ui/lang-items/no_owned_box_lang_item.rs deleted file mode 100644 index c22b44ffca2..00000000000 --- a/tests/ui/lang-items/no_owned_box_lang_item.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Test that we don't ICE when we are missing the owned_box lang item. - -// error-pattern: requires `owned_box` lang_item - -#![feature(lang_items, box_syntax)] -#![no_std] - -use core::panic::PanicInfo; - -fn main() { - let x = box 1i32; -} - -#[lang = "eh_personality"] extern "C" fn eh_personality() {} -#[lang = "eh_catch_typeinfo"] static EH_CATCH_TYPEINFO: u8 = 0; -#[lang = "panic_impl"] fn panic_impl(panic: &PanicInfo) -> ! { loop {} } diff --git a/tests/ui/lang-items/no_owned_box_lang_item.stderr b/tests/ui/lang-items/no_owned_box_lang_item.stderr deleted file mode 100644 index c55c246b5e1..00000000000 --- a/tests/ui/lang-items/no_owned_box_lang_item.stderr +++ /dev/null @@ -1,4 +0,0 @@ -error: requires `owned_box` lang_item - -error: aborting due to previous error - diff --git a/tests/ui/lifetimes/issue-95023.stderr b/tests/ui/lifetimes/issue-95023.stderr index 35c3797c77a..5b93eff8614 100644 --- a/tests/ui/lifetimes/issue-95023.stderr +++ b/tests/ui/lifetimes/issue-95023.stderr @@ -25,6 +25,12 @@ error[E0229]: associated type bindings are not allowed here | LL | impl Fn(&isize) for Error { | ^^^^^^^^^^ associated type not allowed here + | +help: parenthesized trait syntax expands to `Fn<(&isize,), Output=()>` + --> $DIR/issue-95023.rs:3:6 + | +LL | impl Fn(&isize) for Error { + | ^^^^^^^^^^ error[E0220]: associated type `B` not found for `Self` --> $DIR/issue-95023.rs:6:44 diff --git a/tests/ui/lint/unused/unused-allocation.rs b/tests/ui/lint/unused/unused-allocation.rs new file mode 100644 index 00000000000..c1a6f5ceaf1 --- /dev/null +++ b/tests/ui/lint/unused/unused-allocation.rs @@ -0,0 +1,7 @@ +#![feature(rustc_attrs, stmt_expr_attributes)] +#![deny(unused_allocation)] + +fn main() { + _ = (#[rustc_box] Box::new([1])).len(); //~ error: unnecessary allocation, use `&` instead + _ = Box::new([1]).len(); //~ error: unnecessary allocation, use `&` instead +} diff --git a/tests/ui/lint/unused/unused-allocation.stderr b/tests/ui/lint/unused/unused-allocation.stderr new file mode 100644 index 00000000000..c9ccfbd30e5 --- /dev/null +++ b/tests/ui/lint/unused/unused-allocation.stderr @@ -0,0 +1,20 @@ +error: unnecessary allocation, use `&` instead + --> $DIR/unused-allocation.rs:5:9 + | +LL | _ = (#[rustc_box] Box::new([1])).len(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/unused-allocation.rs:2:9 + | +LL | #![deny(unused_allocation)] + | ^^^^^^^^^^^^^^^^^ + +error: unnecessary allocation, use `&` instead + --> $DIR/unused-allocation.rs:6:9 + | +LL | _ = Box::new([1]).len(); + | ^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/macros/bad-concat.stderr b/tests/ui/macros/bad-concat.stderr index 4316fd312c7..d67f3c33d36 100644 --- a/tests/ui/macros/bad-concat.stderr +++ b/tests/ui/macros/bad-concat.stderr @@ -4,7 +4,7 @@ error: expected a literal LL | let _ = concat!(x, y, z, "bar"); | ^ ^ ^ | - = note: only literals (like `"foo"`, `42` and `3.14`) can be passed to `concat!()` + = note: only literals (like `"foo"`, `-42` and `3.14`) can be passed to `concat!()` error: aborting due to previous error diff --git a/tests/ui/macros/concat.stderr b/tests/ui/macros/concat.stderr index 61fb9de1ef9..d65d9354454 100644 --- a/tests/ui/macros/concat.stderr +++ b/tests/ui/macros/concat.stderr @@ -16,7 +16,7 @@ error: expected a literal LL | concat!(foo); | ^^^ | - = note: only literals (like `"foo"`, `42` and `3.14`) can be passed to `concat!()` + = note: only literals (like `"foo"`, `-42` and `3.14`) can be passed to `concat!()` error: expected a literal --> $DIR/concat.rs:5:13 @@ -24,7 +24,7 @@ error: expected a literal LL | concat!(foo()); | ^^^^^ | - = note: only literals (like `"foo"`, `42` and `3.14`) can be passed to `concat!()` + = note: only literals (like `"foo"`, `-42` and `3.14`) can be passed to `concat!()` error: aborting due to 4 previous errors diff --git a/tests/ui/macros/issue-106837.rs b/tests/ui/macros/issue-106837.rs new file mode 100644 index 00000000000..7bbd3d68a90 --- /dev/null +++ b/tests/ui/macros/issue-106837.rs @@ -0,0 +1,10 @@ +fn main() { + concat!(-42); + concat!(-3.14); + + concat!(-"hello"); + //~^ ERROR expected a literal + + concat!(--1); + //~^ ERROR expected a literal +} diff --git a/tests/ui/macros/issue-106837.stderr b/tests/ui/macros/issue-106837.stderr new file mode 100644 index 00000000000..998d6c5eb6f --- /dev/null +++ b/tests/ui/macros/issue-106837.stderr @@ -0,0 +1,18 @@ +error: expected a literal + --> $DIR/issue-106837.rs:5:13 + | +LL | concat!(-"hello"); + | ^^^^^^^^ + | + = note: only literals (like `"foo"`, `-42` and `3.14`) can be passed to `concat!()` + +error: expected a literal + --> $DIR/issue-106837.rs:8:13 + | +LL | concat!(--1); + | ^^^ + | + = note: only literals (like `"foo"`, `-42` and `3.14`) can be passed to `concat!()` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/macros/issue-98790.rs b/tests/ui/macros/issue-98790.rs new file mode 100644 index 00000000000..8fe6fc41d10 --- /dev/null +++ b/tests/ui/macros/issue-98790.rs @@ -0,0 +1,24 @@ +// run-pass + +macro_rules! stringify_item { + ($item:item) => { + stringify!($item) + }; +} + +macro_rules! repro { + ($expr:expr) => { + stringify_item! { + pub fn repro() -> bool { + $expr + } + } + }; +} + +fn main() { + assert_eq!( + repro!(match () { () => true } | true), + "pub fn repro() -> bool { (match () { () => true, }) | true }" + ); +} diff --git a/tests/ui/macros/nonterminal-matching.stderr b/tests/ui/macros/nonterminal-matching.stderr index 5bbd5439098..762ecc3207f 100644 --- a/tests/ui/macros/nonterminal-matching.stderr +++ b/tests/ui/macros/nonterminal-matching.stderr @@ -18,6 +18,7 @@ LL | macro n(a $nt_item b) { ... LL | complex_nonterminal!(enum E {}); | ------------------------------- in this macro invocation + = note: captured metavariables except for `$tt`, `$ident` and `$lifetime` cannot be compared to other tokens = note: this error originates in the macro `complex_nonterminal` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/tests/ui/macros/rfc-2011-nicer-assert-messages/all-expr-kinds.rs b/tests/ui/macros/rfc-2011-nicer-assert-messages/all-expr-kinds.rs index b8b6f0846bb..e88e24482cc 100644 --- a/tests/ui/macros/rfc-2011-nicer-assert-messages/all-expr-kinds.rs +++ b/tests/ui/macros/rfc-2011-nicer-assert-messages/all-expr-kinds.rs @@ -5,7 +5,7 @@ // needs-unwind Asserting on contents of error message #![allow(path_statements, unused_allocation)] -#![feature(box_syntax, core_intrinsics, generic_assert, generic_assert_internals)] +#![feature(core_intrinsics, generic_assert, generic_assert_internals)] macro_rules! test { ( @@ -127,9 +127,6 @@ fn main() { // block [ { elem } == 3 ] => "Assertion failed: { elem } == 3" - // box - [ box elem == box 3 ] => "Assertion failed: box elem == box 3" - // break [ loop { break elem; } == 3 ] => "Assertion failed: loop { break elem; } == 3" @@ -164,7 +161,7 @@ fn main() { // mac call // match - [ match elem { _ => elem } == 3 ] => "Assertion failed: match elem { _ => elem, } == 3" + [ match elem { _ => elem } == 3 ] => "Assertion failed: (match elem { _ => elem, }) == 3" // ret [ (|| { return elem; })() == 3 ] => "Assertion failed: (|| { return elem; })() == 3" diff --git a/tests/ui/macros/stringify.rs b/tests/ui/macros/stringify.rs index fdc2a7666d6..79d8cd75716 100644 --- a/tests/ui/macros/stringify.rs +++ b/tests/ui/macros/stringify.rs @@ -4,7 +4,6 @@ #![feature(async_closure)] #![feature(box_patterns)] -#![feature(box_syntax)] #![feature(const_trait_impl)] #![feature(decl_macro)] #![feature(generators)] @@ -91,9 +90,6 @@ fn test_block() { #[test] fn test_expr() { - // ExprKind::Box - assert_eq!(stringify_expr!(box expr), "box expr"); - // ExprKind::Array assert_eq!(stringify_expr!([]), "[]"); assert_eq!(stringify_expr!([true]), "[true]"); diff --git a/tests/ui/mir/mir_boxing.rs b/tests/ui/mir/mir_boxing.rs deleted file mode 100644 index 83e1cfb640a..00000000000 --- a/tests/ui/mir/mir_boxing.rs +++ /dev/null @@ -1,10 +0,0 @@ -// run-pass -#![feature(box_syntax)] - -fn test() -> Box { - box 42 -} - -fn main() { - assert_eq!(*test(), 42); -} diff --git a/tests/ui/parser/attr-stmt-expr-attr-bad.rs b/tests/ui/parser/attr-stmt-expr-attr-bad.rs index 469c3855c32..c94a32146b9 100644 --- a/tests/ui/parser/attr-stmt-expr-attr-bad.rs +++ b/tests/ui/parser/attr-stmt-expr-attr-bad.rs @@ -1,7 +1,5 @@ fn main() {} -#[cfg(FALSE)] fn e() { let _ = box #![attr] 0; } -//~^ ERROR an inner attribute is not permitted in this context #[cfg(FALSE)] fn e() { let _ = [#[attr]]; } //~^ ERROR expected expression, found `]` #[cfg(FALSE)] fn e() { let _ = foo#[attr](); } diff --git a/tests/ui/parser/attr-stmt-expr-attr-bad.stderr b/tests/ui/parser/attr-stmt-expr-attr-bad.stderr index 872c560cb51..a857f11fd18 100644 --- a/tests/ui/parser/attr-stmt-expr-attr-bad.stderr +++ b/tests/ui/parser/attr-stmt-expr-attr-bad.stderr @@ -1,26 +1,17 @@ -error: an inner attribute is not permitted in this context - --> $DIR/attr-stmt-expr-attr-bad.rs:3:36 - | -LL | #[cfg(FALSE)] fn e() { let _ = box #![attr] 0; } - | ^^^^^^^^ - | - = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files - = note: outer attributes, like `#[test]`, annotate the item following them - error: expected expression, found `]` - --> $DIR/attr-stmt-expr-attr-bad.rs:5:40 + --> $DIR/attr-stmt-expr-attr-bad.rs:3:40 | LL | #[cfg(FALSE)] fn e() { let _ = [#[attr]]; } | ^ expected expression error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:7:35 + --> $DIR/attr-stmt-expr-attr-bad.rs:5:35 | LL | #[cfg(FALSE)] fn e() { let _ = foo#[attr](); } | ^ expected one of 8 possible tokens error: an inner attribute is not permitted in this context - --> $DIR/attr-stmt-expr-attr-bad.rs:9:36 + --> $DIR/attr-stmt-expr-attr-bad.rs:7:36 | LL | #[cfg(FALSE)] fn e() { let _ = foo(#![attr]); } | ^^^^^^^^ @@ -29,13 +20,13 @@ LL | #[cfg(FALSE)] fn e() { let _ = foo(#![attr]); } = note: outer attributes, like `#[test]`, annotate the item following them error: expected expression, found `)` - --> $DIR/attr-stmt-expr-attr-bad.rs:9:44 + --> $DIR/attr-stmt-expr-attr-bad.rs:7:44 | LL | #[cfg(FALSE)] fn e() { let _ = foo(#![attr]); } | ^ expected expression error: an inner attribute is not permitted in this context - --> $DIR/attr-stmt-expr-attr-bad.rs:12:38 + --> $DIR/attr-stmt-expr-attr-bad.rs:10:38 | LL | #[cfg(FALSE)] fn e() { let _ = x.foo(#![attr]); } | ^^^^^^^^ @@ -44,13 +35,13 @@ LL | #[cfg(FALSE)] fn e() { let _ = x.foo(#![attr]); } = note: outer attributes, like `#[test]`, annotate the item following them error: expected expression, found `)` - --> $DIR/attr-stmt-expr-attr-bad.rs:12:46 + --> $DIR/attr-stmt-expr-attr-bad.rs:10:46 | LL | #[cfg(FALSE)] fn e() { let _ = x.foo(#![attr]); } | ^ expected expression error: an inner attribute is not permitted in this context - --> $DIR/attr-stmt-expr-attr-bad.rs:15:36 + --> $DIR/attr-stmt-expr-attr-bad.rs:13:36 | LL | #[cfg(FALSE)] fn e() { let _ = 0 + #![attr] 0; } | ^^^^^^^^ @@ -59,7 +50,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = 0 + #![attr] 0; } = note: outer attributes, like `#[test]`, annotate the item following them error: an inner attribute is not permitted in this context - --> $DIR/attr-stmt-expr-attr-bad.rs:17:33 + --> $DIR/attr-stmt-expr-attr-bad.rs:15:33 | LL | #[cfg(FALSE)] fn e() { let _ = !#![attr] 0; } | ^^^^^^^^ @@ -68,7 +59,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = !#![attr] 0; } = note: outer attributes, like `#[test]`, annotate the item following them error: an inner attribute is not permitted in this context - --> $DIR/attr-stmt-expr-attr-bad.rs:19:33 + --> $DIR/attr-stmt-expr-attr-bad.rs:17:33 | LL | #[cfg(FALSE)] fn e() { let _ = -#![attr] 0; } | ^^^^^^^^ @@ -77,13 +68,13 @@ LL | #[cfg(FALSE)] fn e() { let _ = -#![attr] 0; } = note: outer attributes, like `#[test]`, annotate the item following them error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:21:34 + --> $DIR/attr-stmt-expr-attr-bad.rs:19:34 | LL | #[cfg(FALSE)] fn e() { let _ = x #![attr] as Y; } | ^ expected one of 8 possible tokens error: an inner attribute is not permitted in this context - --> $DIR/attr-stmt-expr-attr-bad.rs:23:35 + --> $DIR/attr-stmt-expr-attr-bad.rs:21:35 | LL | #[cfg(FALSE)] fn e() { let _ = || #![attr] foo; } | ^^^^^^^^ @@ -92,7 +83,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = || #![attr] foo; } = note: outer attributes, like `#[test]`, annotate the item following them error: an inner attribute is not permitted in this context - --> $DIR/attr-stmt-expr-attr-bad.rs:25:40 + --> $DIR/attr-stmt-expr-attr-bad.rs:23:40 | LL | #[cfg(FALSE)] fn e() { let _ = move || #![attr] foo; } | ^^^^^^^^ @@ -101,7 +92,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = move || #![attr] foo; } = note: outer attributes, like `#[test]`, annotate the item following them error: an inner attribute is not permitted in this context - --> $DIR/attr-stmt-expr-attr-bad.rs:27:35 + --> $DIR/attr-stmt-expr-attr-bad.rs:25:35 | LL | #[cfg(FALSE)] fn e() { let _ = || #![attr] {foo}; } | ^^^^^^^^ @@ -110,7 +101,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = || #![attr] {foo}; } = note: outer attributes, like `#[test]`, annotate the item following them error: an inner attribute is not permitted in this context - --> $DIR/attr-stmt-expr-attr-bad.rs:29:40 + --> $DIR/attr-stmt-expr-attr-bad.rs:27:40 | LL | #[cfg(FALSE)] fn e() { let _ = move || #![attr] {foo}; } | ^^^^^^^^ @@ -119,19 +110,19 @@ LL | #[cfg(FALSE)] fn e() { let _ = move || #![attr] {foo}; } = note: outer attributes, like `#[test]`, annotate the item following them error: expected expression, found `..` - --> $DIR/attr-stmt-expr-attr-bad.rs:31:40 + --> $DIR/attr-stmt-expr-attr-bad.rs:29:40 | LL | #[cfg(FALSE)] fn e() { let _ = #[attr] ..#[attr] 0; } | ^^ expected expression error: expected expression, found `..` - --> $DIR/attr-stmt-expr-attr-bad.rs:33:40 + --> $DIR/attr-stmt-expr-attr-bad.rs:31:40 | LL | #[cfg(FALSE)] fn e() { let _ = #[attr] ..; } | ^^ expected expression error: an inner attribute is not permitted in this context - --> $DIR/attr-stmt-expr-attr-bad.rs:35:41 + --> $DIR/attr-stmt-expr-attr-bad.rs:33:41 | LL | #[cfg(FALSE)] fn e() { let _ = #[attr] &#![attr] 0; } | ^^^^^^^^ @@ -140,7 +131,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = #[attr] &#![attr] 0; } = note: outer attributes, like `#[test]`, annotate the item following them error: an inner attribute is not permitted in this context - --> $DIR/attr-stmt-expr-attr-bad.rs:37:45 + --> $DIR/attr-stmt-expr-attr-bad.rs:35:45 | LL | #[cfg(FALSE)] fn e() { let _ = #[attr] &mut #![attr] 0; } | ^^^^^^^^ @@ -149,7 +140,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = #[attr] &mut #![attr] 0; } = note: outer attributes, like `#[test]`, annotate the item following them error: outer attributes are not allowed on `if` and `else` branches - --> $DIR/attr-stmt-expr-attr-bad.rs:39:37 + --> $DIR/attr-stmt-expr-attr-bad.rs:37:37 | LL | #[cfg(FALSE)] fn e() { let _ = if 0 #[attr] {}; } | -- ^^^^^^^ -- the attributes are attached to this branch @@ -158,7 +149,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = if 0 #[attr] {}; } | the branch belongs to this `if` error: an inner attribute is not permitted in this context - --> $DIR/attr-stmt-expr-attr-bad.rs:41:38 + --> $DIR/attr-stmt-expr-attr-bad.rs:39:38 | LL | #[cfg(FALSE)] fn e() { let _ = if 0 {#![attr]}; } | ^^^^^^^^ @@ -167,13 +158,13 @@ LL | #[cfg(FALSE)] fn e() { let _ = if 0 {#![attr]}; } = note: outer attributes, like `#[test]`, annotate the item following them error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:43:40 + --> $DIR/attr-stmt-expr-attr-bad.rs:41:40 | LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} #[attr] else {}; } | ^ expected one of `.`, `;`, `?`, `else`, or an operator error: outer attributes are not allowed on `if` and `else` branches - --> $DIR/attr-stmt-expr-attr-bad.rs:45:45 + --> $DIR/attr-stmt-expr-attr-bad.rs:43:45 | LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] {}; } | ---- ^^^^^^^ -- the attributes are attached to this branch @@ -182,7 +173,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] {}; } | the branch belongs to this `else` error: an inner attribute is not permitted in this context - --> $DIR/attr-stmt-expr-attr-bad.rs:47:46 + --> $DIR/attr-stmt-expr-attr-bad.rs:45:46 | LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else {#![attr]}; } | ^^^^^^^^ @@ -191,7 +182,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else {#![attr]}; } = note: outer attributes, like `#[test]`, annotate the item following them error: outer attributes are not allowed on `if` and `else` branches - --> $DIR/attr-stmt-expr-attr-bad.rs:49:45 + --> $DIR/attr-stmt-expr-attr-bad.rs:47:45 | LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; } | ---- ^^^^^^^ ------- the attributes are attached to this branch @@ -200,7 +191,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; } | the branch belongs to this `else` error: outer attributes are not allowed on `if` and `else` branches - --> $DIR/attr-stmt-expr-attr-bad.rs:51:50 + --> $DIR/attr-stmt-expr-attr-bad.rs:49:50 | LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 #[attr] {}; } | -- ^^^^^^^ -- the attributes are attached to this branch @@ -209,7 +200,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 #[attr] {}; } | the branch belongs to this `if` error: an inner attribute is not permitted in this context - --> $DIR/attr-stmt-expr-attr-bad.rs:53:51 + --> $DIR/attr-stmt-expr-attr-bad.rs:51:51 | LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 {#![attr]}; } | ^^^^^^^^ @@ -218,7 +209,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 {#![attr]}; } = note: outer attributes, like `#[test]`, annotate the item following them error: outer attributes are not allowed on `if` and `else` branches - --> $DIR/attr-stmt-expr-attr-bad.rs:55:45 + --> $DIR/attr-stmt-expr-attr-bad.rs:53:45 | LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 #[attr] {}; } | -- ^^^^^^^ -- the attributes are attached to this branch @@ -227,7 +218,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 #[attr] {}; } | the branch belongs to this `if` error: an inner attribute is not permitted in this context - --> $DIR/attr-stmt-expr-attr-bad.rs:57:46 + --> $DIR/attr-stmt-expr-attr-bad.rs:55:46 | LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {#![attr]}; } | ^^^^^^^^ @@ -236,13 +227,13 @@ LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {#![attr]}; } = note: outer attributes, like `#[test]`, annotate the item following them error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:59:48 + --> $DIR/attr-stmt-expr-attr-bad.rs:57:48 | LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} #[attr] else {}; } | ^ expected one of `.`, `;`, `?`, `else`, or an operator error: outer attributes are not allowed on `if` and `else` branches - --> $DIR/attr-stmt-expr-attr-bad.rs:61:53 + --> $DIR/attr-stmt-expr-attr-bad.rs:59:53 | LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] {}; } | ---- ^^^^^^^ -- the attributes are attached to this branch @@ -251,7 +242,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] {}; } | the branch belongs to this `else` error: an inner attribute is not permitted in this context - --> $DIR/attr-stmt-expr-attr-bad.rs:63:54 + --> $DIR/attr-stmt-expr-attr-bad.rs:61:54 | LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else {#![attr]}; } | ^^^^^^^^ @@ -260,7 +251,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else {#![attr]}; } = note: outer attributes, like `#[test]`, annotate the item following them error: outer attributes are not allowed on `if` and `else` branches - --> $DIR/attr-stmt-expr-attr-bad.rs:65:53 + --> $DIR/attr-stmt-expr-attr-bad.rs:63:53 | LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; } | ---- ^^^^^^^ --------------- the attributes are attached to this branch @@ -269,7 +260,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {} | the branch belongs to this `else` error: outer attributes are not allowed on `if` and `else` branches - --> $DIR/attr-stmt-expr-attr-bad.rs:67:66 + --> $DIR/attr-stmt-expr-attr-bad.rs:65:66 | LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 #[attr] {}; } | -- ^^^^^^^ -- the attributes are attached to this branch @@ -278,7 +269,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 #[attr] {} | the branch belongs to this `if` error: an inner attribute is not permitted in this context - --> $DIR/attr-stmt-expr-attr-bad.rs:69:67 + --> $DIR/attr-stmt-expr-attr-bad.rs:67:67 | LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {#![attr]}; } | ^^^^^^^^ @@ -287,7 +278,7 @@ LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {#![attr]} = note: outer attributes, like `#[test]`, annotate the item following them error: an inner attribute is not permitted following an outer attribute - --> $DIR/attr-stmt-expr-attr-bad.rs:72:32 + --> $DIR/attr-stmt-expr-attr-bad.rs:70:32 | LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] let _ = 0; } | ------- ^^^^^^^^ not permitted following an outer attribute @@ -298,7 +289,7 @@ LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] let _ = 0; } = note: outer attributes, like `#[test]`, annotate the item following them error: an inner attribute is not permitted following an outer attribute - --> $DIR/attr-stmt-expr-attr-bad.rs:74:32 + --> $DIR/attr-stmt-expr-attr-bad.rs:72:32 | LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] 0; } | ------- ^^^^^^^^ not permitted following an outer attribute @@ -309,7 +300,7 @@ LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] 0; } = note: outer attributes, like `#[test]`, annotate the item following them error: an inner attribute is not permitted following an outer attribute - --> $DIR/attr-stmt-expr-attr-bad.rs:76:32 + --> $DIR/attr-stmt-expr-attr-bad.rs:74:32 | LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo!(); } | ------- ^^^^^^^^ ------- the inner attribute doesn't annotate this item macro invocation @@ -325,7 +316,7 @@ LL + #[cfg(FALSE)] fn s() { #[attr] #[attr] foo!(); } | error: an inner attribute is not permitted following an outer attribute - --> $DIR/attr-stmt-expr-attr-bad.rs:78:32 + --> $DIR/attr-stmt-expr-attr-bad.rs:76:32 | LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo![]; } | ------- ^^^^^^^^ ------- the inner attribute doesn't annotate this item macro invocation @@ -341,7 +332,7 @@ LL + #[cfg(FALSE)] fn s() { #[attr] #[attr] foo![]; } | error: an inner attribute is not permitted following an outer attribute - --> $DIR/attr-stmt-expr-attr-bad.rs:80:32 + --> $DIR/attr-stmt-expr-attr-bad.rs:78:32 | LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo!{}; } | ------- ^^^^^^^^ ------ the inner attribute doesn't annotate this item macro invocation @@ -357,7 +348,7 @@ LL + #[cfg(FALSE)] fn s() { #[attr] #[attr] foo!{}; } | error[E0586]: inclusive range with no end - --> $DIR/attr-stmt-expr-attr-bad.rs:86:35 + --> $DIR/attr-stmt-expr-attr-bad.rs:84:35 | LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } } | ^^^ help: use `..` instead @@ -365,13 +356,13 @@ LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } } = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`) error: expected one of `=>`, `if`, or `|`, found `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:86:38 + --> $DIR/attr-stmt-expr-attr-bad.rs:84:38 | LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } } | ^ expected one of `=>`, `if`, or `|` error[E0586]: inclusive range with no end - --> $DIR/attr-stmt-expr-attr-bad.rs:89:35 + --> $DIR/attr-stmt-expr-attr-bad.rs:87:35 | LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } } | ^^^ help: use `..` instead @@ -379,19 +370,19 @@ LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } } = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`) error: expected one of `=>`, `if`, or `|`, found `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:89:38 + --> $DIR/attr-stmt-expr-attr-bad.rs:87:38 | LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } } | ^ expected one of `=>`, `if`, or `|` error: unexpected token: `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:92:39 + --> $DIR/attr-stmt-expr-attr-bad.rs:90:39 | LL | #[cfg(FALSE)] fn e() { match 0 { 0..=-#[attr] 10 => () } } | ^ error[E0586]: inclusive range with no end - --> $DIR/attr-stmt-expr-attr-bad.rs:94:35 + --> $DIR/attr-stmt-expr-attr-bad.rs:92:35 | LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } } | ^^^ help: use `..` instead @@ -399,47 +390,47 @@ LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } } = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`) error: expected one of `=>`, `if`, or `|`, found `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:94:38 + --> $DIR/attr-stmt-expr-attr-bad.rs:92:38 | LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } } | ^ expected one of `=>`, `if`, or `|` error: unexpected token: `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:98:34 + --> $DIR/attr-stmt-expr-attr-bad.rs:96:34 | LL | #[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); } | ^ error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:98:34 + --> $DIR/attr-stmt-expr-attr-bad.rs:96:34 | LL | #[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); } | ^ expected one of `.`, `;`, `?`, `else`, or an operator error: unexpected token: `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:101:34 + --> $DIR/attr-stmt-expr-attr-bad.rs:99:34 | LL | #[cfg(FALSE)] fn e() { let _ = x.#[attr]foo(); } | ^ error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#` - --> $DIR/attr-stmt-expr-attr-bad.rs:101:34 + --> $DIR/attr-stmt-expr-attr-bad.rs:99:34 | LL | #[cfg(FALSE)] fn e() { let _ = x.#[attr]foo(); } | ^ expected one of `.`, `;`, `?`, `else`, or an operator error: expected statement after outer attribute - --> $DIR/attr-stmt-expr-attr-bad.rs:106:37 + --> $DIR/attr-stmt-expr-attr-bad.rs:104:37 | LL | #[cfg(FALSE)] fn e() { { fn foo() { #[attr]; } } } | ^^^^^^^ error: expected statement after outer attribute - --> $DIR/attr-stmt-expr-attr-bad.rs:108:37 + --> $DIR/attr-stmt-expr-attr-bad.rs:106:37 | LL | #[cfg(FALSE)] fn e() { { fn foo() { #[attr] } } } | ^^^^^^^ -error: aborting due to 53 previous errors +error: aborting due to 52 previous errors For more information about this error, try `rustc --explain E0586`. diff --git a/tests/ui/parser/fn-header-semantic-fail.rs b/tests/ui/parser/fn-header-semantic-fail.rs index cf5d3dab4aa..71f18a27e7c 100644 --- a/tests/ui/parser/fn-header-semantic-fail.rs +++ b/tests/ui/parser/fn-header-semantic-fail.rs @@ -11,7 +11,6 @@ fn main() { extern "C" fn ff4() {} // OK. const async unsafe extern "C" fn ff5() {} //~^ ERROR functions cannot be both `const` and `async` - //~| ERROR cycle detected trait X { async fn ft1(); //~ ERROR functions in traits cannot be declared `async` @@ -34,7 +33,6 @@ fn main() { //~^ ERROR functions in traits cannot be declared `async` //~| ERROR functions in traits cannot be declared const //~| ERROR functions cannot be both `const` and `async` - //~| ERROR cycle detected } impl Y { @@ -44,7 +42,6 @@ fn main() { extern "C" fn fi4() {} // OK. const async unsafe extern "C" fn fi5() {} //~^ ERROR functions cannot be both `const` and `async` - //~| ERROR cycle detected } extern "C" { diff --git a/tests/ui/parser/fn-header-semantic-fail.stderr b/tests/ui/parser/fn-header-semantic-fail.stderr index 2d8bd19a731..7f7b7e835f8 100644 --- a/tests/ui/parser/fn-header-semantic-fail.stderr +++ b/tests/ui/parser/fn-header-semantic-fail.stderr @@ -8,19 +8,19 @@ LL | const async unsafe extern "C" fn ff5() {} | `const` because of this error[E0379]: functions in traits cannot be declared const - --> $DIR/fn-header-semantic-fail.rs:19:9 + --> $DIR/fn-header-semantic-fail.rs:18:9 | LL | const fn ft3(); | ^^^^^ functions in traits cannot be const error[E0379]: functions in traits cannot be declared const - --> $DIR/fn-header-semantic-fail.rs:21:9 + --> $DIR/fn-header-semantic-fail.rs:20:9 | LL | const async unsafe extern "C" fn ft5(); | ^^^^^ functions in traits cannot be const error: functions cannot be both `const` and `async` - --> $DIR/fn-header-semantic-fail.rs:21:9 + --> $DIR/fn-header-semantic-fail.rs:20:9 | LL | const async unsafe extern "C" fn ft5(); | ^^^^^-^^^^^---------------------------- @@ -29,19 +29,19 @@ LL | const async unsafe extern "C" fn ft5(); | `const` because of this error[E0379]: functions in traits cannot be declared const - --> $DIR/fn-header-semantic-fail.rs:31:9 + --> $DIR/fn-header-semantic-fail.rs:30:9 | LL | const fn ft3() {} | ^^^^^ functions in traits cannot be const error[E0379]: functions in traits cannot be declared const - --> $DIR/fn-header-semantic-fail.rs:33:9 + --> $DIR/fn-header-semantic-fail.rs:32:9 | LL | const async unsafe extern "C" fn ft5() {} | ^^^^^ functions in traits cannot be const error: functions cannot be both `const` and `async` - --> $DIR/fn-header-semantic-fail.rs:33:9 + --> $DIR/fn-header-semantic-fail.rs:32:9 | LL | const async unsafe extern "C" fn ft5() {} | ^^^^^-^^^^^------------------------------ @@ -50,7 +50,7 @@ LL | const async unsafe extern "C" fn ft5() {} | `const` because of this error: functions cannot be both `const` and `async` - --> $DIR/fn-header-semantic-fail.rs:45:9 + --> $DIR/fn-header-semantic-fail.rs:43:9 | LL | const async unsafe extern "C" fn fi5() {} | ^^^^^-^^^^^------------------------------ @@ -59,7 +59,7 @@ LL | const async unsafe extern "C" fn fi5() {} | `const` because of this error: functions in `extern` blocks cannot have qualifiers - --> $DIR/fn-header-semantic-fail.rs:51:18 + --> $DIR/fn-header-semantic-fail.rs:48:18 | LL | extern "C" { | ---------- in this `extern` block @@ -72,7 +72,7 @@ LL | fn fe1(); | ~~ error: functions in `extern` blocks cannot have qualifiers - --> $DIR/fn-header-semantic-fail.rs:52:19 + --> $DIR/fn-header-semantic-fail.rs:49:19 | LL | extern "C" { | ---------- in this `extern` block @@ -86,7 +86,7 @@ LL | fn fe2(); | ~~ error: functions in `extern` blocks cannot have qualifiers - --> $DIR/fn-header-semantic-fail.rs:53:18 + --> $DIR/fn-header-semantic-fail.rs:50:18 | LL | extern "C" { | ---------- in this `extern` block @@ -100,7 +100,7 @@ LL | fn fe3(); | ~~ error: functions in `extern` blocks cannot have qualifiers - --> $DIR/fn-header-semantic-fail.rs:54:23 + --> $DIR/fn-header-semantic-fail.rs:51:23 | LL | extern "C" { | ---------- in this `extern` block @@ -114,7 +114,7 @@ LL | fn fe4(); | ~~ error: functions in `extern` blocks cannot have qualifiers - --> $DIR/fn-header-semantic-fail.rs:55:42 + --> $DIR/fn-header-semantic-fail.rs:52:42 | LL | extern "C" { | ---------- in this `extern` block @@ -128,7 +128,7 @@ LL | fn fe5(); | ~~ error: functions cannot be both `const` and `async` - --> $DIR/fn-header-semantic-fail.rs:55:9 + --> $DIR/fn-header-semantic-fail.rs:52:9 | LL | const async unsafe extern "C" fn fe5(); | ^^^^^-^^^^^---------------------------- @@ -137,7 +137,7 @@ LL | const async unsafe extern "C" fn fe5(); | `const` because of this error[E0706]: functions in traits cannot be declared `async` - --> $DIR/fn-header-semantic-fail.rs:17:9 + --> $DIR/fn-header-semantic-fail.rs:16:9 | LL | async fn ft1(); | -----^^^^^^^^^^ @@ -150,7 +150,7 @@ LL | async fn ft1(); = help: add `#![feature(async_fn_in_trait)]` to the crate attributes to enable error[E0706]: functions in traits cannot be declared `async` - --> $DIR/fn-header-semantic-fail.rs:21:9 + --> $DIR/fn-header-semantic-fail.rs:20:9 | LL | const async unsafe extern "C" fn ft5(); | ^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -163,7 +163,7 @@ LL | const async unsafe extern "C" fn ft5(); = help: add `#![feature(async_fn_in_trait)]` to the crate attributes to enable error[E0706]: functions in traits cannot be declared `async` - --> $DIR/fn-header-semantic-fail.rs:29:9 + --> $DIR/fn-header-semantic-fail.rs:28:9 | LL | async fn ft1() {} | -----^^^^^^^^^ @@ -176,7 +176,7 @@ LL | async fn ft1() {} = help: add `#![feature(async_fn_in_trait)]` to the crate attributes to enable error[E0706]: functions in traits cannot be declared `async` - --> $DIR/fn-header-semantic-fail.rs:33:9 + --> $DIR/fn-header-semantic-fail.rs:32:9 | LL | const async unsafe extern "C" fn ft5() {} | ^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -188,115 +188,7 @@ LL | const async unsafe extern "C" fn ft5() {} = note: see issue #91611 for more information = help: add `#![feature(async_fn_in_trait)]` to the crate attributes to enable -error[E0391]: cycle detected when computing type of `main::ff5::{opaque#0}` - --> $DIR/fn-header-semantic-fail.rs:12:44 - | -LL | const async unsafe extern "C" fn ff5() {} - | ^ - | -note: ...which requires borrow-checking `main::ff5`... - --> $DIR/fn-header-semantic-fail.rs:12:5 - | -LL | const async unsafe extern "C" fn ff5() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires processing MIR for `main::ff5`... - --> $DIR/fn-header-semantic-fail.rs:12:5 - | -LL | const async unsafe extern "C" fn ff5() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires const checking `main::ff5`... - --> $DIR/fn-header-semantic-fail.rs:12:5 - | -LL | const async unsafe extern "C" fn ff5() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: ...which requires computing whether `main::ff5::{opaque#0}` is freeze... - = note: ...which requires evaluating trait selection obligation `main::ff5::{opaque#0}: core::marker::Freeze`... - = note: ...which again requires computing type of `main::ff5::{opaque#0}`, completing the cycle -note: cycle used when checking item types in top-level module - --> $DIR/fn-header-semantic-fail.rs:5:1 - | -LL | / #![feature(const_extern_fn)] -LL | | -LL | | fn main() { -LL | | async fn ff1() {} // OK. -... | -LL | | } -LL | | } - | |_^ +error: aborting due to 18 previous errors -error[E0391]: cycle detected when computing type of `main::::ft5::{opaque#0}` - --> $DIR/fn-header-semantic-fail.rs:33:48 - | -LL | const async unsafe extern "C" fn ft5() {} - | ^ - | -note: ...which requires borrow-checking `main::::ft5`... - --> $DIR/fn-header-semantic-fail.rs:33:9 - | -LL | const async unsafe extern "C" fn ft5() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires processing MIR for `main::::ft5`... - --> $DIR/fn-header-semantic-fail.rs:33:9 - | -LL | const async unsafe extern "C" fn ft5() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires const checking `main::::ft5`... - --> $DIR/fn-header-semantic-fail.rs:33:9 - | -LL | const async unsafe extern "C" fn ft5() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: ...which requires computing whether `main::::ft5::{opaque#0}` is freeze... - = note: ...which requires evaluating trait selection obligation `main::::ft5::{opaque#0}: core::marker::Freeze`... - = note: ...which again requires computing type of `main::::ft5::{opaque#0}`, completing the cycle -note: cycle used when checking item types in top-level module - --> $DIR/fn-header-semantic-fail.rs:5:1 - | -LL | / #![feature(const_extern_fn)] -LL | | -LL | | fn main() { -LL | | async fn ff1() {} // OK. -... | -LL | | } -LL | | } - | |_^ - -error[E0391]: cycle detected when computing type of `main::::fi5::{opaque#0}` - --> $DIR/fn-header-semantic-fail.rs:45:48 - | -LL | const async unsafe extern "C" fn fi5() {} - | ^ - | -note: ...which requires borrow-checking `main::::fi5`... - --> $DIR/fn-header-semantic-fail.rs:45:9 - | -LL | const async unsafe extern "C" fn fi5() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires processing MIR for `main::::fi5`... - --> $DIR/fn-header-semantic-fail.rs:45:9 - | -LL | const async unsafe extern "C" fn fi5() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires const checking `main::::fi5`... - --> $DIR/fn-header-semantic-fail.rs:45:9 - | -LL | const async unsafe extern "C" fn fi5() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: ...which requires computing whether `main::::fi5::{opaque#0}` is freeze... - = note: ...which requires evaluating trait selection obligation `main::::fi5::{opaque#0}: core::marker::Freeze`... - = note: ...which again requires computing type of `main::::fi5::{opaque#0}`, completing the cycle -note: cycle used when checking item types in top-level module - --> $DIR/fn-header-semantic-fail.rs:5:1 - | -LL | / #![feature(const_extern_fn)] -LL | | -LL | | fn main() { -LL | | async fn ff1() {} // OK. -... | -LL | | } -LL | | } - | |_^ - -error: aborting due to 21 previous errors - -Some errors have detailed explanations: E0379, E0391, E0706. +Some errors have detailed explanations: E0379, E0706. For more information about an error, try `rustc --explain E0379`. diff --git a/tests/ui/parser/issues/issue-65846-rollback-gating-failing-matcher.rs b/tests/ui/parser/issues/issue-65846-rollback-gating-failing-matcher.rs index 76c07bbfd81..b0e8f4d9988 100644 --- a/tests/ui/parser/issues/issue-65846-rollback-gating-failing-matcher.rs +++ b/tests/ui/parser/issues/issue-65846-rollback-gating-failing-matcher.rs @@ -5,11 +5,17 @@ #[allow(unused_macro_rules)] macro_rules! m { - ($e:expr) => { 0 }; // This fails on the input below due to `, foo`. - ($e:expr,) => { 1 }; // This also fails to match due to `foo`. - (box $e:expr, foo) => { 2 }; // Successful matcher, we should get `2`. + ($e:expr) => { + 0 + }; // This fails on the input below due to `, foo`. + ($e:expr,) => { + 1 + }; // This also fails to match due to `foo`. + (do yeet $e:expr, foo) => { + 2 + }; // Successful matcher, we should get `2`. } fn main() { - assert_eq!(2, m!(box 42, foo)); + assert_eq!(2, m!(do yeet 42, foo)); } diff --git a/tests/ui/parser/removed-syntax-box.fixed b/tests/ui/parser/removed-syntax-box.fixed new file mode 100644 index 00000000000..09d1304b775 --- /dev/null +++ b/tests/ui/parser/removed-syntax-box.fixed @@ -0,0 +1,14 @@ +// run-rustfix + +fn main() { + #[allow(dead_code)] + struct T { + a: u8, + b: u8, + } + let _ = Box::new(()); //~ ERROR `box_syntax` has been removed + let _ = Box::new(1); //~ ERROR `box_syntax` has been removed + let _ = Box::new(T { a: 12, b: 18 }); //~ ERROR `box_syntax` has been removed + let _ = Box::new([5; 30]); //~ ERROR `box_syntax` has been removed + let _: Box<()> = Box::new(()); //~ ERROR `box_syntax` has been removed +} diff --git a/tests/ui/parser/removed-syntax-box.rs b/tests/ui/parser/removed-syntax-box.rs new file mode 100644 index 00000000000..1f5061b02c7 --- /dev/null +++ b/tests/ui/parser/removed-syntax-box.rs @@ -0,0 +1,14 @@ +// run-rustfix + +fn main() { + #[allow(dead_code)] + struct T { + a: u8, + b: u8, + } + let _ = box (); //~ ERROR `box_syntax` has been removed + let _ = box 1; //~ ERROR `box_syntax` has been removed + let _ = box T { a: 12, b: 18 }; //~ ERROR `box_syntax` has been removed + let _ = box [5; 30]; //~ ERROR `box_syntax` has been removed + let _: Box<()> = box (); //~ ERROR `box_syntax` has been removed +} diff --git a/tests/ui/parser/removed-syntax-box.stderr b/tests/ui/parser/removed-syntax-box.stderr new file mode 100644 index 00000000000..46b891587d5 --- /dev/null +++ b/tests/ui/parser/removed-syntax-box.stderr @@ -0,0 +1,57 @@ +error: `box_syntax` has been removed + --> $DIR/removed-syntax-box.rs:9:13 + | +LL | let _ = box (); + | ^^^^^^ + | +help: use `Box::new()` instead + | +LL | let _ = Box::new(()); + | ~~~~~~~~~~~~ + +error: `box_syntax` has been removed + --> $DIR/removed-syntax-box.rs:10:13 + | +LL | let _ = box 1; + | ^^^^^ + | +help: use `Box::new()` instead + | +LL | let _ = Box::new(1); + | ~~~~~~~~~~~ + +error: `box_syntax` has been removed + --> $DIR/removed-syntax-box.rs:11:13 + | +LL | let _ = box T { a: 12, b: 18 }; + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `Box::new()` instead + | +LL | let _ = Box::new(T { a: 12, b: 18 }); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: `box_syntax` has been removed + --> $DIR/removed-syntax-box.rs:12:13 + | +LL | let _ = box [5; 30]; + | ^^^^^^^^^^^ + | +help: use `Box::new()` instead + | +LL | let _ = Box::new([5; 30]); + | ~~~~~~~~~~~~~~~~~ + +error: `box_syntax` has been removed + --> $DIR/removed-syntax-box.rs:13:22 + | +LL | let _: Box<()> = box (); + | ^^^^^^ + | +help: use `Box::new()` instead + | +LL | let _: Box<()> = Box::new(()); + | ~~~~~~~~~~~~ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/pattern/pat-tuple-field-count-cross.stderr b/tests/ui/pattern/pat-tuple-field-count-cross.stderr index d9295746158..0d7f2e4af69 100644 --- a/tests/ui/pattern/pat-tuple-field-count-cross.stderr +++ b/tests/ui/pattern/pat-tuple-field-count-cross.stderr @@ -113,8 +113,8 @@ LL | pub enum E1 { Z0, Z1(), S(u8, u8, u8) } | help: use the tuple variant pattern syntax instead | -LL | E1::Z1(/* fields */) => {} - | ~~~~~~~~~~~~~~~~~~~~ +LL | E1::Z1() => {} + | ~~~~~~~~ help: a unit variant with a similar name exists | LL | E1::Z0 => {} diff --git a/tests/ui/reachable/expr_box.rs b/tests/ui/reachable/expr_box.rs deleted file mode 100644 index 00328ea0131..00000000000 --- a/tests/ui/reachable/expr_box.rs +++ /dev/null @@ -1,8 +0,0 @@ -#![feature(box_syntax)] -#![allow(unused_variables)] -#![deny(unreachable_code)] - -fn main() { - let x = box return; //~ ERROR unreachable - println!("hi"); -} diff --git a/tests/ui/reachable/expr_box.stderr b/tests/ui/reachable/expr_box.stderr deleted file mode 100644 index ea6472cbeab..00000000000 --- a/tests/ui/reachable/expr_box.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: unreachable expression - --> $DIR/expr_box.rs:6:13 - | -LL | let x = box return; - | ^^^^------ - | | | - | | any code following this expression is unreachable - | unreachable expression - | -note: the lint level is defined here - --> $DIR/expr_box.rs:3:9 - | -LL | #![deny(unreachable_code)] - | ^^^^^^^^^^^^^^^^ - -error: aborting due to previous error - diff --git a/tests/ui/rfc-2632-const-trait-impl/gate.rs b/tests/ui/rfc-2632-const-trait-impl/gate.rs index f2cd26c91b6..d1c93ab9f95 100644 --- a/tests/ui/rfc-2632-const-trait-impl/gate.rs +++ b/tests/ui/rfc-2632-const-trait-impl/gate.rs @@ -1,5 +1,13 @@ // gate-test-const_closures + fn main() { (const || {})(); //~^ ERROR: const closures are experimental } + +macro_rules! e { + ($e:expr) => {} +} + +e!((const || {})); +//~^ ERROR const closures are experimental diff --git a/tests/ui/rfc-2632-const-trait-impl/gate.stderr b/tests/ui/rfc-2632-const-trait-impl/gate.stderr index 30edc4127e1..11cc2cd569a 100644 --- a/tests/ui/rfc-2632-const-trait-impl/gate.stderr +++ b/tests/ui/rfc-2632-const-trait-impl/gate.stderr @@ -1,12 +1,21 @@ error[E0658]: const closures are experimental - --> $DIR/gate.rs:3:6 + --> $DIR/gate.rs:4:6 | LL | (const || {})(); - | ^^^^^^^^^^^ + | ^^^^^ | = note: see issue #106003 for more information = help: add `#![feature(const_closures)]` to the crate attributes to enable -error: aborting due to previous error +error[E0658]: const closures are experimental + --> $DIR/gate.rs:12:5 + | +LL | e!((const || {})); + | ^^^^^ + | + = note: see issue #106003 for more information + = help: add `#![feature(const_closures)]` to the crate attributes to enable + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.rs new file mode 100644 index 00000000000..0d59e50264e --- /dev/null +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.rs @@ -0,0 +1,7 @@ +// only-x86_64 + +#![feature(target_feature_11)] + +#[target_feature(enable = "avx2")] +fn main() {} +//~^ ERROR `main` function is not allowed to have `#[target_feature]` diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.stderr new file mode 100644 index 00000000000..cfafbd52286 --- /dev/null +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.stderr @@ -0,0 +1,8 @@ +error: `main` function is not allowed to have `#[target_feature]` + --> $DIR/issue-108645-target-feature-on-main.rs:6:1 + | +LL | fn main() {} + | ^^^^^^^^^ `main` function is not allowed to have `#[target_feature]` + +error: aborting due to previous error + diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.rs new file mode 100644 index 00000000000..50e8ce2fdd5 --- /dev/null +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.rs @@ -0,0 +1,9 @@ +// only-x86_64 + +#![feature(start)] +#![feature(target_feature_11)] + +#[start] +#[target_feature(enable = "avx2")] +//~^ ERROR `start` is not allowed to have `#[target_feature]` +fn start(_argc: isize, _argv: *const *const u8) -> isize { 0 } diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.stderr new file mode 100644 index 00000000000..07687f3c7f4 --- /dev/null +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.stderr @@ -0,0 +1,11 @@ +error: `start` is not allowed to have `#[target_feature]` + --> $DIR/issue-108645-target-feature-on-start.rs:7:1 + | +LL | #[target_feature(enable = "avx2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | fn start(_argc: isize, _argv: *const *const u8) -> isize { 0 } + | -------------------------------------------------------- `start` is not allowed to have `#[target_feature]` + +error: aborting due to previous error + diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs index 7314fa8cced..9108f27b5f7 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs @@ -18,4 +18,10 @@ impl Foo for Bar { unsafe fn unsf_foo(&self) {} } +trait Qux { + #[target_feature(enable = "sse2")] + //~^ ERROR cannot be applied to safe trait method + fn foo(&self) {} +} + fn main() {} diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr index 07d6e090059..eb0f18edd34 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr @@ -1,3 +1,12 @@ +error: `#[target_feature(..)]` cannot be applied to safe trait method + --> $DIR/trait-impl.rs:22:5 + | +LL | #[target_feature(enable = "sse2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot be applied to safe trait method +LL | +LL | fn foo(&self) {} + | ------------- not an `unsafe` function + error: `#[target_feature(..)]` cannot be applied to safe trait method --> $DIR/trait-impl.rs:13:5 | @@ -7,5 +16,5 @@ LL | LL | fn foo(&self) {} | ------------- not an `unsafe` function -error: aborting due to previous error +error: aborting due to 2 previous errors diff --git a/tests/ui/self/arbitrary_self_types_trait.rs b/tests/ui/self/arbitrary_self_types_trait.rs index 973c7cae85a..c4651ec7177 100644 --- a/tests/ui/self/arbitrary_self_types_trait.rs +++ b/tests/ui/self/arbitrary_self_types_trait.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(unused_allocation)] use std::rc::Rc; @@ -13,7 +14,7 @@ impl Trait for Vec { } fn main() { - let v = vec![1,2,3]; + let v = vec![1, 2, 3]; - assert_eq!(&[1,2,3], Box::new(Rc::new(v)).trait_method()); + assert_eq!(&[1, 2, 3], Box::new(Rc::new(v)).trait_method()); } diff --git a/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.rs b/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.rs new file mode 100644 index 00000000000..582b480aa25 --- /dev/null +++ b/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.rs @@ -0,0 +1,12 @@ +#![feature(min_specialization)] + +// An impl that has an erroneous const substitution should not specialize one +// that is well-formed. + +struct S; + +impl Copy for S {} +impl Copy for S {} +//~^ ERROR conflicting implementations of trait `Copy` for type `S<_>` + +fn main() {} diff --git a/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.stderr b/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.stderr new file mode 100644 index 00000000000..a3906a9a22f --- /dev/null +++ b/tests/ui/specialization/min_specialization/bad-const-wf-doesnt-specialize.stderr @@ -0,0 +1,11 @@ +error[E0119]: conflicting implementations of trait `Copy` for type `S<_>` + --> $DIR/bad-const-wf-doesnt-specialize.rs:9:1 + | +LL | impl Copy for S {} + | -------------------------------- first implementation here +LL | impl Copy for S {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `S<_>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/static/static-mut-not-constant.rs b/tests/ui/static/static-mut-not-constant.rs index 2091fffd418..d501e5c2956 100644 --- a/tests/ui/static/static-mut-not-constant.rs +++ b/tests/ui/static/static-mut-not-constant.rs @@ -1,6 +1,4 @@ -#![feature(box_syntax)] - -static mut a: Box = box 3; -//~^ ERROR allocations are not allowed in statics +static mut a: Box = Box::new(3); +//~^ ERROR cannot call non-const fn fn main() {} diff --git a/tests/ui/static/static-mut-not-constant.stderr b/tests/ui/static/static-mut-not-constant.stderr index a0fa245156f..8411a1557b4 100644 --- a/tests/ui/static/static-mut-not-constant.stderr +++ b/tests/ui/static/static-mut-not-constant.stderr @@ -1,9 +1,12 @@ -error[E0010]: allocations are not allowed in statics - --> $DIR/static-mut-not-constant.rs:3:28 +error[E0015]: cannot call non-const fn `Box::::new` in statics + --> $DIR/static-mut-not-constant.rs:1:28 | -LL | static mut a: Box = box 3; - | ^^^^^ allocation not allowed in statics +LL | static mut a: Box = Box::new(3); + | ^^^^^^^^^^^ + | + = note: calls in statics are limited to constant functions, tuple structs and tuple variants + = note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell error: aborting due to previous error -For more information about this error, try `rustc --explain E0010`. +For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/str/str-escape.rs b/tests/ui/str/str-escape.rs index 0264632fd24..10a72421f24 100644 --- a/tests/ui/str/str-escape.rs +++ b/tests/ui/str/str-escape.rs @@ -1,11 +1,31 @@ // check-pass +// ignore-tidy-tab + fn main() { let s = "\ "; //~^^^ WARNING multiple lines skipped by escaped newline + assert_eq!(s, ""); + let s = "foo\   bar "; - //~^^^ WARNING non-ASCII whitespace symbol '\u{a0}' is not skipped + //~^^^ WARNING whitespace symbol '\u{a0}' is not skipped + assert_eq!(s, "foo  bar\n "); + + let s = "a\ + b"; + assert_eq!(s, "ab"); + + let s = "a\ + b"; + assert_eq!(s, "ab"); + + let s = "a\ + b"; + //~^^ WARNING whitespace symbol '\u{c}' is not skipped + // '\x0c' is ASCII whitespace, but it may not need skipped + // discussion: https://github.com/rust-lang/rust/pull/108403 + assert_eq!(s, "a\x0cb"); } diff --git a/tests/ui/str/str-escape.stderr b/tests/ui/str/str-escape.stderr index b2501f1a214..43b4f7e36f6 100644 --- a/tests/ui/str/str-escape.stderr +++ b/tests/ui/str/str-escape.stderr @@ -1,5 +1,5 @@ warning: multiple lines skipped by escaped newline - --> $DIR/str-escape.rs:3:14 + --> $DIR/str-escape.rs:5:14 | LL | let s = "\ | ______________^ @@ -7,15 +7,25 @@ LL | | LL | | "; | |_____________^ skipping everything up to and including this point -warning: non-ASCII whitespace symbol '\u{a0}' is not skipped - --> $DIR/str-escape.rs:7:17 +warning: whitespace symbol '\u{a0}' is not skipped + --> $DIR/str-escape.rs:11:17 | LL | let s = "foo\ | _________________^ LL | |   bar - | | ^ non-ASCII whitespace symbol '\u{a0}' is not skipped + | | ^ whitespace symbol '\u{a0}' is not skipped | |___| | -warning: 2 warnings emitted +warning: whitespace symbol '\u{c}' is not skipped + --> $DIR/str-escape.rs:25:15 + | +LL | let s = "a\ + | _______________^ +LL | | b"; + | | ^- whitespace symbol '\u{c}' is not skipped + | |____| + | + +warning: 3 warnings emitted diff --git a/tests/ui/structs-enums/align-struct.rs b/tests/ui/structs-enums/align-struct.rs index f5418e754b2..54092542f98 100644 --- a/tests/ui/structs-enums/align-struct.rs +++ b/tests/ui/structs-enums/align-struct.rs @@ -1,5 +1,5 @@ // run-pass -#![allow(dead_code)] +#![allow(dead_code, unused_allocation)] use std::mem; @@ -20,7 +20,6 @@ struct AlignMany(i32); // Raising alignment may not alter size. #[repr(align(8))] -#[allow(dead_code)] struct Align8Many { a: i32, b: i32, @@ -29,9 +28,8 @@ struct Align8Many { } enum Enum { - #[allow(dead_code)] A(i32), - B(Align16) + B(Align16), } // Nested alignment - use `#[repr(C)]` to suppress field reordering for sizeof test @@ -73,7 +71,7 @@ struct AlignLarge { union UnionContainsAlign { a: Align16, - b: f32 + b: f32, } impl Align16 { @@ -158,7 +156,7 @@ pub fn main() { // Note that the size of Nested may change if struct field re-ordering is enabled assert_eq!(mem::align_of::(), 16); assert_eq!(mem::size_of::(), 48); - let a = Nested{ a: 1, b: 2, c: Align16(3), d: 4}; + let a = Nested { a: 1, b: 2, c: Align16(3), d: 4 }; assert_eq!(mem::align_of_val(&a), 16); assert_eq!(mem::align_of_val(&a.b), 4); assert_eq!(mem::align_of_val(&a.c), 16); @@ -179,8 +177,8 @@ pub fn main() { assert_eq!(a.0, 15); assert_eq!(mem::align_of_val(a), 16); assert_eq!(mem::size_of_val(a), 16); - }, - _ => () + } + _ => (), } assert!(is_aligned_to(&e, 16)); @@ -197,8 +195,8 @@ pub fn main() { } // arrays of aligned elements should also be aligned - assert_eq!(mem::align_of::<[Align16;2]>(), 16); - assert_eq!(mem::size_of::<[Align16;2]>(), 32); + assert_eq!(mem::align_of::<[Align16; 2]>(), 16); + assert_eq!(mem::size_of::<[Align16; 2]>(), 32); let a = [Align16(0), Align16(1)]; assert_eq!(mem::align_of_val(&a[0]), 16); @@ -209,7 +207,7 @@ pub fn main() { assert_eq!(mem::align_of_val(Box::new(Align16(0)).as_ref()), 16); // check heap array is aligned - let a = vec!(Align16(0), Align16(1)); + let a = vec![Align16(0), Align16(1)]; assert_eq!(mem::align_of_val(&a[0]), 16); assert_eq!(mem::align_of_val(&a[1]), 16); @@ -224,16 +222,14 @@ pub fn main() { assert_eq!(mem::align_of::(), 16); assert_eq!(mem::size_of::(), 32); - let a = AlignContainsPacked4C { a: Packed4C{ a: 1, b: 2 }, b: 3 }; + let a = AlignContainsPacked4C { a: Packed4C { a: 1, b: 2 }, b: 3 }; assert_eq!(mem::align_of_val(&a), 16); assert_eq!(mem::align_of_val(&a.a), 4); assert_eq!(mem::align_of_val(&a.b), mem::align_of::()); assert_eq!(mem::size_of_val(&a), 32); assert!(is_aligned_to(&a, 16)); - let mut large = Box::new(AlignLarge { - stuff: [0; 0x10000], - }); + let mut large = Box::new(AlignLarge { stuff: [0; 0x10000] }); large.stuff[0] = 132; *large.stuff.last_mut().unwrap() = 102; assert_eq!(large.stuff[0], 132); diff --git a/tests/ui/suggestions/expected-boxed-future-isnt-pinned.stderr b/tests/ui/suggestions/expected-boxed-future-isnt-pinned.stderr index 90ea0623952..b827beb504d 100644 --- a/tests/ui/suggestions/expected-boxed-future-isnt-pinned.stderr +++ b/tests/ui/suggestions/expected-boxed-future-isnt-pinned.stderr @@ -69,18 +69,15 @@ note: required by a bound in `Pin::

    ::new` error[E0308]: mismatched types --> $DIR/expected-boxed-future-isnt-pinned.rs:28:5 | +LL | fn zap() -> BoxFuture<'static, i32> { + | ----------------------- expected `Pin + Send + 'static)>>` because of return type LL | / async { LL | | 42 LL | | } - | | ^ - | | | - | |_____expected `Pin>`, found `async` block - | arguments to this function are incorrect + | |_____^ expected `Pin>`, found `async` block | - = note: expected struct `Pin + Send>>` + = note: expected struct `Pin + Send + 'static)>>` found `async` block `[async block@$DIR/expected-boxed-future-isnt-pinned.rs:28:5: 30:6]` -note: function defined here - --> $SRC_DIR/core/src/future/mod.rs:LL:COL help: you need to pin and box this expression | LL ~ Box::pin(async { diff --git a/tests/ui/threads-sendsync/issue-43733-2.rs b/tests/ui/threads-sendsync/issue-43733-2.rs index 32baeec4359..8f7a9c08375 100644 --- a/tests/ui/threads-sendsync/issue-43733-2.rs +++ b/tests/ui/threads-sendsync/issue-43733-2.rs @@ -21,7 +21,7 @@ impl Key { } #[cfg(target_thread_local)] -use std::thread::__FastLocalKeyInner as Key; +use std::thread::__LocalKeyInner as Key; static __KEY: Key<()> = Key::new(); //~^ ERROR `UnsafeCell>` cannot be shared between threads diff --git a/tests/ui/threads-sendsync/issue-43733.rs b/tests/ui/threads-sendsync/issue-43733.rs index 935e02944b9..0eadef3e3e8 100644 --- a/tests/ui/threads-sendsync/issue-43733.rs +++ b/tests/ui/threads-sendsync/issue-43733.rs @@ -1,8 +1,8 @@ // ignore-wasm32 // revisions: mir thir // [thir]compile-flags: -Z thir-unsafeck -// normalize-stderr-test: "__FastLocalKeyInner::::get" -> "$$LOCALKEYINNER::::get" -// normalize-stderr-test: "__OsLocalKeyInner::::get" -> "$$LOCALKEYINNER::::get" +// normalize-stderr-test: "__LocalKeyInner::::get" -> "$$LOCALKEYINNER::::get" +// normalize-stderr-test: "__LocalKeyInner::::get" -> "$$LOCALKEYINNER::::get" #![feature(thread_local)] #![feature(cfg_target_thread_local, thread_local_internals)] @@ -12,10 +12,10 @@ type Foo = std::cell::RefCell; #[cfg(target_thread_local)] #[thread_local] -static __KEY: std::thread::__FastLocalKeyInner = std::thread::__FastLocalKeyInner::new(); +static __KEY: std::thread::__LocalKeyInner = std::thread::__LocalKeyInner::new(); #[cfg(not(target_thread_local))] -static __KEY: std::thread::__OsLocalKeyInner = std::thread::__OsLocalKeyInner::new(); +static __KEY: std::thread::__LocalKeyInner = std::thread::__LocalKeyInner::new(); fn __getit(_: Option<&mut Option>>) -> std::option::Option<&'static Foo> { __KEY.get(Default::default) diff --git a/tests/ui/traits/issue-87558.stderr b/tests/ui/traits/issue-87558.stderr index 494274d8c30..b647f9794bd 100644 --- a/tests/ui/traits/issue-87558.stderr +++ b/tests/ui/traits/issue-87558.stderr @@ -17,6 +17,12 @@ error[E0229]: associated type bindings are not allowed here | LL | impl Fn(&isize) for Error { | ^^^^^^^^^^ associated type not allowed here + | +help: parenthesized trait syntax expands to `Fn<(&isize,), Output=()>` + --> $DIR/issue-87558.rs:3:6 + | +LL | impl Fn(&isize) for Error { + | ^^^^^^^^^^ error: aborting due to 3 previous errors diff --git a/tests/ui/traits/new-solver/canonical-int-var-eq-in-response.rs b/tests/ui/traits/new-solver/canonical-int-var-eq-in-response.rs new file mode 100644 index 00000000000..3f7316a2279 --- /dev/null +++ b/tests/ui/traits/new-solver/canonical-int-var-eq-in-response.rs @@ -0,0 +1,21 @@ +// check-pass + +trait Mirror { + type Assoc; +} + +impl Mirror for T { + type Assoc = T; +} + +trait Test {} +impl Test for i64 {} +impl Test for u64 {} + +fn mirror_me(t: T, s: ::Assoc) where ::Assoc: Test {} + +fn main() { + let mut x = 0; + mirror_me(x, 1); + x = 1i64; +} diff --git a/tests/ui/typeck/lazy-norm/cast-checks-handling-projections.rs b/tests/ui/traits/new-solver/cast-checks-handling-projections.rs similarity index 78% rename from tests/ui/typeck/lazy-norm/cast-checks-handling-projections.rs rename to tests/ui/traits/new-solver/cast-checks-handling-projections.rs index 5ff567cd07c..3b261062f78 100644 --- a/tests/ui/typeck/lazy-norm/cast-checks-handling-projections.rs +++ b/tests/ui/traits/new-solver/cast-checks-handling-projections.rs @@ -1,5 +1,5 @@ // compile-flags: -Ztrait-solver=next -// known-bug: unknown +// check-pass fn main() { (0u8 + 0u8) as char; diff --git a/tests/ui/typeck/lazy-norm/equating-projection-cyclically.rs b/tests/ui/traits/new-solver/equating-projection-cyclically.rs similarity index 100% rename from tests/ui/typeck/lazy-norm/equating-projection-cyclically.rs rename to tests/ui/traits/new-solver/equating-projection-cyclically.rs diff --git a/tests/ui/typeck/lazy-norm/equating-projection-cyclically.stderr b/tests/ui/traits/new-solver/equating-projection-cyclically.stderr similarity index 100% rename from tests/ui/typeck/lazy-norm/equating-projection-cyclically.stderr rename to tests/ui/traits/new-solver/equating-projection-cyclically.stderr diff --git a/tests/ui/traits/new-solver/int-var-alias-eq.rs b/tests/ui/traits/new-solver/int-var-alias-eq.rs new file mode 100644 index 00000000000..2da387db4a9 --- /dev/null +++ b/tests/ui/traits/new-solver/int-var-alias-eq.rs @@ -0,0 +1,18 @@ +// check-pass +// compile-flags: -Ztrait-solver=next + +// HIR typeck ends up equating `<_#0i as Add>::Output == _#0i`. +// Want to make sure that we emit an alias-eq goal for this, +// instead of treating it as a type error and bailing. + +fn test() { + // fallback + let x = 1 + 2; +} + +fn test2() -> u32 { + // expectation from return ty + 1 + 2 +} + +fn main() {} diff --git a/tests/ui/traits/new-solver/lazy-nested-obligations-1.rs b/tests/ui/traits/new-solver/lazy-nested-obligations-1.rs new file mode 100644 index 00000000000..af00cbb3ba8 --- /dev/null +++ b/tests/ui/traits/new-solver/lazy-nested-obligations-1.rs @@ -0,0 +1,13 @@ +// check-pass +// compile-flags: -Ztrait-solver=next +// Issue 94358 + +fn foo(_: C) +where + for <'a> &'a C: IntoIterator, + for <'a> <&'a C as IntoIterator>::IntoIter: ExactSizeIterator, +{} + +fn main() { + foo::<_>(vec![true, false]); +} diff --git a/tests/ui/traits/new-solver/lazy-nested-obligations-2.rs b/tests/ui/traits/new-solver/lazy-nested-obligations-2.rs new file mode 100644 index 00000000000..32addd829dc --- /dev/null +++ b/tests/ui/traits/new-solver/lazy-nested-obligations-2.rs @@ -0,0 +1,23 @@ +// check-pass +// compile-flags: -Ztrait-solver=next +// Issue 95863 + +pub trait With { + type F; +} + +impl With for i32 { + type F = fn(&str); +} + +fn f(_: &str) {} + +fn main() { + let _: V = V(f); + pub struct V(::F); + + pub enum E3 { + Var(::F), + } + let _: E3 = E3::Var(f); +} diff --git a/tests/ui/traits/new-solver/lazy-nested-obligations-3.rs b/tests/ui/traits/new-solver/lazy-nested-obligations-3.rs new file mode 100644 index 00000000000..baf39957240 --- /dev/null +++ b/tests/ui/traits/new-solver/lazy-nested-obligations-3.rs @@ -0,0 +1,38 @@ +// check-pass +// compile-flags: -Ztrait-solver=next +// Issue 96750 + +use std::marker::PhantomData; + +trait AsyncFn { + type Output; +} +trait RequestFamily { + type Type<'a>; +} +trait Service {} + +struct MyFn; +impl AsyncFn for MyFn { + type Output = (); +} + +impl RequestFamily for String { + type Type<'a> = String; +} + +struct ServiceFromAsyncFn(F, PhantomData); + +impl Service for ServiceFromAsyncFn +where + Req: RequestFamily, + F: AsyncFn, + F: for<'a> AsyncFn, Output = O>, +{ +} + +fn assert_service() -> impl Service { + ServiceFromAsyncFn(MyFn, PhantomData) +} + +fn main() {} diff --git a/tests/ui/traits/new-solver/nested-obligations-with-bound-vars-gat.rs b/tests/ui/traits/new-solver/nested-obligations-with-bound-vars-gat.rs new file mode 100644 index 00000000000..92bad959095 --- /dev/null +++ b/tests/ui/traits/new-solver/nested-obligations-with-bound-vars-gat.rs @@ -0,0 +1,43 @@ +// check-pass +// compile-flags: -Ztrait-solver=next +// Issue 96230 + +use std::fmt::Debug; + +trait Classic { + type Assoc; +} + +trait Gat { + type Assoc<'a>; +} + +struct Foo; + +impl Classic for Foo { + type Assoc = (); +} + +impl Gat for Foo { + type Assoc<'i> = (); +} + +fn classic_debug(_: T) +where + T::Assoc: Debug, +{ +} + +fn gat_debug(_: T) +where + for<'a> T::Assoc<'a>: Debug, +{ +} + +fn main() { + classic_debug::(Foo); // fine + classic_debug(Foo); // fine + + gat_debug::(Foo); // fine + gat_debug(Foo); // boom +} diff --git a/tests/ui/traits/new-solver/normalize-param-env-1.rs b/tests/ui/traits/new-solver/normalize-param-env-1.rs new file mode 100644 index 00000000000..b02a5d62330 --- /dev/null +++ b/tests/ui/traits/new-solver/normalize-param-env-1.rs @@ -0,0 +1,40 @@ +// check-pass +// compile-flags: -Ztrait-solver=next +// Issue 108933 + +trait Add { + type Sum; +} + +impl Add<()> for () { + type Sum = (); +} + +type Unit = <() as Add<()>>::Sum; + +trait Trait { + type Output; +} + +fn f() +where + T: Trait<()>, + >::Output: Sized, +{ +} + +fn g() +where + T: Trait, + >::Output: Sized, +{ +} + +fn h() +where + T: Trait<()>, + >::Output: Sized, +{ +} + +fn main() {} diff --git a/tests/ui/traits/new-solver/normalize-param-env-2.rs b/tests/ui/traits/new-solver/normalize-param-env-2.rs new file mode 100644 index 00000000000..7c2cebdd200 --- /dev/null +++ b/tests/ui/traits/new-solver/normalize-param-env-2.rs @@ -0,0 +1,26 @@ +// check-pass +// compile-flags: -Ztrait-solver=next +// Issue 92505 + +trait A { + type I; + + fn f() + where + Self::I: A, + { + } +} + +impl A for () { + type I = (); + + fn f() + where + Self::I: A, + { + <() as A>::f(); + } +} + +fn main() {} diff --git a/tests/ui/traits/new-solver/normalize-param-env-3.rs b/tests/ui/traits/new-solver/normalize-param-env-3.rs new file mode 100644 index 00000000000..ce2974b2a16 --- /dev/null +++ b/tests/ui/traits/new-solver/normalize-param-env-3.rs @@ -0,0 +1,32 @@ +// check-pass +// compile-flags: -Ztrait-solver=next +// Issue 100177 + +trait GenericTrait {} + +trait Channel: GenericTrait { + type T; +} + +trait Sender { + type Msg; + + fn send() + where + C: Channel; +} + +impl Sender for T { + type Msg = (); + + fn send() + where + C: Channel, + { + } +} + +// This works +fn foo(ch: C) where C: Channel {} + +fn main() {} diff --git a/tests/ui/traits/new-solver/runaway-impl-candidate-selection.rs b/tests/ui/traits/new-solver/runaway-impl-candidate-selection.rs new file mode 100644 index 00000000000..1dca86d3630 --- /dev/null +++ b/tests/ui/traits/new-solver/runaway-impl-candidate-selection.rs @@ -0,0 +1,15 @@ +// compile-flags: -Ztrait-solver=next + +// In the new solver, we are trying to select `::Item: Debug`, +// which, naively can be unified with every impl of `Debug` if we're not careful. +// This test makes sure that we treat projections with inference var substs as +// placeholders during fast reject. + +fn iter() -> ::Item { + todo!() +} + +fn main() { + println!("{:?}", iter::<_>()); + //~^ ERROR type annotations needed +} diff --git a/tests/ui/traits/new-solver/runaway-impl-candidate-selection.stderr b/tests/ui/traits/new-solver/runaway-impl-candidate-selection.stderr new file mode 100644 index 00000000000..47004821ad7 --- /dev/null +++ b/tests/ui/traits/new-solver/runaway-impl-candidate-selection.stderr @@ -0,0 +1,9 @@ +error[E0282]: type annotations needed + --> $DIR/runaway-impl-candidate-selection.rs:13:22 + | +LL | println!("{:?}", iter::<_>()); + | ^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `iter` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/transmutability/issue-101739-1.rs b/tests/ui/transmutability/issue-101739-1.rs index bcb8b158edf..2b966609108 100644 --- a/tests/ui/transmutability/issue-101739-1.rs +++ b/tests/ui/transmutability/issue-101739-1.rs @@ -6,7 +6,7 @@ mod assert { pub fn is_transmutable() where Dst: BikeshedIntrinsicFrom, //~ ERROR cannot find type `Dst` in this scope - //~^ ERROR mismatched types + //~^ the constant `ASSUME_ALIGNMENT` is not of type `Assume` { } } diff --git a/tests/ui/transmutability/issue-101739-1.stderr b/tests/ui/transmutability/issue-101739-1.stderr index 7c6b533ef5f..f0fa93722b8 100644 --- a/tests/ui/transmutability/issue-101739-1.stderr +++ b/tests/ui/transmutability/issue-101739-1.stderr @@ -4,13 +4,15 @@ error[E0412]: cannot find type `Dst` in this scope LL | Dst: BikeshedIntrinsicFrom, | ^^^ not found in this scope -error[E0308]: mismatched types - --> $DIR/issue-101739-1.rs:8:50 +error: the constant `ASSUME_ALIGNMENT` is not of type `Assume` + --> $DIR/issue-101739-1.rs:8:14 | LL | Dst: BikeshedIntrinsicFrom, - | ^^^^^^^^^^^^^^^^ expected `Assume`, found `bool` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: required by a bound in `BikeshedIntrinsicFrom` + --> $SRC_DIR/core/src/mem/transmutability.rs:LL:COL error: aborting due to 2 previous errors -Some errors have detailed explanations: E0308, E0412. -For more information about an error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0412`. diff --git a/tests/ui/typeck/issue-105946.rs b/tests/ui/typeck/issue-105946.rs index bf01751d5f6..f53f31138f8 100644 --- a/tests/ui/typeck/issue-105946.rs +++ b/tests/ui/typeck/issue-105946.rs @@ -1,12 +1,10 @@ fn digit() -> str { - return {}; - //~^ ERROR: mismatched types [E0308] + return {}; + //~^ ERROR: mismatched types [E0308] } fn main() { - let [_y..] = [box 1, box 2]; + let [_y..] = [Box::new(1), Box::new(2)]; //~^ ERROR: cannot find value `_y` in this scope [E0425] //~| ERROR: `X..` patterns in slices are experimental [E0658] - //~| ERROR: box expression syntax is experimental; you can call `Box::new` instead [E0658] - //~| ERROR: box expression syntax is experimental; you can call `Box::new` instead [E0658] //~| ERROR: pattern requires 1 element but array has 2 [E0527] } diff --git a/tests/ui/typeck/issue-105946.stderr b/tests/ui/typeck/issue-105946.stderr index d803de4df47..26c3b7fbc84 100644 --- a/tests/ui/typeck/issue-105946.stderr +++ b/tests/ui/typeck/issue-105946.stderr @@ -1,49 +1,31 @@ error[E0425]: cannot find value `_y` in this scope --> $DIR/issue-105946.rs:6:10 | -LL | let [_y..] = [box 1, box 2]; +LL | let [_y..] = [Box::new(1), Box::new(2)]; | ^^ not found in this scope error[E0658]: `X..` patterns in slices are experimental --> $DIR/issue-105946.rs:6:10 | -LL | let [_y..] = [box 1, box 2]; +LL | let [_y..] = [Box::new(1), Box::new(2)]; | ^^^^ | = note: see issue #67264 for more information = help: add `#![feature(half_open_range_patterns_in_slices)]` to the crate attributes to enable -error[E0658]: box expression syntax is experimental; you can call `Box::new` instead - --> $DIR/issue-105946.rs:6:19 - | -LL | let [_y..] = [box 1, box 2]; - | ^^^^^ - | - = note: see issue #49733 for more information - = help: add `#![feature(box_syntax)]` to the crate attributes to enable - -error[E0658]: box expression syntax is experimental; you can call `Box::new` instead - --> $DIR/issue-105946.rs:6:26 - | -LL | let [_y..] = [box 1, box 2]; - | ^^^^^ - | - = note: see issue #49733 for more information - = help: add `#![feature(box_syntax)]` to the crate attributes to enable - error[E0308]: mismatched types - --> $DIR/issue-105946.rs:2:10 + --> $DIR/issue-105946.rs:2:12 | -LL | return {}; - | ^^ expected `str`, found `()` +LL | return {}; + | ^^ expected `str`, found `()` error[E0527]: pattern requires 1 element but array has 2 --> $DIR/issue-105946.rs:6:9 | -LL | let [_y..] = [box 1, box 2]; +LL | let [_y..] = [Box::new(1), Box::new(2)]; | ^^^^^^ expected 2 elements -error: aborting due to 6 previous errors +error: aborting due to 4 previous errors Some errors have detailed explanations: E0308, E0425, E0527, E0658. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/typeck/issue-87935-unsized-box-expr.rs b/tests/ui/typeck/issue-87935-unsized-box-expr.rs deleted file mode 100644 index cd2a82074ed..00000000000 --- a/tests/ui/typeck/issue-87935-unsized-box-expr.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![feature(box_syntax)] -// Box expression needs to be movable, and hence has to be of a Sized type. -fn main() { - let _x: Box<[u32]> = box { loop {} }; - //~^ ERROR: the size for values of type `[u32]` cannot be known at compilation time - - // Check that a deduced size does not cause issues. - let _y: Box<[u32]> = box []; - let _z: Box<[u32; 0]> = box { loop {} }; -} diff --git a/tests/ui/typeck/issue-87935-unsized-box-expr.stderr b/tests/ui/typeck/issue-87935-unsized-box-expr.stderr deleted file mode 100644 index 9ff822352a1..00000000000 --- a/tests/ui/typeck/issue-87935-unsized-box-expr.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0277]: the size for values of type `[u32]` cannot be known at compilation time - --> $DIR/issue-87935-unsized-box-expr.rs:4:30 - | -LL | let _x: Box<[u32]> = box { loop {} }; - | ^^^^^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `[u32]` - = note: the type of a box expression must have a statically known size - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/typeck/lazy-norm/cast-checks-handling-projections.stderr b/tests/ui/typeck/lazy-norm/cast-checks-handling-projections.stderr deleted file mode 100644 index 6b09ccd5214..00000000000 --- a/tests/ui/typeck/lazy-norm/cast-checks-handling-projections.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0271]: type mismatch resolving `char == ::Output` - --> $DIR/cast-checks-handling-projections.rs:5:5 - | -LL | (0u8 + 0u8) as char; - | ^^^^^^^^^^^ types differ - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/unpretty/box.rs b/tests/ui/unpretty/box.rs new file mode 100644 index 00000000000..81f5c88d790 --- /dev/null +++ b/tests/ui/unpretty/box.rs @@ -0,0 +1,9 @@ +// compile-flags: -Zunpretty=hir +// check-pass + +#![feature(stmt_expr_attributes, rustc_attrs)] + +fn main() { + let _ = #[rustc_box] + Box::new(1); +} diff --git a/tests/ui/unpretty/box.stdout b/tests/ui/unpretty/box.stdout new file mode 100644 index 00000000000..0c6e012e698 --- /dev/null +++ b/tests/ui/unpretty/box.stdout @@ -0,0 +1,14 @@ +// compile-flags: -Zunpretty=hir +// check-pass + +#![feature(stmt_expr_attributes, rustc_attrs)] +#[prelude_import] +use ::std::prelude::rust_2015::*; +#[macro_use] +extern crate std; + +fn main() { + let _ = + #[rustc_box] + Box::new(1); + } diff --git a/triagebot.toml b/triagebot.toml index f8752fdcaca..a81f66fe168 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -185,7 +185,7 @@ trigger_files = [ "src/tools/x", "configure", "Cargo.toml", - "config.toml.example", + "config.example.toml", "src/stage0.json" ] @@ -430,7 +430,7 @@ message = "The Miri subtree was changed" cc = ["@rust-lang/miri"] [mentions."src/tools/rust-analyzer"] -cc = ["@rust-lang/wg-rls-2"] +cc = ["@rust-lang/rust-analyzer"] [mentions."src/tools/rustfmt"] cc = ["@rust-lang/rustfmt"] @@ -501,6 +501,7 @@ compiler-team-contributors = [ "@TaKO8Ki", "@Nilstrieb", "@WaffleLapkin", + "@b-naber", ] compiler = [ "compiler-team",